안동민 개발노트

안동민 개발노트

정적 메타데이터 설정동적 메타데이터 생성Open Graph 태그 활용robots.txt 및 sitemap.xml 설정
본문 시작
  1. 홈
  2. 문서
  3. Next.js
  4. 13장 : SEO 및 메타데이터
  5. 정적 메타데이터 설정
  1. Next.js
  2. 정적 메타데이터 설정

정적 메타데이터 설정

layout과 page의 metadata 객체로 제목·설명·canonical·아이콘을 선언해 검색 엔진에 페이지 정보를 전달합니다.

검색 엔진 최적화(SEO)는 웹사이트가 검색 결과에서 더 잘 노출되도록 만드는 필수 과정입니다.

SEO의 핵심 요소 중 하나가 메타데이터(Metadata) 설정입니다.

메타데이터는 페이지 내용을 검색 엔진과 소셜 플랫폼에 설명해, 페이지가 어떻게 인덱싱되고 노출될지 결정하는 데 큰 영향을 줍니다.

Next.js 13부터 도입된 App Router는 라우트별로 정적 메타데이터(Static Metadata)를 선언하는 API를 제공합니다.

이 절에서는 정적 메타데이터의 역할, App Router에서 설정하는 방법, SEO에 영향을 주는 주요 메타데이터 속성을 다룹니다.


메타데이터란 무엇이며 왜 중요한가요?

메타데이터는 "데이터에 대한 데이터"를 의미하며, 웹 페이지의 맥락에서는 해당 페이지의 콘텐츠를 설명하는 정보입니다.

HTML <head> 태그 내에 <meta> 태그, <title> 태그 등을 통해 정의됩니다.

메타데이터가 중요한 이유
  • 검색 엔진 최적화 (SEO): 검색 엔진 크롤러는 메타데이터를 읽어 페이지의 주제와 관련성을 파악하고 검색 결과에 반영합니다. 메타데이터는 페이지를 설명하는 신호이며, 설정만으로 특정 순위나 색인을 보장하지 않습니다.
  • 소셜 미디어 공유: Open Graph (OG) 및 Twitter Card 메타데이터는 웹 페이지가 Facebook, Twitter, LinkedIn 등 소셜 미디어 플랫폼에 공유될 때 미리보기(제목, 설명, 이미지)가 어떻게 표시될지 정의합니다. 이는 링크 클릭률을 높이는 데 중요한 역할을 합니다.
  • 사용자 경험 개선: 브라우저 탭의 제목, 북마크 이름 등은 메타데이터를 통해 결정됩니다. 명확한 제목과 설명은 사용자에게 페이지의 내용을 미리 알려주는 역할을 합니다.

App Router에서 정적 메타데이터 설정하기

Next.js App Router에서 정적 메타데이터를 설정하는 가장 일반적이고 권장되는 방법은 metadata 객체를 layout.tsx 또는 page.tsx 파일에서 export하는 것입니다.

Next.js는 라우트의 metadata 선언을 해석해 필요한 태그를 생성합니다. 정적 객체는 값이 고정되었다는 뜻이며, 라우트 전체가 빌드 시점에만 렌더된다는 뜻은 아닙니다. 이 export는 Server Component에서 사용합니다.

기본 메타데이터 설정

애플리케이션 전체에 적용될 기본 메타데이터는 루트 layout.tsx 파일에서 설정합니다.

src/app/layout.tsx
import type { Metadata, Viewport } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';

const inter = Inter({ subsets: ['latin'] });

