← 전체 글로 돌아가기

Next.js

Next.js 배포 후 메타데이터가 이상할 때

블로그 글의 제목, 설명, OG 이미지가 SNS에서 제대로 안 보인다면, 메타데이터 태그를 확인해야 한다.

블로그 글을 공유했는데 Facebook이나 Twitter에서 제목이 이상하게 보인다고 한다. 이럴 땐 메타데이터를 확인해야 한다.

배포된 페이지의 메타데이터 확인하기

# 공개 URL의 HTML 헤더 확인
curl -s https://example.com/blog/my-post | head -50

# 또는 grep으로 필터링
curl -s https://example.com/blog/my-post | grep -E '<title>|<meta|og:|twitter:'

HTML의 <head> 섹션에 다음이 있어야 한다:

<title>글의 제목</title>
<meta name="description" content="글의 요약" />
<meta property="og:title" content="글의 제목" />
<meta property="og:description" content="글의 요약" />
<meta property="og:image" content="이미지 URL" />
<meta property="og:url" content="https://example.com/blog/my-post" />
<meta name="twitter:card" content="summary_large_image" />

이 태그들이 없거나 잘못되면 SNS에서 제대로 표시되지 않는다.

Next.js에서 메타데이터 설정하기

// app/blog/[slug]/page.tsx
import { Metadata } from 'next';

export async function generateMetadata({
  params,
}: {
  params: { slug: string };
}): Promise<Metadata> {
  const post = await fetchPost(params.slug);

  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      url: `https://example.com/blog/${post.slug}`,
      images: [
        {
          url: post.ogImage,
          width: 1200,
          height: 630,
        },
      ],
    },
    twitter: {
      card: 'summary_large_image',
      title: post.title,
      description: post.excerpt,
      images: [post.ogImage],
    },
  };
}

Pages Router를 쓰면:

// pages/blog/[slug].tsx
import Head from 'next/head';

export default function Post({ post }) {
  return (
    <>
      <Head>
        <title>{post.title}</title>
        <meta name="description" content={post.excerpt} />
        <meta property="og:title" content={post.title} />
        <meta property="og:description" content={post.excerpt} />
        <meta property="og:image" content={post.ogImage} />
        <meta property="og:url" content={`https://example.com/blog/${post.slug}`} />
        <meta name="twitter:card" content="summary_large_image" />
        <meta name="twitter:title" content={post.title} />
        <meta name="twitter:description" content={post.excerpt} />
        <meta name="twitter:image" content={post.ogImage} />
      </Head>
      <article>{post.content}</article>
    </>
  );
}

빌드 후 메타데이터 확인하기

# 로컬 개발 서버에서 확인
npm run dev
curl http://localhost:3000/blog/my-post | grep og:

# 빌드 후 프로덕션 빌드 서버에서 확인
npm run build
npm run start
curl http://localhost:3000/blog/my-post | grep og:

로컬에서는 괜찮은데 배포 후 이상하면 배포 환경의 설정을 확인해야 한다.

동적 페이지의 메타데이터

동적 페이지 [slug]에서 메타데이터를 가져올 때는 데이터 소스가 접근 가능한지 확인한다.

export async function generateMetadata({ params }) {
  try {
    const post = await fetchPost(params.slug);
    // 데이터를 못 가져오면?
    if (!post) {
      return {
        title: 'Post not found',
      };
    }
    return {
      title: post.title,
      // ...
    };
  } catch (error) {
    console.error('Failed to fetch post:', error);
    return {
      title: 'Error loading post',
    };
  }
}

getStaticProps에서 에러가 나면 메타데이터도 제대로 생성되지 않는다.

캐시 문제 확인하기

메타데이터는 동적이므로 캐시 헤더를 주의해야 한다.

# HTML의 캐시 헤더
curl -I https://example.com/blog/my-post

# Cache-Control이 no-cache인지 확인
# 또는 max-age가 짧은지 확인 (600초 이하)

HTML을 오래 캐시하면 메타데이터가 업데이트되지 않는다.

OG 이미지가 없다면

export async function generateMetadata({ params }) {
  const post = await fetchPost(params.slug);

  return {
    openGraph: {
      title: post.title,
      images: post.ogImage || '/default-og-image.png', // 기본값 설정
    },
  };
}

OG 이미지가 없으면 기본 이미지를 사용하도록 설정하면 좋다.

배포 후 메타데이터 검증하기

# 페이스북 공유 디버거
https://developers.facebook.com/tools/debug/sharing/

# 트위터 카드 검증
https://cards-dev.twitter.com/validator

# 또는 curl로 직접 확인
curl -s https://example.com/blog/my-post | grep -E 'og:|twitter:' | head -20

배포 후 이 도구들로 메타데이터가 제대로 생성되는지 확인한다.

문제 해결 체크리스트

  1. <head> 섹션에 메타데이터 태그가 있는가?
  2. og:image URL이 https://로 시작하는가?
  3. 이미지 파일이 실제로 존재하는가?
  4. 캐시 헤더가 no-cache 또는 짧은 max-age인가?
  5. 데이터 소스(API, 데이터베이스)가 빌드 시간에 접근 가능한가?
  6. 동적 페이지라면 에러 처리가 있는가?

이 항목들을 모두 확인하면 메타데이터 문제를 해결할 수 있다.