{
  "id": "efff31cc-1960-4e56-b073-9ace8ec09908",
  "slug": "ref/khub/geocare-std-partner-jsonld-fill-v1-0",
  "doc_type": "ref",
  "title": "STD-PARTNER-JSONLD-FILL v1.0",
  "title_en": null,
  "one_liner": "# 📐 STD-PARTNER-JSONLD-FILL v1.0 — 사이트 풍부 schema 적재 표준 > Track : `T-PARTNER-JSONLD-FILL` > 작성 : 2026-05-10 KST | 검증 사례: bizspring.co.kr 70 URL > 적용 ",
  "summary_bullets": [],
  "body_md": "# 📐 STD-PARTNER-JSONLD-FILL v1.0 — 사이트 풍부 schema 적재 표준\n\n> Track    : `T-PARTNER-JSONLD-FILL`\n> 작성     : 2026-05-10 KST | 검증 사례: bizspring.co.kr 70 URL\n> 적용     : 모든 GEOcare 등록 사이트 (PREMIUM_GEO 파트너 사이트 포함)\n> 짝 표준  : STD-USER-FLOW v1.0, STD-SITE-GEO-FLOW v1.1, STD-DEPLOY v1.1, STD-EDGE-01\n\n---\n\n## 0. 적용 시점\n\n다음 중 하나라도 해당 시 본 표준 적용:\n- 사이트가 GEOcare에 등록되고 EEAT 분석 완료\n- 사이트가 schema.geocare.ai EF v5 호출 사용 시작\n- PREMIUM_GEO 파트너 사이트 (Brand Hub bmp.ai 운영)\n\n## 1. 모델 — 3 SSOT (UPSERT 통일)\n\n```\n┌──────────────────────────────────────────────────────────────┐\n│ ① gp_geoex_site_jsonld    (UNIQUE site_domain)               │\n│   - Organization + WebSite (도메인 단위, 1행)                  │\n│   - 메인 페이지 정보 흡수 가능                                  │\n├──────────────────────────────────────────────────────────────┤\n│ ② gp_geoex_brand_jsonld   (UNIQUE url_prefix)                │\n│   - Brand × N (멀티브랜드 사이트는 여러 행)                      │\n│   - URL prefix longest match로 결정                            │\n├──────────────────────────────────────────────────────────────┤\n│ ③ gp_geoex_eeat_analysis  (UNIQUE site+url, 기존 테이블)       │\n│   - 신규 컬럼 3개:                                              │\n│     • generated_jsonld (jsonb)        ← 페이지 schema array     │\n│     • jsonld_schema_types (text[])                             │\n│     • jsonld_generated_at (timestamptz)                        │\n│   - WebPage / FAQPage / Article 등 모든 page-level             │\n└──────────────────────────────────────────────────────────────┘\n\n[송출 EF]\nEF v5 (geobh-jsonld) → RPC v3 (get_jsonld_by_url_v3)\n  → 3 SSOT 병합 → @graph 단일 응답 (?pretty=1 옵션)\n\n[배제]\ngp_geoex_jsonld_log\n  - Read-only archive (이력 테이블)\n  - 신규 INSERT 금지 (가비지 발생 패턴)\n  - dup_ratio 2.43 vs eeat_analysis 1.00 (UPSERT 모범)\n```\n\n## 2. 적용 절차 (신규 사이트)\n\n### 2-1. siteGEO 적재 (1회, 도메인 단위)\n\n```sql\nINSERT INTO gp_geoex_site_jsonld (site_domain, organization_schema, website_schema, source_kpi, created_by)\nVALUES (\n  'https://example.com',\n  jsonb_build_object(\n    '@id', 'https://example.com/#organization',\n    '@type', 'Organization',\n    'name', '브랜드명',\n    'alternateName', jsonb_build_array('alias1', 'alias2'),\n    'url', 'https://example.com',\n    'description', '...',\n    'knowsAbout', jsonb_build_array('핵심키워드1', '핵심키워드2'),\n    'makesOffer', jsonb_build_array(\n      jsonb_build_object('@type', 'Offer', 'itemOffered', jsonb_build_object('@type', 'Service', 'name', '제품1'))\n    )\n  ),\n  jsonb_build_object(\n    '@id', 'https://example.com/#website',\n    '@type', 'WebSite',\n    'url', 'https://example.com',\n    'name', '...',\n    'inLanguage', 'ko-KR',\n    'publisher', jsonb_build_object('@id', 'https://example.com/#organization'),\n    'potentialAction', jsonb_build_object(\n      '@type', 'SearchAction',\n      'target', jsonb_build_object('@type', 'EntryPoint', 'urlTemplate', 'https://example.com/search?q={search_term_string}'),\n      'query-input', 'required name=search_term_string'\n    )\n  ),\n  'manual',\n  'OPS or Claude'\n)\nON CONFLICT (site_domain) DO UPDATE \n  SET organization_schema = EXCLUDED.organization_schema,\n      website_schema = EXCLUDED.website_schema;\n```\n\n### 2-2. brandGEO 적재 (브랜드별 1회)\n\n```sql\nINSERT INTO gp_geoex_brand_jsonld (site_domain, brand_settings_id, url_prefix, brand_name, brand_schema, source_kpi)\nSELECT \n  '<host>',\n  bs.id,\n  bs.site_domain,\n  bs.brand_name,\n  jsonb_build_object(\n    '@id', rtrim(bs.site_domain, '/') || '/#brand',\n    '@type', 'Brand',\n    'name', bs.brand_name,\n    'alternateName', COALESCE(to_jsonb(bs.brand_aliases), '[]'::jsonb),\n    'parentOrganization', jsonb_build_object('@id', '<host>/#organization'),\n    'description', '...',\n    'knowsAbout', COALESCE(to_jsonb(bs.brand_keywords), '[]'::jsonb),\n    'makesOffer', CASE \n      WHEN bs.product_names IS NOT NULL AND array_length(bs.product_names, 1) > 0 \n      THEN (SELECT jsonb_agg(jsonb_build_object('@type', 'Offer', 'itemOffered', jsonb_build_object('@type', 'Service', 'name', p))) FROM unnest(bs.product_names) p)\n      ELSE '[]'::jsonb\n    END\n  ),\n  'manual'\nFROM gp_geoex_brand_settings bs\nWHERE bs.site_domain LIKE '<host>%'\nON CONFLICT (url_prefix) DO UPDATE\n  SET brand_schema = EXCLUDED.brand_schema,\n      brand_name = EXCLUDED.brand_name;\n```\n\n### 2-3. URL-specific UPSERT (eeat_analysis 활용, 일괄)\n\n```sql\nWITH urls AS (\n  SELECT DISTINCT ON (ee.url) \n    ee.id, ee.url, ee.site_domain,\n    ua.title, ua.page_type, ua.total_score\n  FROM gp_geoex_eeat_analysis ee\n  LEFT JOIN gp_geoex_url_analysis ua \n    ON ua.url = ee.url AND ua.site_domain LIKE '<host>%'\n  WHERE ee.site_domain LIKE '<host>%'\n  ORDER BY ee.url, ua.total_score DESC NULLS LAST\n),\nurl_brand AS (\n  SELECT u.*, br.brand_schema, br.url_prefix\n  FROM urls u\n  LEFT JOIN LATERAL (\n    SELECT brand_schema, url_prefix\n    FROM gp_geoex_brand_jsonld\n    WHERE (u.url LIKE url_prefix || '%') OR (u.url = rtrim(url_prefix, '/'))\n    ORDER BY length(url_prefix) DESC LIMIT 1\n  ) br ON true\n)\nUPDATE gp_geoex_eeat_analysis ee\nSET \n  generated_jsonld = jsonb_build_array(\n    jsonb_build_object(\n      '@id', ub.url || '#webpage',\n      '@type', 'WebPage',\n      'url', ub.url,\n      'name', COALESCE(NULLIF(ub.title, ''), '<host_name>'),\n      'description', COALESCE(ub.brand_schema->>'description', '<fallback>'),\n      'isPartOf', jsonb_build_object('@id', '<host>/#website'),\n      'about', jsonb_build_object('@id', COALESCE(ub.brand_schema->>'@id', '<host>/#organization')),\n      'inLanguage', 'ko-KR'\n    )\n  ),\n  jsonld_schema_types = ARRAY['WebPage'],\n  jsonld_generated_at = NOW()\nFROM url_brand ub\nWHERE ee.id = ub.id;\n```\n\n## 3. 검증 4단계\n\n| 단계 | 검증 | SQL/명령 |\n|:-:|---|---|\n| 1 | 3 SSOT 적재 카운트 | `SELECT COUNT FROM site_jsonld / brand_jsonld / eeat_analysis WHERE generated_jsonld IS NOT NULL` |\n| 2 | RPC v3 호출 (5+ URL 샘플) | `SELECT * FROM get_jsonld_by_url_v3('<sample_url>')` |\n| 3 | EF v5 직접 호출 | `curl \"schema.geocare.ai/functions/v1/geobh-jsonld?url=<url>&pretty=1\"` |\n| 4 | 사이트 view-source (PHP 캐시 만료 후) | 브라우저 view-source — `<script type=\"application/ld+json\">` 풍부 schema |\n\n## 4. 환각 방지 정책 (G-02 ABSOLUTE RULES)\n\n본 표준 적용 시 다음 금지:\n- ❌ FAQPage `acceptedAnswer`에 `eeat_analysis.suggested_faqs`의 `answer_direction` 직접 사용 (질문은 있으나 답변 본문 부재 → 환각 위험)\n- ❌ Article `author`/`datePublished` 추정 (정보 부재 시 OMIT OVER FAKE)\n- ❌ Service `provider`/`offers` 추정 (제품/가격 정보 부재)\n\n본 표준은 **WebPage 기본 schema만 일괄 자동 적재**. 페이지 단위 풍부 schema(FAQPage/Article 등)는 별 트랙 (사람 또는 LLM 검수 후).\n\n## 5. 변경 시 영향 (Cross-Room)\n\n본 표준 변경 시:\n- GEOCARE Snapshot 갱신\n- PREMIUM_GEO 통보 (bmp.ai 파트너 사이트 영향)\n- SALES 통보 (영업 자료 갱신)\n\n## 6. 검증 사례\n\n| 사이트 | 적재 | 검증 |\n|---|---|:-:|\n| bizspring.co.kr | site (1) + brand (3) + eeat_analysis (69 URL) | ✅ EF v5 6 시나리오 ALL PASS (5/10) |\n\n## 7. RPC v3 정의 본문 (재현 가능)\n\n```sql\nCREATE OR REPLACE FUNCTION get_jsonld_by_url_v3(p_url TEXT)\nRETURNS TABLE (\n  cached BOOLEAN,\n  data JSONB\n) AS $$\nDECLARE\n  v_url_norm TEXT;\n  v_site_domain TEXT;\n  v_org JSONB;\n  v_website JSONB;\n  v_brand JSONB;\n  v_page JSONB;\n  v_graph JSONB;\nBEGIN\n  v_url_norm := CASE \n    WHEN p_url ~ '^https?://[^/]+/?$' THEN p_url\n    ELSE rtrim(p_url, '/')\n  END;\n  v_site_domain := substring(p_url FROM '^(https?://[^/]+)');\n  \n  SELECT organization_schema, website_schema \n    INTO v_org, v_website\n  FROM gp_geoex_site_jsonld \n  WHERE site_domain = v_site_domain LIMIT 1;\n  \n  SELECT brand_schema INTO v_brand\n  FROM gp_geoex_brand_jsonld\n  WHERE (p_url LIKE url_prefix || '%') OR (p_url = rtrim(url_prefix, '/'))\n  ORDER BY length(url_prefix) DESC LIMIT 1;\n  \n  SELECT generated_jsonld INTO v_page\n  FROM gp_geoex_eeat_analysis\n  WHERE url IN (p_url, v_url_norm, v_url_norm || '/')\n    AND generated_jsonld IS NOT NULL\n    AND jsonb_typeof(generated_jsonld) = 'array'\n  ORDER BY jsonld_generated_at DESC NULLS LAST LIMIT 1;\n  \n  v_graph := '[]'::jsonb;\n  IF v_org IS NOT NULL THEN v_graph := v_graph || jsonb_build_array(v_org); END IF;\n  IF v_website IS NOT NULL THEN v_graph := v_graph || jsonb_build_array(v_website); END IF;\n  IF v_brand IS NOT NULL THEN v_graph := v_graph || jsonb_build_array(v_brand); END IF;\n  IF v_page IS NOT NULL THEN v_graph := v_graph || v_page; END IF;\n  \n  IF jsonb_typeof(v_graph) = 'array' AND v_graph != '[]'::jsonb THEN\n    RETURN QUERY SELECT \n      (v_page IS NOT NULL),\n      jsonb_build_array(jsonb_build_object(\n        '@context', 'https://schema.org',\n        '@graph', v_graph\n      ));\n  END IF;\nEND;\n$$ LANGUAGE plpgsql STABLE;\n\nGRANT EXECUTE ON FUNCTION get_jsonld_by_url_v3(TEXT) TO anon, authenticated, service_role;\n```\n\n## 8. EF v5 관련\n\n- 라이브: `https://schema.geocare.ai/functions/v1/geobh-jsonld?url=<URL>&pretty=1`\n- 응답 메타: `ef_version: v5, rpc_version: v3, sources: ['site_jsonld', 'brand_jsonld', 'eeat_analysis.generated_jsonld']`\n- 백업 (롤백): KHub `8ce966ce` (EF v2 코드 — RPC v1.1)\n- 배포 방식: Supabase Management API multipart (학습 #L25~L27)\n\n## 9. 적용 워크플로우 (신규 사이트별)\n\n```\n1. 사이트가 GEOcare 등록 + EEAT 분석 완료 확인\n2. brand_settings 등록 확인 (멀티브랜드면 N행)\n3. site_jsonld INSERT (도메인 1회)\n4. brand_jsonld INSERT (브랜드별)\n5. eeat_analysis UPSERT (URL 일괄)\n6. 검증 4단계 완료\n7. (사이트 측) v2.10 PHP 박혀 있으면 캐시 만료 1h 후 라이브\n   또는 즉시: 캐시 디렉터리 클리어 + view-source 재호출\n```\n\n## 10. 변경 이력\n\n| 일자 | 버전 | 변경 |\n|---|---|---|\n| 2026-05-10 | v1.0 | 초판. T-BIZSPRING-JSONLD-FILL 검증 사례 기반. 3 SSOT UPSERT 모델. EF v5 / RPC v3. |\n",
  "related_slugs": [],
  "faq": [],
  "jsonld": null,
  "keywords": [],
  "source_id": "fceee150-c9b1-4a20-924a-3f7e5a8ff89a",
  "video_url": null,
  "duration_sec": null,
  "thumbnail_url": null,
  "image_url": null,
  "caption": null,
  "solution_slug": null,
  "captured_at": null,
  "width": null,
  "height": null,
  "created_at": "2026-08-24T11:53:44.997497+00:00",
  "updated_at": "2026-08-24T11:53:44.997497+00:00",
  "visibility": "public",
  "source_stage": "1",
  "anonymized": true,
  "source_key": "legacy-khub",
  "origin": "ingest",
  "locked": false,
  "locked_by": null,
  "locked_at": null,
  "gate_state": {
    "g1": {
      "hits": [],
      "pass": true
    },
    "g2": {
      "pass": true,
      "total": 38,
      "issues": [
        "숫자·규격 같은 검증 가능한 구체가 부족합니다",
        "핵심 요약 불릿이 3개 미만입니다",
        "Q&A 쌍이 3개 미만입니다"
      ],
      "applies": true
    },
    "g3": {
      "pass": true,
      "required": false
    },
    "stage": "1",
    "decided_at": "2026-08-24T11:53:44.334Z"
  },
  "raw_id": "60546f60-fe4d-4bd0-aed5-fb5b6b7647e1",
  "search_text": "STD-PARTNER-JSONLD-FILL v1.0  # 📐 STD-PARTNER-JSONLD-FILL v1.0 — 사이트 풍부 schema 적재 표준 > Track : `T-PARTNER-JSONLD-FILL` > 작성 : 2026-05-10 KST | 검증 사례: bizspring.co.kr 70 URL > 적용   # 📐 STD-PARTNER-JSONLD-FILL v1.0 — 사이트 풍부 schema 적재 표준\n\n> Track    : `T-PARTNER-JSONLD-FILL`\n> 작성     : 2026-05-10 KST | 검증 사례: bizspring.co.kr 70 URL\n> 적용     : 모든 GEOcare 등록 사이트 (PREMIUM_GEO 파트너 사이트 포함)\n> 짝 표준  : STD-USER-FLOW v1.0, STD-SITE-GEO-FLOW v1.1, STD-DEPLOY v1.1, STD-EDGE-01\n\n---\n\n## 0. 적용 시점\n\n다음 중 하나라도 해당 시 본 표준 적용:\n- 사이트가 GEOcare에 등록되고 EEAT 분석 완료\n- 사이트가 schema.geocare.ai EF v5 호출 사용 시작\n- PREMIUM_GEO 파트너 사이트 (Brand Hub bmp.ai 운영)\n\n## 1. 모델 — 3 SSOT (UPSERT 통일)\n\n```\n┌──────────────────────────────────────────────────────────────┐\n│ ① gp_geoex_site_jsonld    (UNIQUE site_domain)               │\n│   - Organization + WebSite (도메인 단위, 1행)                  │\n│   - 메인 페이지 정보 흡수 가능                                  │\n├──────────────────────────────────────────────────────────────┤\n│ ② gp_geoex_brand_jsonld   (UNIQUE url_prefix)                │\n│   - Brand × N (멀티브랜드 사이트는 여러 행)                      │\n│   - URL prefix longest match로 결정                            │\n├──────────────────────────────────────────────────────────────┤\n│ ③ gp_geoex_eeat_analysis  (UNIQUE site+url, 기존 테이블)       │\n│   - 신규 컬럼 3개:                                              │\n│     • generated_jsonld (jsonb)        ← 페이지 schema array     │\n│     • jsonld_schema_types (text[])                             │\n│     • jsonld_generated_at (timestamptz)                        │\n│   - WebPage / FAQPage / Article 등 모든 page-level             │\n└──────────────────────────────────────────────────────────────┘\n\n[송출 EF]\nEF v5 (geobh-jsonld) → RPC v3 (get_jsonld_by_url_v3)\n  → 3 SSOT 병합 → @graph 단일 응답 (?pretty=1 옵션)\n\n[배제]\ngp_geoex_jsonld_log\n  - Read-only archive (이력 테이블)\n  - 신규 INSERT 금지 (가비지 발생 패턴)\n  - dup_ratio 2.43 vs eeat_analysis 1.00 (UPSERT 모범)\n```\n\n## 2. 적용 절차 (신규 사이트)\n\n### 2-1. siteGEO 적재 (1회, 도메인 단위)\n\n```sql\nINSERT INTO gp_geoex_site_jsonld (site_domain, organization_schema, website_schema, source_kpi, created_by)\nVALUES (\n  'https://example.com',\n  jsonb_build_object(\n    '@id', 'https://example.com/#organization',\n    '@type', 'Organization',\n    'name', '브랜드명',\n    'alternateName', jsonb_build_array('alias1', 'alias2'),\n    'url', 'https://example.com',\n    'description', '...',\n    'knowsAbout', jsonb_build_array('핵심키워드1', '핵심키워드2'),\n    'makesOffer', jsonb_build_array(\n      jsonb_build_object('@type', 'Offer', 'itemOffered', jsonb_build_object('@type', 'Service', 'name', '제품1'))\n    )\n  ),\n  jsonb_build_object(\n    '@id', 'https://example.com/#website',\n    '@type', 'WebSite',\n    'url', 'https://example.com',\n    'name', '...',\n    'inLanguage', 'ko-KR',\n    'publisher', jsonb_build_object('@id', 'https://example.com/#organization'),\n    'potentialAction', jsonb_build_object(\n      '@type', 'SearchAction',\n      'target', jsonb_build_object('@type', 'EntryPoint', 'urlTemplate', 'https://example.com/search?q={search_term_string}'),\n      'query-input', 'required name=search_term_string'\n    )\n  ),\n  'manual',\n  'OPS or Claude'\n)\nON CONFLICT (site_domain) DO UPDATE \n  SET organization_schema = EXCLUDED.organization_schema,\n      website_schema = EXCLUDED.website_schema;\n```\n\n### 2-2. brandGEO 적재 (브랜드별 1회)\n\n```sql\nINSERT INTO gp_geoex_brand_jsonld (site_domain, brand_settings_id, url_prefix, brand_name, brand_schema, source_kpi)\nSELECT \n  '<host>',\n  bs.id,\n  bs.site_domain,\n  bs.brand_name,\n  jsonb_build_object(\n    '@id', rtrim(bs.site_domain, '/') || '/#brand',\n    '@type', 'Brand',\n    'name', bs.brand_name,\n    'alternateName', COALESCE(to_jsonb(bs.brand_aliases), '[]'::jsonb),\n    'parentOrganization', jsonb_build_object('@id', '<host>/#organization'),\n    'description', '...',\n    'knowsAbout', COALESCE(to_jsonb(bs.brand_keywords), '[]'::jsonb),\n    'makesOffer', CASE \n      WHEN bs.product_names IS NOT NULL AND array_length(bs.product_names, 1) > 0 \n      THEN (SELECT jsonb_agg(jsonb_build_object('@type', 'Offer', 'itemOffered', jsonb_build_object('@type', 'Service', 'name', p))) FROM unnest(bs.product_names) p)\n      ELSE '[]'::jsonb\n    END\n  ),\n  'manual'\nFROM gp_geoex_brand_settings bs\nWHERE bs.site_domain LIKE '<host>%'\nON CONFLICT (url_prefix) DO UPDATE\n  SET brand_schema = EXCLUDED.brand_schema,\n      brand_name = EXCLUDED.brand_name;\n```\n\n### 2-3. URL-specific UPSERT (eeat_analysis 활용, 일괄)\n\n```sql\nWITH urls AS (\n  SELECT DISTINCT ON (ee.url) \n    ee.id, ee.url, ee.site_domain,\n    ua.title, ua.page_type, ua.total_score\n  FROM gp_geoex_eeat_analysis ee\n  LEFT JOIN gp_geoex_url_analysis ua \n    ON ua.url = ee.url AND ua.site_domain LIKE '<host>%'\n  WHERE ee.site_domain LIKE '<host>%'\n  ORDER BY ee.url, ua.total_score DESC NULLS LAST\n),\nurl_brand AS (\n  SELECT u.*, br.brand_schema, br.url_prefix\n  FROM urls u\n  LEFT JOIN LATERAL (\n    SELECT brand_schema, url_prefix\n    FROM gp_geoex_brand_jsonld\n    WHERE (u.url LIKE url_prefix || '%') OR (u.url = rtrim(url_prefix, '/'))\n    ORDER BY length(url_prefix) DESC LIMIT 1\n  ) br ON true\n)\nUPDATE gp_geoex_eeat_analysis ee\nSET \n  generated_jsonld = jsonb_build_array(\n    jsonb_build_object(\n      '@id', ub.url || '#webpage',\n      '@type', 'WebPage',\n      'url', ub.url,\n      'name', COALESCE(NULLIF(ub.title, ''), '<host_name>'),\n      'description', COALESCE(ub.brand_schema->>'description', '<fallback>'),\n      'isPartOf', jsonb_build_object('@id', '<host>/#website'),\n      'about', jsonb_build_object('@id', COALESCE(ub.brand_schema->>'@id', '<host>/#organization')),\n      'inLanguage', 'ko-KR'\n    )\n  ),\n  jsonld_schema_types = ARRAY['WebPage'],\n  jsonld_generated_at = NOW()\nFROM url_brand ub\nWHERE ee.id = ub.id;\n```\n\n## 3. 검증 4단계\n\n| 단계 | 검증 | SQL/명령 |\n|:-:|---|---|\n| 1 | 3 SSOT 적재 카운트 | `SELECT COUNT FROM site_jsonld / brand_jsonld / eeat_analysis WHERE generated_jsonld IS NOT NULL` |\n| 2 | RPC v3 호출 (5+ URL 샘플) | `SELECT * FROM get_jsonld_by_url_v3('<sample_url>')` |\n| 3 | EF v5 직접 호출 | `curl \"schema.geocare.ai/functions/v1/geobh-jsonld?url=<url>&pretty=1\"` |\n| 4 | 사이트 view-source (PHP 캐시 만료 후) | 브라우저 view-source — `<script type=\"application/ld+json\">` 풍부 schema |\n\n## 4. 환각 방지 정책 (G-02 ABSOLUTE RULES)\n\n본 표준 적용 시 다음 금지:\n- ❌ FAQPage `acceptedAnswer`에 `eeat_analysis.suggested_faqs`의 `answer_direction` 직접 사용 (질문은 있으나 답변 본문 부재 → 환각 위험)\n- ❌ Article `author`/`datePublished` 추정 (정보 부재 시 OMIT OVER FAKE)\n- ❌ Service `provider`/`offers` 추정 (제품/가격 정보 부재)\n\n본 표준은 **WebPage 기본 schema만 일괄 자동 적재**. 페이지 단위 풍부 schema(FAQPage/Article 등)는 별 트랙 (사람 또는 LLM 검수 후).\n\n## 5. 변경 시 영향 (Cross-Room)\n\n본 표준 변경 시:\n- GEOCARE Snapshot 갱신\n- PREMIUM_GEO 통보 (bmp.ai 파트너 사이트 영향)\n- SALES 통보 (영업 자료 갱신)\n\n## 6. 검증 사례\n\n| 사이트 | 적재 | 검증 |\n|---|---|:-:|\n| bizspring.co.kr | site (1) + brand (3) + eeat_analysis (69 URL) | ✅ EF v5 6 시나리오 ALL PASS (5/10) |\n\n## 7. RPC v3 정의 본문 (재현 가능)\n\n```sql\nCREATE OR REPLACE FUNCTION get_jsonld_by_url_v3(p_url TEXT)\nRETURNS TABLE (\n  cached BOOLEAN,\n  data JSONB\n) AS $$\nDECLARE\n  v_url_norm TEXT;\n  v_site_domain TEXT;\n  v_org JSONB;\n  v_website JSONB;\n  v_brand JSONB;\n  v_page JSONB;\n  v_graph JSONB;\nBEGIN\n  v_url_norm := CASE \n    WHEN p_url ~ '^https?://[^/]+/?$' THEN p_url\n    ELSE rtrim(p_url, '/')\n  END;\n  v_site_domain := substring(p_url FROM '^(https?://[^/]+)');\n  \n  SELECT organization_schema, website_schema \n    INTO v_org, v_website\n  FROM gp_geoex_site_jsonld \n  WHERE site_domain = v_site_domain LIMIT 1;\n  \n  SELECT brand_schema INTO v_brand\n  FROM gp_geoex_brand_jsonld\n  WHERE (p_url LIKE url_prefix || '%') OR (p_url = rtrim(url_prefix, '/'))\n  ORDER BY length(url_prefix) DESC LIMIT 1;\n  \n  SELECT generated_jsonld INTO v_page\n  FROM gp_geoex_eeat_analysis\n  WHERE url IN (p_url, v_url_norm, v_url_norm || '/')\n    AND generated_jsonld IS NOT NULL\n    AND jsonb_typeof(generated_jsonld) = 'array'\n  ORDER BY jsonld_generated_at DESC NULLS LAST LIMIT 1;\n  \n  v_graph := '[]'::jsonb;\n  IF v_org IS NOT NULL THEN v_graph := v_graph || jsonb_build_array(v_org); END IF;\n  IF v_website IS NOT NULL THEN v_graph := v_graph || jsonb_build_array(v_website); END IF;\n  IF v_brand IS NOT NULL THEN v_graph := v_graph || jsonb_build_array(v_brand); END IF;\n  IF v_page IS NOT NULL THEN v_graph := v_graph || v_page; END IF;\n  \n  IF jsonb_typeof(v_graph) = 'array' AND v_graph != '[]'::jsonb THEN\n    RETURN QUERY SELECT \n      (v_page IS NOT NULL),\n      jsonb_build_array(jsonb_build_object(\n        '@context', 'https://schema.org',\n        '@graph', v_graph\n      ));\n  END IF;\nEND;\n$$ LANGUAGE plpgsql STABLE;\n\nGRANT EXECUTE ON FUNCTION get_jsonld_by_url_v3(TEXT) TO anon, authenticated, service_role;\n```\n\n## 8. EF v5 관련\n\n- 라이브: `https://schema.geocare.ai/functions/v1/geobh-jsonld?url=<URL>&pretty=1`\n- 응답 메타: `ef_version: v5, rpc_version: v3, sources: ['site_jsonld', 'brand_jsonld', 'eeat_analysis.generated_jsonld']`\n- 백업 (롤백): KHub `8ce966ce` (EF v2 코드 — RPC v1.1)\n- 배포 방식: Supabase Management API multipart (학습 #L25~L27)\n\n## 9. 적용 워크플로우 (신규 사이트별)\n\n```\n1. 사이트가 GEOcare 등록 + EEAT 분석 완료 확인\n2. brand_settings 등록 확인 (멀티브랜드면 N행)\n3. site_jsonld INSERT (도메인 1회)\n4. brand_jsonld INSERT (브랜드별)\n5. eeat_analysis UPSERT (URL 일괄)\n6. 검증 4단계 완료\n7. (사이트 측) v2.10 PHP 박혀 있으면 캐시 만료 1h 후 라이브\n   또는 즉시: 캐시 디렉터리 클리어 + view-source 재호출\n```\n\n## 10. 변경 이력\n\n| 일자 | 버전 | 변경 |\n|---|---|---|\n| 2026-05-10 | v1.0 | 초판. T-BIZSPRING-JSONLD-FILL 검증 사례 기반. 3 SSOT UPSERT 모델. EF v5 / RPC v3. |\n",
  "embedding": null,
  "embedded_at": null,
  "embedding_hash": null,
  "backlinks": [],
  "html_url": "https://bizspring.ai/kb/ref/khub/geocare-std-partner-jsonld-fill-v1-0",
  "markdown_url": "https://bizspring.ai/kb/ref/khub/geocare-std-partner-jsonld-fill-v1-0.md"
}