// 1. Metadata 객체 export
export const metadata: Metadata = {
  title: 'Next.js 튜토리얼 - 성능 최적화와 SEO', // 웹사이트의 기본 제목
  description: 'Next.js App Router를 사용하여 성능 최적화 및 SEO 설정을 학습하는 튜토리얼입니다.', // 웹사이트의 기본 설명
  keywords: ['Next.js', 'React', '성능 최적화', 'SEO', '웹 개발'], // 관련 키워드
  authors: [{ name: 'Your Name', url: 'https://personal-lab.dev' }], // 개발자 정보
  creator: 'Your Name', // 제작자 정보
  publisher: 'Your Company', // 발행자 정보

  // Open Graph (소셜 미디어 공유 최적화)
  openGraph: {
    title: 'Next.js 튜토리얼 - 성능 최적화와 SEO',
    description: 'Next.js App Router를 사용하여 성능 최적화 및 SEO 설정을 학습하는 튜토리얼입니다.',
    url: 'https://personal-lab.dev', // 소셜 그래프에서 페이지를 식별하는 URL
    siteName: 'Next.js 학습 사이트', // 웹사이트 이름
    images: [
      {
        url: 'https://personal-lab.dev/og-image.png', // 소셜 미디어 공유 시 표시될 이미지
        width: 1200,
        height: 630,
        alt: 'Next.js 튜토리얼 이미지',
      },
    ],
    locale: 'ko_KR', // 언어 및 지역 (예: 한국어)
    type: 'website', // 페이지 타입 (website, article 등)
  },

  // Twitter Card (트위터 공유 최적화)
  twitter: {
    card: 'summary_large_image', // 카드 타입 (summary, summary_large_image, app, player)
    title: 'Next.js 튜토리얼 - 성능 최적화와 SEO',
    description: 'Next.js App Router를 사용하여 성능 최적화 및 SEO 설정을 학습하는 튜토리얼입니다.',
    creator: '@yourtwitterhandle', // 트위터 계정
    images: ['https://personal-lab.dev/twitter-image.png'], // 트위터 공유 시 표시될 이미지
  },

  // 페이지의 색인·링크 처리 지침 (robots.txt의 크롤링 규칙과 별개)
  robots: {
    index: true, // 이 페이지를 인덱싱할지 여부
    follow: true, // 이 페이지의 링크를 따라갈지 여부
    nocache: true, // 크롤러별 지원이 다른 지시어이며 HTTP 캐시 설정이 아님
    googleBot: { // GoogleBot 전용 설정
      index: true,
      follow: false,
      noimageindex: true, // 이미지 인덱싱 방지
      'max-video-preview': -1,
      'max-snippet': -1,
    },
  },

};

// viewport와 themeColor는 Metadata가 아니라 Viewport API로 선언합니다.
export const viewport: Viewport = {
  width: 'device-width',
  initialScale: 1,
  themeColor: '#FFFFFF',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ko">
      <body className={inter.className}>{children}</body>
    </html>
  );
}

페이지별 메타데이터 설정

특정 페이지에만 적용되는 메타데이터는 해당 페이지의 page.tsx 파일에서 metadata 객체를 export하여 설정합니다.

페이지에서 선언한 필드는 상위의 같은 필드보다 우선하며, 선언하지 않은 필드는 상위 값을 이어받습니다.

src/app/dashboard/page.tsx
import type { Metadata } from 'next';

// 1. Dashboard 페이지의 metadata 객체 export
export const metadata: Metadata = {
  title: '대시보드 - 내 계정 요약', // 이 페이지의 고유 제목 (루트 레이아웃의 제목을 덮어씀)
  description: '사용자 계정의 대시보드입니다. 최신 활동, 알림 및 설정에 접근하세요.', // 이 페이지의 고유 설명
  // Open Graph 등 다른 속성도 여기에 추가하거나 덮어쓸 수 있습니다.
  openGraph: {
    title: '대시보드 - Next.js 학습',
    description: '나만의 Next.js 대시보드 페이지입니다.',
    images: ['https://personal-lab.dev/dashboard-og.png'],
  },
  // robots: { index: false, follow: false }, // 특정 페이지를 검색에서 제외하고 싶을 때
};

export default function DashboardPage() {
  return (
    <div>
      <h1>대시보드</h1>
      <p>환영합니다! 이곳은 대시보드 페이지입니다.</p>
    </div>
  );
}
메타데이터 병합 규칙

