Files
2nd/10_Wiki/Topics/AI_and_ML/JSON-LD-Structured-Data.md
T
2026-05-10 22:08:15 +09:00

6.7 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-json-ld-structured-data JSON-LD Structured Data 10_Wiki/Topics verified self
JSON-LD
Structured Data
Schema.org JSON-LD
none A 0.9 applied
seo
json-ld
schema-org
structured-data
web
2026-05-10 pending
language framework
javascript schema.org

JSON-LD Structured Data

매 한 줄

"매 JSON-LD = 검색엔진에게 페이지 의미를 직접 말해주는 메타 데이터 레이어". HTML 마크업과 분리된 <script type="application/ld+json"> 블록으로 Schema.org vocabulary를 사용해 entity, relation, property를 선언한다. Google이 권장하는 structured data 형식이며 rich snippet/knowledge panel/AI Overviews 노출의 입장권.

매 핵심

매 왜 JSON-LD인가

  • 분리성: HTML rendering과 독립 — 마크업 변경 없이 추가 가능.
  • Google 권장: Microdata/RDFa보다 우선순위 높음 (2026 기준).
  • AI 친화적: LLM-powered search (Perplexity, ChatGPT Search, Google AI Overviews)가 entity graph 추출에 활용.
  • 유지보수: Template engine에서 server-side 생성 쉬움.

매 핵심 vocabulary

  • @context: 보통 "https://schema.org".
  • @type: Article, Product, Organization, BreadcrumbList, FAQPage, Recipe 등.
  • @id: entity의 canonical URI (graph 연결용).
  • @graph: 같은 페이지 내 다중 entity 묶음.

매 응용

  1. E-commerce → Product + Offer + AggregateRating.
  2. 미디어/블로그 → Article + Author(Person) + Publisher(Organization).
  3. 로컬 비즈니스 → LocalBusiness + Address + OpeningHours.
  4. FAQ/HowTo → FAQPage / HowTo (rich snippet 가시성 매우 높음).
  5. SaaS → SoftwareApplication + AggregateRating.

💻 패턴

Article schema (블로그/뉴스)

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "JSON-LD 완전 정복",
  "image": ["https://example.com/cover.jpg"],
  "datePublished": "2026-05-10T08:00:00+09:00",
  "dateModified": "2026-05-10T08:00:00+09:00",
  "author": {
    "@type": "Person",
    "name": "홍길동",
    "url": "https://example.com/author/hong"
  },
  "publisher": {
    "@type": "Organization",
    "name": "Example",
    "logo": {
      "@type": "ImageObject",
      "url": "https://example.com/logo.png"
    }
  }
}
</script>

Product + Offer (E-commerce)

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Mechanical Keyboard X1",
  "image": "https://example.com/x1.jpg",
  "sku": "X1-BLK",
  "brand": { "@type": "Brand", "name": "Acme" },
  "offers": {
    "@type": "Offer",
    "url": "https://example.com/p/x1",
    "priceCurrency": "KRW",
    "price": "189000",
    "availability": "https://schema.org/InStock",
    "itemCondition": "https://schema.org/NewCondition"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.7",
    "reviewCount": "412"
  }
}
</script>

BreadcrumbList

{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com/" },
    { "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://example.com/blog" },
    { "@type": "ListItem", "position": 3, "name": "JSON-LD" }
  ]
}

FAQPage (rich snippet 효과 큼)

{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [{
    "@type": "Question",
    "name": "JSON-LD를 어디에 넣어야 하나요?",
    "acceptedAnswer": {
      "@type": "Answer",
      "text": "<head> 또는 <body> 어디든 가능. <head> 권장."
    }
  }]
}

Next.js 13+ App Router 동적 생성

// app/blog/[slug]/page.tsx
export default async function Page({ params }) {
  const post = await getPost(params.slug);
  const jsonLd = {
    "@context": "https://schema.org",
    "@type": "Article",
    headline: post.title,
    datePublished: post.publishedAt,
    author: { "@type": "Person", name: post.author },
  };
  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      <article>{/* content */}</article>
    </>
  );
}

@graph로 다중 entity 연결

{
  "@context": "https://schema.org",
  "@graph": [
    { "@type": "Organization", "@id": "https://example.com/#org", "name": "Example" },
    { "@type": "WebSite", "@id": "https://example.com/#site", "publisher": { "@id": "https://example.com/#org" } },
    { "@type": "WebPage", "@id": "https://example.com/page#webpage", "isPartOf": { "@id": "https://example.com/#site" } }
  ]
}

검증 (Node 스크립트)

// scripts/validate-jsonld.mjs
import { readFileSync } from "fs";
const html = readFileSync(process.argv[2], "utf8");
const matches = html.matchAll(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/g);
for (const m of matches) {
  try { JSON.parse(m[1]); console.log("OK"); }
  catch (e) { console.error("INVALID:", e.message); process.exit(1); }
}

매 결정 기준

상황 Approach
단일 entity 페이지 단순 @type 객체
다중 entity (sitelinks 노리기) @graph 배열
CMS/headless 빌드 시 SSG/SSR로 inject
동적 콘텐츠 (가격 변동) SSR로 매 요청 생성

기본값: SSR로 page-level JSON-LD 생성 + Google Rich Results Test로 검증.

🔗 Graph

🤖 LLM 활용

언제: schema 자동 생성 (페이지 콘텐츠 → JSON-LD), 누락 type 추천, validation 에러 해석. 언제 X: Schema.org spec의 정확성 검증 (LLM이 deprecated property 추천 가능 — 항상 Google Rich Results Test로 cross-check).

안티패턴

  • Hidden content: 페이지에 없는 정보를 JSON-LD에만 — Google penalty.
  • 잘못된 @type: Article인데 Product로 — 무시됨.
  • 다중 동일 type: 한 페이지에 Article 3개 — 어떤 게 main인지 모호.
  • 수동 매번 작성: template/helper 없이 — 일관성 깨짐, typo.
  • 검증 누락: production 배포 후 무효 JSON 발견.

🧪 검증 / 중복

  • Verified (Google Search Central docs, schema.org 2026).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — 매 format, Next.js App Router 패턴, @graph 추가