본문으로 건너뛰기

안동민 개발노트

본문 시작

Open Graph 태그 활용

Open Graph의 제목·설명·URL·이미지 속성을 정적·동적 메타데이터로 설정해 공유 링크 미리보기를 제어합니다.

웹 페이지의 내용이 소셜 미디어 플랫폼(페이스북, X(트위터), 링크드인 등)에서 공유될 때, 단순히 링크만 표시되는 것보다 풍부한 미리보기(제목, 설명, 이미지)가 함께 표시될 때 사용자의 클릭을 유도할 확률이 훨씬 높아집니다.

이러한 미리보기를 제어하는 표준 프로토콜이 바로 Open Graph (OG) 프로토콜입니다.

Open Graph 태그는 HTML <head>에 포함되는 메타 태그로, 소셜 미디어 플랫폼이 페이지 콘텐츠를 해석하고 미리보기로 표시하는 데 필요한 구조화된 정보를 제공합니다.

Next.js App Router는 Open Graph 설정 API를 제공합니다.

이 절에서는 Open Graph 태그의 중요성, 주요 속성, Next.js App Router에서 이를 설정하는 방법, 그리고 효과적인 활용 전략에 대해 상세히 알아보겠습니다.

OG 태그는 head의 메타데이터를 공유 카드로 바꾼다

플랫폼은 링크를 열기 전에 HTML의 Open Graph 값을 읽고 카드 제목·설명·이미지를 구성한다.

  1. 1
    핵심 1

    OG 태그는 head의 메타데이터를 공유 카드로 바꾼다

  2. 2
    핵심 2

    OG 태그는 head의 메타데이터를 공유 카드로 바꾼다

  3. 3
    핵심 3

    플랫폼은 링크를 열기 전에 HTML의 Open Graph 값을 읽고 카드 제목·설명·이미지를 구성한다.

  4. 4
    핵심 4

    HTML head og:title/description/image 원본 값 Crawler 링크 미리 읽기 캐시 가능 Card 이미지+제목+설명 사용자에게 노출…


Open Graph 프로토콜이란?

Open Graph 프로토콜은 페이스북이 웹 페이지를 소셜 그래프의 객체로 통합하기 위해 2010년에 도입한 기술입니다.

이후 많은 소셜 미디어 플랫폼과 메신저 앱(카카오톡, 슬랙 등)이 이 프로토콜을 채택하여 링크 공유 시 사용자에게 일관되고 풍부한 시각적 경험을 제공하고 있습니다.

OG 태그가 제대로 설정되지 않으면, 소셜 미디어 플랫폼은 페이지의 제목, 설명, 이미지를 임의로 추출하여 표시할 수 있으며, 이 경우 미리보기가 어색하거나 정보가 누락되어 링크 클릭률이 낮아질 수 있습니다.


주요 Open Graph 태그 속성

OG 태그는 og: 접두사를 사용하여 정의됩니다.

