generateMetadata

이 페이지는 generateMetadata 및 정적 메타데이터 객체를 사용한 구성 기반 메타데이터 옵션을 모두 다룹니다.

layout.tsx | page.tsx
import type { Metadata } from 'next'
 
// 정적 메타데이터
export const metadata: Metadata = {
  title: '...',
}
 
// 동적 메타데이터
export async function generateMetadata({ params }) {
  return {
    title: '...',
  }
}
layout.js | page.js
// 정적 메타데이터
export const metadata = {
  title: '...',
}
 
// 동적 메타데이터
export async function generateMetadata({ params }) {
  return {
    title: '...',
  }
}

알아두면 좋은 정보:

  • metadata 객체와 generateMetadata 함수는 서버 컴포넌트에서만 지원됩니다.
  • 동일한 경로 세그먼트에서 metadata 객체와 generateMetadata 함수를 모두 내보낼 수 없습니다.

metadata 객체

정적 메타데이터를 정의하려면 layout.js 또는 page.js 파일에서 Metadata 객체를 내보내세요.

layout.tsx | page.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: '...',
  description: '...',
}
 
export default function Page() {}
layout.js | page.js
export const metadata = {
  title: '...',
  description: '...',
}
 
export default function Page() {}

지원되는 옵션의 전체 목록은 메타데이터 필드를 참조하세요.

generateMetadata 함수

동적 정보(예: 현재 경로 매개변수, 외부 데이터 또는 상위 세그먼트의 metadata)에 의존하는 동적 메타데이터는 generateMetadata 함수를 내보내어 설정할 수 있으며, 이 함수는 Metadata 객체를 반환합니다.

app/products/[id]/page.tsx
import type { 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> {
  // 경로 매개변수 읽기
  const id = params.id
 
  // 데이터 가져오기
  const product = await fetch(`https://.../${id}`).then((res) => res.json())
 
  // 선택적으로 상위 메타데이터를 접근하고 확장(대체하지 않음)
  const previousImages = (await parent).openGraph?.images || []
 
  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  }
}
 
export default function Page({ params, searchParams }: Props) {}
app/products/[id]/page.js
export async function generateMetadata({ params, searchParams }, parent) {
  // 경로 매개변수 읽기
  const id = params.id
 
  // 데이터 가져오기
  const product = await fetch(`https://.../${id}`).then((res) => res.json())
 
  // 선택적으로 상위 메타데이터를 접근하고 확장(대체하지 않음)
  const previousImages = (await parent).openGraph?.images || []
 
  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  }
}
 
export default function Page({ params, searchParams }) {}

매개변수

generateMetadata 함수는 다음 매개변수를 수락합니다:

  • props - 현재 경로의 매개변수를 포함하는 객체:

    • params - 루트 세그먼트부터 generateMetadata가 호출된 세그먼트까지의 동적 경로 매개변수 객체를 포함하는 객체입니다. 예시:

      경로URLparams
      app/shop/[slug]/page.js/shop/1{ slug: '1' }
      app/shop/[tag]/[item]/page.js/shop/1/2{ tag: '1', item: '2' }
      app/shop/[...slug]/page.js/shop/1/2{ slug: ['1', '2'] }
    • searchParams - 현재 URL의 검색 매개변수 (opens in a new tab)를 포함하는 객체입니다. 예시:

      URLsearchParams
      /shop?a=1{ a: '1' }
      /shop?a=1&b=2{ a: '1', b: '2' }
      /shop?a=1&a=2{ a: ['1', '2'] }
  • parent - 상위 경로 세그먼트에서 해결된 메타데이터의 프로미스입니다.

반환 값

generateMetadata는 하나 이상의 메타데이터 필드를 포함하는 Metadata 객체를 반환해야 합니다.

알아두면 좋은 정보:

  • 메타데이터가 런타임 정보에 의존하지 않는 경우, generateMetadata 대신 정적 metadata 객체를 사용하여 정의해야 합니다.
  • fetch 요청은 generateMetadata, generateStaticParams, 레이아웃, 페이지 및 서버 컴포넌트 간에 동일한 데이터를 위해 자동으로 메모이제이션됩니다. fetch가 사용 불가능한 경우, React cache를 사용할 수 있습니다.
  • searchParamspage.js 세그먼트에서만 사용할 수 있습니다.
  • redirect()notFound() Next.js 메서드는 generateMetadata 내에서도 사용할 수 있습니다.

