o2o-infinith-demo/docs/NEXT_SESSION_SUPPORTERS_AUTOBUILD.md

162 lines
16 KiB
Markdown

# 다음 세션 프롬프트 · "병원 URL 하나로 서포터즈 사이트까지 자동 빌드"
작성 2026-09-04 · 작성자 Claude (haewon 세션 핸드오버) · 상태: 착수 전
> **2026-09-07 갱신:** 이 문서의 §0 프롬프트와 §5 작업 순서는 `docs/NEXT_SESSION_SUPPORTERS_AUTOBUILD_v2.md`가 대체한다. §1 자산 위치·§3 파이프라인·§4 이식 코드·§6 함정은 그대로 유효하다. 서포터즈 샘플은 v3(글 8편)에서 v4(글 18편, 고객용 화법, 신뢰 층)로 바뀌었다.
---
## 0. 세션 시작 시 그대로 붙여 넣을 프롬프트
```
INFINITH 저장소(브랜치 feature/discovery-url-cta)에서 "AI Discovery 자동 빌드" 기능을 개발한다.
목표: /discovery 랜딩의 URL 입력 CTA에 병원 홈페이지 URL을 넣으면, 진단 리포트(/discovery/:id)까지 만들던 기존 파이프라인을 확장해
서포터즈 사이트(supporters/ Astro 프로젝트와 같은 구조)까지 자동 생성·미리보기 배포하는 것이다.
먼저 docs/NEXT_SESSION_SUPPORTERS_AUTOBUILD.md 를 읽고, 그 문서의 §5 단계 순서대로 진행한다.
핵심 제약: 뷰성형외과 수작업 빌드에서 확정된 원칙(전후사진·후기 인용 금지, 홈페이지 문장 복사 금지, 감수 예정 표기, noindex 기본, 발행 게이트 통과 필수)을 코드로 강제한다.
결정이 필요한 것은 문서 §7을 보고 haewon에게 묻는다. 존댓말, 엠대시 금지.
```
---
## 1. 지금까지 만든 것 (위치)
| 자산 | 위치 | 상태 |
|---|---|---|
| AI Discovery 랜딩 (뷰성형외과 제안용) | `src/pages/DiscoveryLandingPage.tsx` + `src/components/discovery/` 9종 (Hero·WhyNow·AbmrLoop·HowItWorks·SampleReport·HonestyStrip·Roadmap·DiscoveryCta·DiscoveryLeadModal) | 프로덕션 https://infinith-demo.vercel.app/discovery (main). 단일 HTML 시안 `docs/reports/landing/ai_discovery_landing.html` |
| URL 입력 CTA | `DiscoveryLeadModal.tsx` → Supabase `discovery_leads` insert (URL·연락처 필수, 상호 선택). 실패 시 o2oteam@o2o.kr mailto 폴백 | 브랜치 `feature/discovery-url-cta`, 프로덕션 미배포. 마이그레이션 `supabase/migrations/20260831_discovery_leads.sql` **미적용** (supabase login 후 db push 필요) |
| 진단 리포트 페이지 | `src/pages/DiscoveryReportPage.tsx`, 결과 레지스트리 `src/data/discoveryResults.ts`, 뷰성형외과 결과 `src/data/discovery_viewclinic.ts` (50/C) | 새 병원 = 결과 파일 + 레지스트리 1줄 |
| 기준표 | `src/data/aeoGeoRubric.ts` (36항목, A20/B20/C25/D20/E10/F5), 문서 `docs/AEO_GEO_RUBRIC.md`, 채점 `src/lib/discoveryScore.ts` | v1.0. 미검증은 분모 제외 |
| 자동 채점기 | `scripts/audit_aeo_geo.py <main_url> <service_url> --id --name --skip-ts --json` (auto 21~22항목) | stdlib+curl. 노드 순회가 역순(stack.pop)이라 sameAs는 Organization 타입 노드가 먼저 잡힘 → 서포터즈 스키마는 `@type: ['MedicalClinic','Organization']` |
| 기존 백엔드 파이프라인 | `supabase/functions/` discover-channels → collect-channel-data → generate-report → generate-content-plan, scrape-website, enrich-channels, `_shared/` (extractSocialLinks·verifyHandles·dataQuality·retry) | 실제 연동: YouTube Data API, Apify, Naver Search API, Firecrawl, Perplexity. 문서 `docs/AI_DISCOVERY_BUILD2_HANDOVER.md`, `docs/PIPELINE_IMPROVEMENT_PLAN.md` |
| 질문 뱅크 | `docs/reports/viewclinic/03_question_bank/Viewclinic_AI_Discovery_Question_Bank.md` (120문항, 7카테고리), 러너 `scripts/run_question_bank_openai.py`, 통합 `scripts/merge_qb_results.py`, 엑셀 `scripts/export_qb_excel.py` | 뷰성형외과 2엔진 실측 완료 |
| **서포터즈 샘플 사이트 (수작업 v3)** | `supporters/` (Astro 7 정적, 별도 Vercel 프로젝트 `view-supporters-sample`, https://view-supporters-sample.vercel.app, noindex) | 이 세션의 산출물. 자동화의 정답지(golden sample) |
| 감사 비교 엑셀 | `docs/reports/viewclinic/04_supporters/Viewclinic_Supporters_vs_Official_AEO_Audit.xlsx`, 빌더 `scripts/build_supporters_audit_xlsx.py` | 공식 50/C · 샘플 78/B · 색인 허용 86/A. 시트 4 "E 측정 인프라 연결 가이드" |
| 설계 문서 | `docs/INFINITH_Viewclinic_Supporters_Site_Design_v0.1.md` | 중복 회피 6규칙, 법무 게이트, 네이버 2트랙 |
| 기업은행 블로그 감사 | `docs/reports/ibk/IBK_Blog_AEO_GEO_Audit_2026-09-04.md` | 54/C, Perplexity 3/3 인용. 글 구조(세 줄 요약·질문형 H3·표·FAQ)의 근거 |
### supporters/ 구조 (자동 생성기가 만들어야 할 결과물)
```
supporters/
astro.config.mjs site = SITE_URL env, build.format 'file', trailingSlash never
vercel.json cleanUrls, X-Robots-Tag noindex, build env PUBLIC_INDEXABLE=false
scripts/check.mjs 발행 게이트: JSON-LD 파싱·Article/FAQPage 존재·img alt·H1=1·금칙어·홈페이지 문장 40자 연속 일치
scripts/home_text.txt 홈페이지 본문(공백 제거) — 중복 검사 기준
scripts/build_news.py 뉴스룸 데이터 빌더 (게시판 크롤 + 네이버 뉴스 API → src/data/news.json, 부정 기사 제외 키워드)
src/data/factSheet.json 병원 사실 단일 원본 (NAP·진료시간·층·진료과·안전·이력·표면 집계)
src/data/authors.json 서포터즈(가명·샘플) + 감수 원장(실제 약력·사진·sameAs)
src/data/videos.json 채널 전체 조회수 집계 → 롱폼 Top10(정보형, 쇼츠 판별은 /shorts/{id} 200 응답)·정보형 쇼츠+서포터즈 답·전체
src/data/news.json 1,137건 (보도자료/기고·인터뷰/언급, 주제 13종)
src/content/posts/*.md 글 8편. frontmatter: title(질문형)·category·qbIds·summary[3]·author·reviewer·reviewStatus·videos[]·gallery[]·faq[]·sources[]·thumbnail
src/lib.ts clinicSchema/personSchema/physicianSchema, INDEXABLE 플래그
src/layouts/Base.astro 헤더(병원 로고+서포터즈)·이전 페이지 버튼(홈 제외)·플로팅 위로·푸터·JSON-LD·canonical(index.html 제거)
src/components/ Summary(세 줄 요약, 연하늘 #F2F6FF 단색)·VideoLite(썸네일+클릭 iframe)·Gallery·RelatedVideos·Faq·ReviewBox·Sources·Disclosure·FactBlock·PostCard
src/pages/ index(라이트 파스텔 히어로·다크 글래스 KPI·Doctors Explain·시설 갤러리)·posts/[id]·posts/index·videos·newsroom·clinic·about·authors/[id]·robots.txt.ts·sitemap.xml.ts
public/img/{clinic,doctors,safety,exam,breast,care}/ 홈페이지에서 내려받은 92장 + hero-collage.jpg + view-logo.png
```
---
## 2. 이 세션에서 확정된 결정 (자동화가 지켜야 할 규칙)
1. 디자인은 INFINITH 토큰(`infinith-frontend-design` 스킬): 네이비 #0A1128, 퍼플 #6C5CE7, 그라디언트 #4F1DA1→#021341, Playfair(영문 제목·숫자)+Pretendard, 다크/라이트 리듬, 다크 위 흰 카드, 대각선 그림자, 파스텔 상태색. **텍스트 많은 블록은 그라디언트 금지, 연하늘 단색.**
2. 글 구조 = 기업은행 블로그 검증 구조: 세 줄 요약 → 본문(질문형 H2, HTML 표) → 원본 영상 → 갤러리 → 이어서 볼 영상 → FAQ → 작성·감수 박스 → 참고 자료 → 병원 정보 블록 → 대가 고지.
3. 넣지 않는 것: 전후 사진, 환자 후기·경험담 인용(**공정위 2026-07-12 뒷광고 제재 병원**), 타 병원 비교, 효과 보장, 가격(경영진 결정 전), 후기·리뷰·토크·변신 성격 영상, 비교 광고 소지 이미지(마취과 상주 43% vs 23.1%).
4. 리뷰 페이지는 만들지 않는다(haewon 결정 2026-09-04). Google Places 키 불필요.
5. 영상: 롱폼 Top10 기본 탭, 정보형만. 쇼츠는 정보형만 + 서포터즈 한 문단 답. 채널 전체 순위는 참고 탭.
6. 뉴스룸: 병원 배포 기사 링크 트리. 제3자 규제·사건 보도 제외(키워드 목록은 build_news.py NEG). 본문 인용 없음.
7. 서포터즈 실명·원장 감수는 확정 전 → 가명·"감수 예정" 표기, reviewedBy 스키마는 감수 완료 글에만.
8. 기본 noindex + robots Disallow. 발행은 `PUBLIC_INDEXABLE=true` 환경변수 하나로 전환(robots 전면 허용 + AI 봇 명시 + sitemap 선언).
9. 병원 자산 사용은 파트너십 전제(허락 받은 것으로 간주). 단 이미지는 재문맥화(새 alt·캡션·출처·캡처일).
10. 경영진 미결: 도메인(별도 vs 서브도메인), 서포터즈 모집, 법무 확인 주체, POC 8주 범위 포함 여부, 가격 노출.
---
## 3. 목표 기능의 파이프라인 (제안)
```
[입력] 병원 홈페이지 URL (+ 상호 선택) ← /discovery 랜딩 CTA (DiscoveryLeadModal)
│
Phase 0 discover-channels (기존) → 유튜브·인스타·페북·블로그·강남언니·GBP 핸들
Phase 1 audit_aeo_geo (기존 py → Edge Function 이식 또는 워커) → discovery_<id> 결과 (50/C 등)
Phase 2 자산 수집 (신규 collect-supporter-assets)
├ 홈페이지: Firecrawl 페이지 텍스트(병원소개·의료진·오시는길·안전·시술 메인) + 이미지 URL 목록(필터: logo/icon/btn/popup/전후/before-after 제외) + 로고
├ 유튜브: uploads 전량 videos.list(statistics·contentDetails·status) → 조회수 순위, 쇼츠 판별(/shorts/{id} 200), 자막(youtube_transcript_api, IP 차단 대비 프록시/캐시/큐)
├ 강남언니: 병원 페이지 공개 집계(평점·후기수·의료진수·진료시간)
├ 뉴스: 홈페이지 언론보도 게시판(있으면) + 네이버 뉴스 API 6질의 → 분류·부정 제외
└ 의료진: 홈페이지 의료진 페이지 → 이름·직함·약력·사진 → authors.json physicians
Phase 3 팩트 시트 생성 (factSheet.json) ← 사실 추출 + 표기 단일화(주소 1형태, 전화, 진료시간 출처 표기)
Phase 4 질문 선정 ← QB 템플릿(카테고리 7종)을 업종·시술로 인스턴스화 → 첫 배치 8~10문항 (A1·B1·D4·E1·G1 비율)
Phase 5 글 생성 (Claude) ← 문항 + 관련 영상 자막 + 홈페이지 사실 → frontmatter 규격 md
프롬프트 규칙: 세 줄 요약, 질문형 H2, 표 1개 이상, FAQ 5~8, 출처 accessed 날짜, 금칙어, 개인차 문장, 홈페이지 문장 복사 금지
Phase 6 사이트 조립 ← supporters/ 템플릿 복제 + 데이터 주입 + 이미지 다운로드·리사이즈 + 로고
Phase 7 발행 게이트 (check.mjs) 통과 못 하면 중단 → 실패 항목을 리포트에 표시
Phase 8 미리보기 배포 ← Vercel 프로젝트 `supporters-<clinicId>` 생성·배포(noindex) → URL을 discovery 리포트와 discovery_leads 행에 기록
Phase 9 감사 재실행 (색인 허용 빌드 로컬) → 공식 vs 서포터즈 비교표 (build_supporters_audit_xlsx.py 로직 재사용) → 리포트 페이지에 표시
[출력] /discovery/<id> 에 "서포터즈 미리보기" 카드 + 비교 점수 + 남은 과제
```
권장 실행 형태: 오래 걸리는 Phase 2·5·8은 Edge Function 시간 제한(150초)을 넘기므로, **Supabase 큐(테이블 + cron) + 별도 워커(Node, Vercel 서버리스 아님)** 또는 GitHub Actions 워커로 돌리고 진행 상태를 `supporter_builds` 테이블에 기록해 랜딩이 폴링한다.
---
## 4. 재사용할 코드 (이식 대상)
| 기능 | 지금 있는 곳 | 이식 방향 |
|---|---|---|
| 채널 발견·검증 | `supabase/functions/discover-channels`, `_shared/extractSocialLinks.ts`, `verifyHandles.ts` | 그대로 사용 |
| 자동 채점 | `scripts/audit_aeo_geo.py` | Node/TS 이식(`supabase/functions/discover-aeo-geo`) 또는 워커에서 python 호출 |
| 유튜브 통계·쇼츠 판별 | 이 세션 스크래치 스니펫 (uploads.json → videos.list 50개 배치 → /shorts/{id} 200 판별) | 워커 모듈 `collect-youtube-ranking.ts` |
| 뉴스 수집·분류 | `supporters/scripts/build_news.py` (OUTLET 맵, NEG, TOPICS, REL/Q1/Q2 정규식, 게시판 파서) | 워커 모듈 |
| 이미지 필터·리사이즈·컨택트시트 | 이 세션 스니펫 (PIL, 1600px, 제외 정규식 `icon|logo|btn|popup|floating|banner|before|after`) | 워커 모듈 |
| 발행 게이트 | `supporters/scripts/check.mjs` | 그대로. home_text.txt는 Phase 2 텍스트로 생성 |
| 비교 엑셀 | `scripts/build_supporters_audit_xlsx.py` | 점수 비교 로직만 TS로 옮겨 리포트 카드에 표시 |
| 글 템플릿·스키마 | `supporters/src/pages/posts/[id].astro`, `src/lib.ts` | 템플릿 저장소로 분리(`templates/supporters/`) |
---
## 5. 작업 순서 (권장)
1. **템플릿화**: `supporters/`에서 뷰성형외과 고유값을 전부 `factSheet.json`·`authors.json`·`videos.json`·`news.json`·`posts/`·`public/img/`로 몰아넣고, 나머지를 `templates/supporters-astro/`로 복제. 빈 데이터로도 빌드되게 empty-state 처리.
2. **워커 뼈대**: `workers/supporters-build/` (Node 22). 입력 `{clinicId, url}` → 단계별 함수, 각 단계 결과를 `supporter_builds` 테이블(단계·상태·로그·산출 경로)에 기록.
3. **Phase 2 수집기** 3종(홈페이지·유튜브·뉴스)을 뷰성형외과로 회귀 테스트: 결과가 수작업 v3와 같은 팩트 시트·Top10·뉴스 건수(±5%)가 나오면 통과.
4. **Phase 5 글 생성 프롬프트**: 뷰성형외과 글 8편을 정답지로 두고, 같은 문항·자막으로 생성한 결과가 게이트를 통과하고 세 줄 요약·표·FAQ 규격을 지키는지 확인. 금칙어·경험담 표현은 게이트에서 막는다.
5. **Phase 8 배포기**: Vercel API로 프로젝트 생성·환경변수·배포. 도메인은 `supporters-<id>.vercel.app`.
6. **랜딩 연결**: DiscoveryLeadModal 제출 → `supporter_builds` 행 생성 → 진행 상태 UI(단계 9개) → 완료 시 리포트 페이지에 미리보기 카드. `discovery_leads` 마이그레이션 먼저 적용.
7. **두 번째 병원으로 검증**: `data/plastic surgery clinics url.json`에서 한 곳 골라 끝까지 돌린다. 수작업 개입 없이 게이트 통과가 목표.
---
## 6. 이번 세션에서 밟은 함정 (반드시 회피)
- youtube_transcript_api는 25편 후 `IpBlocked`. 자막은 큐 + 지연 + 프록시 또는 YouTube Data API captions(OAuth 필요) 대안 설계. 자막 없는 영상은 제목·설명만으로 글을 쓰지 말고 "근거 부족"으로 건너뛴다.
- Apify 계정 미납(`Too many outstanding invoices`) → 인스타 수집 불가. 인스타는 선택 단계로.
- YouTube search API `order=viewCount`는 3건만 반환 → uploads 전량 + videos.list로 집계.
- 쇼츠는 3분까지 가능 → 길이 60초 기준만으로 판별 금지, `/shorts/{id}` 200 응답으로 판별.
- Firecrawl 결과에 팝업·상담폼 텍스트가 섞임 → NOISE 필터 필요. 홈페이지 게시판 썸네일 alt가 비어 있으면 문서 페이지에서 제목 재수집.
- 채점기 canonical 검사: Astro `Astro.url.pathname`이 `/index.html`로 잡힘 → 정규화 필수. 주소는 지번 병기 시 2종으로 잡힘 → 팩트 블록은 도로명 1형태만.
- zsh에서 `[id].astro` 경로는 따옴표 필수(glob).
- Vercel `build.format: 'file'`은 `cleanUrls: true` 없으면 /posts/x 404.
- `npm run build | grep`은 파이프라인 때문에 게이트 실패를 삼킨다 → `if npm run build > log; then deploy; fi`.
- Google Places 키 없음. 필요 없음(리뷰 페이지 폐기).
- `.env`의 GOOGLE_PLACES_API_KEY는 비어 있음. YOUTUBE·NAVER·FIRECRAWL·PERPLEXITY·OPENAI 키는 있음.
---
## 7. haewon에게 물어볼 것 (착수 전)
1. 워커 실행 위치: Supabase 큐 + Node 워커(서버 필요) vs GitHub Actions vs Vercel 백그라운드 함수 중 어디로 할지.
2. 첫 배치 글 수(8편 유지?)와 생성 모델(Claude API, 회사 키 유무).
3. 자동 생성 글의 감수 워크플로: 병원에 초안 링크를 보내는 이메일/노션 연동을 넣을지.
4. 두 번째 검증 병원 선정(`data/plastic surgery clinics url.json`).
5. 미리보기 배포를 Vercel 프로젝트 자동 생성으로 할지, 하나의 프로젝트에 `/<id>/` 경로로 묶을지.
---
## 8. 완료 기준
- 랜딩에서 URL 제출 → 사람 개입 없이 9단계 완주 → 미리보기 URL이 리포트에 표시된다.
- 뷰성형외과 URL로 돌렸을 때 팩트 시트·Top10·뉴스룸이 수작업 v3와 일치(±5%)하고, 게이트 0오류, 색인 허용 빌드 감사 80점 이상.
- 두 번째 병원에서도 게이트 0오류로 완주.
- 금칙 사항(전후·후기 인용·비교·효과 보장)이 게이트에서 실제로 차단되는 테스트 케이스가 있다.