가장 일반적으로 사용되는 필수 및 선택적 속성은 다음과 같습니다.

  • og:title: (필수) 웹 페이지의 제목입니다. 50~60자 이내로 간결하고 매력적으로 작성하는 것이 좋습니다.
    <meta property="og:title" content="Next.js로 배우는 SEO 가이드" />
  • og:type: (필수) 웹 페이지 콘텐츠의 유형입니다. website (일반 웹사이트), article (블로그 게시물, 기사), book, profile, video.movie 등 다양한 타입이 있습니다.
    <meta property="og:type" content="article" />
  • og:image: (필수) 소셜 미디어 미리보기에서 표시될 이미지의 URL입니다. 이미지는 고품질이어야 하며, 적절한 크기(권장: 1200x630px)와 비율(1.91:1)을 유지하는 것이 좋습니다. 절대 경로(Full URL)를 사용해야 합니다.
    <meta property="og:image" content="https://commerce-lab.dev/images/seo-guide-og.png" />
  • og:url: (필수) 웹 페이지의 표준 URL입니다. 이 URL은 페이지에 접근할 수 있는 정식 주소여야 합니다.
    <meta property="og:url" content="https://commerce-lab.dev/blog/nextjs-seo-guide" />
  • og:description: (선택) 웹 페이지의 간략한 설명입니다. 150~160자 이내로 작성하는 것이 좋으며, 제목과 함께 사용자가 클릭할지 여부를 결정하는 데 중요한 역할을 합니다.
    <meta property="og:description" content="Next.js App Router에서 Open Graph 태그를 활용하여 소셜 미디어 공유를 최적화하는 방법을 알아봅니다." />
  • og:site_name: (선택) 웹사이트의 이름입니다. 제목과 중복되지 않게 웹사이트의 브랜드를 나타냅니다.
    <meta property="og:site_name" content="My Next.js Blog" />
  • og:locale: (선택) 콘텐츠의 언어 및 지역입니다 (예: ko_KR for 한국어, en_US for 미국 영어).
    <meta property="og:locale" content="ko_KR" />
  • og:image:width, og:image:height: (선택) og:image에 지정된 이미지의 너비와 높이(픽셀)입니다. 이 정보를 제공하면 소셜 미디어 플랫폼이 이미지를 미리 로드하고 레이아웃 시프트 없이 올바르게 표시하는 데 도움이 됩니다.
    <meta property="og:image:width" content="1200" />
    <meta property="og:image:height" content="630" />
  • article:published_time, article:author 등 (og:typearticle일 경우): 특정 og:type에 따라 추가적인 메타 속성을 정의할 수 있습니다.

Next.js App Router에서 Open Graph 태그 설정하기

Open Graph 값은 route 계층에서 가까운 곳이 덮어쓴다

반복되는 브랜드 정보와 데이터별 정보를 나누면 최종 공유 카드가 페이지 내용과 일치한다.

  1. root
    app/layout.tsx

    siteName·locale·기본 이미지 같은 전역값

  2. section
    blog/layout.tsx

    문서·블로그 등 섹션별 기본 문맥

  3. page
    generateMetadata

    slug·id 데이터로 title·image를 생성

  4. output
    Final head

    까운 값이 덮어써 og:title·image·url 확정

Next.js App Router에서는 13장 1절 정적 메타데이터 설정과 13장 2절 동적 메타데이터 생성에서 다룬 metadata 객체를 사용해 Open Graph 태그를 설정합니다.

metadata.openGraph 객체 안에 필요한 속성을 정의하면, Next.js가 자동으로 HTML <head>에 해당하는 <meta property="og:..." /> 태그를 생성해 줍니다.

정적 Open Graph 태그 설정

애플리케이션 전체의 기본 Open Graph 정보를 루트 레이아웃인 src/app/layout.tsx에 설정합니다.

src/app/layout.tsx
import type { Metadata } from 'next';
// ... (기존 임포트 및 폰트 설정) ...

export const metadata: Metadata = {
  metadataBase: new URL('https://commerce-lab.dev'),
  // ... (기존 title, description, keywords 등) ...

  // Open Graph 설정
  openGraph: {
    title: '나만의 Next.js 웹사이트',
    description: 'Next.js App Router를 활용하여 구축된 현대적인 웹사이트입니다.',
    url: 'https://commerce-lab.dev', // 웹사이트의 기본 URL
    siteName: 'Next.js 데모 사이트', // 웹사이트 이름
    images: [
      {
        url: 'https://commerce-lab.dev/og-default.png', // 기본 OG 이미지 (공유될 때 표시)
        width: 1200,
        height: 630,
        alt: 'Next.js 데모 사이트 로고',
      },
    ],
    locale: 'ko_KR',
    type: 'website',
  },

  // ... (twitter, robots 등 기타 메타데이터) ...
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ko">
      <body>{children}</body>
    </html>
  );
}

동적 Open Graph 태그 생성

블로그 게시물이나 상품 상세 페이지처럼 URL 파라미터에 따라 내용이 달라지는 경우, generateMetadata 함수를 사용하여 Open Graph 태그를 동적으로 생성합니다.

이 경우, 페이지/레이아웃 레벨의 openGraph 설정이 루트 레이아웃의 설정을 덮어쓰거나 병합합니다.

