Next.js (14. Metadata and OG images) 정리
0. 메타데이터와 OG 이미지
Metadata API는 SEO와 소셜 공유를 위한 메타데이터를 정의하는 세 가지 방식을 제공한다.
- 정적
metadata객체:layout.tsx또는page.tsx에서metadata를 export한다.
- 동적
generateMetadata함수:- 데이터에 의존하는 메타데이터를 fetch해 반환한다.
- 파일 기반:
- 특수 파일명(favicon, OG 이미지 등)으로 정적 또는 동적 메타데이터를 추가한다.
metadata객체와generateMetadata함수는 Server Component에서만 사용할 수 있다.
Next.js는 설정된 메타데이터를 기반으로 관련<head>태그를 자동으로 생성한다.
1. 메타데이터 설정
1-1. 기본 메타 태그
라우트에 별도의 메타데이터를 정의하지 않아도 항상 추가되는 기본 태그가 두 가지 있다.
1
2
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
| 태그 | 역할 | 한 줄 설명 |
|---|---|---|
| charset | 문자 인코딩 설정 | 글자가 깨지지 않게 함 |
| viewport | 화면 크기/비율 설정 | 모바일 화면에 맞게 조정 |
1-2. 정적 메타데이터
layout.tsx 또는 page.tsx에서 Metadata 객체를 export해 정적 메타데이터를 정의한다.
1
2
3
4
5
6
7
8
9
10
// app/blog/layout.tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My Blog',
description: '...',
}
export default function Layout() {}
1-3. 동적 메타데이터 — generateMetadata
데이터에 의존하는 메타데이터는 generateMetadata 함수로 동적으로 생성한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// app/blog/[slug]/page.tsx
import type { Metadata, ResolvingMetadata } from 'next'
type Props = {
params: Promise<{ slug: string }>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
const slug = (await params).slug
// 포스트 정보 페칭
const post = await fetch(`https://api.vercel.app/blog/${slug}`).then((res) =>
res.json()
)
return {
title: post.title,
description: post.description,
}
}
export default function Page({ params, searchParams }: Props) {}
2. generateMetadata 활용 패턴
2-1. 스트리밍 메타데이터
동적 렌더링 페이지에서 Next.js는 메타데이터를 별도로 스트리밍해 UI 렌더링을 차단하지 않는다. generateMetadata가 완료되면 HTML에 주입되므로 시각적 콘텐츠를 먼저 스트리밍할 수 있다.
Twitterbot,Slackbot,Bingbot같은 봇과 크롤러에는 스트리밍 메타데이터가 비활성화된다. 이들은<head>태그에 메타데이터가 포함된 HTML을 요구하며, User Agent 헤더로 감지된다. 프리렌더링된 페이지는 빌드 타임에 메타데이터가 결정되므로 스트리밍을 사용하지 않는다.
2-2. 데이터 메모이제이션 — React cache
메타데이터와 페이지 모두에서 동일한 데이터를 중복 없이 한 번만 fetch하려면 React cache를 사용한다.
1
2
3
4
5
6
7
8
9
10
// app/lib/data.ts
import { cache } from 'react'
import { db } from '@/app/lib/db'
// getPost는 두 번 사용되지만 한 번만 실행된다
export const getPost = cache(async (slug: string) => {
const res = await db.query.posts.findFirst({ where: eq(posts.slug, slug) })
return res
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// app/blog/[slug]/page.tsx
import { getPost } from '@/app/lib/data'
export async function generateMetadata({
params,
}: {
params: { slug: string }
}) {
const post = await getPost(params.slug)
return {
title: post.title,
description: post.description,
}
}
export default async function Page({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug)
return <div>{post.title}</div>
}
3. OG 이미지
3-1. 파일 기반 메타데이터 종류
특수 파일명을 사용해 메타데이터를 정적 또는 동적으로 추가할 수 있다.
| 파일 | 설명 |
|---|---|
favicon.ico, apple-icon.jpg, icon.jpg | 파비콘 및 앱 아이콘 |
opengraph-image.jpg, twitter-image.jpg | OG 이미지 |
robots.txt | 크롤러 접근 규칙 |
sitemap.xml | 사이트맵 |
3-2. 정적 OG 이미지
app 폴더 루트에 opengraph-image.jpg 파일을 추가하면 전체 사이트에 적용되는 정적 OG 이미지가 설정된다.
특정 라우트에만 적용하려면 해당 폴더 안에 파일을 추가한다. 더 구체적인 경로의 이미지가 상위 폴더의 이미지보다 우선 적용된다.
3-3. 동적 OG 이미지 — ImageResponse
ImageResponse를 사용하면 JSX와 CSS로 동적 OG 이미지를 생성할 수 있다. 데이터에 따라 달라지는 OG 이미지가 필요할 때 유용하다.
blog 폴더 안에 opengraph-image.tsx 파일을 추가하고 next/og에서 ImageResponse를 import한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og'
import { getPost } from '@/app/lib/data'
// 이미지 메타데이터
export const size = {
width: 1200,
height: 630,
}
export const contentType = 'image/png'
// 이미지 생성
export default async function Image({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug)
return new ImageResponse(
(
// ImageResponse JSX 요소
<div
style={{
fontSize: 128,
background: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{post.title}
</div>
)
)
}
ImageResponse는 flexbox와 절대 위치 지정, 커스텀 폰트, 텍스트 줄바꿈, 중앙 정렬, 중첩 이미지를 지원한다.display: grid같은 고급 레이아웃은 지원하지 않는다.


