Next.js 15 메타태그 완벽 가이드 — og:image부터 hreflang까지
Next.js 15는 App Router 기반의 Metadata API를 통해 정적·동적 메타태그를 선언적으로 관리할 수 있다. generateMetadata() 함수로 페이지별 동적 og:image, title, description을 자동 생성할 수 있다.
핵심 요약
- Next.js 15는 App Router 기반의 Metadata API를 통해 정적·동적 메타태그를 선언적으로 관리할 수 있다.
generateMetadata()함수로 페이지별 동적 og:image, title, description을 자동 생성할 수 있다.- Twitter Card, hreflang, canonical URL, 구조화 데이터(JSON-LD)까지 단일 파일에서 제어 가능하다.
- 잘못 설정된 메타태그는 소셜 미리보기 오류와 검색 순위 하락으로 직결되므로, 메타태그 검사 도구로 배포 전 반드시 점검하자.
Next.js 15 Metadata API란 무엇인가?
| 항목 | 값 |
|---|---|
| Next.js 버전 | 15 |
| 메타데이터 관리 방식 | Metadata API |
| 동적 메타태그 생성 함수 | generateMetadata() |
| 지원되는 메타태그 종류 | og:image, title, description, Twitter Card, hreflang, canonical URL, JSON-LD |
| 메타태그 검사 도구 링크 | 메타태그 검사 도구 |
Next.js 13부터 도입된 App Router는 기존 컴포넌트 방식을 대체하는 Metadata API를 제공한다. Next.js 15에서는 이 API가 더욱 정교해져, layout.tsx와 page.tsx 파일에서 metadata 객체를 내보내거나 generateMetadata() 함수를 사용하는 두 가지 방식으로 메타태그를 제어할 수 있다.
정적 메타데이터
가장 간단한 형태는 metadata 객체를 직접 내보내는 방식이다.
// app/page.tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Next.js 15 메타태그 가이드',
description: 'og:image부터 hreflang까지, Next.js 메타태그의 모든 것',
}이 방식은 콘텐츠가 빌드 시점에 확정되는 정적 페이지에 적합하다. 블로그 홈이나 소개 페이지처럼 내용이 자주 바뀌지 않는 경우에 사용한다.
동적 메타데이터: generateMetadata()
블로그 포스트나 상품 상세 페이지처럼 URL 파라미터에 따라 내용이 달라지는 경우에는 generateMetadata() 함수를 사용한다.
// app/blog/[slug]/page.tsx
import type { Metadata, ResolvingMetadata } from 'next'
type Props = {
params: { slug: string }
}
export async function generateMetadata(
{ params }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
const post = await fetchPost(params.slug)
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [{ url: post.ogImage, width: 1200, height: 630 }],
},
}
}generateMetadata()는 서버에서 실행되므로 데이터베이스 조회나 외부 API 호출이 가능하다. 반환값은 Metadata 타입이며, Next.js가 자동으로 태그에 주입한다.
og:image를 제대로 설정하는 방법은 무엇인가?
Open Graph 이미지는 카카오톡, 슬랙, 트위터 등 소셜 플랫폼에서 링크를 공유할 때 나타나는 미리보기 이미지다. 잘못 설정하면 빈 이미지나 깨진 레이아웃이 노출되어 클릭률이 크게 떨어진다.
기본 og:image 설정
export const metadata: Metadata = {
openGraph: {
title: '페이지 제목',
description: '페이지 설명',
url: 'https://example.com/blog/my-post',
siteName: '사이트 이름',
images: [
{
url: 'https://example.com/og/my-post.png',
width: 1200,
height: 630,
alt: '게시글 대표 이미지',
},
],
locale: 'ko_KR',
type: 'article',
},
}Next.js의 동적 og:image 생성 — ImageResponse
Next.js 15는 next/og의 ImageResponse를 활용해 서버에서 OG 이미지를 동적으로 렌더링하는 기능을 지원한다. 별도의 이미지 편집 없이 코드로 디자인을 제어할 수 있다.
// app/og/route.tsx
import { ImageResponse } from 'next/og'
export const runtime = 'edge'
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const title = searchParams.get('title') ?? '기본 제목'
return new ImageResponse(
(
<div
style={{
display: 'flex',
fontSize: 60,
background: '#0f172a',
color: 'white',
width: '100%',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
padding: '40px',
}}
>
{title}
</div>
),
{ width: 1200, height: 630 }
)
}이후 generateMetadata()에서 해당 엔드포인트를 참조하면 된다.
images: [{ url: `https://example.com/og?title=${encodeURIComponent(post.title)}` }]Twitter Card 설정
Twitter(현 X)는 자체 메타태그 규격을 사용하지만, Next.js Metadata API는 이를 twitter 키로 통합 관리한다.
export const metadata: Metadata = {
twitter: {
card: 'summary_large_image',
title: '페이지 제목',
description: '페이지 설명',
creator: '@handle',
images: ['https://example.com/og/my-post.png'],
},
}card 값은 summary, summary_large_image, app, player 중 선택한다. 블로그 포스트에는 summary_large_image가 가장 적합하다.
hreflang으로 다국어 SEO를 완성하는 방법
hreflang 태그는 동일한 콘텐츠의 언어·지역별 버전을 검색 엔진에 알려주는 신호다. 한국어와 영어 버전을 함께 운영하는 사이트라면 반드시 설정해야 한다. 설정이 누락되면 구글이 언어별 페이지를 중복 콘텐츠로 판단해 검색 노출에 불이익을 줄 수 있다.
alternates로 hreflang 설정
export const metadata: Metadata = {
alternates: {
canonical: 'https://example.com/ko/blog/my-post',
languages: {
'ko-KR': 'https://example.com/ko/blog/my-post',
'en-US': 'https://example.com/en/blog/my-post',
'ja-JP': 'https://example.com/ja/blog/my-post',
},
},
}Next.js는 이 설정을 기반으로 아래와 같은 태그를 자동 생성한다.
<link rel="canonical" href="https://example.com/ko/blog/my-post" />
<link rel="alternate" hreflang="ko-KR" href="https://example.com/ko/blog/my-post" />
<link rel="alternate" hreflang="en-US" href="https://example.com/en/blog/my-post" />
<link rel="alternate" hreflang="ja-JP" href="https://example.com/ja/blog/my-post" />canonical URL 설정의 중요성
canonical URL은 동일하거나 유사한 콘텐츠가 여러 URL에 존재할 때 검색 엔진에게 "이 URL이 원본"임을 알리는 태그다. 페이지네이션, UTM 파라미터, www 유무 차이 등으로 중복 URL이 발생하는 경우 canonical 설정이 없으면 검색 점수가 분산된다.
alternates: {
canonical: 'https://example.com/blog/my-post',
}절대 경로를 사용하고, HTTPS와 www 여부를 사이트 전체에 걸쳐 일관되게 유지해야 한다.
구조화 데이터(JSON-LD)로 리치 결과 획득하기
구조화 데이터는 검색 결과에 별점, 작성자, 게시일 등의 추가 정보를 표시하는 리치 결과를 가능하게 한다. Next.js에서는 태그를 직접 삽입하는 방식을 권장한다.
// app/blog/[slug]/page.tsx
export default async function BlogPost({ params }: Props) {
const post = await fetchPost(params.slug)
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: post.title,
description: post.excerpt,
author: {
'@type': 'Person',
name: post.author,
},
datePublished: post.publishedAt,
dateModified: post.updatedAt,
image: post.ogImage,
}
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<article>{/* 콘텐츠 */}</article>
</>
)
}블로그 포스트에는 Article, 상품 페이지에는 Product, FAQ 페이지에는 FAQPage 스키마를 사용한다. Google의 리치 결과 테스트 도구로 유효성을 검증한 뒤 메타태그 검사 도구로 최종 확인하는 것을 권장한다.
메타태그 설정 시 자주 발생하는 실수
title 템플릿 활용
layout.tsx에서 title.template을 설정하면 하위 페이지의 title에 자동으로 사이트 이름이 붙는다.
// app/layout.tsx
export const
---
**참고:** [Cloudflare 개발자 문서](https://developers.cloudflare.com)
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "Next.js 15 메타태그 완벽 가이드 — og:image부터 hreflang까지",
"url": "https://millionscode.com/blog/nextjs-15-meta-tags-guide",
"author": {
"@type": "Person",
"name": "MillionsCode",
"url": "https://millionscode.com/about"
},
"publisher": {
"@type": "Organization",
"name": "MillionsCode",
"logo": {
"@type": "ImageObject",
"url": "https://millionscode.com/favicon.svg",
"width": 512,
"height": 512
}
},
"image": {
"@type": "ImageObject",
"url": "https://millionscode.com/og-default.png",
"width": 1200,
"height": 628
},
"dateModified": "2026-05-19"
}
</script>
🔧 이 글과 관련된 무료 도구
이 글과 관련된 상품 (Next.js)[광고/제휴]
이 포스팅은 쿠팡 파트너스, 아마존 어소시에이트, 알리익스프레스 제휴 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다. 이는 상품 가격에 영향을 주지 않습니다.
As an Amazon Associate, Coupang Partner, and AliExpress affiliate, I earn from qualifying purchases at no extra cost to you.
관련 글
2026년 가장 인기 있는 AI 코딩 도구 Claude Code, Cursor, GitHub Copilot 3종을 월 가격·1M 컨텍스트·한국어...
IT블로그 SEO 2026 — 구글 알고리즘 변화와 대응 전략2026년 블로그 SEO 완벽 가이드. 구글 E-E-A-T·AI Overview·코어 업데이트 대응 전략. 롱테일 키워드·FAQ 구조·테크니컬 ...
IT2026 NordVPN vs ExpressVPN vs Surfshark — VPN 속도·가격·보안 비교2026년 기준 NordVPN, ExpressVPN, Surfshark 3대 VPN의 속도, 가격, 서버 수, 노로그 정책, 스트리밍 지원을 비...
IT2026 맥북 에어 M4 vs 삼성 갤럭시북4 vs 레노버 요가 — 개발자 노트북 비교2026년 기준 맥북 에어 M4, 삼성 갤럭시북4 프로, 레노버 요가 슬림 7i의 CPU, 배터리, 디스플레이, 개발 워크플로우를 비교합니다....