src/app/products/[id]/page.tsx
import type { Metadata, ResolvingMetadata } from 'next';
import { notFound } from 'next/navigation';

// 가상의 상품 데이터 조회 함수
async function getProduct(id: string) {
  const products = [
    { id: '1', name: '고급 Next.js 신발', description: '편안함과 스타일을 모두 잡은 신발입니다.', price: 120000, imageUrl: 'https://example.com/images/shoe1.jpg' },
    { id: '2', name: 'React 개발자 후드티', description: '개발자를 위한 편안하고 트렌디한 후드티입니다.', price: 55000, imageUrl: 'https://example.com/images/hoodie2.jpg' },
  ];
  return products.find(p => p.id === id);
}

type Props = {
  params: Promise<{ id: string }>;
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
};

export async function generateMetadata(
  { params }: Props,
  parent: ResolvingMetadata // 상위 메타데이터 (루트 layout.tsx 등)
): Promise<Metadata> {
  const { id } = await params;
  const product = await getProduct(id);

  if (!product) {
    notFound();
  }

  // 상위 openGraph 이미지를 가져와 현재 이미지와 병합
  const previousImages = (await parent).openGraph?.images || [];

  return {
    title: product.name,
    description: product.description,
    openGraph: {
      title: product.name,
      description: product.description,
      url: `https://commerce-lab.dev/products/${product.id}`,
      siteName: 'Next.js 쇼핑몰', // 웹사이트 이름 (필요에 따라 덮어쓸 수 있음)
      images: [
        {
          url: product.imageUrl,
          width: 800,
          height: 600,
          alt: product.name,
        },
        ...previousImages, // 기본 OG 이미지도 함께 포함 (선택 사항)
      ],
      type: 'website', // Metadata 타입이 지원하는 Open Graph 유형을 사용
    },
    // Twitter Card도 비슷하게 설정 가능
    twitter: {
      card: 'summary_large_image',
      title: product.name,
      description: product.description,
      images: [product.imageUrl],
    }
  };
}

export default async function ProductDetailPage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  const product = await getProduct(id);

  if (!product) {
    notFound();
  }

  return (
    <div style={{ padding: '20px', maxWidth: '800px', margin: '20px auto', border: '1px solid #FF5722', borderRadius: '10px', boxShadow: '0 4px 8px rgba(0,0,0,0.1)' }}>
      <h1 style={{ color: '#FF5722', textAlign: 'center', marginBottom: '20px' }}>{product.name}</h1>
      <p style={{ textAlign: 'center', fontSize: '1.2em', color: '#666' }}>가격: {product.price.toLocaleString()}원</p>
      {product.imageUrl && (
        <img src={product.imageUrl} alt={product.name} style={{ maxWidth: '100%', height: 'auto', display: 'block', margin: '20px auto' }} />
      )}
      <p style={{ lineHeight: 1.6, fontSize: '1.1em', color: '#333' }}>{product.description}</p>
    </div>
  );
}

Next.js의 Metadata 타입에는 Open Graph의 product 유형이 없습니다.

상품명·가격·재고처럼 검색 엔진에 전달할 상품 의미는 Product JSON-LD로 별도 표현합니다.


효과적인 Open Graph 태그 활용 전략

운영 환경에서는 태그를 작성하는 것만큼 공유 디버거로 확인하고 플랫폼 캐시를 갱신하는 절차도 중요합니다.

OG 태그는 작성보다 배포 후 확인이 더 중요하다

