icon
13장 : SEO 및 메타데이터

Open Graph 태그 활용

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

Open Graph 태그는 HTML <head> 섹션에 포함되는 메타 태그의 한 종류로, 웹 페이지의 콘텐츠에 대한 구조화된 정보를 제공하여 소셜 미디어 플랫폼이 해당 콘텐츠를 올바르게 "이해"하고 멋지게 표시할 수 있도록 돕습니다. Next.js App Router에서는 이러한 Open Graph 태그를 손쉽게 설정할 수 있는 강력한 방법을 제공합니다.

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


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://yourwebsite.com/images/seo-guide-og.png" />
  • og:url: (필수) 웹 페이지의 표준 URL입니다. 이 URL은 페이지에 접근할 수 있는 정식 주소여야 합니다.
    <meta property="og:url" content="https://yourwebsite.com/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 태그 설정하기

Next.js App Router에서는 13장 1절 "정적 메타데이터 설정" 및 13장 2절 "동적 메타데이터 생성"에서 다룬 metadata 객체를 사용하여 Open Graph 태그를 설정합니다. metadata.openGraph 객체 안에 필요한 속성들을 정의하면 Next.js가 자동으로 HTML <head>에 해당하는 <meta property="og:..." /> 태그를 생성해 줍니다.

정적 Open Graph 태그 설정

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

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

export const metadata: Metadata = {
  // ... (기존 title, description, keywords 등) ...

  // Open Graph 설정
  openGraph: {
    title: '나만의 Next.js 웹사이트',
    description: 'Next.js App Router를 활용하여 구축된 현대적인 웹사이트입니다.',
    url: 'https://yourwebsite.com', // 웹사이트의 기본 URL
    siteName: 'Next.js 데모 사이트', // 웹사이트 이름
    images: [
      {
        url: 'https://yourwebsite.com/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 설정이 루트 레이아웃의 설정을 덮어쓰거나 병합합니다.

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

// 가상의 상품 데이터 조회 함수
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: { id: string };
  searchParams: { [key: string]: string | string[] | undefined };
};

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

  if (!product) {
    return { title: '상품을 찾을 수 없습니다.' };
  }

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

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

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

  if (!product) {
    return <div>상품을 찾을 수 없습니다.</div>;
  }

  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>
  );
}

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

  • 고품질 이미지 사용: og:image는 소셜 미디어 미리보기의 핵심입니다. 고해상도(최소 1200x630px), 시각적으로 매력적이며, 페이지 내용을 잘 나타내는 이미지를 사용해야 합니다.
  • 텍스트 오버레이 피하기: 이미지 위에 중요한 텍스트를 직접 오버레이하는 것은 피하는 것이 좋습니다. 소셜 미디어 플랫폼은 이미지를 축소하거나 잘라낼 수 있기 때문입니다.
  • 절대 URL 사용: og:urlog:image 속성에는 반드시 https://로 시작하는 절대 URL을 사용해야 합니다. 상대 경로는 인식되지 않습니다.
  • 캐시 문제 해결: 소셜 미디어 플랫폼은 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 태그는 웹 페이지의 소셜 미디어 가시성과 매력을 극대화하는 강력한 도구입니다. Next.js App Router의 내장된 메타데이터 기능을 활용하면 이러한 태그들을 쉽게 관리하고, 사용자에게 최적화된 공유 경험을 제공할 수 있습니다.