문서
App Router 사용하기
generateMetadata

generateMetadata

이 페이지에서는 generateMetadata와 정적 metadata 객체를 사용한 모든 설정 기반 메타데이터 옵션을 다룹니다.

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와 같은 동적 정보에 의존합니다. Metadata 객체를 반환하는 generateMetadata 함수를 내보내서 설정할 수 있습니다.

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의 검색 매개변수를 포함하는 객체입니다. 예시:

      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 세그먼트에서만 사용할 수 있습니다.
  • Next.js의 redirect()notFound() 메서드도 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: "...",
  },
};
Default

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>
Template

title.template하위 라우트 세그먼트에 정의된 titles에 접두사나 접미사를 추가하는 데 사용할 수 있습니다.

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아무 효과가 없습니다.

Absolute

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을 무시합니다.
    • title.templatepage.js에서 효과가 없습니다. 페이지는 항상 라우트의 종료 세그먼트이기 때문입니다.

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 필드가 절대 URL 대신 상대 경로를 사용할 수 있습니다.
  • 필드의 상대 경로는 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는 무시됩니다.
  • metadataBase를 구성하지 않고 URL 기반 metadata 필드에 상대 경로를 사용하면 빌드 오류가 발생합니다.
  • Next.js는 metadataBase(예: https://acme.com/)와 상대 필드(예: /path) 사이의 중복 슬래시를 단일 슬래시(예: https://acme.com/path)로 정규화합니다.

기본값

구성되지 않은 경우 metadataBase에는 기본값이 있습니다.

Vercel에서:

  • 프로덕션 배포의 경우 VERCEL_URL이 사용됩니다.
  • 프리뷰 배포의 경우 VERCEL_BRANCH_URL이 우선순위를 가지며, 없는 경우 VERCEL_URL로 대체됩니다.

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

이러한 환경 변수에 대한 자세한 내용은 시스템 환경 변수 문서를 참조하세요.

URL 구성

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

  • metadataBasemetadata 필드 사이의 후행 슬래시는 정규화됩니다.
  • metadata 필드의 "절대" 경로(일반적으로 전체 URL 경로를 대체함)는 "상대" 경로(metadataBase의 끝에서 시작)로 취급됩니다.

예를 들어, 다음과 같은 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"),
};

위의 metadataBase를 상속하고 자체 값을 설정하는 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" />

알아두면 좋은 점:

  • Open Graph 이미지에는 파일 기반 메타데이터 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의 Chromium 빌드에서 더 이상 지원되지 않으므로 더 이상 필요하지 않습니다.

themeColor

더 이상 사용되지 않음: Next.js 14부터 metadatathemeColor 옵션은 더 이상 사용되지 않습니다. 대신 viewport 구성을 사용하세요.

manifest

웹 애플리케이션 매니페스트 사양에 정의된 웹 애플리케이션 매니페스트입니다.

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

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

Twitter 카드 마크업 참조에서 자세히 알아보세요.

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="@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: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

더 이상 사용되지 않음: Next.js 14부터 metadataviewport 옵션은 더 이상 사용되지 않습니다. 대신 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"
/>
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

역사적 관심이 있는 기록, 문서 또는 기타 자료의 모음을 설명합니다 (출처).

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 계정을 웹페이지에 연결할 수 있습니다 Facebook 문서

알아두면 좋은 점: 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 메서드를 사용하여 문서의 <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 문서를 참조하세요.

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

원본에 대한 연결을 미리 시작합니다. MDN 문서를 참조하세요.

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

리소스 요청 전에 도메인 이름을 해결하려고 시도합니다. MDN 문서를 참조하세요.

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

알아두면 좋은 점:

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

타입

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

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, themeColor, colorSchemeviewport 구성을 위해 더 이상 사용되지 않습니다.
v13.2.0metadatagenerateMetadata 도입.