공유 플랫폼은 캐시와 크롤러 정책을 갖고 있으므로, 실제 head 출력과 카드 결과가 같은지 끝까지 확인해야 한다.

  1. 태그 작성

    title, description, image, url을 페이지 데이터와 맞춘다.

  2. 배포 URL

    외부에서 접근 가능한 절대 URL과 이미지 응답을 확인한다.

  3. head 출력

    meta property 값이 배포된 HTML에 있는지 본다.

  4. 카드 확인

    Facebook, X, Slack, Kakao 등 주요 채널에서 미리보기를 본다.

  5. 캐시 갱신

    수정 후 디버거가 새 값을 다시 수집하도록 만든다.

  6. https 절대 URL, 1200x630 비율, 200 응답

    미지 URL https 절대 URL, 1200x630 비율, 200 응답 상대 경로, 403, 작은 썸네일이면 카드 품질이 바로 흔들린다.

  7. 제목은 짧고 설명은 클릭 이유를 보충

    텍스트 제목은 짧고 설명은 클릭 이유를 보충 본문 첫 줄을 임의 추출하면 플랫폼마다 다른 카드가 만들어진다.

  8. 코드 배포와 플랫폼 카드가 같은 시점인지 확인

    캐시 코드 배포와 플랫폼 카드가 같은 시점인지 확인 캐시를 갱신하지 않으면 예전 카드가 계속 노출될 수 있다.

  • 고품질 이미지 사용: og:image는 소셜 미디어 미리보기의 핵심입니다. 고해상도(최소 1200x630px), 시각적으로 매력적이며, 페이지 내용을 잘 나타내는 이미지를 사용해야 합니다.
  • 텍스트 오버레이 피하기: 이미지 위에 중요한 텍스트를 직접 오버레이하는 것은 피하는 것이 좋습니다. 소셜 미디어 플랫폼은 이미지를 축소하거나 잘라낼 수 있기 때문입니다.
  • 최종 절대 URL 확인: 소셜 플랫폼에 출력되는 og:urlog:image는 절대 URL이어야 합니다. Next.js Metadata API에서는 metadataBase를 설정한 뒤 상대 경로를 사용해도 절대 URL로 해석되므로, 생성된 <meta> 값이 올바른 공개 주소인지 확인합니다.
  • 캐시 문제 해결: 소셜 미디어 플랫폼은 OG 태그를 캐싱하는 경향이 있습니다. 태그를 수정한 후에는 해당 플랫폼의 디버깅 도구를 사용하여 캐시를 새로고침해야 변경 사항이 적용됩니다.
  • 다양한 og:type 활용: 페이지의 성격에 맞는 og:type을 설정하여 검색 엔진과 소셜 미디어 플랫폼에 더 정확한 정보를 제공합니다. 예를 들어, 동영상 페이지에는 video.movie, 레시피 페이지에는 article 또는 website 타입을 사용하고 추가적인 article: 속성을 활용할 수 있습니다.
  • Twitter Card와 함께 사용: 트위터는 자체 twitter: 메타 태그를 사용하지만, og: 태그를 폴백(fallback)으로 활용합니다. 따라서 og: 태그를 잘 설정해두면 트위터에서도 기본적인 미리보기가 가능합니다. 다만 트위터 고유의 카드 타입을 활용하려면 twitter: 태그를 별도로 설정하는 것이 좋습니다 (예: summary_large_image).
  • og:locale 설정: 다국어 사이트의 경우 og:locale을 사용하여 콘텐츠의 기본 언어를 명시하고, og:locale:alternate를 사용하여 다른 언어 버전을 지정할 수 있습니다.

다음 다이어그램은 페이지 유형에 따라 어떤 Open Graph 필드를 우선 선택해야 하는지 정리한 것입니다.

페이지 유형별 Open Graph 필드

모든 페이지에 같은 기본 이미지와 설명을 쓰면 공유 카드의 구분력이 떨어진다. 사용자가 판단해야 할 정보를 유형별로 앞에 둔다.

  1. website
    브랜드 첫인상

    site_name, title, image · 서비스 정체성이 보이지 않으면 하위 페이지처럼 느껴진다.

  2. article
    읽을 이유

    title, description, published_time · 본문 첫 문장만 노출되면 글의 맥락이 약해진다.

  3. product
    상품 식별

    title, image, url · 상품명과 대표 이미지가 없으면 무엇을 공유했는지 알기 어렵다.

  4. video
    재생 기대

    video, image, duration · 영상 신호가 빠지면 일반 링크처럼 보여 클릭 동기가 줄어든다.

  5. profile
    인물 식별

    title, description, image · 이름만 남으면 소속, 역할, 신뢰 맥락이 빠진다.

아래 다이어그램은 Open Graph 태그를 작성한 뒤 배포, head 출력 확인, 플랫폼 캐시 갱신까지 이어지는 검증 흐름을 정리한 것입니다.