Next.js는 루트 레이아웃에서 페이지로 내려오며 메타데이터를 해석하지만, 모든 필드를 깊게 병합하지는 않습니다.

  • title, description 같은 단순 필드는 가까운 하위 세그먼트의 값이 상위 값을 덮어씁니다.
  • openGraph, robots 같은 중첩 필드도 하위 객체가 상위 객체를 얕게 교체합니다. 하위 openGraph에 description만 쓰면 상위 openGraph.images가 자동으로 보존되지 않습니다.
  • 상위 배열이나 중첩 값을 함께 쓰려면 공통 변수를 임포트하거나 generateMetadata에서 parent를 읽어 명시적으로 합칩니다.

대시보드 예제의 메타데이터 합성

앞의 루트 layout과 Dashboard page가 함께 적용되고 별도 메타데이터 파일이 없는 경우의 선언 해석입니다. 외부 검색·공유 결과를 관측한 표가 아닙니다.

대시보드 예제의 메타데이터 합성
필드선택되는 선언원문에서 확인할 점
title·descriptionDashboard page의 값같은 최상위 필드에 가까운 세그먼트의 값이 적용됩니다.
openGraphDashboard page의 객체title·description·images를 다시 선언합니다. 루트의 siteName·locale·url은 자동으로 이어지지 않습니다.
twitter루트 layout의 객체page에서 선언하지 않아 루트의 기본 카드 내용이 남습니다.
robots루트 layout의 객체page의 robots 예시는 주석이므로 실행되는 설정이 아닙니다. Googlebot용 follow는 false입니다.
title·description
선택되는 선언: Dashboard page의 값
원문에서 확인할 점: 같은 최상위 필드에 가까운 세그먼트의 값이 적용됩니다.
openGraph
선택되는 선언: Dashboard page의 객체
원문에서 확인할 점: title·description·images를 다시 선언합니다. 루트의 siteName·locale·url은 자동으로 이어지지 않습니다.
twitter
선택되는 선언: 루트 layout의 객체
원문에서 확인할 점: page에서 선언하지 않아 루트의 기본 카드 내용이 남습니다.
robots
선택되는 선언: 루트 layout의 객체
원문에서 확인할 점: page의 robots 예시는 주석이므로 실행되는 설정이 아닙니다. Googlebot용 follow는 false입니다.

주요 메타데이터 속성 상세 설명

Metadata 타입은 다양한 SEO 및 소셜 미디어 관련 속성을 제공합니다.

  • title
    • <title> 태그에 해당하는 값입니다. 브라우저 탭에 표시되며, 검색 결과의 제목을 정하는 데 사용될 수 있지만, 검색 엔진이 다른 문구를 선택할 수도 있습니다.
    • 페이지의 내용을 간결하고 명확하게 요약해야 합니다.
    • 권장 형식: 페이지 제목 | 웹사이트 이름 또는 웹사이트 이름 - 페이지 제목
  • description
    • <meta name="description" content="..." /> 태그에 해당합니다. 검색 엔진 결과의 스니펫(요약)으로 사용될 수 있습니다.
    • 페이지의 내용을 1~2문장으로 요약하며, 관련 키워드를 포함하는 것이 좋습니다.
  • keywords
    • <meta name="keywords" content="..." /> 태그에 해당합니다. Google Search는 이 태그를 색인이나 순위에 사용하지 않습니다. 위 코드는 Metadata API가 제공하는 필드의 선언 예시입니다.
  • authors, creator, publisher
    • 콘텐츠의 저자, 제작자, 발행자 정보를 제공합니다.
  • openGraph (OG)
    • Facebook, LinkedIn 등 Open Graph 프로토콜을 사용하는 소셜 미디어 플랫폼에 페이지가 공유될 때 표시될 정보를 정의합니다.
    • title, description, url, siteName, images, locale, type 등의 속성이 있습니다. images는 배열로 여러 이미지를 지정할 수 있습니다.
  • twitter
    • Twitter Card를 정의하여 트위터에 페이지가 공유될 때 표시될 정보를 제어합니다.
    • card 타입 (summary, summary_large_image 등), title, description, creator, images 등의 속성이 있습니다.
  • robots
    • <meta name="robots" content="..." /> 태그에 해당하며, 검색 엔진 크롤러에게 페이지를 어떻게 처리할지 지시합니다.
    • index: true/false, follow: true/false 등이 주요 속성입니다. index: false는 해당 페이지를 검색 결과에서 제외하라는 의미입니다.
    • 크롤러가 이 지침을 읽으려면 페이지에 접근할 수 있어야 합니다. robots.txt 차단은 색인 제외나 인증을 대신하지 않습니다. nocache도 브라우저·CDN·Next 데이터 캐시를 제어하지 않습니다.
  • Viewport API
    • viewport 객체 또는 generateViewport 함수로 화면 배율과 themeColor를 선언합니다. 이 값들은 Metadata 객체에 넣지 않습니다.
  • icons
    • 파비콘(favicon) 및 기타 웹 아이콘을 설정합니다. 다음은 기존 metadata 객체에 넣을 icons 필드 예시이며, 같은 파일에 두 번째 metadata export를 추가하지 않습니다.
    src/app/layout.tsx
    import type { Metadata } from 'next';
    
    export const metadata: Metadata = {
      icons: {
        icon: '/logo.png', // 기본 파비콘 대체 경로
        shortcut: '/logo.png',
        apple: '/logo.png', // iOS 홈 화면 아이콘
        other: {
          rel: 'apple-touch-icon-precomposed',
          url: '/logo.png',
        },
      },
    };

