본문 바로가기
C.W.K.
Stream
Lesson 06 of 08 · published

Metadata API로 검색·공유 정보를 만들기

~20 min · metadata, OG image, SEO

Level 0호기심
0 XP0/68 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

정적 metadata는 객체로 내보내

페이지나 레이아웃에서 metadata 객체를 내보내면 Next.js가 문서 head를 만들어. 중첩 레이아웃의 값은 합쳐지고 더 가까운 페이지 값이 우선해. 루트에서 %s | My App 같은 title template을 두면 하위 제목을 일관되게 조합할 수 있어.

데이터에 따라 달라지면 generateMetadata를 써

글 제목, 상품명, 사용자 이름처럼 라우트 데이터가 있어야 정해지는 태그는 비동기 generateMetadata({ params })에서 반환해. 페이지와 같은 데이터 소스를 읽되 중복 요청과 캐시 정책이 어긋나지 않게 설계해.

Open Graph 이미지도 라우트 자산이 될 수 있어

구간에 opengraph-image.tsx를 두면 Satori 기반 ImageResponse가 React JSX를 이미지로 렌더링해. 별도 디자인 파일을 매번 만들지 않고 제목이나 작성자에 맞춘 공유 이미지를 서버에서 생성할 수 있어.

보이는 페이지와 공유 카드가 같은 약속을 해야 해

제목·설명·이미지가 실제 내용과 맞는지 배포된 URL로 확인해. 코드에서 객체가 맞아 보여도 crawler가 접근하지 못하거나 이미지 글꼴이 깨질 수 있어.

라우트별 canonical URL, title, description, locale, OG 이미지를 실제 콘텐츠 데이터에서 함께 만들고 sitemap과 같은 source를 공유해. 동적 metadata가 페이지와 같은 query를 중복 실행하지 않는지 확인하고, 비공개·초안 페이지가 색인과 공유 이미지에 노출되지 않게 상태를 반영해.

metadata가 풍부하다고 검색 순위가 자동으로 오르지는 않아. crawler가 200 응답과 실제 본문을 읽을 수 있고 제목이 내용과 맞아야 해. 디자인 도구 없이 OG 이미지를 만든다는 편의도 배포된 글꼴·줄바꿈·긴 제목을 검증하지 않으면 깨진 카드만 빠르게 만들 뿐이야. 정적·동적 페이지의 title, description, canonical, OG 이미지를 배포 URL에서 crawler 방식으로 요청해. 긴 제목·한글·없는 이미지·초안 상태를 넣어 카드가 어떻게 실패하는지 보고, sitemap과 실제 공개 라우트가 서로 누락 없이 맞는지 비교해.

정상·404·권한 제한 경로의 원본 HTML에서 제목, canonical, robots, Open Graph 값을 확인해. 화면만 맞고 검색·공유 계약이 어긋나는 경우를 따로 잡아야 해.

Code

정적 메타데이터와 제목 템플릿·tsx
// app/layout.tsx
import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: { template: '%s | My App', default: 'My App' },
  description: 'Built with Next.js',
  openGraph: {
    title: 'My App',
    description: 'Built with Next.js',
    url: 'https://myapp.com',
    siteName: 'My App',
    type: 'website',
  },
  twitter: { card: 'summary_large_image' },
};
블로그 글의 동적 메타데이터·tsx
// app/blog/[slug]/page.tsx
import type { Metadata } from 'next';

type Props = { params: Promise<{ slug: string }> };

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { slug } = await params;
  const post = await getPost(slug);
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: { images: [post.ogImage] },
  };
}
JSX로 동적 Open Graph 이미지 만들기·tsx
// app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og';

export const size = { width: 1200, height: 630 };
export const contentType = 'image/png';

export default async function OG({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug);
  return new ImageResponse(
    (
      <div style={{
        fontSize: 64, background: '#0f172a', color: 'white',
        width: '100%', height: '100%', padding: 48, display: 'flex',
        flexDirection: 'column', justifyContent: 'flex-end',
      }}>
        <div>{post.title}</div>
      </div>
    ),
    { ...size }
  );
}

External links

Exercise

정적 페이지에는 metadata, 동적 페이지에는 generateMetadata, 한 구간에는 opengraph-image.tsx를 추가해. 배포된 공유 카드까지 확인해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.