import sys sys.path.insert(0, "/Users/haewonkam/.claude/skills/haewon_signature_deck/scripts") import hsd from hsd import Deck OUT = sys.argv[1] d = Deck(footer_base="INFINITH · Viewclinic AI Answer Readiness") # 01 Cover d.cover( eyebrow="INFINITH AI DISCOVERY · AEO / GEO AUDIT v1.0", title="VIEWCLINIC", subtitle="AI Answer Readiness Report", lede=["뷰성형외과 홈페이지가 ChatGPT·Gemini·Perplexity 답변에 인용될 준비가 됐는지 36개 항목으로 진단했습니다.", "결과는 44점(C). 크롤러 접근은 정상이지만, AI가 인용할 문장과 엔티티 근거가 없습니다."], meta=[("PREPARED FOR", "뷰성형외과"), ("PREPARED BY", "INFINITH AI Discovery"), ("AUDITED", "2026. 08. 28"), ("SCOPE", "viewclinic.com · 36 criteria")], ) # 02 Executive summary (KPI 4-up) s = d.slide(eyebrow="EXECUTIVE SUMMARY", title="읽을 수는 있지만, ", title_accent="인용할 근거가 없다", lede=["36개 항목 중 28개를 실측했고, 오너 접근이 필요한 8개는 점수 분모에서 제외했습니다.", "접근성(A)은 92%로 상위권, 엔티티(B) 33%·콘텐츠(C) 20%가 총점을 끌어내립니다."]) s.kpi_4up([ ("44 / 100", "AI Answer Readiness", "등급 C. 검증 항목 75점 만점 중 33.1점"), ("92%", "A · 크롤러 접근성", "AI 검색봇 12종 전부 허용, SSR 렌더링"), ("20%", "C · 인용 가능 콘텐츠", "정의문 0, FAQ 0, HTML 표 0, H1 5개"), ("8 / 36", "미검증 항목", "강남언니·GBP·GA4·로그는 오너 확인 필요"), ]) # 03 What AI sees s = d.slide(eyebrow="WHAT AI SEES · 진단의 결론", title="문제는 차단이 아니라 ", title_accent="내용이다") L, R = s.cards_2up() L.pill("정상").rows([ ("크롤러 접근 정상", "robots.txt가 OAI-SearchBot·PerplexityBot 등 12종을 허용. Cloudflare 차단 없음"), ("서버 렌더링 본문", "WordPress가 본문을 서버에서 렌더. JS 없이 10,757자 추출됨"), ("YouTube 활발", "VIEW SHORTS·FILM 채널이 최신 영상을 꾸준히 발행. 오픈웹 언급 자산"), ]) R.pill("치명", tone="rival").rows([ ("누구인지 확정 불가", "MedicalClinic 스키마 없음, sameAs 비어 있음, 시술 목록 JSON-LD는 문법 오류로 무효"), ("인용할 문장 없음", "H1 5개가 전부 영문 라벨. FAQ 0개, HTML 표 0개, 정의문 0개"), ("15개월 갱신 신호 없음", "post-sitemap 최신 lastmod 2024-05-31. dateModified 마크업 없음"), ]) # 04 Scorecard table s = d.slide(eyebrow="SCORECARD · 6 CATEGORIES", title="카테고리별 점수와 ", title_accent="검증 범위", lede="점수는 검증된 항목만으로 정규화했습니다. 미검증 항목을 확인하면 총점이 바뀔 수 있습니다.") W, Rail = s.wide_rail() W.grid( headers=["카테고리", "배점", "획득", "판정"], col_w=[2.45, 0.80, 0.95, 0.85], start=0.20, header_tones=[hsd.MUTED, hsd.MUTED, hsd.VIOLET, hsd.MUTED], rows=[ ["A · AI 크롤러 접근성", "20", "15.7 / 17", "92% 우수"], ["B · 엔티티 식별·구조화", "20", "6.7 / 20", "33% 미흡"], ["C · 답변 인용 가능 콘텐츠", "25", "5.0 / 25", "20% 치명"], ["D · AI 인용 표면 확보", "20", "4.0 / 6", "67% 양호"], ["E · AI 가시성 측정 인프라", "10", "0 / 2", "0% 미설치"], ], ) Rail.bullets("읽는 법", items=[ "획득 분모는 검증 항목 배점 합. D·E는 미검증이 많아 분모가 작다", "F 기술 위생은 5점 중 1.7점(33%). 표에서 생략", "총점 = 33.1 / 75 = 44점, 등급 C", ]) # 05 A 접근성 s = d.slide(eyebrow="A · ACCESS & CRAWLABILITY · 92%", title="크롤러 접근은 ", title_accent="열려 있다") L, R = s.cards_2up() L.pill("우수 · 3/3").rows([ ("A1 검색용 AI 봇 허용", "robots.txt: Disallow는 /wp-admin/ 뿐. 12종 UA 실제 GET 전부 200"), ("A2 CDN 차단 없음", "nginx 직접 응답. Cloudflare 헤더·AI UA 챌린지 없음"), ("A4 HTTPS 정합", "http://viewclinic.com → 301 → https://www.viewclinic.com/ 정본 수렴"), ]) R.pill("보완 · 2/3", tone="neutral").rows([ ("A5 sitemap.xml", "선언·200 응답. 페이지 249개. 단 post lastmod 최신이 2024-05-31"), ("A7 응답 속도·무게", "TTFB 0.135s는 양호. 초기 HTML 838KB로 기준 300KB의 2.8배"), ("A6 Bing 색인 (미검증)", "ChatGPT 검색은 Bing 인덱스 의존. Webmaster 등록 여부 오너 확인 필요"), ]) # 06 B 엔티티 s = d.slide(eyebrow="B · ENTITY & STRUCTURED DATA · 33%", title="AI가 이 병원을 ", title_accent="하나의 엔티티로 못 묶는다") L, R = s.cards_2up() L.pill("치명", tone="rival").rows([ ("B1 JSON-LD 문법 무효", "ItemList 블록 쉼표 누락(line 11). 시술 목록 구조화 데이터 전체가 파서에서 버려짐"), ("B2 MedicalClinic 없음", "Organization·WebSite만 존재. 주소·전화·진료시간·전문분야 미구조화"), ("B3 Physician 없음", "의료진 정보는 텍스트·이미지로만. 누가 수술하는지 기계가 못 읽음"), ]) R.pill("미흡", tone="neutral").rows([ ("B5 sameAs 비어 있음", "푸터에 YouTube·Instagram·Facebook 링크가 있으나 스키마에 미연결"), ("B4 NAP 표기 2종", "\"서울 강남구 봉은사로 107\" / \"서울특별시 강남구 봉은사로 107,\" 혼재"), ("B7 단일 언어", "hreflang 없음. 외국인 환자 대상 언어 페이지 미확인"), ]) # 07 C 콘텐츠 s = d.slide(eyebrow="C · ANSWER-READY CONTENT · 20%", title="답변엔진이 잘라 갈 ", title_accent="단락이 없다") L, R = s.cards_2up() L.pill("0점 항목", tone="rival").rows([ ("C1 정의문 없음", "메인 첫 화면은 슬라이더와 \"VIEW SELFIE\" 라벨. 시술 페이지 첫 텍스트는 상담 폼"), ("C2 FAQ 없음", "메인·가슴성형 페이지에서 FAQ·Q&A 섹션 탐지 안 됨"), ("C3 H1 5개, 전부 라벨", "VIEW SELFIE / EVENT / SHORTS / FILM / SPECIALITY. 시술 페이지는 H1 없음"), ]) R.pill("부분 점수", tone="neutral").rows([ ("C7 의료진 권위 신호 있음", "모티바 KOL·복지부 표창·갈더마 방문 등 17회 언급. 단 구조화 안 됨"), ("C4 수치 표기 미흡", "\"보형물 사용량 1위\" 같은 주장형. 가격대·수술시간·회복기간 표 없음"), ("C6 신선도 신호 약함", "dateModified 없음. 유튜브 활동이 사이트 갱신으로 전달되지 않음"), ]) # 08 D 표면 + E 측정 s = d.slide(eyebrow="D · SURFACES / E · MEASUREMENT", title="병의원 AI 인용의 핵심 표면은 ", title_accent="아직 미확인") L, R = s.cards_2up() L.pill("D · 표면 67%").rows([ ("D1 강남언니 (미검증)", "2026-08 실측: 병의원 질의는 강남언니 등 버티컬이 인용을 장악. P0 확인 항목"), ("D2 Google 비즈니스 프로필", "소유 여부 미확인. Gemini·AI Mode의 구조화 데이터 원천"), ("D5 네이버 플레이스", "리뷰 776건. 단 네이버 자산은 글로벌 AI 답변 기여도 0, AI 브리핑 전용"), ]) R.pill("E · 측정 0%", tone="rival").rows([ ("E1 크롤러 로그", "nginx 직접 호스팅. AI 봇 히트 추출·IP 검증 파이프라인 없음"), ("E2 GA4 AI 유입 채널", "chatgpt.com·perplexity.ai 커스텀 채널그룹 없음. 오너 접근 필요"), ("E4 기준선 스팟체크", "미실시. 질의 10개 × 5회 반복 측정이 개선 증명의 전제"), ]) # 09 Action P0 / P1 (dark) s = d.slide(eyebrow="ACTION PLAN · P0 / P1", title="즉시 고칠 것과 ", title_accent="4주 안에 만들 것", dark=True) L, R = s.cards_2up() L.dark_block("P0 · 즉시 (1일~1주)", "엔티티·표면·측정", [ "깨진 JSON-LD 수정 + MedicalClinic 스키마 삽입, sameAs 연결", "강남언니·GBP 실사, 4개 표면 NAP 한 글자까지 통일", "GA4 AI 채널그룹 + 서버로그 IP 검증 + GSC·Bing 등록", ]) R.dark_block("P1 · 4주 내", "콘텐츠 구조", [ "핵심 페이지 첫 단락에 정의문, 시술별 FAQ 5~8개 + FAQPage 스키마", "페이지당 H1 1개 서술형, 비교표를 HTML table로, Physician 스키마", "WordPress 5.5.3 업그레이드, alt 누락 111개 보완, HTML 무게 절감", ]) # 10 Roadmap (asym) s = d.slide(eyebrow="ROADMAP · 8 WEEKS", title="8주 안에 ", title_accent="C에서 B로") L, R = s.cards_asym() L.timeline("실행 순서", [ ("Week 1", "JSON-LD 수정, MedicalClinic·sameAs 삽입. 강남언니·GBP 실사"), ("Week 2", "측정 인프라 설치. 기준선 스팟체크 1회 (질의 10개 × 5회)"), ("Week 3–6", "정의문·FAQ·H1 재설계·HTML 표. WordPress 업그레이드"), ("Week 7–8", "재진단. 미검증 8개 항목 반영해 총점 재산정"), ]) R.numbered("P2 · 지속 항목", [ "dateModified 마크업 활성화, 월 1회 사이트 갱신", "유튜브 영상을 사이트 글로 전사해 신선도 전달", "티스토리·언론 기고로 naver.com 외 언급 도메인 확보", "분기 1회 반복 측정, 언급률과 신뢰구간으로만 보고", ]) # 11 What we don't sell (3-up) s = d.slide(eyebrow="WHAT WE DON'T SELL · 정직성 규약", title="근거 없는 것은 ", title_accent="팔지 않는다") c1, c2, c3 = s.cards_3up() c1.bullets("만들지 않는 것", items=[ "llms.txt. Google 공식 효과 0, 채택률 0.13%, 서버 로그에 요청 자체가 없음", "네이버 블로그 증량. robots.txt가 AI 크롤링 전면 금지, 글로벌 AI 기여도 0", ]) c2.bullets("약속하지 않는 것", items=[ "\"스키마 넣으면 인용됩니다\". 인과 증거 없음, 위생 조치로만 설명", "순위·노출 보장. LLM 답변은 동일 질문에도 10~34% 흔들림", ]) c3.bullets("측정 규율", items=[ "단발 조회로 성과 보고 금지. 반복 측정 + 신뢰구간만", "답변엔진 UI 자동 조회 금지. OpenAI·Perplexity 약관 위반", "AI 유입은 항상 하한값. 35~70%가 Direct로 유실", ]) # 12 Rubric method (3-up) s = d.slide(eyebrow="METHOD · AEO / GEO RUBRIC v1.0", title="36개 항목, 100점, ", title_accent="근거 등급을 명시") c1, c2, c3 = s.cards_3up() c1.bullets("채점 방식", items=[ "항목별 0~3 레벨 × 배점. 미검증은 0점이 아니라 분모 제외", "A 20 · B 20 · C 25 · D 20 · E 10 · F 5", "등급 A 81+ · B 61~80 · C 41~60 · D 40 이하", ]) c2.bullets("배점 근거", items=[ "차단되면 절대 인용될 수 없다는 사실은 논쟁이 없다. A·B에 무게", "GEO 콘텐츠 전술 효과는 학술적으로 논쟁 중. C는 좋은 콘텐츠 원칙으로만", "병의원은 강남언니가 인용을 장악. D1 단일 항목 최고 배점 6", ]) c3.bullets("항목마다 붙는 태그", items=[ "근거 등급: 실측 / 학술 / 벤더 / 추정", "통제 가능성: 직접 / 영향 / 불가", "채점 방식: 자동 / 반자동 / 수동. 자동 항목은 Edge Function으로 이식", ]) # 13 Closing d.closing(eyebrow="NEXT STEP", title="확인 2개면 점수가 ", title_accent="실제 노출력에 수렴합니다.", lede=["강남언니 프로필과 Google 비즈니스 프로필 상태만 확인하면 미검증 8개 중 가장 큰 10점이 채워집니다.", "그 다음은 P0 세 가지: 스키마, NAP 통일, 측정 인프라."], signature="INFINITH AI Discovery · AEO / GEO Audit v1.0 · 2026. 08. 28") d.save(OUT) print("saved", OUT)