메타데이터 필드

title

title 속성은 문서의 제목을 설정하는 데 사용됩니다. 단순한 문자열 또는 선택적인 템플릿 객체로 정의할 수 있습니다.

문자열

layout.js | page.js
export const metadata = {
  title: 'Next.js',
}
<head> output
<title>Next.js</title>

템플릿 객체

app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: {
    template: '...',
    default: '...',
    absolute: '...',
  },
}
app/layout.js
export const metadata = {
  title: {
    default: '...',
    template: '...',
    absolute: '...',
  },
}
기본값

title.defaulttitle을 정의하지 않은 하위 경로 세그먼트에 대체 제목을 제공하는 데 사용할 수 있습니다.

app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: {
    default: 'Acme',
  },
}
app/about/page.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {}
 
// 출력: <title>Acme</title>
템플릿

title.template하위 경로 세그먼트에서 정의된 title에 접두사 또는 접미사를 추가하는 데 사용할 수 있습니다.

app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: {
    template: '%s | Acme',
    default: 'Acme', // 템플릿을 생성할 때 기본값이 필요합니다
  },
}
app/layout.js
export const metadata = {
  title: {
    template: '%s | Acme',
    default: '
 
Acme', // 템플릿을 생성할 때 기본값이 필요합니다
  },
}
app/about/page.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'About',
}
 
// 출력: <title>About | Acme</title>
app/about/page.js
export const metadata = {
  title: 'About',
}
 
// 출력: <title>About | Acme</title>

알아두면 좋은 정보:

  • title.template하위 경로 세그먼트에 적용되며, 정의된 세그먼트에는 적용되지 않습니다. 따라서:

    • title.template를 추가할 때 title.default필수입니다.
    • layout.js에 정의된 title.template는 동일한 경로 세그먼트의 page.js에 정의된 title에 적용되지 않습니다.
    • page.js에 정의된 title.template는 효과가 없습니다. 페이지는 항상 종단 세그먼트(하위 경로 세그먼트가 없음)이기 때문입니다.
  • 경로에 title 또는 title.default가 정의되지 않은 경우, title.template효과가 없습니다.

절대값

title.absolute는 상위 세그먼트에 설정된 title.template무시하는 제목을 제공하는 데 사용할 수 있습니다.

app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: {
    template: '%s | Acme',
  },
}
app/layout.js
export const metadata = {
  title: {
    template: '%s | Acme',
  },
}
app/about/page.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: {
    absolute: 'About',
  },
}
 
// 출력: <title>About</title>
app/about/page.js
export const metadata = {
  title: {
    absolute: 'About',
  },
}
 
// 출력: <title>About</title>

알아두면 좋은 정보:

  • layout.js

    • title(문자열) 및 title.default는 하위 세그먼트(자신의 title을 정의하지 않은 세그먼트)의 기본 제목을 정의합니다. 이 제목은 상위 세그먼트의 title.template을 보강합니다.
    • title.absolute는 하위 세그먼트의 기본 제목을 정의합니다. 상위 세그먼트의 title.template을 무시합니다.
    • title.template는 하위 세그먼트를 위한 새 제목 템플릿을 정의합니다.
  • page.js

    • 페이지가 자체 제목을 정의하지 않은 경우 가장 가까운 상위 부모의 해결된 제목이 사용됩니다.
    • title(문자열)은 경로 제목을 정의합니다. 상위 세그먼트의 title.template을 보강합니다.
    • title.absolute는 경로 제목을 정의합니다. 상위 세그먼트의 title.template을 무시합니다.
    • page.jstitle.template는 경로의 종단 세그먼트이기 때문에 효과가 없습니다.

description

layout.js | page.js
export const metadata = {
  description: 'The React Framework for the Web',
}
<head> output
<meta name="description" content="The React Framework for the Web" />

기본 필드

