Files
Andrew Yim 1706a820fe feat(seo-schema-generator): merge site-extraction + source-to-schema into one skill
Unify the two schema-generation scenarios into a single slot-17 skill, both
feeding one claims register -> build -> validate(16) pipeline:

- Mode 1 (existing site): NEW scripts/extract_site_claims.py turns URLs / local
  HTML / a directory into a claims register. Existing JSON-LD -> CONFIRMED;
  title/OpenGraph -> PENDING (never auto-shipped). + site-extraction-methodology.md
  and bundled fixtures/site/ demo pages.
- Mode 2 (not-yet-published site): land the source-to-schema engine
  (build_schema_drafts.py, type_templates.json, claims/source registers, 3 refs,
  sample_claims.csv) from the Desktop builder.
- Rewrite SKILL.md (v2.0) around the two-mode framing; the claims register is the
  shared pivot. Only CONFIRMED, non-conflicting claims become schema; unfilled
  template slots are pruned, never emitted as placeholders.
- Retire the old template-fill generator (code/ + desktop/); update root CLAUDE.md.

Self-tested both chains end-to-end: Mode 2 sample -> build -> validate PASS (P0=0);
Mode 1 fixtures -> extract -> build -> validate PASS (P0=0), JSON-LD round-trips with
nested address intact. Fixed two adapter bugs (nested node promotion; relative-path URI).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 00:38:40 +09:00

168 lines
11 KiB
Markdown

