Next js SEO 다이나믹(동적) 페이지 메타데이터 생성하기 (Feat. generateMetadata)

Next.js에서 동적 페이지의 메타데이터를 생성하는 방법을 알아보겠습니다.


Summary

이전에 Next js를 사용한 SEO에 대한 글을 작성했었습니다.

이번에는 Next js에서 제공하는 generateMetadata 함수를 사용하여 다이나믹(동적) 페이지의 메타데이터를 생성하는 방법을 알아보겠습니다.

해당 페이지의 설명은 Next.js 최신 버전인 App Router를 기준으로 작성되었습니다.

목차


기존 metadata

기존 metadata는 next js에서 제공하는 내보내기 상수 변수에 객체를 정적으로 값을 할당하여 사용합니다.

export const metadata: Metadata = {
  title: 'Next js SEO 가이드',
  description: 'Next.js에서 SEO를 설정하는 방법을 알아보겠습니다.',
  // ... 생략
};

이렇게 내보내기 객체에서 값을 할당하면 해당 페이지의 메타데이터를 설정할 수 있습니다.

하지만 이 방법은 각 page나 layout에서 정적인 값을 사용하기 때문에 동적인 페이지에서는 사용하기 어렵습니다.

이유는 동적 페이지에는 여러 페이지가 렌더링 되기때문에 각 페이지마다 다른 메타데이터를 설정해야 하는데, 정적인 값으로는 이를 해결할 수 없습니다.

위 metadata 방식을 사용하면 모든 동적 페이지에서 동일한 메타데이터가 적용되기 때문에 SEO에 좋지 않습니다.

이런 문제를 해결하기 위해 Next.js에서는 generateMetadata 함수를 제공합니다.


generatemetadata 사용전 유의점

generateMetadata 함수를 사용하기 전에는 다음과 같은 유의점이 있습니다.

generateMetadata는 서버 구성요소에서만 사용할 수 있습니다.

Next.js는 기본적으로 서버 구성요소입니다. 하지만 클라이언트 구성요소, 즉 "use client"에서는 사용할 수 없습니다.

해당 다이나믹 폴더 내부의 [id]/page.tsx에서 사용하는 것을 추천 드립니다.


generatemetadata 사용하기

generateMetadata 함수는 다음과 같이 사용할 수 있습니다.

app/[id]/page.tsx
// next js 공식 문서 예시 코드
 
import { Metadata, ResolvingMetadata } from 'next';
 
type Props = {
  params: { id: string };
  searchParams: { [key: string]: string | string[] | undefined };
};
 
export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  // params에서 id 추출
  const id = params.id;
 
  // fetch data
  const product = await fetch(`https://.../${id}`).then((res) => res.json());
 
  // 선택적으로 상위 메타데이터에 액세스하고 확장(대체하지 않음)
  const previousImages = (await parent).openGraph?.images || [];
 
  return {
    title: product.title,
    description: product.description,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  };
}
 
export default function Page({ params, searchParams }: Props) {
  const id = params.id;
  const product = await fetch(`https://.../${id}`).then((res) => res.json());
 
  const pageData = product.find((p) => p.id === id);
 
  if (!pageData) notFound();
 
  return (
    <div>
      <h1>내용</h1>
    </div>
  );
}

위 코드에서 generateMetadata 함수는 다음과 같은 인자를 받습니다.

  • params: 페이지의 동적 경로 매개변수
  • searchParams: 페이지의 동적 쿼리 매개변수
  • parent: 상위 메타데이터

이 함수는 페이지가 렌더링 될 때마다 호출되며, 페이지의 메타데이터를 동적으로 생성할 수 있습니다.

위 코드에서는 fetch를 사용하여 동적 데이터를 가져와 메타데이터를 생성하고 있습니다.

이렇게 generateMetadata 함수를 사용하면 동적 페이지에서도 각 페이지마다 다른 메타데이터를 설정할 수 있습니다.

이렇게 설정된 메타데이터는 페이지가 렌더링 될 때마다 호출되기 때문에 페이지마다 다른 메타데이터가 적용됩니다.

위 코드에서 만약 다른 파일에서 fetch 함수를 사용한다면 다음과 같이 fetch 함수를 따로 분리하여 사용할 수 있습니다.

getProduct.ts
// 예시
export async function fetchProduct(id: string) {
  return fetch(`https://.../${id}`).then((res) => res.json());
}

그리고 fetch 함수를 사용하는 코드는 다음과 같이 변경할 수 있습니다.

app/[id]/page.tsx
import { fetchProduct } from './getProduct';
 
export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  const id = params.id;
  const product = await fetchProduct(id);
 
  const previousImages = (await parent).openGraph?.images || [];
 
  return {
    title: product.title,
    description: product.description,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  };
}

파일을 분리하는 것은 코드의 가독성을 높이고, 재사용성을 높일 수 있습니다.

generateMetadata는 기본 metadata에서 제공하는 대부분의 모든 속성을 사용할 수 있습니다.

여기서 눈여겨 볼 점은 openGraph 속성 및 twitter 속성입니다.

openGraph 속성과 twitter 속성의 내용은 같기 때문에 openGraph 속성을 설명하겠습니다.

SEO를 설정하면서 각각의 페이지의 openGraph 속성과 twitter 속성을 다르게 설정하고 싶을 수 있습니다.

만약 가져오는 데이터에서 썸네일 이미지가 있다면, 해당 이미지를 사용하고, 없다면 기본 이미지를 사용하고 싶을 수 있습니다.

next js generateMetadata 함수의 인자로 parent를 사용하면 상위 메타데이터를 가져올 수 있습니다.

[id]/page.tsx
export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  const id = params.id;
  const product = await fetchProduct(id);
 
  const previousImages = (await parent).openGraph?.images || [];
 
  return {
    title: product.title,
    description: product.description,
    openGraph: {
      images: [product.thumbnail, ...previousImages],
    },
  };
}

위 코드는 만약 가져오는 데이터에서 썸네일 이미지가 있다면, 해당 이미지를 사용하고, 없다면 기본 이미지를 사용하도록 설정한 코드입니다.


동적인 openGraph 및 twitter 이미지 생성하기

이미 동적인 이미지를 생성하여 사용하는 방법을 제가 작성한 글을 참고하시면 됩니다.

`Next js dynamic opengraph 및 twitter card 이미지 생성하기`

마치며

이번 글에서는 Next.js에서 동적 페이지의 메타데이터를 생성하는 방법을 알아보았습니다.

generateMetadata 함수를 사용하면 동적 페이지에서도 각 페이지마다 다른 메타데이터를 설정할 수 있습니다.

이를 통해 SEO에 더욱 효과적으로 접근할 수 있습니다.

참고 문서

`Next.js 공식 문서 - generateMetadata`

관련 태그