본문 바로가기
개발(Development)/Next.js 13

[Next.js 13] head 태그 - title 및 meta 데이터 추가/수정 방법(Metadata API)

by 카레유 2023. 8. 29.

# Next.js 13 - <head> 메타 데이터 추가/수정 방법 (Metadata API)

검색최적화(SEO)를 위해서는 HTML페이지의 <head> 태그에 메타 데이터를 잘 정의해주어야 한다.

 

그러나 직접 루트 레이아웃(`/app/layout.js`) 파일의 html 태그에 head 태그를 삽입하고 수정해서는 안 된다.


공식 문서에도 다음과 같이 쓰여있다. (not에 볼드체 처리가 되어 있다.)

 

"You should not manually add <head> tags such as <title> and <meta> to root layouts. Instead, you should use the Metadata API."

 

따라서 Next.js가 제공하는 MetaData API를 이용해 meta data를 추가 정의해야 한다.

MetaData API를 이용하면 Next.js 에서 추가적인 최적화 기능까지 제공된다.

 

기본적인 MetaData API는 아래의 2가지가 제공된다.( layout.js 및 page.js 파일에서 사용 가능)

 

  • metadata 객체 : 변하지 않는 정적 메타 데이터(Static meta data)를 정의할 때 사용한다.
  • generateMetadata() 함수: 서버 등에서 매번 불러와야 하는 하는 동적 메타 데이터(Dynamic meta data)를 정의할 때 사용한다.

 

import { Metadata } from 'next'
 
// Static metadata
export const metadata: Metadata = {
  title: '...',
}
 
// Dynamic metadata
export async function generateMetadata({ params }) {
  return {
    title: '...',
  }
}

 

단, Server Component 로 정의된 layout.js 및 page.js 파일에서만 사용 가능하며,

또한 동일한 경로( Route segment)에서 metadata 객체와 generateMetadata()를 동시에 둘다 사용(export)하는 건 불가하다.


# metadata 객체 (Static Metadata)

변하지 않는 title, description, keyword 값을 정의할 때는

page.js 나 layout.js 파일 내부에서 metadata 객체를 정의하여 export해주면 된다.

 

`layout.tsx`파일 / `page.tsx` 파일:

import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'MyTitle',
  description: 'myDescription',
  keyword: 'keyword1, keyword2'
};

 

MetaData 타입의 필드값은 공식 문서를 참조.

 

# generateMetadata() 함수 (Dynamic Metadata)

접속하는 URL, 쿼리스트링에 따라 바뀌는 값이나, 수시로 변경되는 데이터 베이스 값을 기반으로 meta 데이터를 정의해야 하는 경우도 있다.

 

이처럼 상황에 따라 변하는 데이터를 기반으로 title, description, keyword 등을 정의할 때는

page.js나 layout.js 파일 내부에서 generateMetadata() 함수를 정의하여 export 해줘야 한다.

 

  1. page.js나 layout.js 파일 내부에 generateMetadata() 함수를 정의하여 export 하고,
  2. 함수 내부에서는 param(다이나믹 라우트 경로), searchParams(쿼리 스트링)을 Props으로 받아서
  3. 메타 데이터 객체를 만들어 return 해주면 된다.

 

`app/products/[id]/page.tsx` 파일:

import { Metadata } from 'next';

type Props = {
  params: { id: string };
  searchParams: { [key: string]: string | string[] | undefined };
};

// async 함수로 정의 가능!
export async function generateMetadata({ params, searchParams }: Props): Promise<Metadata> {
  // 다이나믹 라우팅 접속 경로 [id] 파악
  const id = params.id;

  // 함수 내부에서 바로 fetch 호출 가능
  const product = await fetch(`https://.../${id}`).then((res) => res.json());

  // 접속할 때마다 변하는 데이터로 만든 metadata 객체 반환
  return {
    title: product.title,
  };
}

export default function Page({ params, searchParams }: Props) {
  // ...
}

 

generateMetadata() 함수의 특징

  • async 함수로 정의하여, 내부에서 await fetch() 호출도 가능하다.
  • generateMetadata(), generateStaticParams(), Layout, Page 및 Server Component 에서 동일한 경로의 fetch() 요청이 여러 번 발생하는 경우, Request Memoization을 통해 자동으로 중복 제거 처리되므로 성능에 영향을 미치지 않는다.(마음껏 여러번 써도 된다)
  • generateMetaData() 함수 내부에서는 redirect() 및 notFound() 호출도 가능하다.

 

generateMetadata() 함수의 파라미터 

  • props
    • params: 다이나믹 라우팅(Dynamic Route) 접속 경로
    • searchParams: 쿼리 스트링. (단, page.js 에 정의된 generateMetadata() 함수에서만 취득 가능하다)

이 외에 parent 파라미터도 존재하며, 더 상세한 내용은 공식문서 참조

 

generateMetadata() 함수의 리턴

generateMetadata()함수는 최종적으로 한 개 이상의 필드값을 갖는 Metadata 객체를 반환해야 한다.


# meta data 우선 순위

metadata는 page.js 파일은 물론, 중첩 적용된 여러 layout.js 파일에서 정의할 수 있다.

이렇게 여러 파일들에 적용된 meta data는 다음과 같은 우선순위로 적용된다.

 

  1. 페이지(page.js) : 우선 순위 가장 높음!
  2. 중첩 레이아웃(nested layout)
  3. 루트 레이아웃(root layout) : 우선 순위 가장 낮음!

 

예를 들어,

- 루트 레이아웃에서 title을 "카레유 짱" 이라고 정의 했어도,

- 페이지(page.js)에서 title을 "카레유 완전 짱" 이라고 정의하면, 더 높은 우선순위로 적용된다.

 

- 하지만 만약 루트 레이아웃에만 title을 "카레유 짱" 이라고 적용하고,

- 페이지에선 아무 것도 적용하지 않았다면,

- 루트레이아웃에 정의한 "카레유 짱"이 title에 적용될 것이다.


# 주의 사항

Server Component 로 정의된 layout.js 및 page.js 파일에서만 사용 가능하다.

동일한 경로( Route segment)에서 metadata 객체와 generateMetadata()를 동시에 둘다 사용(export)하는 건 불가하다.


Next.js 공식 문서의 MetaData API Reference 를 참고하여 정리한 글이다.

 

 

댓글