Next.js App Router Guide: JSON-LD (Structured Data)

무엇을 해결하나?

JSON-LD는 검색엔진(SEO)과 AI가 페이지의 “의미(구조화된 정보)”를 이해하도록 돕는 구조화 데이터 포맷입니다. 제품(Product), 사람(Person), 이벤트(Event) 등 다양한 엔티티를 설명할 수 있어요.

Next.js의 현재 권장 방식은 layout 또는 page에서 <script type="application/ld+json">로 렌더링하는 것입니다.

기본 구현 패턴 (App Router)

1) JSON-LD 객체 만들기

예시: Product 스키마

// app/products/[id]/page.tsx (예시)
export default async function Page({ params }) {
  const { id } = await params
  const product = await getProduct(id)

  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.name,
    image: product.image,
    description: product.description,
  }

  return (
    <section>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{
          __html: JSON.stringify(jsonLd).replace(/</g, '\\u003c'),
        }}
      />
      {/* ... */}
    </section>
  )
}

보안 주의 (XSS)

가이드 예시는 JSON.stringify를 사용하며, 악의적인 문자열이 섞이면 XSS 가능성이 있을 수 있다고 경고합니다.
따라서 예시처럼 < 문자를 유니코드로 바꾸는 식으로(예: replace(/</g, '\u003c')) 잠재적으로 위험한 문자열을 정리(sanitize) 하는 방식을 고려해야 합니다.

  • 팀/조직의 보안 가이드에 맞는 sanitize 방식을 적용
  • 커뮤니티 대안(예: serialize-javascript) 사용 고려

검증/테스트

  • Google: Rich Results Test
  • 일반: Schema Markup Validator

TypeScript 타입 지정(선택)

커뮤니티 패키지(schema-dts)로 JSON-LD 객체를 타입 안전하게 작성할 수 있습니다.

import { Product, WithContext } from 'schema-dts'

const jsonLd: WithContext<Product> = {
  '@context': 'https://schema.org',
  '@type': 'Product',
  name: 'Next.js Sticker',
  image: 'https://nextjs.org/imgs/sticker.png',
  description: 'Dynamic at the speed of static.',
}