Next.js 교재 · 7편 · SEO 메타

Metadata API로 SEO 설정 — title·OG·sitemap 한 묶음

head 태그 직접 만지지 않고 객체 export 한 번으로 메타가 다 박힌다.

메타데이터 태그들이 페이지 위로 떠오르는 컨셉 일러스트

웹 검색 결과에서 클릭률을 결정하는 건 본문이 아니라 제목·설명·OG 이미지 세 줄. 트위터·페이스북·디스코드에 링크 붙여넣었을 때 뜨는 카드 한 장이 트래픽의 시작이다.

옛 React 에선 react-helmet 같은 라이브러리로 <head> 를 동적으로 조작했다. Next.js App Router 는 그걸 한 객체 export 로 단순화했다. metadata 또는 generateMetadata 한 번 export 하면 끝.

1. 정적 metadata — 객체 한 번 export

가장 단순한 형태. page.tsx 또는 layout.tsx 에서 metadata 객체를 export.

// app/about/page.tsx import type { Metadata } from 'next'; export const metadata: Metadata = { title: '소개 | 내 사이트', description: '저는 누구이며 이 사이트는 무엇을 하는지.', }; export default function AboutPage() { return <main>…</main>; }

이 객체를 Next 가 빌드 타임에 읽어 <head> 에 자동 삽입한다:

<head> <title>소개 | 내 사이트</title> <meta name="description" content="저는 누구이며 …"> </head>

<head> 를 직접 만질 필요가 없다. Metadata 타입을 import 해두면 IDE 자동완성으로 가능한 필드를 다 볼 수 있다.

2. OG 와 Twitter 카드 — 공유 미리보기의 핵심

가장 많이 쓰는 확장. openGraphtwitter 필드.

export const metadata: Metadata = { title: 'Next.js 입문 — React와 차이', description: '메타 프레임워크가 뭔지 정리.', openGraph: { title: 'Next.js 입문 — React와 차이', description: '메타 프레임워크가 뭔지 정리.', images: ['/og-nextjs-intro.png'], // 1200×630 권장 type: 'article', url: 'https://my-site.com/blog/nextjs-intro', }, twitter: { card: 'summary_large_image', title: 'Next.js 입문', description: '메타 프레임워크 정리.', images: ['/og-nextjs-intro.png'], }, };

이 한 객체로 트위터·페이스북·카카오톡·디스코드 — 모든 공유 미리보기가 한 번에 세팅된다.

OG 이미지 표준 — 비율 1.91:1, 해상도 1200×630, 용량 100KB 안쪽 권장. 텍스트는 가운데 1/3 안에만 (모바일에서 위아래 잘림). 절대 URL 또는 /og.png 같은 사이트 내 경로.

3. 동적 metadata — generateMetadata 함수

블로그 글마다 다른 제목·설명이 필요할 때. 정적 객체 대신 함수를 export.

// app/blog/[slug]/page.tsx import type { Metadata } from 'next'; export async function generateMetadata({ params, }: { params: Promise<{ slug: string }>; }): Promise<Metadata> { const { slug } = await params; const post = await fetchPost(slug); return { title: post.title, description: post.excerpt, openGraph: { images: [post.coverImage], }, }; } export default async function BlogPostPage({ params }) { // 페이지 본문 }

함수 안에서 DB·API 를 호출해 동적으로 메타데이터를 구성. Next 가 빌드 타임 또는 요청 타임에 호출해 <head> 를 채운다.

4. 자주 쓰는 필드 7가지

필드역할
title탭 이름 + 검색 결과 제목. ≤60자 권장.
description검색 스니펫. 120-160자.
openGraphSNS 공유 카드 메타.
twitter트위터 카드 (있으면 OG 우선 덮어씀).
robots인덱싱 허용·차단 (noindex 등).
alternatescanonical URL · 다국어 hreflang.
iconsfavicon · apple-touch-icon.

특히 alternates.canonical 은 SEO 의 비밀 무기. 같은 콘텐츠가 두 URL 로 접근될 때 (예: ?utm_source=x 파라미터) 검색엔진에 "이게 원본" 을 알려준다. 중복 콘텐츠 페널티 방지.

5. sitemap.xml 과 robots.txt — 파일 한 번 만들면 끝

SEO 필수 파일 두 가지. App Router 는 이것도 자동 생성한다.

sitemap.ts — 검색엔진용 페이지 목록

// app/sitemap.ts import type { MetadataRoute } from 'next'; export default async function sitemap(): Promise<MetadataRoute.Sitemap> { const posts = await fetchAllPosts(); return [ { url: 'https://my-site.com', lastModified: new Date() }, { url: 'https://my-site.com/about', lastModified: new Date() }, ...posts.map(p => ({ url: `https://my-site.com/blog/${p.slug}`, lastModified: p.updatedAt, })), ]; }

이 파일을 만들면 /sitemap.xml 이 자동 생성된다. Google Search Console 에 등록할 URL.

robots.ts — 크롤러 가이드

// app/robots.ts import type { MetadataRoute } from 'next'; export default function robots(): MetadataRoute.Robots { return { rules: { userAgent: '*', allow: '/', disallow: '/admin/' }, sitemap: 'https://my-site.com/sitemap.xml', }; }

이 파일이 /robots.txt 가 된다. 어드민 경로 제외·sitemap 위치 지정.

흔한 함정metadata서버 컴포넌트에서만 export 가능. 'use client' 가 붙은 파일에서 export 하면 무시. 클라이언트에서 동적으로 title 바꾸려면 document.title 직접 수정 또는 부모 server 컴포넌트의 generateMetadata 사용.

요약 — 7편 좌표

여기까지 정리. App Router 의 SEO 는 metadata 객체 또는 generateMetadata 함수 한 번 export 로 끝. title·description·openGraph·twitter 가 핵심 4종, alternates.canonical 이 중복 콘텐츠 방어. app/sitemap.tsapp/robots.ts 두 파일로 sitemap.xml·robots.txt 자동 생성. 메타데이터는 server component 에서만 — 이 한 줄만 기억하면 함정 90% 가 없어진다. 다음 편에선 서버에서 데이터 가져오기로 본격적인 데이터 fetching 시작.

다음 편 예고 — 서버에서 데이터 가져오기

fetch 와 async Server Component 로 데이터 fetching. 8편.

© 2026 주나이테크(주) @JUNAITECH