Open Graph 배포 검증 파이프라인

로컬 metadata 객체가 맞아도 배포 URL과 플랫폼 캐시에서 다르게 보일 수 있다. 검증 단계를 고정해야 누락을 줄인다.

  1. 1. 빌드
    metadata 타입

    필수 OG 필드가 누락되지 않았는지 확인한다. · TypeScript

  2. 2. URL
    canonical과 이미지

    외부 크롤러가 절대 URL을 200 응답으로 읽는다. · curl

  3. 3. head
    실제 meta 출력

    배포 HTML에 본문 데이터와 같은 값이 들어간다. · View Source

  4. 4. 카드
    플랫폼 미리보기

    미지 비율과 텍스트 잘림이 허용 범위다. · Debugger

  5. 5. 캐시
    예전 카드 제거

    수정 후 디버거 재수집으로 새 카드가 보인다. · Refresh

아래 다이어그램은 Open Graph 제목, 설명, 이미지, canonical URL이 공유 미리보기 품질로 이어지는 점검 기준입니다.

Open Graph 품질은 URL에서 공유 카드까지 이어서 확인한다

metadata 선언만 보지 않고 crawler가 실제로 읽는 절대 URL과 이미지 결과를 검증한다.

  1. page
    Metadata 생성

    title·description·canonical을 route 데이터로 구성

  2. asset
    OG image

    절대 URL·권장 비율·읽을 수 있는 대비 확보

  3. crawler
    공개 접근

    인증·robots·redirect가 수집을 막지 않는지 확인

  4. preview
    Cache 확인

    플랫폼 debugger에서 최신 태그를 다시 수집

  5. fallback
    기본 카드

    미지 실패에도 제목과 설명이 남음

아래 다이어그램은 Open Graph의 title, description, image, URL이 공유 카드로 이어지는 구성을 보여줍니다.

플랫폼은 OG 필드를 읽어 하나의 공유 카드로 조립한다

태그는 코드 안에 흩어져 있지만 사용자는 한 장의 카드로 본다.

  1. 1
    핵심 1

    플랫폼은 OG 필드를 읽어 하나의 공유 카드로 조립한다

  2. 2
    핵심 2

    플랫폼은 OG 필드를 읽어 하나의 공유 카드로 조립한다

  3. 3
    핵심 3

    태그는 코드 안에 흩어져 있지만 사용자는 한 장의 카드로 본다.

  4. 4
    핵심 4

    og:image 시각 영역 가장 먼저 보임 og:title 클릭 제목 핵심 약속 og:description 판단 문장 보조 맥락 og:url 정규 링크 공유 대상…

Open Graph 태그는 공유 미리보기의 제목, 설명, 이미지를 명확히 전달하는 메타데이터입니다.

Next.js App Router의 내장된 메타데이터 기능을 활용하면 이러한 태그들을 쉽게 관리하고, 사용자에게 최적화된 공유 경험을 제공할 수 있습니다.

아래 다이어그램은 Open Graph 태그 활용을 검색 노출, 공유 미리보기, 크롤러 신호 기준으로 점검합니다.

Open Graph 최종 점검표

Open Graph는 SEO 전부는 아니지만, 공유 미리보기와 크롤러가 페이지를 해석하는 방식에 직접 영향을 준다.

  1. 중앙 기준
    코드 metadata, 배포된 head, 플랫폼 카드가 일치해야 한다

    셋 중 하나라도 다르면 사용자가 보는 공유 경험은 아직 검증된 상태가 아니다.

  2. 검색 노출
    title, description, canonical

    검색 결과와 공유 제목의 의도가 같은지 확인한다.

  3. 공유 카드
    og:title, og:description, og:image

    카드만 봐도 클릭 이유와 페이지 주제가 보인다.

  4. 크롤러
    절대 URL, 접근 권한, 이미지 응답

    외부 플랫폼이 200 응답으로 head와 이미지를 읽는다.

  5. 운영 갱신
    디버거 결과와 플랫폼 캐시

    수정된 값이 실제 카드에 반영되는지 다시 수집한다.