gemini-2.0-flash 는 Google 이 내려 404 를 돌려주고 있었다(2026-10-07 확인). 분석이 조용히 빈 결과로 끝나던 상태다.
새 Gemini 키(서비스 계정 바인딩) 프로젝트에서도 호출되는 3.5-flash 로 바꾸고, 내부 추론 토큰을 감안해 출력 한도를 4096 으로 올린다.
운영 collect-channel-data v24 로 배포(운영 index.ts 는 로컬과 달라 운영 소스에 이 파일만 바꿔 배포), Supabase GEMINI_API_KEY 는 새 키로 교체.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 처리방침: 처리 목적별 법적 근거, 국외 이전 근거 조항·이전받는 자 연락처·거부 방법과 효과, 거래기록 보관 근거를 국세기본법으로
- 이용약관: AI 기본법 제31조 생성형 AI 사전 고지 조항 추가, 외부 서비스 면책을 회사 고의·과실 없는 경우로 한정
- 검토 문서 docs/legal/webforai_privacy_terms_review_2026-10-07.html
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- vite.config define 의 GEMINI_API_KEY 를 지운다
- API 대시보드의 import.meta.env 동적 조회를 공개 변수 목록으로 바꾼다. 동적 조회 때문에 VITE_ 변수 전체(VITE_CREATOMATE_API_KEY 포함)가 번들에 들어갔다
- 모든 배포 빌드 끝에 서버 전용 키 값이 번들에 있으면 빌드를 실패시킨다
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
브라우저가 키를 들고 외부 API 를 직접 부르던 구조를 api/gemini-image.js, api/creatomate-render.js 로 옮긴다.
브라우저는 채널·포맷·주제 선택값만 보내고, 서버가 허용 목록과 대조한 뒤 프롬프트·영상 구성을 만든다.
키는 서버 환경변수 GEMINI_API_KEY·CREATOMATE_API_KEY 에만 둔다. 출처는 infinith-demo·localhost 만 받는다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
webforai.kr 빌드 결과에서 실제 병원명·도메인, Google·Supabase 키, reports/·assets/clients/ 파일을 찾으면
빌드를 멈춰 Vercel 배포가 진행되지 않게 한다. 목록은 plugins/public-bundle-denylist.json.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- src/lib/site.ts 의 SHOW_PRICING·SHOW_SAMPLE_REPORT·SHOW_USE_CASES 로 라우트·헤더·랜딩 버튼을 한 곳에서 켜고 끈다. 공개 배포 기본값은 모두 꺼짐
- 리포트 본문을 DiscoveryReportView 로 분리하고, 가상 데이터만 그리는 /discovery/sample 을 만든다
- 예시 리포트 경로는 병원 작업 공간 메뉴 대신 일반 헤더를 쓴다
- Use Cases 데이터(src/data/useCases.ts, 비어 있음)와 섹션을 두고, 사례가 있을 때만 섹션·메뉴가 보인다
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/supabase.ts 의 최상단 createClient() 가 부수효과로 남아, 쓰는 곳이 없는 공개 배포에도
Supabase URL·anon 키가 실렸다. 이 키로 marketing_reports 등 실제 병원 데이터 테이블을 읽을 수 있었다.
supabase 모듈을 부수효과 없는 모듈로 표시해 쓰지 않으면 번들에서 빠지게 한다. infinith-demo 빌드는 그대로 쓴다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
public/ 은 infinith-demo 와 공용이라 reports/viewclinic(영문 진단 리포트 HTML·PDF)과
assets/clients/(병원 채널 스크린샷)가 공개 배포에도 올라가 주소만 알면 열렸다.
discovery 빌드의 writeBundle 에서 두 폴더를 지운다. infinith-demo 빌드는 그대로다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- discovery 빌드에만 GTM(GTM-WQNTZVZH) 스니펫을 넣는다. GA4(G-013Z3BFY35)와 키 이벤트는 컨테이너 안에서 설정한다
- 문의 접수 성공 시 dataLayer 에 generate_lead(lead_source) 를 보낸다. 개인정보는 넣지 않는다
- GTM 가져오기 파일(docs/analytics/gtm_webforai_import.json): GA4 기본 태그, generate_lead·click_tel·click_email
- 개인정보처리방침 제1·5·8조에 Google 애널리틱스 분석 쿠키, 수집 항목, 국외 이전, 거부 방법을 반영한다
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 랜딩 샘플 리포트·AI 답변 예시·문의 서식 예시를 가상 A성형외과로 바꾼다 (discovery_sample, 고정 시드 무작위 판정)
- VITE_SITE=discovery 배포는 랜딩·요금·로그인·약관만 연다. 실제 병원 진단 결과와 내부 화면 경로는 홈으로 돌린다
- src/data·src/services 를 부수효과 없는 모듈로 표시해 쓰지 않는 실제 병원 목업이 공개 번들에서 빠지게 한다
- 공개 배포 번들에 GEMINI_API_KEY 를 넣지 않는다
- 헤더 Product·Use Cases 는 공개 배포에서 숨기고, 푸터에 개인정보처리방침·이용약관 페이지를 연결한다
- 기준표 설명의 실제 병원 예시 문구를 일반 예시로 바꾼다
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
브라우저로 내려간 값은 무엇이든 공개다. VITE_LEAD_TOKEN 이 번들에 평문으로
박혀 있었고(실제로 추출해 확인했다) 토큰은 보호 장치가 되지 못했다. 토큰을
새로 발급해도 똑같이 공개된다.
- api/lead.js 신규. 토큰과 Apps Script 주소를 서버 환경변수(LEAD_TOKEN·
LEAD_ENDPOINT)로만 들고 요청을 전달한다. VITE_ 접두사를 쓰지 않아 번들에
들어가지 않는다.
- Origin 허용 목록으로 webforai.kr·www·aio2o.webforai.kr 만 받는다. 토큰과
달리 Origin 은 브라우저가 채우고 스크립트가 바꿀 수 없다. 다른 사이트에
폼을 심어 이 주소를 때리는 것을 막는다. aio2o 는 정적 배포라 자체 함수를
둘 수 없어 이 함수를 같이 쓴다(교차 출처라 CORS 를 명시).
- submitLead 는 같은 출처 /api/lead 로 보낸다. 클라이언트 환경변수가 없어졌다.
- vercel.json 의 전체 경로 리라이트에서 /api 를 제외한다. 안 그러면 함수
요청까지 index.html 로 삼킨다.
Vercel 환경변수: LEAD_ENDPOINT·LEAD_TOKEN 추가, VITE_LEAD_* 2개 삭제.
Vite 는 코드에서 안 쓰더라도 VITE_ 접두사 변수를 전부 번들에 넣기 때문에
삭제까지 해야 토큰이 사라진다(삭제 전 재배포에서 여전히 검출됐다).
검증: 운영 번들에서 토큰 0건·script.google.com 0건. /api/lead 정상 응답
{"ok":true,"mailed":true}. 다른 Origin 403. SPA 라우트(/discovery/:id·
/actions/:id) 200 유지. 실제 폼 제출 접수 확인.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
haewon 2026-10-01 지시로 두 사이트에서 팩스 0504-335-6463 을 뺀다.
aio2o.webforai.kr 쪽도 같은 날 함께 제거했다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
한 번 문의를 보낸 뒤 닫았다가 다시 열면 폼 대신 "문의가 접수되었습니다"
화면이 그대로 떠서 두 번째 문의를 할 수 없었다. 모달을 닫아도 컴포넌트가
언마운트되지 않아 state 가 'done' 으로 남아 있던 탓이다.
열릴 때마다 입력값과 상태를 비운다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
푸터 (haewon 2026-10-01)
- aio2o.webforai.kr 푸터와 같은 내용으로 맞춘다: 법인명·본사·연구소 주소와
전화·팩스·이메일. 값의 원본은 그쪽 data/facts.json 이며 지어내지 않았다.
- 전화 표기를 점에서 하이픈으로 바꿔 두 사이트를 같게 한다.
문의 폼 보호 장치
- 허니팟: 사람 눈에 안 보이는 입력칸(website)을 두고, 값이 차 있으면 스크립트가
저장하지 않는다. display:none 을 건너뛰는 봇이 있어 화면 밖으로 밀어내고
aria-hidden·tabIndex -1 로 보조기기와 탭 이동에서는 뺀다.
- 속도 제한: Apps Script 가 한 시간 30건까지만 받는다. VITE_LEAD_TOKEN 은 빌드
시점에 번들로 들어가 공개값이라 보호 장치가 못 된다. 주소와 토큰이 알려져도
피해가 상한 안에 묶이게 한다.
- 알림 실패 분리: 메일 발송이 실패해도 행은 남기고 접수로 응답한다. 지금까지는
메일이 막히면(한도 소진·구글 장애) 저장됐는데도 고객 화면에 "전송 실패"가 떠
접수된 줄 모르고 돌아가게 되어 있었다. 시트에 '알림' 열을 추가해 '보냄' /
'실패(메일만)' 을 적어 메일이 안 간 문의를 찾을 수 있게 한다.
토큰 실값은 저장소에 넣지 않는다. 배포본 Apps Script 와 Vercel 환경변수에만 둔다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
haewon 지정. 1행(로고 · 저작권/태그라인 · 메뉴) → 구분선 → 연락처 한 줄.
- 문의하기를 연락처 줄 오른쪽 버튼에서 1행 메뉴로 옮긴다. aio2o 푸터가
회사 소개·솔루션과 같은 줄에 문의하기를 두는 것과 맞춘다.
- 연락처는 "전화 … · 이메일 …" 한 줄로 왼쪽 정렬하고 값에만 밑줄을 준다.
- 사업자명·주소는 넣지 않는다. 확인된 값이 없어 지어내지 않는다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Navbar 는 backdrop-blur-md 를 쓴다. backdrop-filter 가 걸린 요소는
position:fixed 자손의 기준 블록이 되므로, 모달 오버레이의 inset-0 이
뷰포트가 아니라 nav 박스(1396x80)로 잡혔다. 그 결과 모달이 헤더 높이
안에서 잘려 페이지 콘텐츠가 위를 덮는 것처럼 보였다.
오버레이를 document.body 로 포털한다. 조상의 기준 블록·쌓임 맥락에서
완전히 벗어나므로 푸터 등 다른 위치에서 열어도 같은 문제가 생기지 않는다.
실측: 수정 전 오버레이 높이 80px → 수정 후 893px(뷰포트와 일치),
화면 중앙 좌표가 모달 내부로 잡히는 것 확인.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
연락처 통일
- 사이트 안에 이메일이 contact@o2o.kr · o2oteam@o2o.kr 두 갈래로 갈라져
있던 것을 sales@o2o.kr 하나로 통일 (lib/contact.ts 단일 출처)
- 전화번호 010.2755.6463 을 처음으로 명시. discoveryContact.ts 는
기존 import 경로를 깨지 않도록 재내보내기만 한다
푸터·문의 폼
- 푸터에 전화·이메일과 문의하기 버튼 추가 (사업자명·주소는 확인된 값이
없어 넣지 않는다)
- ContactModal 신규: 성함·회사·전화·이메일·문의내용. 화면 구조는 기존
DiscoveryLeadModal 을 그대로 따라 모달이 두 종류로 보이지 않게 한다
- Navbar 문의하기를 mailto 에서 모달로 교체. 메일 앱이 없는 사용자가
mailto 에서 이탈하던 것을 막는다
수신 경로
- submitLead: Apps Script 웹앱으로 POST. 이 사이트는 서버 없는 SPA 라
중간 서버를 두지 않았다. Apps Script 는 CORS 프리플라이트에 응답하지
않아 Content-Type 을 text/plain 으로 보내 simple request 로 맞춘다
- scripts/apps-script/lead-intake.gs: 구글시트 행 추가와 알림 메일을
한 실행에서 처리. 시트 트리거는 스크립트가 쓴 변경에 늦거나 누락되는
경우가 있어 doPost 안에서 바로 보낸다
- 엔드포인트 미설정·전송 실패 시 접수된 것처럼 보이지 않게 하고 메일
폴백을 띄운다 (기존 DiscoveryLeadModal 과 같은 규칙)
토큰 실값은 저장소에 넣지 않는다. 배포본 Apps Script 와 Vercel
환경변수(VITE_LEAD_ENDPOINT · VITE_LEAD_TOKEN)에만 둔다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
기존 상단 띠 배너("샘플 사이트입니다")는 스크롤하면 사라져 병원이
둘러보는 동안에는 초안인지 최종본인지 판단이 서지 않았다.
- sticky 헤더 안에 "초안" 배지 추가(스크롤해도 계속 보임), 영문판도 동일
- 문구를 "샘플 사이트입니다"에서 상태 중심으로 변경:
"초안입니다. 검색에 노출되지 않으며, 병원 확인 전입니다."
- 띠 배너 글자 크기·굵기 상향(0.83rem/500 → 0.95rem/600)
- 인쇄 시 배지 숨김(기존 배너 규칙과 동일하게)
- INDEXABLE=true(공개본)에서는 배너·배지 모두 그대로 뜨지 않음
templates/supporters-astro(정본)와 supporters/(현재 작업 사본) 양쪽에
동일하게 반영. 이미 배포된 서포터즈 사이트(예: supporters-jkplastic)에는
재빌드·재배포 전까지 적용되지 않는다.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
증거(진단)와 처방(할 일)을 한 페이지에 두면 스크롤이 너무 길어져
둘 다 읽기 힘들다는 지적에 따라 단계를 나눴다.
- /actions/:id 신규: Action Plan(P0~P2) + What We Don't Sell + 빌드 제안
(ActionPlanSection·IntegritySection 신규 컴포넌트, Tag는 두 페이지가
같이 써서 공용 컴포넌트로 뺌)
- /discovery/🆔 위 섹션 제거, 진단(점수·근거·Scorecard·Rubric)만 남김.
페이지 맨 아래 중복으로 박혀 있던 확인 항목 패널(ClinicInputsPanel)도
제거 — /supporters/:id 전용 단계가 이미 있어 두 번 보여줄 필요가 없었다
- 진단 데이터가 없는 병원이 /actions/:id 로 오면 /discovery/:id 로 되돌림
- ClinicNav: 5단계로 확장(진단 리포트→개선 제안→사이트 빌드→사진 확인→
확인 항목), 다음 단계 플로팅 버튼 추가(기존엔 이전 단계만 있었다)
- App.tsx: 라우트 전환 시 스크롤을 맨 위로 되돌림. 스크롤을 내린 채
다음 단계를 누르면 새 페이지가 같은 위치(짧으면 빈 하단)에서 시작하던
문제 수정. 인앱 해시 앵커(#breakdown)가 있으면 건드리지 않음
- 사이트 빌드/확인 항목 화면의 미리보기 버튼을 "초안 사이트 열기"로
통일(공개 후에는 "공개본 사이트 열기")
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- 영문 사이트(jkplastic.com/en) 기준 v1.0 45/C, AEO 40/D, GEO 27/D
- audit_aeo_geo.py 자동 채점(21항목) + 영문 페이지 11개 원본 실측 보정
- 자동 채점의 국문 전제 정규식이 놓친 B4(NAP)·C4(수치)·F1(alt) 보정
- 외부 실측 가능한 C1·C4·C7·C8·D1·D3 확정, 오너 계정 필요한
A6·D2·D4·E1~E4는 미검증으로 분모 제외
- discoveryResults.ts 레지스트리에 jkplastic 등록
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
버튼만 두면 무엇에 동의하는지 모른 채 누르게 된다. 진단을 요약하고, 이 빌드가 무엇을
보완하는지 말한 뒤 누르게 한다.
처음 만든 판은 실행 항목을 카테고리로 갈라 "이 빌드로 채워지는 항목 5건"이라고 했는데
그 5건이 깨진 JSON-LD 수정·GBP 전화번호 등록·H1 재설계처럼 전부 병원 홈페이지 몫이었다.
카테고리는 무엇에 관한 문제인지를 말할 뿐 누가 고치는지를 말하지 않는다. 지키지 못할
약속이라 프레임을 바꿨다.
빌드는 홈페이지를 한 줄도 고치지 않는다. AI가 인용할 새 표면을 하나 더 만들어 약한
카테고리(콘텐츠·엔티티·표면)를 우회 보완할 뿐이다. 화면도 그렇게 말한다.
"이 빌드가 대신할 수 없는 것"으로 실행 항목이 그대로 병원 몫임을 밝힌다.
- 다크 섹션 위 주 버튼을 .btn-on-dark 로 뺀다. 기본 CTA 그라디언트(#4F1DA1→#021341)는
끝 색이 배경(#0A1128)과 거의 같아 버튼으로 읽히지 않았다. 밝은 보라와 글로우로 떼어 놓는다.
- 문구에서 "결과"를 뺀다. 빌드가 끝나도 사진 확인과 확인 항목이 남아 최종이 아니고,
누른 적 없는 사람에게 "결과 보기"부터 보이는 것도 맞지 않는다.
- 이전 단계로 가는 플로팅 버튼을 왼쪽 아래에 고정한다. 오른쪽 아래는 주 행동 자리이고,
좁은 화면에서는 상단 절차 메뉴가 가로 스크롤 안으로 밀린다. 첫 단계에서는 띄우지 않는다.
- 떠 있던 승인 바와 BuildSiteCta 는 이 섹션과 역할이 겹쳐 지운다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"한 장씩 봐 주세요" 같은 요청문이 읽고 넘어가는 설명문과 같은 무게로 놓여 있어
지나치기 쉬웠다. 역할별 토큰과 클래스를 두고 화면에 적용한다.
.ui-ask 요청문. 사람이 무엇을 해야 하는지 말하는 문장 (17px·600·인디고)
.ui-ask-block 문단으로 서는 요청문. 왼쪽 규칙선으로 설명과 구분한다
.ui-body 설명 본문 (16px·slate-600)
.ui-note 보조·출처 (14px·slate-400)
크기는 기존 화면을 세어 맞췄다. text-base 167회·text-sm 58회가 지배적이라
body 를 16px, note 를 14px 로 두면 치환해도 크기가 변하지 않고 역할과 색만 정리된다.
한글 줄바꿈: word-break keep-all 로 어절을 지키고, 긴 URL·영문 토큰만
overflow-wrap anywhere 로 자른다. text-wrap 으로 마지막 줄에 한 어절만 남는 것을
줄이고, 62ch 로 줄 길이를 묶는다. 제목에도 keep-all 과 balance 를 건다.
다크 섹션은 .on-dark 로 값을 뒤집고, 그 안의 흰 카드는 .on-light 로 되돌린다.
이걸 두지 않으면 흰 글씨가 흰 배경에 얹혀 보이지 않는다(디자인 시스템 기록된 실수).
사진 확인 화면의 썸네일 줄이 누를 수 있는 것으로 안 읽혀 같이 고친다. 72px 카드,
호버 반응, 순번 배지, 현재 위치 표시, 안 정한 것 아래 띠, 범례, 이전·다음 이동을 둔다.
상태가 안 보이는 것이 크기보다 큰 문제였다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
예전 전략마케팅 툴은 분석 진행 화면에서 단계를 보여 주고 리포트로 넘어갔다.
지금 버전은 진단과 사이트 빌드가 서로 모르는 상태로 떨어져 있어, 진단을 읽은 병원이
다음에 무엇을 하는지 알 수 없었다. 진단 → 빌드 → 사진 확인 → 확인 항목을 한 줄로 잇는다.
- /build/:id 를 추가한다. 예전 /report/loading 의 단계 UX(스피너·체크·진행바)를 잇되,
진행 상황을 프런트가 만들지 않고 supporter_builds 를 폴링해 워커가 쓴 값을 읽는다.
프런트가 파이프라인을 돌리지 않으므로 창을 닫아도 빌드는 계속되고, 재개 로직이 필요 없다.
- 워커 17단계를 고객이 읽을 수 있는 5묶음으로 접는다(src/lib/buildPhases.ts). 진행률은
묶음이 아니라 끝난 워커 단계 수로 세어 한 묶음 안에서도 바가 움직인다.
- 경고로 끝난 단계는 완료 화면에 "확인 대기로 발행된 부분"으로 남긴다. 자동으로 못 채운
값을 감추지 않는다. 멈춤은 발행 게이트와 실행 실패를 구분해 문구를 나눈다.
- 진단 리포트의 Action Plan 뒤에 빌드 구간을 둔다. 무엇을 만들어 주는지와 버튼만 둔다.
버튼은 supporter_builds 에 queued 를 넣고 폴러가 집어 간다.
- 상단 메뉴를 경로로 가른다. 병원 작업 경로에서는 마케팅 메뉴 대신 4단계 절차를 띄운다.
Pricing·Use Cases 는 사진을 고르는 중인 병원에게 방해다.
- 진단 데이터가 없는 병원(빌드만 한 곳)은 막다른 길 대신 빌드·사진 확인으로 잇는다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
랜딩 신청 모달은 남긴 연락처로 안내한다고 말하는데 보내는 코드가 없다.
운영자도 신청이 들어온 사실을 알 방법이 없다. discovery_leads 는 anon insert 만
열려 있고 폴러는 supporter_builds 만 본다.
메일을 셋으로 나눈다. 접수 확인(신청자)과 신규 신청 알림(운영자)은 insert 트리거로
자동 보내고, 완료 안내는 사람이 보낸다. 자동으로 보내면 사진 검토를 건너뛴 링크가
나간다. 발송은 Edge Function 에 두고 프런트에서 부르지 않는다(anon 키 스팸 경로).
email_log 로 중복 발송을 막고 안 갔다는 문의에 답할 근거를 남긴다.
연락처가 전화번호일 수 있어 contact_kind 를 함께 저장한다.
발송 서비스 계정·발신 도메인·"리포트"의 정의는 결정 대기로 8절에 적었다.
개인 업무 문서(주간보고)는 저장소에 올리지 않는다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
싣기 어려운 사진(환자·전후 비교·시술 장면·효과 주장 그래픽)은 파일명이나
설명문으로 걸러지지 않아 사람이 눈으로 봐야 한다. 그 과정이 제품 밖에 있어
빌드마다 검토서를 따로 만들어 보냈다.
/images/:clinicId 를 추가한다. supporter_builds.report.images 를 읽어 미리보기
사이트의 사진을 한 장씩 보여 주고, 사용·제외를 supporter_inputs(key=image_review)
에 쌓는다. 워커가 apply_inputs 에서 읽어 다음 빌드에 반영한다.
- 첫 화면 대표 → 사이트에 실림 → 아직 안 쓰임 순으로 정렬한다. 중간에 그만둬도
실제로 노출되는 사진은 다 보게 된다
- 판정 기준은 법 조항이 아니라 눈으로 확인할 수 있는 질문으로 적는다
- 중간 저장한다. 같은 key 는 최신 행이 유효값이라 다시 들어오면 이어서 본다
- 사진 파일은 preview_url 에서 불러온다. 배포 전 빌드는 안내 문구로 빠진다
대상이 병원 담당자라 제작 과정과 내부 규칙은 화면에 쓰지 않는다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
주변 안내(/stay·/en/stay)의 축제 목록이 계절 묶음만 있고, 회복 일정 플래너에서 고른
기간과 이어지지 않았다(haewon 2026-09-16). 플래너가 체류 기간(도착일~출국 가능일)을
이 기기의 localStorage 에 남기고, 주변 안내는 그 기간과 겹치는 행사를 위로 올린다.
- Planner.astro: 결과를 그릴 때 체류 기간만 저장(시술·수술일은 저장하지 않음, 서버로도
가지 않음). 행사 섹션에 "주변 안내에서 이 기간의 행사 전체 보기" 링크(/stay#plan:from..to)
- stay.astro·en/stay.astro: 카드에 data-start·data-end·data-km, "체류 기간" 칩(기본 숨김),
기간 안내 문단. 스크립트가 URL 해시 또는 localStorage 에서 기간을 읽어 겹치는 카드를
맨 앞으로 옮기고 칩을 켠다. 기간이 짧은 행사가 앞, 90일을 넘는 상시 행사는 뒤('상시 진행')
- 기간이 없거나 겹침이 없으면 화면은 이전과 같다. 저장소를 못 쓰는 브라우저는 조용히 건너뛴다
- global.css: 겹치는 카드 테두리·오른쪽 배지, 숨긴 칩
- run.mjs: planStrings.json 을 코드 갱신 목록에 넣는다. 기존 병원 폴더에 옛 문구 파일이
남아 링크 글자가 비어 나온 것을 캡처로 발견했다
검증: 원진 빌드 게이트 통과, plan.test 14건 통과, 로컬 미리보기(Playwright)로
플래너 저장→주변 안내 정렬(20/24건, 짧은 행사 먼저)·해시 진입·기간 없음·영문·모바일 확인.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
미리보기 카드와 일부 브라우저는 <link rel="icon"> 을 보지 않고 /favicon.ico 를
그대로 요청한다. vercel.json 의 SPA 리라이트가 모든 경로를 index.html 로 보내서
아이콘 자리에 HTML(text/html 2,589B)이 도착했고, 공유 카드에 회색 링크 아이콘이 나왔다.
public/ 은 infinith-demo 배포와 공용이라 파일명을 discovery- 로 나눠 두었으므로,
빌드 때 이 배포의 아이콘만 루트 이름으로 복사한다. infinith-demo 빌드는 그대로다.
검증: discovery 빌드에 favicon.ico(16·32·48)·favicon-32·192·apple-touch-icon 생성,
기본 빌드에는 생기지 않음. tsc 0 에러.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 회의록 검토, Global Medical Hub PRD v0.2 검토, 경쟁 플랫폼·규제·시장 딥리서치,
병원 AEO/GEO 서비스 경쟁분석(4-10), 현재 조직으로의 사업 가능성, MOU·POC 권고 범위.
- 워드 보고서는 haewon 편집본이다. 초기 빌더(build_mou_hub_competition_docx.py)는
결과 파일이 있으면 멈추고, 이후 변경은 patch_aeo_geo_competition_docx.py 처럼 제자리 삽입한다
- MD 초안 동봉
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
특정 서비스명(ChatGPT와 Perplexity) 대신 AI로 바꿨다. webforai.kr 공유 이미지의
같은 문장도 함께 바꿨다. 아직 배포하지 않았다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
네이버 AI 브리핑은 공식 API가 없어 지금까지 답변 노출을 재지 못했다.
SerpApi naver_ai_overview(제3자 SERP API)로 조회하는 엔진을 붙였다(haewon 결정 2026-09-15).
search.naver.com robots.txt 가 수집을 금지하므로 고객 리포트·랜딩에는 넣지 않고
내부 측정까지만 반영했다. 고객 노출은 법무 확인 뒤에 따로 한다.
- run_question_bank_openai.py: --engine naver_briefing, --device, --no-cache(반복 측정 시 1시간 캐시 무시).
브리핑이 없는 문항은 answer 를 비우고 briefing=false 로 기록. 인용 출처(sources) 보관
- monthly_ai_measure.py: 기본 엔진 3개(키 없으면 네이버 건너뜀),
supporter_builds.report.naverBriefing(노출률·언급·출처 상위, legalReviewed=false)
- sentiment_qb.py: 빈 답변은 판정 API를 부르지 않고 미언급 처리
- merge_qb_results.py: NAVER 열·브리핑 없음 표시, 측정일을 생성일이 아닌 엔진별 실제 날짜로
- export_qb_excel.py: 네이버 열·NAVER상세 시트, --out 으로 기존 파일 보존.
TOP50 점수는 기존 정의(ChatGPT+Perplexity, 만점 840) 유지
실측(뷰성형외과, 모바일, 2026-09-15): 브리핑 노출 119/120, 브랜드 질문 언급 36/36,
논브랜드 1/84. 인용 출처는 네이버 블로그 87·카페 51·굿닥 23·뷰 공식 20 순.
SerpApi 123회, 감성 판정 $0.37.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
해외 환자가 받는 링크라 카카오톡·슬랙 미리보기의 제목·설명을 영어로 낸다(haewon 2026-09-15).
site.json shareNameEn 이 있을 때만 적용하고, 없으면 기존 한국어 카드를 유지한다.
- 홈·한국어 페이지: "<영문명> Supporters · Answers to the questions patients ask first" + 영어 설명
- 영문 페이지: 자기 영어 제목·설명을 쓰되 섞인 한국어 상호를 영문 표기로 바꾼다
- 글(article): 본문이 한국어라 제목·설명은 그대로, 사이트 이름만 영어
- <title>·meta description 은 페이지 언어 그대로(검색 결과용)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
카카오톡·슬랙·iMessage 에 사이트 주소를 붙이면 회색 링크 아이콘만 나왔다.
webforai.kr 은 og:image 와 파비콘이 없었고, 서포터즈 사이트는 robots.txt 가
미리보기 봇까지 막았으며 파비콘이 Astro 기본 로고였다.
- scripts/make_share_assets.py: 1200x630 공유 이미지(JPEG)와 파비콘 세트 생성.
서포터즈는 해외 환자용 영어 판(병원 공식 영문 표기 shareNameEn), 파비콘은 영문 첫 글자.
- 워커 build 단계에서 자동 생성. briefs/<clinic>/clinic.json 에 shareNameEn 과 출처.
- Base.astro: og:image 기본값 /og.jpg, 크기·twitter:card·아이콘 링크.
- robots.txt(샘플): 미리보기 봇만 허용. 검색·AI 크롤러 차단과 noindex 는 유지.
- vite.config.ts: discovery 빌드에만 og:image·아이콘 태그. public/ 은 infinith-demo 와
공용이라 파일명에 discovery- 접두어.
- Astro 기본 favicon.svg 삭제는 직전 커밋(f2fe99b)에 섞여 들어갔다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
세 서포터즈 사이트의 네이버 소유 확인이 모두 배포됐다. 값 3개가 서로 다른 것과
사이트별로 맞게 나가는 것을 확인했다.
구글은 webforai.kr 도메인 속성을 DNS TXT 로 확인해 서브도메인 전부가 한 번에 덮인다.
네이버는 DNS 를 받지 않아 사이트마다 메타로 따로 확인한다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 서치어드바이저 발급분. 사이트마다 값이 달라 옮겨 쓰면 확인되지 않는다.
배포 확인: 뷰와 원진이 서로 다른 값을 내고 오라클은 아직 없다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
템플릿은 data 단계에서만 복사돼, --from build 로 돌리면 그 사이에 고친 화면 코드가
기존 병원 폴더에 닿지 않았다. 오늘 두 건이 이 이유로 배포되지 않았다.
- 검색엔진 소유 확인 메타: site.json 에 값이 들어갔는데 화면에 안 나왔다
- 회복 일정 플래너 계측(plan_procedure_select·plan_result·plan_save): 세 사이트 모두 없었다
refreshTemplateCode() 가 build 단계에서 화면 코드만 다시 덮는다.
src/layouts·components·pages·styles·lib 과 lib.ts·content.config.ts 가 대상이고,
병원 데이터·글·이미지는 건드리지 않는다. 병원별로 갈리는 astro.config.mjs·vercel.json 의
치환값, 구조 기준선, home_text 도 덮지 않는다. 내용이 같은 파일은 건너뛴다.
뷰 브리프에 네이버 서치어드바이저 소유 확인 값을 넣었다(viewpl.webforai.kr 용).
사이트마다 값이 달라 옮겨 쓰면 확인되지 않는다는 것을 주석에 적었다.
검증: 세 사이트 재배포 후 뷰에 소유 확인 메타가 나오고 다른 두 곳에는 안 나온다.
플래너 계측은 세 곳 모두 번들에 들어갔다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
네이버 서치어드바이저는 DNS TXT 를 받지 않아 메타 태그로만 소유가 확인된다.
구글은 webforai.kr 도메인 속성을 DNS TXT 로 확인해 메타가 필요 없다.
VITE_SITE=discovery 빌드에만 들어간다. infinith-demo 빌드에는 나가지 않는다.
서포터즈 사이트는 코드가 달라 briefs/<clinic>/clinic.json 의 site.naverSiteVerification
으로 각각 넣는다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>