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에서 이를 설정하는 방법, 그리고 효과적인 활용 전략에 대해 상세히 알아보겠습니다.
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_KRfor 한국어,en_USfor 미국 영어).<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:type이article일 경우): 특정og:type에 따라 추가적인 메타 속성을 정의할 수 있습니다.
Next.js App Router에서 Open Graph 태그 설정하기
반복되는 브랜드 정보와 데이터별 정보를 나누면 최종 공유 카드가 페이지 내용과 일치한다.
- rootapp/layout.tsx
siteName·locale·기본 이미지 같은 전역값
- sectionblog/layout.tsx
문서·블로그 등 섹션별 기본 문맥
- pagegenerateMetadata
slug·id 데이터로 title·image를 생성
- outputFinal 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에 설정합니다.
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 설정이 루트 레이아웃의 설정을 덮어쓰거나 병합합니다.
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 태그 활용 전략
운영 환경에서는 태그를 작성하는 것만큼 공유 디버거로 확인하고 플랫폼 캐시를 갱신하는 절차도 중요합니다.
공유 플랫폼은 캐시와 크롤러 정책을 갖고 있으므로, 실제 head 출력과 카드 결과가 같은지 끝까지 확인해야 한다.
- 태그 작성
title, description, image, url을 페이지 데이터와 맞춘다.
- 배포 URL
외부에서 접근 가능한 절대 URL과 이미지 응답을 확인한다.
- head 출력
meta property 값이 배포된 HTML에 있는지 본다.
- 카드 확인
Facebook, X, Slack, Kakao 등 주요 채널에서 미리보기를 본다.
- 캐시 갱신
수정 후 디버거가 새 값을 다시 수집하도록 만든다.
- https 절대 URL, 1200x630 비율, 200 응답
미지 URL https 절대 URL, 1200x630 비율, 200 응답 상대 경로, 403, 작은 썸네일이면 카드 품질이 바로 흔들린다.
- 제목은 짧고 설명은 클릭 이유를 보충
텍스트 제목은 짧고 설명은 클릭 이유를 보충 본문 첫 줄을 임의 추출하면 플랫폼마다 다른 카드가 만들어진다.
- 코드 배포와 플랫폼 카드가 같은 시점인지 확인
캐시 코드 배포와 플랫폼 카드가 같은 시점인지 확인 캐시를 갱신하지 않으면 예전 카드가 계속 노출될 수 있다.
- 고품질 이미지 사용:
og:image는 소셜 미디어 미리보기의 핵심입니다. 고해상도(최소 1200x630px), 시각적으로 매력적이며, 페이지 내용을 잘 나타내는 이미지를 사용해야 합니다. - 텍스트 오버레이 피하기: 이미지 위에 중요한 텍스트를 직접 오버레이하는 것은 피하는 것이 좋습니다. 소셜 미디어 플랫폼은 이미지를 축소하거나 잘라낼 수 있기 때문입니다.
- 최종 절대 URL 확인: 소셜 플랫폼에 출력되는
og:url과og:image는 절대 URL이어야 합니다. Next.js Metadata API에서는metadataBase를 설정한 뒤 상대 경로를 사용해도 절대 URL로 해석되므로, 생성된<meta>값이 올바른 공개 주소인지 확인합니다. - 캐시 문제 해결: 소셜 미디어 플랫폼은 OG 태그를 캐싱하는 경향이 있습니다. 태그를 수정한 후에는 해당 플랫폼의 디버깅 도구를 사용하여 캐시를 새로고침해야 변경 사항이 적용됩니다.
- Facebook Sharing Debugger: https://developers.facebook.com/tools/debug/
- X (Twitter) Card Validator: https://cards-dev.twitter.com/validator
- 다양한
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 필드를 우선 선택해야 하는지 정리한 것입니다.
모든 페이지에 같은 기본 이미지와 설명을 쓰면 공유 카드의 구분력이 떨어진다. 사용자가 판단해야 할 정보를 유형별로 앞에 둔다.
- website브랜드 첫인상
site_name, title, image · 서비스 정체성이 보이지 않으면 하위 페이지처럼 느껴진다.
- article읽을 이유
title, description, published_time · 본문 첫 문장만 노출되면 글의 맥락이 약해진다.
- product상품 식별
title, image, url · 상품명과 대표 이미지가 없으면 무엇을 공유했는지 알기 어렵다.
- video재생 기대
video, image, duration · 영상 신호가 빠지면 일반 링크처럼 보여 클릭 동기가 줄어든다.
- profile인물 식별
title, description, image · 이름만 남으면 소속, 역할, 신뢰 맥락이 빠진다.
아래 다이어그램은 Open Graph 태그를 작성한 뒤 배포, head 출력 확인, 플랫폼 캐시 갱신까지 이어지는 검증 흐름을 정리한 것입니다.
로컬 metadata 객체가 맞아도 배포 URL과 플랫폼 캐시에서 다르게 보일 수 있다. 검증 단계를 고정해야 누락을 줄인다.
- 1. 빌드metadata 타입
필수 OG 필드가 누락되지 않았는지 확인한다. · TypeScript
- 2. URLcanonical과 이미지
외부 크롤러가 절대 URL을 200 응답으로 읽는다. · curl
- 3. head실제 meta 출력
배포 HTML에 본문 데이터와 같은 값이 들어간다. · View Source
- 4. 카드플랫폼 미리보기
미지 비율과 텍스트 잘림이 허용 범위다. · Debugger
- 5. 캐시예전 카드 제거
수정 후 디버거 재수집으로 새 카드가 보인다. · Refresh
아래 다이어그램은 Open Graph 제목, 설명, 이미지, canonical URL이 공유 미리보기 품질로 이어지는 점검 기준입니다.
metadata 선언만 보지 않고 crawler가 실제로 읽는 절대 URL과 이미지 결과를 검증한다.
- pageMetadata 생성
title·description·canonical을 route 데이터로 구성
- assetOG image
절대 URL·권장 비율·읽을 수 있는 대비 확보
- crawler공개 접근
인증·robots·redirect가 수집을 막지 않는지 확인
- previewCache 확인
플랫폼 debugger에서 최신 태그를 다시 수집
- fallback기본 카드
미지 실패에도 제목과 설명이 남음
아래 다이어그램은 Open Graph의 title, description, image, URL이 공유 카드로 이어지는 구성을 보여줍니다.
Open Graph 태그는 공유 미리보기의 제목, 설명, 이미지를 명확히 전달하는 메타데이터입니다.
Next.js App Router의 내장된 메타데이터 기능을 활용하면 이러한 태그들을 쉽게 관리하고, 사용자에게 최적화된 공유 경험을 제공할 수 있습니다.
아래 다이어그램은 Open Graph 태그 활용을 검색 노출, 공유 미리보기, 크롤러 신호 기준으로 점검합니다.
Open Graph는 SEO 전부는 아니지만, 공유 미리보기와 크롤러가 페이지를 해석하는 방식에 직접 영향을 준다.
- 중앙 기준코드 metadata, 배포된 head, 플랫폼 카드가 일치해야 한다
셋 중 하나라도 다르면 사용자가 보는 공유 경험은 아직 검증된 상태가 아니다.
- 검색 노출title, description, canonical
검색 결과와 공유 제목의 의도가 같은지 확인한다.
- 공유 카드og:title, og:description, og:image
카드만 봐도 클릭 이유와 페이지 주제가 보인다.
- 크롤러절대 URL, 접근 권한, 이미지 응답
외부 플랫폼이 200 응답으로 head와 이미지를 읽는다.
- 운영 갱신디버거 결과와 플랫폼 캐시
수정된 값이 실제 카드에 반영되는지 다시 수집한다.