layout.js | page.js
export const metadata = {
  generator: 'Next.js',
  applicationName: 'Next.js',
  referrer: 'origin-when-cross-origin',
  keywords: ['Next.js', 'React', 'JavaScript'],
  authors: [{ name: 'Seb' }, { name: 'Josh', url: 'https://nextjs.org' }],
  creator: 'Jiachi Liu',
  publisher: 'Sebastian Markbåge',
  formatDetection: {
    email: false,
    address: false,
    telephone: false,
  },
}
<head> output
<meta name="application-name" content="Next.js" />
<meta name="author" content="Seb" />
<link rel="author" href="https://nextjs.org" />
<meta name="author" content="Josh" />
<meta name="generator" content="Next.js" />
<meta name="keywords" content="Next.js,React,JavaScript" />
<meta name="referrer" content="origin-when-cross-origin" />
<meta name="color-scheme" content="dark" />
<meta name="creator" content="Jiachi Liu" />
<meta name="publisher" content="Sebastian Markbåge" />
<meta name="format-detection" content="telephone=no, address=no, email=no" />

metadataBase

metadataBase는 완전히 자격 있는 URL이 필요한 metadata 필드에 대해 기본 URL 접두사를 설정하는 편리한 옵션입니다.

  • metadataBase를 사용하면 현재 경로 세그먼트 및 하위 세그먼트에 정의된 URL 기반 metadata 필드가 상대 경로를 사용할 수 있습니다.
  • 필드의 상대 경로는 metadataBase와 함께 구성되어 완전히 자격을 갖춘 URL이 됩니다.
  • 구성되지 않은 경우, metadataBase기본 값으로 자동으로 채워집니다.
layout.js | page.js
export const metadata = {
  metadataBase: new URL('https://acme.com'),
  alternates: {
    canonical: '/',
    languages: {
      'en-US': '/en-US',
      'de-DE': '/de-DE',
    },
  },
  openGraph: {
    images: '/og-image.png',
  },
}
<head> output
<link rel="canonical" href="https://acme.com" />
<link rel="alternate" hreflang="en-US" href="https://acme.com/en-US" />
<link rel="alternate" hreflang="de-DE" href="https://acme.com/de-DE" />
<meta property="og:image" content="https://acme.com/og-image.png" />

