주요 포인트 한눈에 보기
이 문서는 Next.js App Router 환경에서 metadata API를 사용하는 올바른 방법을 단계별로 설명합니다.
먼저 기본적인 메타데이터 설정 방식을 이해한 뒤, 실제 포트폴리오 코드에 적용된 잘못된 예제를 살펴보고
그 문제점을 분석합니다. 이를 통해 왜 해당 방식이 문제가 되는지와,
App Router에 맞는 올바른 메타데이터 구조를 어떻게 학습하고 적용해야 하는지를 함께 정리합니다.
metadata 개념과 적용 위치
Next.js App Router에서 metadata란, 브라우저와 검색 엔진, 그리고 SNS 크롤러가
페이지를 어떻게 인식해야 하는지를 정의하는 정보 집합입니다.
페이지 제목(title), 설명(description), 검색 엔진 노출 정보(SEO),
그리고 Open Graph·Twitter Card와 같은 공유 미리보기 정보가 여기에 포함됩니다.
SEO 목적의 메타데이터는 metadata API로 관리하는 것이 권장됩니다.
즉, “이 페이지는 어떤 메타 정보를 가진다”라고 객체 형태로 명확히 선언하면,
Next.js가 내부적으로 이를 해석하여 적절한 <head> 태그를 자동으로 생성합니다.
이 구조의 핵심 장점은 메타데이터 관리 위치가 명확해진다는 점입니다.
레이아웃 단위(layout.tsx)에서는 사이트 전체에 공통으로 적용되는 메타 정보를 정의하고,
페이지 단위(page.tsx)에서는 해당 페이지에만 필요한 메타 정보를 추가로 선언할 수 있습니다.
Next.js는 이 정보를 자동으로 병합하여 최종 결과를 만들어 줍니다.
그 결과, 개발자는 SEO, Open Graph, Twitter Card를 각각 따로 신경 쓰며
head 태그를 직접 조작할 필요가 없어지고,
메타데이터를 하나의 일관된 방식으로 안전하게 관리할 수 있게 됩니다.
metadata 넣는 기본 방법
metadata를 넣는 가장 기본적인 방법은 layout.tsx 또는 page.tsx 파일 상단에서
export const metadata를 선언하는 것입니다.
이 객체는 “이 페이지가 어떤 정보로 설명되어야 하는지”를 정의하는 역할을 하며,
Next.js가 이를 기반으로 head 태그를 자동 생성합니다.
중요한 점은, metadata는 실제 렌더링 로직과 분리되어 관리된다는 점입니다.
컴포넌트 내부에서 meta 태그를 직접 작성하지 않고,
파일 단위로 메타 정보를 선언함으로써 구조가 명확해집니다.
layout.tsx에 정의한 metadata는 모든 하위 페이지에 공통으로 적용되며,
page.tsx에 metadata를 추가로 선언하면 해당 페이지의 메타 정보만 덮어쓰는 방식으로 동작합니다.
이 병합 규칙 덕분에 페이지별 SEO 설정을 안전하게 관리할 수 있습니다.
import type { Metadata } from "next";
/**
* ✅ App Router 표준 metadata 구성 예제
* - SEO
* - Open Graph (카카오톡, 페이스북 등)
* - Twitter Card
* - 검색엔진 크롤링
* - 아이콘 / 앱 정보
*/
export const metadata: Metadata = {
/**
* 기준 URL
* - 상대 경로로 작성된 og:image, canonical 계산에 사용
*/
metadataBase: new URL("https://example.com"),
/**
* 기본 SEO 메타
*/
title: "STYNA - 깔끔한 스타일 쇼핑몰",
description: "최신 패션 트렌드를 만나보세요",
keywords: [
"패션 쇼핑몰",
"온라인 쇼핑",
"의류",
"STYNA",
"트렌드 패션",
],
/**
* 문서 작성자 / 발행자 정보
*/
authors: [{ name: "STYNA" }],
creator: "STYNA",
publisher: "STYNA",
/**
* 검색 엔진 크롤링 제어
*/
robots: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
"max-image-preview": "large",
"max-snippet": -1,
"max-video-preview": -1,
},
},
/**
* Open Graph
* - 카카오톡, 페이스북, 슬랙 미리보기
*/
openGraph: {
type: "website",
url: "https://example.com",
siteName: "STYNA",
title: "STYNA - 깔끔한 스타일 쇼핑몰",
description: "최신 패션 트렌드를 만나보세요",
locale: "ko_KR",
images: [
{
url: "/og-image.png",
width: 1200,
height: 630,
alt: "STYNA 쇼핑몰 미리보기 이미지",
},
],
},
/**
* Twitter Card
*/
twitter: {
card: "summary_large_image",
title: "STYNA - 깔끔한 스타일 쇼핑몰",
description: "최신 패션 트렌드를 만나보세요",
images: ["/og-image.png"],
},
/**
* 아이콘 / 파비콘
*/
icons: {
icon: [
{ url: "/favicon.ico", sizes: "16x16" },
{ url: "/favicon.ico", sizes: "32x32" },
],
apple: [{ url: "/apple-touch-icon.png", sizes: "180x180" }],
shortcut: "/favicon.ico",
},
/**
* 사이트 인증 (필요 시)
*/
verification: {
google: "google-site-verification-code",
other: {
"naver-site-verification": "naver-site-verification-code",
},
},
/**
* PWA / 앱 관련
*/
applicationName: "STYNA",
themeColor: "#000000",
};
위 예시는 App Router 환경에서 가장 많이 사용되는 기본 메타데이터 구성입니다.
실무에서는 프로젝트 규모와 목적에 따라 항목을 추가하거나 단순화할 수 있지만,
이 구조를 기준으로 이해하면 metadata API의 역할을 빠르게 파악할 수 있습니다.
내 포트폴리오 코드 구조
아래 코드는 실제 포트폴리오 프로젝트에서 사용하던 layout.tsx 구조를
메타데이터 학습 목적에 맞게 요약하여 정리한 예시입니다.
metadata API를 사용하고 있음에도 불구하고,
head 태그를 직접 작성하여 중복된 메타 관리가 발생하던 구조입니다.
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({
variable: "--font-inter",
subsets: ["latin"],
display: "swap",
});
export const metadata: Metadata = {
metadataBase: new URL("https://example.com"),
title: "STYNA - 깔끔한 스타일 쇼핑몰",
description: "최신 패션 트렌드를 만나보세요",
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
<head>
<meta name="description" content="최신 패션 트렌드" />
</head>
<body className={inter.variable}>{children}</body>
</html>
);
}
문제점 분석
metadata API와 head 수동 선언이 동시에 존재하면서
동일한 메타 정보가 중복 정의되고 있습니다.
이로 인해 크롤러는 어떤 메타 정보를 기준으로 해석해야 하는지
명확히 판단하기 어려워집니다.
해결 방법 정리
해결 방법은 명확합니다.
SEO, Open Graph, 아이콘, 테마 컬러와 같은 메타 정보는
모두 metadata API로 이동시키고,
layout.tsx의 head 태그에서는 불필요한 메타 선언을 제거합니다.
이렇게 하면 Next.js의 메타 병합 전략을 온전히 활용할 수 있습니다.
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({
variable: "--font-inter",
subsets: ["latin"],
display: "swap",
});
// ✅ 모든 메타데이터는 metadata API에서만 관리
export const metadata: Metadata = {
metadataBase: new URL("https://example.com"),
title: "STYNA - 깔끔한 스타일 쇼핑몰",
description: "최신 패션 트렌드를 만나보세요",
};
// ✅ RootLayout는 화면 구조만 담당 (head 직접 조작 ❌)
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
<body className={inter.variable}>
{children}
</body>
</html>
);
}
정리
Next.js App Router에서는 metadata API를 중심으로 메타데이터를 선언적으로 관리하는 것이 핵심입니다.
이 문서에서 살펴본 것처럼, 잘못된 구조를 직접 비교해 보면
왜 head 태그를 수동으로 조작하는 방식이 권장되지 않는지 명확히 이해할 수 있습니다.
올바른 metadata 사용 방식은 유지보수성과 SEO 안정성을 동시에 확보하는 기반이 됩니다.
FAQ
Q. metadata와 head를 같이 쓰면 안 되나요?
App Router에서는 metadata API가 head 생성을 담당하므로,
동일 목적의 메타 태그를 직접 작성하면 충돌이 발생할 수 있습니다.
Q. 파비콘도 metadata로 옮겨야 하나요?
아이콘, apple-touch-icon, shortcut icon 모두 metadata.icons에서 관리하는 것이 권장됩니다.
Q. 이 구조가 SEO에 바로 악영향을 주나요?
즉시 패널티가 발생하지는 않지만, 크롤러 해석 불일치로 인해
검색 결과 미리보기 품질이 저하될 수 있습니다.