# Source-to-Schema 표준 프로세스 (미발행 사이트용 Schema 저작)
> 대상: 아직 발행되지 않은 웹사이트의 구조화 데이터(JSON-LD)를 **텍스트 소스로부터 저작**하는 작업.
> 짝 스킬: 생성/저작은 `17-seo-schema-generator`(본 문서 = Mode 2 소스 기반), 검증은 `16-seo-schema-validator`.
---
## 0. 왜 이 작업이 "발행된 페이지 기반"보다 어려운가
발행된 페이지 기반 스키마는 **DOM이라는 단일 진실원본(single source of truth)** 이 이미 있고, 거기서 *추출*만 하면 된다. 미발행 사이트는 그 진실원본이 존재하지 않기 때문에 다음 네 가지 난점이 동시에 발생한다.
| # | 난점 | 결과적으로 생기는 결함 | 대응 원칙 |
|---|------|----------------------|-----------|
| 1 | 사실이 여러 출처에 흩어져 있고 서로 **충돌** (DART vs 위키 vs 브로셔) | NAP 불일치, 값 모순 | **출처 권위 위계**로 단일 값 확정 |
| 2 | 붙일 **URL이 아직 없음** | placeholder/TODO 누출 (최다 P0) | 확정값 없으면 **키 자체를 생성하지 않음** |
| 3 | 엔티티 식별을 **사람이 수동**으로 (어느 "신라"인가) | 잘못된 sameAs, 엔티티 혼선 | **Wikidata/Wikipedia 정합(reconciliation)** |
| 4 | 무엇을 만들지 **범위 자체가 미정** | 누락/과잉 엔티티 | **엔티티-타입 맵**으로 범위 선확정 |
**결론적 설계 원칙**: 스키마로 굳히기 *전에* "출처 → 클레임(claim) 확정"을 먼저 끝낸다. 정제되지 않은 사실을 곧장 JSON-LD에 부으면 모든 충돌·공백이 그대로 스키마 결함이 된다. 그래서 본 프로세스의 중심축은 **클레임 레지스터(claims register)** — 출처가 추적되고 충돌이 해소된 사실 대장 — 이며, **CONFIRMED 클레임만 스키마가 된다.**
---
## 9단계 표준 프로세스
각 단계는 목적 / 입력 / 절차 / 산출 / 완료기준(AC) / 리스크로 명세한다.
### 1단계. 온라인 정통 소스(authentic source) 수집
- **목적**: 권위 있는 1차 출처를 폭넓게 확보한다.
- **입력**: 대상 기업/브랜드명, 법인명, 도메인.
- **절차**: 다음을 수집·기록 → `templates/source-register.csv`
- 기업공시(**DART**) — 법인명/설립일/주소/대표자 (법적 사실의 최상위 권위)
- **공식 홈페이지**(About/푸터/IR), 지속가능경영보고서, 뉴스룸/미디어, 뉴스레터
- **Wikidata / 위키백과** — 엔티티 식별자(Q-ID)와 sameAs 후보
- 채용 사이트 — JobPosting 원천
- 공식 **YouTube 채널** — VideoObject 원천
- 공식 소셜/디지털 채널 — sameAs 후보
- 주요 미디어 기사, 인물정보 사이트 — 보조/교차검증용
- **산출**: 소스 레지스터(출처별 1행, 권위순위·언어·커버 엔티티 기록).
- **AC**: 각 핵심 엔티티에 대해 **최소 2개 독립 출처** 확보(교차검증 가능).
- **리스크**: 비공식·오래된 출처를 1차로 오인 → 권위순위를 반드시 명시.
### 2단계. 오프라인 콘텐츠 수집
- **목적**: 온라인에 없는 1차 사실(브랜드 서사, 시설 스펙, 공식 표기) 확보.
- **입력**: 브로셔 PDF, 사보/경영보고서, 프레스 킷, 보도자료 모음, 발간물.
- **절차**: 파일을 소스 레지스터에 등록(파일 경로·발행일·언어). PDF는 텍스트 추출(스캔본은 OCR) 후 출처 표기 유지.
- **산출**: 오프라인 출처도 동일 레지스터에 통합.
- **AC**: 모든 오프라인 소스에 `retrieved_date``authority` 기재.
- **리스크**: PDF 추출 시 인코딩 깨짐/표 붕괴 → 추출 직후 육안 점검.
### 3단계. 정규화 & 정제(distill)
- **목적**: 수집물을 **클레임 단위**로 분해하고 노이즈·중복·빈값을 제거한다.
- **입력**: 1·2단계 소스.
- **절차**:
1. 각 소스에서 사실을 (엔티티, 속성, 값, 출처) 단위로 추출 → `templates/claims-register.csv`
2. 동일 (엔티티, 속성)의 **중복 통합**, 빈값/노이즈 제거
3. 출처 충돌 시 `conflict=Y`로 표시(해소 전까지 스키마 진입 차단)
4. 표기 정규화: 전화(E.164), 날짜(ISO 8601), 언어코드(BCP-47), 국가(ISO 3166-1 alpha-2)
- **산출**: 1차 클레임 레지스터(아직 미확정 포함).
- **AC**: 모든 핵심 속성이 단일 정규화 값 후보를 가짐(또는 conflict/pending로 명시).
- **리스크**: 정규화 누락이 다운스트림 VAL 결함으로 직결 → 3단계에서 포맷 확정.
### 4단계. 텍스트 분석 & Knowledge Graph 교차검증
- **목적**: 클레임을 외부 KG와 대조해 **이중 점검**하고, 동시에 현황·개선과제를 도출한다.
- **입력**: 1차 클레임 레지스터.
- **절차**:
1. **엔티티 정합**: Wikidata Q-ID, 위키백과, Google Knowledge Panel과 대조 → `sameAs` 확정
2. 핵심 사실(설립일, 법인명, 대표자)을 KG 값과 비교 → 불일치는 `conflict`
3. KG에 **존재하지 않거나 빈약한 엔티티**를 식별 → *개선과제 자료*로 별도 기록(런칭 후 KG 강화 목표)
4. 충돌·미확정을 권위 위계로 해소 → `status=CONFIRMED` 승격
- **산출**: 확정 클레임 레지스터 + **KG 현황/개선과제 메모**(별도 컨설팅 산출물로 활용).
- **AC**: 모든 핵심 엔티티에 검증된 `sameAs` 1개 이상; conflict 0건.
- **리스크**: 동명 엔티티 오정합(예: "신라" 왕조 vs 호텔) → Q-ID로 못박기.
- 참고: `references/source-authority-hierarchy.md`
### 5단계. 유형별 활용 스키마 타입 분류
- **목적**: 어떤 엔티티에 어떤 schema.org 타입을 쓸지 범위를 확정한다.
- **입력**: 확정 클레임 + 페이지/엔티티 목록.
- **절차**: 엔티티·소스 유형을 타입에 매핑(아래는 본 스킬 기본 매핑).
| 소스/엔티티 | schema.org 타입 |
|-------------|-----------------|
| 법인·브랜드(공시/공식) | `Organization` |
| 언어별 사이트 | `WebSite` |
| 호텔 프로퍼티 | `Hotel` (= LocalBusiness 계열) |
| 임원/인물 | `Person` |
| 채용공고 | `JobPosting` |
| 공식 영상 | `VideoObject` |
| FAQ/프레스킷 Q&A | `FAQPage` |
| 사이트 내비게이션 | `BreadcrumbList` |
- **산출**: 엔티티-타입 맵(엔티티별 타입 + 필수 속성 목록).
- **AC**: 모든 대상 엔티티에 타입과 Google 필수 속성 목록이 배정됨.
- **리스크**: 과잉 타입 부여(불필요한 타입은 검증 부담만 가중) → "리치결과 가치 있는 타입" 우선.
- 참고: `references/entity-and-type-map.md`
### 6단계. 타입별 템플릿 설정 & 초안 추출 (자동화)
- **목적**: 확정 클레임 + 타입 템플릿으로 JSON-LD 초안을 **자동 생성**한다.
- **입력**: 확정 클레임 레지스터(`status=CONFIRMED`), `scripts/type_templates.json`.
- **절차**:
```bash
python scripts/build_schema_drafts.py path/to/claims_register.csv \
--templates scripts/type_templates.json --out drafts_out
```
- CONFIRMED·비충돌 클레임만 스키마가 됨. PENDING/REJECTED/conflict/공란은 **제외 후 보고**.
- 미충족 슬롯은 **키 자체를 삭제**(placeholder 누출 원천 차단).
- 엔티티 간 `@id` 참조로 엔티티 그래프 형성(Hotel→parentOrganization 등).
- **산출**: `drafts/*.jsonld`, 검증기 입력용 `schema_drafts_dataset.csv`, `build_report.md`(제외 클레임 목록).
- **AC**: 초안에 placeholder/빈 객체 0건; 모든 제외 클레임이 보고서에 사유와 함께 기재.
- **리스크**: 템플릿 누락 타입은 건너뜀(보고됨) → 5단계 맵과 템플릿 동기화.
### 7단계. 리뷰·검토·수정 + 리뷰 가이드
- **목적**: 초안을 사람·고객이 검토하되, **원본 JSON이 아니라 결함 리포트**로 검토하게 한다.
- **입력**: 6단계 초안 + 검증기 결과.
- **절차**:
1. 초안을 **즉시 검증기에 통과**(아래 8단계)시켜 P0=0 게이트부터 확보
2. 남은 항목을 `templates/review-guide.md` 기준으로 검토(사실 정확성·표기·번역)
3. 고객 검토는 P0가 0인 깨끗한 초안에 대해서만, 결함 리포트 기준으로 진행
- **산출**: 수정 반영 초안 + 리뷰 가이드 체크 결과.
- **AC**: 모든 P0 해소; 사실 정확성 검토 서명(저작자·검수자).
- **리스크**: 사람이 원본 JSON을 직접 보면 "오류 과다" 문제 재발 → 반드시 리포트 기반 검토.
- 참고: `templates/review-guide.md`
### 8단계. 수정 초안의 rich result 적격성 점검
- **목적**: 리치결과 적격성을 (1) 오프라인 게이트 + (2) Google 온라인 테스트로 이중 확인.
- **입력**: 수정 초안 데이터셋.
- **절차**:
```bash
# 오프라인 게이트 (반드시 zero P0)
python ../16-seo-schema-validator/scripts/validate_schema.py drafts_out/schema_drafts_dataset.csv --out qa_out
```
- 게이트 PASS 후, **표본 엔트리**를 Google Rich Results Test에 통과(이 런타임은 오프라인이라 온라인 테스트는 사용자가 수행)시켜 캡처.
- **산출**: 검증기 리포트(Gate PASS) + Rich Results Test 표본 통과 캡처.
- **AC**: P0=0, P1 트리아지 완료, 온라인 테스트 표본 green.
- **리스크**: 오프라인 규칙은 호텔 도메인 큐레이션 부분집합 → 온라인 표본 검사로 보완.
### 9단계. 발행 후 유효성 검증 & KG 변화 측정
- **목적**: 배포된 스키마가 저작 초안과 일치하는지 확인하고, 4단계의 KG 개선과제 달성도를 측정한다.
- **입력**: 라이브 URL.
- **절차**:
1. 검증기 **Mode B**(라이브 URL)로 렌더링된 스키마 재검증 → `seo-comprehensive-audit` 4단계 연계
2. GSC "리치 결과" 리포트 모니터링(신규 오류 0 유지)
3. 4단계 KG 메모 대비 Knowledge Panel/Wikidata 노출·정확도 변화 측정
- **산출**: 발행 후 검증 리포트 + KG 변화 측정(전/후 비교).
- **AC**: 라이브 스키마 = 저작 초안; GSC 신규 구조화데이터 오류 0; KG 개선과제 진척 기록.
- **리스크**: 렌더링 단계에서 JS로 스키마 누락/변형 → Mode B로 실측.
---
## 스테이지 게이트 (설계→개발→테스트→안정화→런칭 후)
| 게이트 | 단계 | DoD(완료 정의) |
|--------|------|----------------|
| **G1 설계** | 1·2·5 | 소스 레지스터 완료(엔티티당 ≥2출처), 엔티티-타입 맵 확정(타입+필수속성) |
| **G2 개발** | 3·4·6 | 클레임 레지스터 CONFIRMED·conflict 0, 빌더 실행 → 초안 placeholder 0 |
| **G3 테스트** | 7·8 | 검증기 **zero P0**, P1 트리아지(`decision-log`), 사실정확성 검수 서명 |
| **G4 안정화** | 8 | Google Rich Results Test 표본 green, 재실행 무회귀 |
| **G5 런칭 후** | 9 | 라이브=초안 일치, GSC 신규오류 0, KG 변화 측정 기록 |
---
## 산출물 일람
- `templates/source-register.csv` — 1·2단계 출처 대장
- `templates/claims-register.csv` — 3·4단계 사실 대장(스키마의 원천)
- `templates/review-guide.md` — 7단계 검토 기준
- `scripts/type_templates.json` — 6단계 타입별 JSON-LD 템플릿
- `scripts/build_schema_drafts.py` — 6단계 초안 자동 생성
- (검증) `16-seo-schema-validator` — 7·8·9단계 게이트