알아두면 좋은 정보:

  • metadataBase는 URL 기반 metadata 필드가 모든 경로에 적용되도록 루트 app/layout.js에 설정됩니다.
  • 절대 URL이 필요한 모든 URL 기반 metadata 필드는 metadataBase 옵션으로 구성할 수 있습니다.
  • metadataBase는 하위 도메인(예: https://app.acme.com) 또는 기본 경로(예: https://acme.com/start/from/here)를 포함할 수 있습니다.
  • metadata 필드가 절대 URL을 제공하는 경우 metadataBase는 무시됩니다.
  • URL 기반 metadata 필드에서 상대 경로를 사용하고 metadataBase를 구성하지 않으면 빌드 오류가 발생합니다.
  • Next.js는 metadataBase(예: https://acme.com/)와 상대 필드(예: /path) 사이의 중복 슬래시를 하나의 슬래시(예: https://acme.com/path)로 정규화합니다.

기본 값

구성되지 않은 경우, metadataBase기본 값을 가집니다.

Vercel에서:

  • 프로덕션 배포의 경우 VERCEL_PROJECT_PRODUCTION_URL이 사용됩니다.
  • 미리보기 배포의 경우 VERCEL_BRANCH_URL이 우선 순위를 가지며, 존재하지 않을 경우 VERCEL_URL로 대체됩니다.

이러한 값이 존재하는 경우 기본 값으로 사용되며, 그렇지 않으면 http://localhost:${process.env.PORT || 3000}으로 대체됩니다. 이는 오픈 그래프 이미지가 로컬 빌드 및 Vercel 미리보기 및 프로덕션 배포에서 작동할 수 있도록 합니다. 기본 값을 재정의할 때는 환경 변수를 사용하여 URL을 계산하는 것이 좋습니다. 이를 통해 로컬 개발, 스테이징 및 프로덕션 환경에 대한 URL을 구성할 수 있습니다.

이러한 환경 변수에 대한 자세한 내용은 시스템 환경 변수 (opens in a new tab) 문서를 참조하세요.

URL 구성

URL 구성은 기본 디렉토리 탐색 의미보다 개발자의 의도를 우선합니다.

  • metadataBasemetadata 필드 사이의 후행 슬래시는 정규화됩니다.
  • "절대" 경로는 "상대" 경로로 간주됩니다.

예를 들어, 다음과 같은 metadataBase를 가정합니다:

app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  metadataBase: new URL('https
 
://acme.com'),
}
app/layout.js
export const metadata = {
  metadataBase: new URL('https://acme.com'),
}

다음과 같이 자신의 값을 설정한 metadata 필드는 다음과 같이 해결됩니다:

metadata 필드해결된 URL
/https://acme.com
./https://acme.com
paymentshttps://acme.com/payments
/paymentshttps://acme.com/payments
./paymentshttps://acme.com/payments
../paymentshttps://acme.com/payments
https://beta.acme.com/paymentshttps://beta.acme.com/payments

openGraph

layout.js | page.js
export const metadata = {
  openGraph: {
    title: 'Next.js',
    description: 'The React Framework for the Web',
    url: 'https://nextjs.org',
    siteName: 'Next.js',
    images: [
      {
        url: 'https://nextjs.org/og.png', // 반드시 절대 URL이어야 함
        width: 800,
        height: 600,
      },
      {
        url: 'https://nextjs.org/og-alt.png', // 반드시 절대 URL이어야 함
        width: 1800,
        height: 1600,
        alt: 'My custom alt',
      },
    ],
    videos: [
      {
        url: 'https://nextjs.org/video.mp4', // 반드시 절대 URL이어야 함
        width: 800,
        height: 600,
      },
    ],
    locale: 'en_US',
    type: 'website',
  },
}
<head> output
<meta property="og:title" content="Next.js" />
<meta property="og:description" content="The React Framework for the Web" />
<meta property="og:url" content="https://nextjs.org/" />
<meta property="og:site_name" content="Next.js" />
<meta property="og:locale" content="en_US" />
<meta property="og:image:url" content="https://nextjs.org/og.png" />
<meta property="og:image:width" content="800" />
<meta property="og:image:height" content="600" />
<meta property="og:image:url" content="https://nextjs.org/og-alt.png" />
<meta property="og:image:width" content="1800" />
<meta property="og:image:height" content="1600" />
<meta property="og:image:alt" content="My custom alt" />
<meta property="og:type" content="website" />
layout.js | page.js
export const metadata = {
  openGraph: {
    title: 'Next.js',
    description: 'The React Framework for the Web',
    type: 'article',
    publishedTime: '2023-01-01T00:00:00.000Z',
    authors: ['Seb', 'Josh'],
  },
}
<head> output
<meta property="og:title" content="Next.js" />
<meta property="og:description" content="The React Framework for the Web" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2023-01-01T00:00:00.000Z" />
<meta property="article:author" content="Seb" />
<meta property="article:author" content="Josh" />

알아두면 좋은 정보:

  • 오픈 그래프 이미지에 대해서는 파일 기반 메타데이터 API를 사용하는 것이 더 편리할 수 있습니다. 구성 내보내기를 실제 파일과 동기화할 필요 없이 파일 기반 API가 자동으로 올바른 메타데이터를 생성해줍니다.

robots

layout.tsx | page.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  robots: {
    index: false,
    follow: true,
    nocache: true,
    googleBot: {
      index: true,
      follow: false,
      noimageindex: true,
      'max-video-preview': -1,
      'max-image-preview': 'large',
      'max-snippet': -1,
    },
  },
}
<head> output
<meta name="robots" content="noindex, follow, nocache" />
<meta
  name="googlebot"
  content="index, nofollow, noimageindex, max-video-preview:-1, max-image-preview:large, max-snippet:-1"
/>

icons

알아두면 좋은 정보: 가능하면 아이콘에 대해 파일 기반 메타데이터 API를 사용하는 것이 좋습니다. 구성 내보내기를 실제 파일과 동기화할 필요 없이 파일 기반 API가 자동으로 올바른 메타데이터를 생성해줍니다.

layout.js | page.js
export const metadata = {
  icons: {
    icon: '/icon.png',
    shortcut: '/shortcut-icon.png',
    apple: '/apple-icon.png',
    other: {
      rel: 'apple-touch-icon-precomposed',
      url: '/apple-touch-icon-precomposed.png',
    },
  },
}
<head> output
<link rel="shortcut icon" href="/shortcut-icon.png" />
<link rel="icon" href="/icon.png" />
<link rel="apple-touch-icon" href="/apple-icon.png" />
<link
  rel="apple-touch-icon-precomposed"
  href="/apple-touch-icon-precomposed.png"
/>
layout.js | page.js
export const metadata = {
  icons: {
    icon: [
      { url: '/icon.png' },
      new URL('/icon.png', 'https://example.com'),
      { url: '/icon-dark.png', media: '(prefers-color-scheme: dark)' },
    ],
    shortcut: ['/shortcut-icon.png'],
    apple: [
      { url: '/apple-icon.png' },
      { url: '/apple-icon-x3.png', sizes: '180x180', type: 'image/png' },
    ],
    other: [
      {
        rel: 'apple-touch-icon-precomposed',
        url: '/apple-touch-icon-precomposed.png',
      },
    ],
  },
}
<head> output
<link rel="shortcut icon" href="/shortcut-icon.png" />
<link rel="icon" href="/icon.png" />
<link rel="icon" href="https://example.com/icon.png" />
<link rel="icon" href="/icon-dark.png" media="(prefers-color-scheme: dark)" />
<link rel="apple-touch-icon" href="/apple-icon.png" />
<link
  rel="apple-touch-icon-precomposed"
  href="/apple-touch-icon-precomposed.png"
/>
<link
  rel="apple-touch-icon"
  href="/apple-icon-x3.png"
  sizes="180x180"
  type="image/png"
/>

알아두면 좋은 정보: msapplication-* 메타 태그는 더 이상 Microsoft Edge의 크로미움 빌드에서 지원되지 않으며, 따라서 더 이상 필요하지 않습니다.

themeColor

폐기됨: metadatathemeColor 옵션은 Next.js 14부터 폐기되었습니다. 대신 viewport 구성을 사용하세요.

manifest

웹 애플리케이션 매니페스트 사양 (opens in a new tab)에 정의된 웹 애플리케이션 매니페스트입니다.

layout.js | page.js
export const metadata = {
  manifest: 'https://nextjs.org/manifest.json',
}
<head> output
<link rel="manifest" href="https://nextjs.org/manifest.json" />

twitter

트위터 사양은 (놀랍게도) X(이전 트위터)뿐만 아니라 다른 곳에서도 사용됩니다.

트위터 카드 마크업 참조 (opens in a new tab)에서 자세히 알아보세요.

layout.js | page.js
export const metadata = {
  twitter: {
    card: 'summary_large_image',
    title: 'Next.js',
    description: 'The React Framework for the Web',
    siteId: '1467726470533754880',
    creator: '@nextjs',
    creatorId: '1467726470533754880',
    images: ['https://nextjs.org/og.png'], // 반드시 절대 URL이어야 함
  },
}
<head> output
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site:id" content="1467726470533754880" />
<meta
  name="twitter:creator"
  content="@next
 
js"
/>
<meta name="twitter:creator:id" content="1467726470533754880" />
<meta name="twitter:title" content="Next.js" />
<meta name="twitter:description" content="The React Framework for the Web" />
<meta name="twitter:image" content="https://nextjs.org/og.png" />
layout.js | page.js
export const metadata = {
  twitter: {
    card: 'app',
    title: 'Next.js',
    description: 'The React Framework for the Web',
    siteId: '1467726470533754880',
    creator: '@nextjs',
    creatorId: '1467726470533754880',
    images: {
      url: 'https://nextjs.org/og.png',
      alt: 'Next.js Logo',
    },
    app: {
      name: 'twitter_app',
      id: {
        iphone: 'twitter_app://iphone',
        ipad: 'twitter_app://ipad',
        googleplay: 'twitter_app://googleplay',
      },
      url: {
        iphone: 'https://iphone_url',
        ipad: 'https://ipad_url',
      },
    },
  },
}
<head> output
<meta name="twitter:site:id" content="1467726470533754880" />
<meta name="twitter:creator" content="@nextjs" />
<meta name="twitter:creator:id" content="1467726470533754880" />
<meta name="twitter:title" content="Next.js" />
<meta name="twitter:description" content="The React Framework for the Web" />
<meta name="twitter:card" content="app" />
<meta name="twitter:image" content="https://nextjs.org/og.png" />
<meta name="twitter:image:alt" content="Next.js Logo" />
<meta name="twitter:app:name:iphone" content="twitter_app" />
<meta name="twitter:app:id:iphone" content="twitter_app://iphone" />
<meta name="twitter:app:id:ipad" content="twitter_app://ipad" />
<meta name="twitter:app:id:googleplay" content="twitter_app://googleplay" />
<meta name="twitter:app:url:iphone" content="https://iphone_url" />
<meta name="twitter:app:url:ipad" content="https://ipad_url" />
<meta name="twitter:app:name:ipad" content="twitter_app" />
<meta name="twitter:app:name:googleplay" content="twitter_app" />

viewport

폐기됨: metadataviewport 옵션은 Next.js 14부터 폐기되었습니다. 대신 viewport 구성을 사용하세요.

verification

layout.js | page.js
export const metadata = {
  verification: {
    google: 'google',
    yandex: 'yandex',
    yahoo: 'yahoo',
    other: {
      me: ['my-email', 'my-link'],
    },
  },
}
<head> output
<meta name="google-site-verification" content="google" />
<meta name="y_key" content="yahoo" />
<meta name="yandex-verification" content="yandex" />
<meta name="me" content="my-email" />
<meta name="me" content="my-link" />

appleWebApp

layout.js | page.js
export const metadata = {
  itunes: {
    appId: 'myAppStoreID',
    appArgument: 'myAppArgument',
  },
  appleWebApp: {
    title: 'Apple Web App',
    statusBarStyle: 'black-translucent',
    startupImage: [
      '/assets/startup/apple-touch-startup-image-768x1004.png',
      {
        url: '/assets/startup/apple-touch-startup-image-1536x2008.png',
        media: '(device-width: 768px) and (device-height: 1024px)',
      },
    ],
  },
}
<head> output
<meta
  name="apple-itunes-app"
  content="app-id=myAppStoreID, app-argument=myAppArgument"
/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-title" content="Apple Web App" />
<link
  href="/assets/startup/apple-touch-startup-image-768x1004.png"
  rel="apple-touch-startup-image"
/>
<link
  href="/assets/startup/apple-touch-startup-image-1536x2008.png"
  media="(device-width: 768px) and (device-height: 1024px)"
  rel="apple-touch-startup-image"
/>
<meta
  name="apple-mobile-web-app-status-bar-style"
  content="black-translucent"
/>

alternates

layout.js | page.js
export const metadata = {
  alternates: {
    canonical: 'https://nextjs.org',
    languages: {
      'en-US': 'https://nextjs.org/en-US',
      'de-DE': 'https://nextjs.org/de-DE',
    },
    media: {
      'only screen and (max-width: 600px)': 'https://nextjs.org/mobile',
    },
    types: {
      'application/rss+xml': 'https://nextjs.org/rss',
    },
  },
}
<head> output
<link rel="canonical" href="https://nextjs.org" />
<link rel="alternate" hreflang="en-US" href="https://nextjs.org/en-US" />
<link rel="alternate" hreflang="de-DE" href="https://nextjs.org/de-DE" />
<link
  rel="alternate"
  media="only screen and (max-width: 600px)"
  href="https://nextjs.org/mobile"
/>
<link
  rel="alternate"
  type="application/rss+xml"
  href="https://nextjs.org/rss"
/>

appLinks

layout.js | page.js
export const metadata = {
  appLinks: {
    ios: {
      url: 'https://nextjs.org/ios',
      app_store_id: 'app_store_id',
    },
    android: {
      package: 'com.example.android/package',
      app_name: 'app_name_android',
    },
    web: {
      url: 'https://nextjs.org/web',
      should_fallback: true,
    },
  },
}
<head> output
<meta property="al:ios:url" content="https://nextjs.org/ios" />
<meta property="al:ios:app_store_id" content="app_store_id" />
<meta property="al:android:package" content="com.example.android/package" />
<meta property="al:android:app_name" content="app_name_android" />
<meta property="al:web:url" content="https://nextjs.org/web" />
<meta property="al:web:should_fallback" content="true" />

archives

기록물, 문서 또는 기타 자료의 컬렉션을 설명합니다 (출처 (opens in a new tab)).

layout.js | page.js
export const metadata = {
  archives: ['https://nextjs.org/13'],
}
<head> output
<link rel="archives" href="https://nextjs.org/13" />

assets

layout.js | page.js
export const metadata = {
  assets: ['https://nextjs.org/assets'],
}
<head> output
<link rel="assets" href="https://nextjs.org/assets" />

bookmarks

layout.js | page.js
export const metadata = {
  bookmarks: ['https://nextjs.org/13'],
}
<head> output
<link rel="bookmarks" href="https://nextjs.org/13" />

category

layout.js | page.js
export const metadata = {
  category: 'technology',
}
<head> output
<meta name="category" content="technology" />

facebook

Facebook 소셜 플러그인에 특정 Facebook 앱 또는 계정을 연결할 수 있습니다. Facebook 문서 (opens in a new tab)

알아두면 좋은 정보: appId 또는 admins를 지정할 수 있지만, 둘 다 지정할 수는 없습니다.

layout.js | page.js
export const metadata = {
  facebook: {
    appId: '12345678',
  },
}
<head> output
<meta property="fb:app_id" content="12345678" />
layout.js | page.js
export const metadata = {
  facebook: {
    admins: '12345678',
  },
}
<head> output
<meta property="fb:admins" content="12345678" />

여러 개의 fb:admins 메타 태그를 생성하려면 배열 값을 사용할 수 있습니다.

layout.js | page.js
export const metadata = {
  facebook: {
    admins: ['
 
12345678', '87654321'],
  },
}
<head> output
<meta property="fb:admins" content="12345678" />
<meta property="fb:admins" content="87654321" />

other

모든 메타데이터 옵션은 내장 지원을 통해 다뤄져야 합니다. 그러나 사이트에 특정한 사용자 정의 메타데이터 태그나 새로 릴리스된 메타데이터 태그가 있을 수 있습니다. other 옵션을 사용하여 사용자 정의 메타데이터 태그를 렌더링할 수 있습니다.

layout.js | page.js
export const metadata = {
  other: {
    custom: 'meta',
  },
}
<head> output
<meta name="custom" content="meta" />

같은 키 메타 태그를 여러 개 생성하려면 배열 값을 사용할 수 있습니다.

layout.js | page.js
export const metadata = {
  other: {
    custom: ['meta1', 'meta2'],
  },
}
<head> output
<meta name="custom" content="meta1" /> <meta name="custom" content="meta2" />

지원되지 않는 메타데이터

다음 메타데이터 유형은 현재 내장 지원이 없습니다. 그러나 레이아웃 또는 페이지 자체에서 여전히 렌더링할 수 있습니다.

메타데이터권장 사항
<meta http-equiv="...">redirect(), 미들웨어, 보안 헤더를 통해 적절한 HTTP 헤더를 사용하세요.
<base>레이아웃 또는 페이지 자체에서 태그를 렌더링하세요.
<noscript>레이아웃 또는 페이지 자체에서 태그를 렌더링하세요.
<style>Next.js에서 스타일링하는 방법을 알아보세요.
<script>스크립트 사용 방법을 알아보세요.
<link rel="stylesheet" />레이아웃 또는 페이지 자체에서 스타일시트를 직접 import하세요.
<link rel="preload />ReactDOM preload 메서드를 사용하세요.
<link rel="preconnect" />ReactDOM preconnect 메서드를 사용하세요.
<link rel="dns-prefetch" />ReactDOM prefetchDNS 메서드를 사용하세요.

리소스 힌트

<link> 요소에는 브라우저가 외부 리소스가 필요할 가능성이 높다는 것을 암시할 수 있는 여러 rel 키워드가 있습니다. 브라우저는 이 정보를 사용하여 키워드에 따라 사전 로드 최적화를 적용합니다.

메타데이터 API는 이러한 힌트를 직접 지원하지 않지만, 새로운 ReactDOM 메서드 (opens in a new tab)를 사용하여 문서의 <head>에 안전하게 삽입할 수 있습니다.

app/preload-resources.tsx
'use client'
 
import ReactDOM from 'react-dom'
 
export function PreloadResources() {
  ReactDOM.preload('...', { as: '...' })
  ReactDOM.preconnect('...', { crossOrigin: '...' })
  ReactDOM.prefetchDNS('...')
 
  return '...'
}
app/preload-resources.js
'use client'
 
import ReactDOM from 'react-dom'
 
export function PreloadResources() {
  ReactDOM.preload('...', { as: '...' })
  ReactDOM.preconnect('...', { crossOrigin: '...' })
  ReactDOM.prefetchDNS('...')
 
  return '...'
}
<link rel="preload">

페이지 렌더링(브라우저) 수명 주기 초기에 리소스 로드를 시작합니다. MDN 문서 (opens in a new tab).

ReactDOM.preload(href: string, options: { as: string })
<head> output
<link rel="preload" href="..." as="..." />
<link rel="preconnect">

원본으로의 연결을 사전에 시작합니다. MDN 문서 (opens in a new tab).

ReactDOM.preconnect(href: string, options?: { crossOrigin?: string })
<head> output
<link rel="preconnect" href="..." crossorigin />
<link rel="dns-prefetch">

리소스가 요청되기 전에 도메인 이름을 해결하려고 시도합니다. MDN 문서 (opens in a new tab).

ReactDOM.prefetchDNS(href: string)
<head> output
<link rel="dns-prefetch" href="..." />

알아두면 좋은 정보:

  • 이러한 메서드는 현재 클라이언트 컴포넌트에서만 지원되며, 초기 페이지 로드 시 여전히 서버 사이드 렌더링이 됩니다.
  • Next.js의 내장 기능인 next/font, next/imagenext/script는 관련 리소스 힌트를 자동으로 처리합니다.

타입

메타데이터에 타입 안전성을 추가하려면 Metadata 타입을 사용할 수 있습니다. IDE에서 내장 TypeScript 플러그인을 사용하는 경우 수동으로 타입을 추가할 필요는 없지만, 원하는 경우 여전히 명시적으로 추가할 수 있습니다.

metadata 객체

layout.tsx | page.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'Next.js',
}

generateMetadata 함수

일반 함수

layout.tsx | page.tsx
import type { Metadata } from 'next'
 
export function generateMetadata(): Metadata {
  return {
    title: 'Next.js',
  }
}

비동기 함수

layout.tsx | page.tsx
import type { Metadata } from 'next'
 
export async function generateMetadata(): Promise<Metadata> {
  return {
    title: 'Next.js',
  }
}

세그먼트 props와 함께

layout.tsx | page.tsx
import type { Metadata } from 'next'
 
type Props = {
  params: { id: string }
  searchParams: { [key: string]: string | string[] | undefined }
}
 
export function generateMetadata({ params, searchParams }: Props): Metadata {
  return {
    title: 'Next.js',
  }
}
 
export default function Page({ params, searchParams }: Props) {}

상위 메타데이터와 함께

layout.tsx | page.tsx
import type { Metadata, ResolvingMetadata } from 'next'
 
export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata,
): Promise<Metadata> {
  return {
    title: 'Next.js',
  }
}

JavaScript 프로젝트

JavaScript 프로젝트에서는 JSDoc을 사용하여 타입 안전성을 추가할 수 있습니다.

layout.js | page.js
/** @type {import("next").Metadata} */
export const metadata = {
  title: 'Next.js',
}

버전 기록

버전변경 사항
v13.2.0viewport, themeColorcolorSchemeviewport 구성으로 대체되어 폐기되었습니다.
v13.2.0metadatagenerateMetadata 도입.