정적 메타데이터 설정 시 고려사항

  • 일관성 유지: 웹사이트 전체에서 메타데이터의 형식과 내용에 일관성을 유지하는 것이 중요합니다.
  • 중복 피하기: 각 페이지마다 고유하고 관련성 높은 제목과 설명을 제공하여 중복된 콘텐츠로 인한 SEO 문제를 피합니다.
  • 키워드 스터핑 지양: keywords나 description에 과도하게 키워드를 나열하는 것은 검색 엔진에 부정적인 영향을 줄 수 있습니다. 자연스러운 문장으로 작성하세요.
  • 이미지 경로 확인: 최종 og:image와 Twitter 이미지 URL은 절대 URL이어야 합니다. Metadata API에서는 루트에 metadataBase를 지정하면 /og-image.png 같은 상대 경로를 사용할 수 있으며 Next.js가 절대 URL로 해석합니다. 어떤 방식을 쓰든 실제 공개 주소에서 이미지가 열리는지 확인합니다.
  • 대표 URL 구분: openGraph.url은 소셜 그래프의 식별 주소입니다. 검색용 <link rel="canonical">이 필요하면 alternates.canonical을 별도로 선언합니다. 위 예제에는 이 필드가 없습니다.
  • 정확성: 웹 페이지의 실제 내용을 정확하게 반영하는 메타데이터를 작성해야 합니다.
  • 모바일 친화적: viewport 설정을 통해 모바일 기기에서의 표시를 최적화해야 합니다.
  • 테스트: 페이지를 배포한 후 Google Search Console, Open Graph Debugger (Facebook), Twitter Card Validator 등의 도구를 사용하여 메타데이터가 올바르게 인식되는지 확인합니다.

Next.js App Router의 정적 메타데이터 기능을 사용하면 페이지별 제목, 설명, 공유 정보를 코드 가까이에 선언할 수 있습니다.

검색 노출과 소셜 공유 미리보기는 이 메타데이터의 일관성에 영향을 받습니다.

메모이제이션 및 리렌더링 최적화

이전 페이지

동적 메타데이터 생성

다음 페이지

이 페이지의 목차

메타데이터란 무엇이며 왜 중요한가요?App Router에서 정적 메타데이터 설정하기기본 메타데이터 설정페이지별 메타데이터 설정주요 메타데이터 속성 상세 설명정적 메타데이터 설정 시 고려사항