- viewclinic/01_readiness_report (진단 덱 v1.0·v2_표준캔버스·v1.1 + 빌더) / 02_poc_proposal (POC 정본·v3 + 빌더) / 03_question_bank (QB 120문항·실측·엑셀) / 04_supporters (GEO 계획·콘텐츠 기획·감사 엑셀) - taeha/ (태하 리포트 + 빌더), ibk/ (기업은행 블로그 감사), landing/ (랜딩 시안·스크린샷·HTML 내보내기) - README.md에 구조·정본/파생본·빌드 명령 안내 - 경로 참조 갱신: 핸드오버 문서 4개, QB 스크립트 3개, 빌더 docstring, discovery 페이지 주석 - 이전에 삭제된 POC v2_표준캔버스 2파일 삭제 반영 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
160 lines
16 KiB
Markdown
160 lines
16 KiB
Markdown
# 다음 세션 프롬프트 · "병원 URL 하나로 서포터즈 사이트까지 자동 빌드"
|
|
|
|
작성 2026-09-04 · 작성자 Claude (haewon 세션 핸드오버) · 상태: 착수 전
|
|
|
|
---
|
|
|
|
## 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오류로 완주.
|
|
- 금칙 사항(전후·후기 인용·비교·효과 보장)이 게이트에서 실제로 차단되는 테스트 케이스가 있다.
|