Commit Graph

37 Commits

Author SHA1 Message Date
Haewon Kam
c0804dd2bd feat(supporters): 서포터즈 사이트 초안 표시를 눈에 띄게 강화
기존 상단 띠 배너("샘플 사이트입니다")는 스크롤하면 사라져 병원이
둘러보는 동안에는 초안인지 최종본인지 판단이 서지 않았다.

- sticky 헤더 안에 "초안" 배지 추가(스크롤해도 계속 보임), 영문판도 동일
- 문구를 "샘플 사이트입니다"에서 상태 중심으로 변경:
  "초안입니다. 검색에 노출되지 않으며, 병원 확인 전입니다."
- 띠 배너 글자 크기·굵기 상향(0.83rem/500 → 0.95rem/600)
- 인쇄 시 배지 숨김(기존 배너 규칙과 동일하게)
- INDEXABLE=true(공개본)에서는 배너·배지 모두 그대로 뜨지 않음

templates/supporters-astro(정본)와 supporters/(현재 작업 사본) 양쪽에
동일하게 반영. 이미 배포된 서포터즈 사이트(예: supporters-jkplastic)에는
재빌드·재배포 전까지 적용되지 않는다.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 10:27:26 +09:00
Haewon Kam
796fa56106 feat(supporters): 주변 안내 축제를 회복 일정의 체류 기간 기준으로 먼저 보여 준다
주변 안내(/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>
2026-09-16 09:47:01 +09:00
Haewon Kam
eaf60a9cf0 feat(supporters): 공유 카드(og) 영어화
해외 환자가 받는 링크라 카카오톡·슬랙 미리보기의 제목·설명을 영어로 낸다(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>
2026-09-15 09:44:05 +09:00
Haewon Kam
f6aae1c396 feat(share): 링크 공유 미리보기 이미지와 파비콘
카카오톡·슬랙·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>
2026-09-15 09:36:45 +09:00
Haewon Kam
f2fe99b81d docs(reports): 답변엔진 확장 검토 보고서 (120문항 비용 추정·후보별 약관)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 09:36:33 +09:00
Haewon Kam
247d29173d fix(supporters): 검색엔진 소유 확인 메타 태그를 실제로 렌더
site.json 에 googleSiteVerification·naverSiteVerification 키는 있었는데 어디에서도
메타 태그로 내지 않았다. 값을 넣어도 소유 확인이 되지 않는 상태였다.
색인 등록을 시작하려다 드러났다.

Base.astro 가 값이 있을 때만 메타를 낸다. 없으면 태그 자체가 나가지 않는다.
소유 확인은 색인 허용과 무관하므로 noindex 상태에서도 미리 해 둘 수 있다.

구글은 DNS TXT 로도 되지만 네이버 서치어드바이저는 메타 태그나 파일만 받는다.

검증: 값을 넣은 빌드에 메타 2개, 값 없는 빌드에 0개. 게이트 41/41, check 오류 0건, tsc 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 15:33:49 +09:00
Haewon Kam
f26155565c fix(supporters): 클릭 집계에 호텔·항공 이벤트와 place_id 추가
방금 가져온 Planner.astro 가 호텔·항공 링크에 data-ga="hotel"·"flight" 를 붙이는데
Base.astro 의 리스너가 그 두 값을 다루지 않아 클릭이 집계되지 않았다.
메인 체크아웃의 미커밋 Base.astro 에는 이 처리가 있었고, 그쪽을 버리기 전에 옮겼다.

- click_hotel · click_flight 를 리스너에 추가
- data-ga-id 를 place_id 로 함께 보낸다 (어느 장소를 눌렀는지)
- 플래너 이벤트와 수술일 미전송 원칙을 주석에 적었다

검증: 뷰 34페이지, 게이트 41/41, check.mjs 오류 0건, tsc 0 에러, 내보내기 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 09:02:07 +09:00
Haewon Kam
e44ff60537 feat(supporters): 회복 일정 플래너 계측 이벤트 (메인 체크아웃 미커밋분에서 가져옴)
메인 체크아웃(feature/ai-sentiment-rubric)의 작업 트리에 커밋되지 않은 채 남아 있던
플래너 계측 코드가 main 에 없었다. 기록 노트 머리말이 "2026-09-12 추가 작업: 플래너
계측 이벤트 구현"이라고 적은 그 코드다. 브랜치 통합은 커밋된 상태까지만 가져가므로
빠졌고, 그래서 배포된 세 사이트에 플래너 계측이 없었다.

Planner.astro 의 계측 부분만 가져왔다(haewon 결정). 같은 미커밋분이 Base.astro 에도
GA4 태그를 넣는데, main 에는 이미 같은 태그가 들어가 있어 그대로 두었다. 두 구현이
겹치면 태그가 두 번 들어간다.

가져온 것
- track(): window.gtag 가 없으면 아무것도 하지 않는다. GA4 측정 ID 가 비어 있으면
  태그 자체가 안 나가므로 기본 상태에서 조용하다.
- plan_procedure_select · plan_result · plan_save 이벤트.
- leadBucket(): 수술일 원본을 보내지 않고 남은 기간 구간(past·0-7d·8-28d·29-90d·90d+)만
  보낸다. 체류 일수·출국 확정 여부·선호·국적도 값이 아닌 구간·플래그로 보낸다.
- 호텔·항공·상담 링크의 data-ga 속성. Base.astro 의 클릭 리스너가 이 값을 읽는다.

site.json 에 ga4MeasurementId 자리와 설명을 넣었다. 템플릿 빈 데이터셋에는 이미 있었다.

검증: 뷰 34페이지, 게이트 41/41, check.mjs 오류 0건, tsc 0 에러,
빈 데이터 템플릿 16페이지, 내보내기 0. 번들에서 gtag 미존재 시 조기 반환 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 08:55:44 +09:00
Haewon Kam
85a9aa1f54 feat(supporters): 원진 랜딩 히어로를 글로벌 허브 리셉션 사진으로 (haewon 지정) · 갤러리 사진을 카드 후보에서 제외
haewon 육안 검토 결과: 원진 수집 이미지 66장 전부 사용 가능, 제외할 것 없음.
랜딩 히어로는 img_about_cover_03(글로벌 허브 리셉션·대기 공간)으로 지정.
이전 값 img_about_cover_01 은 원장이 모니터를 가리키는 사진이라 글 카드와 겹쳐 보였다.
alt 는 수집 원문("WJ 원진 소개 커버 이미지") 대신 무엇을 찍은 것인지 밝히는 문구로 쓴다.

갤러리에 실린 사진은 글 카드 후보에서 뺀다. 홈에서 글 목록 바로 아래가 갤러리라
카드 마지막 장과 갤러리 첫 장이 같으면 나란히 두 번 보인다.
다만 이렇게 빼서 글 수를 못 채우면 남는 글이 건물 사진으로 떨어져 더 나빠지므로,
후보가 글 수를 채울 수 있을 때만 뺀다.

결과
- 원진: 홈 13장 / 12종 · 최대 연속 1 (이전 9종 · 연속 2)
- 뷰: 홈 8장 / 4종 · 최대 연속 2. 쓸 수 있는 이미지가 3장뿐이라(건물 사진은 히어로)
  5편 중 2편이 건물 사진으로 떨어진다. 병원 제공 시설 사진이 있어야 풀린다.

검증: 두 사이트 npm run build 종료코드 0, check.mjs 오류 0건, 게이트 41/41,
tsc 0 에러, 내보내기 0. 미배포.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-12 16:55:40 +09:00
Haewon Kam
f0857420e1 fix(supporters): 배포를 막던 게이트 두 건 · 대표 이미지 alt 의 효과 주장 제거
뷰 배포가 Vercel 빌드에서 멈췄다. 워커는 통과했는데 Vercel 이 막힌 이유는
--from deploy 가 빌드 게이트를 건너뛰고 바로 배포로 갔고, Vercel 은 자체 npm run build
(astro build && check.mjs)를 돌리기 때문이다. 실패는 두 가지였다.

1. HOME_DUPLICATE: 검토자 표시줄의 원장 이력이 병원 홈페이지 문장과 40자 일치했다.
   이 자리는 병원 의료진 페이지의 이력을 프로필 링크와 함께 그대로 옮기는 귀속 표시라
   설계상 일치한다. 다른 말로 바꿔 쓰면 이력이 사실과 달라진다.
   /stay 의 인용 블록(#recovery-quote)을 이미 같은 이유로 빼고 있어서, 같은 규칙으로
   .doctor-strip 을 QUOTED_BLOCKS 에 넣었다. 금칙어·운영자 어휘 검사는 그대로 적용된다.

2. 대표 이미지 alt 의 효과 주장: 배정된 alt 가 "손상된 피부 세포 재생과 콜라겐 생성으로
   흉터와 색소침착 개선" 처럼 결과를 주장했다. 의료법 56조 2항 8호 대상이고 주제도 맞지 않았다.
   이미지 자체는 문제가 없으므로 빼지 않고, 효과·결과 어휘가 있으면 출처만 밝히는 중립
   문구로 바꾼다. 수집기의 분류를 믿지 않는다(일러스트가 시설로 들어온다)는 이유로
   "시설 사진" 처럼 유형을 단정하지 않고 "홈페이지 이미지"로 쓴다.

검증: 세 사이트 모두 npm run build 종료코드 0(Vercel 이 돌리는 명령과 같다).
게이트 41/41, 샘플 check.mjs 오류 0건, tsc 0 에러, 내보내기 0.
연속 중복은 뷰 1·원진 1 이고, 오라클은 수집 이미지가 없어 그대로다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-12 16:27:30 +09:00
Haewon Kam
dd754cf212 feat(supporters): 글마다 다른 대표 이미지 배정 · 뷰 수집 이미지에서 의료광고 금지 유형 제외
haewon 지적: 목록 화면에 같은 사진이 나란히 선다.

원인은 PostCard 의 대체 순서다. thumbnail·hero·영상이 모두 없으면 건물 사진 하나로
수렴한다. 뷰는 5편 전부, 오라클은 8편 전부가 그랬고, 원진은 6편 중 3편이 같은
소개 커버를 쓰고 있었다.

heroes 단계를 워커에 넣었다(generate 다음, build 앞). scripts/assign_post_heroes.mjs 가
images.json 에서 글마다 다른 사진을 고른다.
- 글 분류별로 어울리는 유형을 먼저 쓴다. 병원 소개·방문은 시설, 시술 정보는 시술 설명
  일러스트, 안전은 장비 순이다. 방문 안내에 시술 일러스트가 붙는 것을 막는다.
- 의료진 사진은 쓰지 않는다. 저자·검토자 표시가 지정 용도이고, 글 대표로 쓰면 그 원장이
  쓴 글로 읽힌다.
- 홈 히어로·건물 사진과 겹치는 글도 다시 배정한다.
- 수집 원문 alt 를 그대로 쓰지 않는다. 원문은 술기 설명 문단이라 고객 화면에 낼 수 없어
  첫 구절만 줄여 쓴다. 캡션의 출처도 페이지 제목이 슬로건일 때 상호로 바꾼다.
- 이미지가 글보다 적으면 남는 글은 비워 둔다. 다시 쓰면 처음 문제로 돌아간다.

작업 중 더 큰 문제를 찾았다. 뷰의 수집 이미지 중 다섯 장이 의료광고 금지 유형인데
전부 시설(clinic)로 분류돼 홈과 /clinic 에 나가고 있었다.
- img02: 밑선 절개 흉터의 1개월·6개월 경과를 나란히 놓은 전후 사진
- stemcell_about_01: 팔에 주사·드레싱을 하는 시술 행위 사진
- view-abdominoplasty 2장, view-motiva-preserve-premium-standard 2장: "이미지 광고 모델" 표기가 있는 모델 신체 사진
- stemcell_count_01_mo: 장비 제조사의 성능 주장 그래픽
파일명과 alt 에 아무 신호가 없어 규칙 문서가 예고한 파일명·alt 검사로는 잡히지 않는다.
briefs/viewclinic/clinic.json 의 excludeImages 로 뺐고, 홈 히어로가 가슴 라인
일러스트였던 것과 건물 사진 alt 가 "가슴확대 시설 사진"이던 것도 함께 고정했다.

육안 검토용 scripts/build_image_review.mjs 를 만들었다. 세 병원 수집 이미지를 한 화면에
모아 분류·쓰이는 곳·의심 표시를 보여준다. 91장 중 파일명·alt 로 걸러진 것은 0장이었다.
이 유형은 사람이 봐야 갈린다는 뜻이다.

결과: 홈·목록의 연속 중복이 뷰 1, 원진 1 로 내려갔다(이전 뷰 6, 원진 3).
오라클은 수집 이미지가 0장이라 배정할 것이 없다. 근거의 이미지 alt 가 EUC-KR 에서
깨져(????) 분류기가 하나도 고르지 못하는 것이 원인으로 좁혀졌으나, 병원 사이트가
응답하지 않아 재수집·검증을 하지 못했다.

검증: 게이트 41/41, check.mjs 오류 0건, tsc 0 에러, 내보내기 종료코드 0. 모두 미배포.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-12 16:15:52 +09:00
Haewon Kam
82aa56fa40 chore(supporters): 통합 뒤 템플릿 재생성 (주변 안내 포함, 영어 홈 안전 기본값)
정본 supporters/ 와 템플릿의 페이지 집합이 같아졌다.
/stay·/en/stay·/en·/plan·/en/plan 이 양쪽에 모두 있다.

검증
- 뷰 34페이지, 게이트 41/41, check.mjs 오류 0건.
- 빈 데이터 템플릿 16페이지, 영어 홈에 병원 고유 사실 0건, 문장 성립.
- 내보내기 종료코드 0, tsc 0 에러.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-12 15:21:31 +09:00
Haewon Kam
bb68bdacad merge: feature/supporters-autobuild 진행분 (주변 안내 /stay, 워커 publish 색인 단계) 반영
머지 뒤에 브랜치가 5개 커밋 앞서 있었다. 주변 안내(/stay·/en/stay) 복원과
워커 publish 단계(IndexNow·검색 콘솔 소유 확인)가 거기 있었고, 먼저 올린 머지가
이 둘을 떨어뜨렸다. supporters/ 정본은 i18n 방식을 유지하고, 사이트맵과 site.json 은
양쪽 추가를 모두 살렸다. 템플릿은 supporters/ 에서 다시 내보낸다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-12 15:19:41 +09:00
Haewon Kam
e7a11ef67a fix(supporters): 영문 사실이 없는 병원에서 영어 홈이 깨지지 않도록 조각 단위로 조립
세 병원 재빌드를 앞두고 빈 데이터로 템플릿을 빌드해 보니 영어 홈이 깨졌다.
제목이 ", Seoul · Recovery planner", 히어로가 ", plan your stay before you fly.",
소개 문장이 "is a at ." 로 나왔다. 게이트는 이것을 오류로 잡지 않는다.
세 병원 모두 factSheetEn 이 비어 있어 그대로 배포하면 깨진 화면이 나간다.

고친 것
- 병원명: factSheetEn 의 영문 표기가 없으면 한국어 상호를 그대로 쓴다. 이름은 지어내지 않는다.
- 소개 문장: 업종·지역·설립연도 중 데이터에 있는 조각만 잇고, 하나도 없으면 문장 자체를 뺀다.
- 히어로 제목: 이름이 없으면 이름 줄을 빼고 문장을 대문자로 시작한다.
- 페이지 제목·설명·JSON-LD 이름: 같은 방식으로 조각을 잇는다. 제목에 박혀 있던 "Seoul" 을 뺐다.
- 머리말 기본값 "VIEW SUPPORTERS" 를 "SUPPORTERS" 로 바꿨다. 다른 병원 화면에 뷰가 나오던 자리다.

검증
- 빈 데이터 템플릿 14페이지: 제목 "Recovery planner, getting here, clinic facts",
  히어로 "Plan your stay before you fly." 로 문장이 성립한다.
- 뷰 32페이지: 제목·히어로·소개 문장이 모두 그대로다.
- 게이트 41/41, check.mjs 오류 0건, tsc 0 에러, 내보내기 종료코드 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-12 15:15:45 +09:00
Haewon Kam
e60ef04503 feat(supporters): 영어 화면의 병원 고유 사실을 데이터에서 읽도록 바꾸고 템플릿 재생성
templates/supporters-astro 는 supporters/ 에서 만들어지는 파생물인데, 정본이 된
supporters/ 코드에 뷰 고유 사실이 문장으로 박혀 있었다. 그대로 내보내면 다른 병원
사이트에 틀린 사실이 나간다. 빈 데이터로 빌드해 보니 실제로
"Founded 확인 대기 · Sinnonhyeon Station Exit 3" 가 나왔다.

데이터로 바꾼 곳 (전부 factSheetEn.json 에 이미 있던 값이다)
- 히어로 이미지 alt: site.json 의 heroImageAltEn 을 읽고, 없으면 한국어 alt 를 쓴다.
- 히어로 칩: 설립연도와 위치를 데이터에 있는 것만 잇는다. 둘 다 없으면 칩을 내지 않는다.
- 방문 카드 요약: factSheetEn 의 airportShort 가 있을 때만 경로를 적는다.
- 병원 카드 요약: 설립연도와 factSheetEn 의 building 첫 문장을 데이터에서 잇는다.
- 공항 카드: factSheetEn 의 airport 가 있을 때만 카드를 낸다.
- 영문 플래너 병원명: factSheetEn 의 shortName 을 읽고, 영문 표기가 없는 병원은
  한국어 상호를 쓴다.
- 404 안내: 상호를 factSheet 에서 읽는다.

템플릿 빈 데이터셋에 factSheetEn.json 이 없어서 뷰의 영문 사실이 통째로 남고 있었다.
빈 factSheetEn.json 을 만들고 site.json 에 heroImageAltEn 자리를 넣었다.

export_template.mjs 의 누락 검사가 상호만 찾아 지명으로 적힌 사실을 놓쳤다.
지명·건물 표현을 패턴에 넣고, 문장을 그리는 코드(src/pages·layouts·components·lib)만
내보내기를 멈추게 하고 데이터·빌드 도구는 알림만 하도록 나눴다. 빌드 도구의 기본 인자
때문에 검사가 늘 실패해 신호가 되지 못하던 문제도 함께 풀린다.

검증
- 빈 데이터 템플릿 빌드 14페이지, 영어 홈에 병원 고유 사실 0건, 사실을 말하던 조각은
  생략되고 요약문만 일반 문장으로 남는다.
- 뷰 사이트 빌드 32페이지, 영어 홈의 칩·공항 카드·요약문이 모두 그대로 나온다.
- 게이트 41/41 통과, check.mjs 오류 0건(경고 23건은 기존 건).
- 루트 tsc --noEmit 0 에러, 내보내기 종료코드 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-12 14:27:57 +09:00
Haewon Kam
da6397ca3c fix(supporters): astro.config 의 /stay→/recovery 리다이렉트 제거 (주변 안내 페이지가 리다이렉트 HTML 로 덮이던 원인)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 18:30:24 +09:00
Haewon Kam
d66e6279ed feat(supporters): 주변 안내(/stay·/en/stay) 복원 · 회복 일정 플래너(/plan)와 별개 페이지로
- 오라클 폴더에 남아 있던 최신본(장소·축제·동선 지도·날씨·시술 후 관리 접이식)을 /stay 로 복원. 제목 Around The Clinic, 회복 일정 플래너 링크
- 내비 '주변 안내'·'Nearby', 언어 짝 /stay↔/en/stay, 사이트맵, /stay 리다이렉트 제거(/recovery→/plan 은 유지)
- 구조 기준선 갱신(내비 항목 추가, haewon 지시 2026-09-11)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 18:28:12 +09:00
Haewon Kam
891b7bdece feat(supporters): 워커 publish 단계 · 배포 뒤 색인 작업 자동화 + 사람 체크리스트
- scripts/publish_site.mjs: 라이브 확인(홈·robots·sitemap·noindex), IndexNow(빙·네이버) 사이트맵 URL 알림, Google Search Console 사이트맵 제출·URL 색인 상태(서비스 계정 있을 때), 사람 몫 체크리스트(publish-checklist.md, status.humanTasks)
- 워커: data 단계에서 IndexNow 키 파일 생성, --indexable(noindex 헤더 제거·승인 글만), --domain(vercel domains add·SITE_URL), deploy 뒤 publish 단계, 상태 published_pending_tasks
- Base.astro: google/naver 소유 확인 메타(site.json googleSiteVerification·naverSiteVerification)
- 완료 정의: 배포가 아니라 색인 작업과 사람 체크리스트까지 (haewon 2026-09-11)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 15:28:48 +09:00
Haewon Kam
008411b929 feat(supporters): 워커 planner 단계에 관리 문장 필터 (효과 주장·홍보·정형 고지·표 조각·재수술 조언 제외) + 세부 시술명 접두
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 14:10:17 +09:00
Haewon Kam
5263593816 style(supporters): 태블릿·모바일 헤더 규칙 이식 (브랜드+언어 전환 한 줄, 메뉴 가로 스크롤) · 로고와 메뉴 겹침 해결
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 14:07:10 +09:00
Haewon Kam
0cc37b1b89 feat(supporters): 원진 큐레이션 장소 브리프(briefs/wonjin/planner.places.json), 플래너 추천 장소 설명을 데이터 유무에 따라 표기
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 13:11:51 +09:00
Haewon Kam
620b2cda59 style(supporters): 지도 출처 줄을 행 밖으로 옮겨 지도 상자와 카드 아래선을 맞춤
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 11:52:37 +09:00
Haewon Kam
df6ad3bbd2 style(supporters): 오시는 길 지도를 카드 높이에 맞춰 늘림
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 11:49:37 +09:00
Haewon Kam
ed8f65030d feat(supporters): 오시는 길 지도·길찾기(VisitMap) 템플릿 반영, 워커 tourism 단계가 factSheet.geo 좌표 기록
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 11:38:53 +09:00
Haewon Kam
aa5b05227f feat(supporters): 회복 일정 플래너를 템플릿·워커에 통합 (/plan·/en/plan), /recovery·/stay 를 /plan 으로 정리
- 템플릿: Planner.astro, lib/plan.ts·tour.ts, styles/plan.css, planStrings, pages plan·en/plan·404. Base 내비 회복 일정 → /plan, 언어 짝 /plan↔/en/plan, 옛 주소 리다이렉트(vercel.json), 사이트맵
- 워커 planner 단계(recovery 다음): scripts/build_planner_data.mjs 가 업종별 기본 규칙표(scripts/template/planner/procedures.plastic|derm.json)에 병원 시술 페이지 원문(recoveryNotes)을 matchKeywords 로 붙이고, 장소는 briefs/<clinic>/planner.places.json(큐레이션) 또는 범용 기본표(관광공사 기준 좌표)로 만든다
- 브리프: viewclinic·oracle 큐레이션 장소. 빈 템플릿(관광 데이터 없음)도 빌드·검증 통과(plan.test 14건)
- 이전 세션의 미커밋 작업(피부과 수집·OCR·게이트·언어 스위치, stay 페이지 제거)도 이 커밋에 함께 들어감. docs/prd 변경은 제외

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 11:27:59 +09:00
Haewon Kam
67f1cb8295 feat(supporters): 한국어 /stay 신설 — 언어 스위치의 짝을 맞춤
언어 스위치의 KO 가 /visit 으로 가고 있었다. 같은 내용의 한국어판이 아니라 짝이
아니었다. /stay 를 만들어 /stay ↔ /en/stay 로 잇는다.

수집 (scripts/fetch_medical_tourism.py --lang ko)
- 국문은 ContentTypeId 가 다르다(관광지 12·문화시설 14·숙박 32·쇼핑 38·음식점 39).
  다국어 코드로 부르면 엉뚱한 것이 온다.
- 국문 서비스는 KorService2 로 붙고, 국문 사진 폴백은 건너뛴다(국문이 원본이다).
- 출력은 medicalTourismKo.json.

국문이 훨씬 두껍다
  전체 123곳(영문 101) · 도보 5분 이내 13곳(영문 2) · 걸어갈 수 있는 곳 66곳(영문 25)
  설명문 146/147 · 사진 147/147
국문 관광지에는 의료관광 등록업소가 섞이지 않아 A02020500 필터가 걸리지 않는다.
영문에서 반경 5km 100건 중 92건이 병원·에이전시였던 것과 대비된다.

화면 (supporters/src/pages/stay.astro)
/en/stay 와 같은 구조에 한국어 문구를 넣었다. 이동 시간 라벨은 데이터에 영문으로
들어 있어 화면에서 만든다(도보 N분 / 차로 N분). 날씨 상태도 한국어로 옮겼고,
검색 링크는 네이버로 보낸다. 식이·회복 안내는 site.json 의 dietGuideKo /
recoveryStagesKo 를 쓰며, 없으면 병원 확인 대기로 둔다.

라우팅
- Base.astro PAIRS 에 '/stay': '/en/stay' 등록. 스위치가 서로를 가리킨다.
- 한국어 네비게이션에 "주변 안내" 추가. 스위치는 EN/KO 만 오가므로 다른 페이지에서
  /stay 로 갈 길이 없었다. 이 항목 추가로 LAYOUT_CHANGED 경고 2건이 남아 있으며
  haewon 승인 전까지 기준선을 갱신하지 않는다.
- 사이트맵에 /stay 추가.

템플릿은 같은 코드에 빈 데이터 스캐폴드를 둔다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 14:46:49 +09:00
Haewon Kam
2fc3f93163 fix(supporters): 동선 지도를 실제 OpenStreetMap 타일로 교체 + 핫링크 차단 대응
지도가 회색 바탕에 점만 찍혀 있어 어디인지 알 수 없었다. OSM 타일을 좌표로 직접
깔고 그 위에 핀을 얹었다. 지도 라이브러리는 부르지 않는다.
- Web Mercator 로 lat/lng → 월드 픽셀을 계산하고, 모든 지점이 여백 안에 들어오는
  가장 확대된 배율을 고른다(뷰성형외과 동선은 z=15).
- 타일은 img 가 아니라 CSS 배경으로 둔다. 장식 요소이고, 지도의 뜻은 감싸는 요소의
  aria-label 이 전한다. 스크린리더가 타일 조각을 하나씩 읽지 않는다.
- OSM 정책에 따라 "© OpenStreetMap contributors" 를 표기한다.

사진 핫링크
- 모든 사진에 referrerpolicy="no-referrer" 를 넣었다. 네이버 썸네일 CDN 이
  리퍼러를 보고 403 을 준다. 서버에서 검사할 때는 리퍼러가 없어 통과해 놓치기 쉽다.
- 사진 alt 를 채웠다. npm run build 의 접근성 게이트가 빈 alt 를 오류로 잡는다.
  로컬에서 astro build 만 돌려 게이트를 건너뛴 탓에 배포에서 처음 걸렸다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 14:01:16 +09:00
Haewon Kam
e32633e9a6 feat(supporters): /en/stay 를 참고 사이트 수준으로 다시 지음 — 설명문·이동시간·계절축제·동선·날씨
haewon 이 준 참고 사이트(스테이,머뭄)의 연동 범위에 맞춰 다시 만들었다. 앞서 만든 것은
거리순 표였고, 실제로 필요한 것은 설명문과 이동 시간이 붙은 카드, 계절로 묶은 축제,
시간표가 있는 동선이었다.

수집 (scripts/fetch_medical_tourism.py)
- detailCommon2 의 overview 를 가져온다. locationBasedList2 는 설명문을 주지 않아
  카드에 이름과 주소만 남았다. 123/125 건 확보.
- 이동 시간을 낸다. 참고 사이트 표기를 역산해 도보는 max(1, round(m/80)), 차는
  직선거리 km x 1.2 로 맞췄다(135m→2분, 566m→7분, 25km→30분). 2km 까지 도보로 보고,
  짧은 거리의 차 시간은 주차·신호 때문에 의미가 없어 최소 5분으로 둔다.
- 축제를 계절로 분류하고, 도보 시간 구간별 건수를 누적으로 집계한다.

사진 (125/125, 전부 실제로 열리는 것 확인)
목록 API 의 firstimage 만 쓰면 38건이 비었다. 폴백을 넷으로 걸었다.
  1) detailImage2 — 이미지 전용 오퍼레이션. 15건 회수(도산공원 7장이 여기 있었다)
  2) 국문 서비스 KorService2 — 영문 항목에 없어도 국문 항목에 있다. 10건
  3) 업체 공식 홈페이지 og:image — 2건
  4) Naver 이미지 검색 API — 11건
og:image 는 실제로 열어 보고 쓴다. 이미지가 아니거나 인스타 CDN 처럼 핫링크를
403 으로 막으면 화면에서 빈 칸이 되기 때문이다.
imageSource 를 항목마다 남겼다. naver 출처는 제3자 저작물이므로 내부 시연 범위이며,
공개 배포 전에는 걷어내거나 라이선스 있는 사진으로 교체해야 한다(haewon 판단).

화면 (supporters/src/pages/en/stay.astro)
- Around the clinic: 도보 시간 누적 필터, 범주별 카드, 사진 위 이동시간 배지, 설명문,
  공식 홈페이지가 있으면 링크하고 없으면 검색으로 보낸다.
- Festivals: 계절 탭.
- Shaping your days: 좌표로 그린 인라인 SVG 지도와 시간표. 외부 지도 라이브러리를
  부르지 않는다. 시각은 가정이며 화면에 그렇게 적었다.
- 오늘 날씨: Open-Meteo, 키 불필요. 빌드가 아니라 브라우저에서 받는다. 정적 빌드에
  넣으면 배포 시각의 날씨가 굳는다. 못 받으면 블록을 통째로 숨긴다.
  문구는 이 페이지의 목록에만 연결한다. 무엇을 해도 되는지는 의학 판단이라 말하지 않는다.
- 섹션 점프 바(글래스, 스티키) + IntersectionObserver 스크롤 스파이.

고친 것
- 항목마다 붙던 KO 태그를 헤더 언어 스위치 하나로 바꿨다. 짝이 있는 페이지는 서로 잇고
  없으면 그 언어의 입구로 보낸다. 영문 페이지가 늘면 PAIRS 표에 줄만 추가한다.
- .chip 클래스가 이미 있어 홈·뉴스룸·영상 페이지를 덮어쓰고 있었다. 새 스타일을
  .stay 아래로 모두 가뒀다.
- factSheet 에 영문 주소(fullEn)와 우편번호를 넣었다. 근거는 뷰성형외과 자신의
  한국관광공사 등재 항목(contentid 3364779)이며 fullEnSource 에 적었다.

템플릿은 같은 코드에 빈 데이터 스캐폴드를 둔다. 뷰성형외과 데이터가 새 병원 빌드로
새면 안 된다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 13:51:28 +09:00
Haewon Kam
a807d5dd9c refactor(supporters): 의료관광 연결을 /discovery 에서 서포터즈 사이트 /en/stay 로 옮김
붙일 자리를 잘못 잡았다. /discovery 는 INFINITH 제품의 진단 화면이고 병원마다 쓰는
범용 도구다. 외국인 환자가 보는 페이지는 병원 서포터즈 사이트다. 그쪽으로 옮겼다.

옮긴 것
- src/components/discovery/MedicalTourismPanel.tsx, src/types/medicalTourism.ts,
  src/data/medicalTourism.json 삭제. DiscoveryReportPage 원복.
- supporters/src/pages/en/stay.astro 신설. 독립 URL 과 자체 WebPage JSON-LD 를 가져
  AI 인용 단위가 된다. sitemap 에 등록하고 /visit 에서 hreflang 링크로 잇는다.
- 수집 스크립트 출력을 supporters/src/data/medicalTourism.json 으로 바꿨다.

영문 페이지 지원
- Base.astro 에 lang prop 추가(기본 ko). html lang · og:locale · 머리말 · 꼬리말이
  따라 바뀐다. 레이아웃을 하나로 유지해 한국어 페이지 동작은 그대로다.
- 의료광고 고지(지원 관계·부작용)는 규정 대상이라 임의로 번역하지 않는다. 병원이
  sponsorNoticeEn / sideEffectNoticeEn 을 확정하기 전에는 한국어 원문을 그대로 싣고
  영문 문구가 확인 대기임을 밝힌다.
- factSheet 에 nameEn / shortNameEn / shortNameEnSource 추가. 뷰성형외과 값의 근거는
  병원 자신의 영문 도메인 viewplasticsurgery.com 이며 한국관광공사 등재명도 같다.

페이지가 만들지 않는 것
- 평점·리뷰·영업시간: TourAPI 미제공이라 싣지 않는다.
- 식당의 회복 적합성: 의학 판단이라 병원이 확정한 영문 문구(site.json dietGuideEn)만
  싣는다. 확인 전에는 어떤 식당도 적합으로 표시하지 않는다고 페이지에 적는다.
- 회복 단계 일수: 병원 확정값(recoveryStagesEn). 단계는 이동 거리로만 정의한다.
- 하단에 이 페이지가 답하지 못하는 것 네 가지를 그대로 적었다.

템플릿 동기화
- templates/supporters-astro 에 같은 변경을 넣되 데이터는 빈 스캐폴드로 둔다.
  뷰성형외과 factSheet 와 관광 데이터가 새 병원 빌드에 새어 들어가면 안 된다.
  빈 데이터로 빌드해 6개 범주가 "No English listings" 로 나오는 것까지 확인했다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 11:42:46 +09:00
Haewon Kam
8776a15183 feat(ga4): AI 유입 트래픽 연동 뼈대 + 리포트 "측정 가능 범위" 절
2026-09-08 작업분. GA4 에서 AI 답변엔진 유입을 채널로 갈라 보는 뼈대를 세웠다.

- data/ai_channels.json: AI 답변엔진 리퍼러 목록(채널 그룹 정의의 근거)
- scripts/ga4_ai_traffic.py: GA4 Data API 로 AI 유입을 뽑는 러너. --mock 으로 합성
  데이터 검증까지 마쳤다. 실제 속성 연결은 GCP 계정이 정해진 뒤다
- supporters/src/layouts/Base.astro + 템플릿 site.json 2곳: ga4MeasurementId 태그.
  쿠키 고지 배너 결정 전까지 미리보기에는 ID 를 넣지 않는다
- scripts/build_discovery_report.ts: Integrity 절에 "측정 가능 범위" 를 넣어
  무엇을 재고 무엇을 못 재는지 리포트가 스스로 밝히게 했다
- 원진 진단 리포트 v2.1(HTML·PDF) 재생성분

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 10:53:54 +09:00
Haewon Kam
4e73798cce fix(supporters): collect_news 가 site.json 을 값이 바뀔 때만 다시 쓴다 (포맷만 바뀌는 diff 방지)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 14:14:42 +09:00
Haewon Kam
484601b0f5 feat(supporters): 뉴스룸 워커 단계 + 뉴스 게이트 규칙, launchd 폴러 설치
- collect_news.mjs: 네이버 뉴스 검색 다중 질의(상호·원장·의료진·주제어, 429 백오프) + 언론보도 게시판 자동 발견/clinic.json newsBoard.
  관련성 = 제목·요약에 병원 이름(v2 §8). 제목·매체·날짜·원문 링크만 저장. news_outlets.mjs 는 build_news.py 매체 표 이식
- gate: BANNED_NEWS_TITLE(전후 사진·비교, 후기, 변신)·NEGATIVE_NEWS(규제·사건, "무사고" 통과)·checkNewsItems → check.mjs·test.mjs(19/19)
- 워커 news 단계(youtube 뒤, images 앞), --news-board/--no-news, status.report.news
- 뷰 news.json: 렛미인 "변신" 기사 41건 규칙으로 제외(excludedByGate 에 목록). 회귀 1,105건(수작업 1,096, 제목 일치 1,033), 원진 1,049건
- launchd 폴러 설치(haewon 지시): PATH 에 프레임워크 python3, bootstrap/bootout 안내
- 함정: JS \W 는 한글을 지움(제목 중복 판정 붕괴), 워드프레스 article:published_time 은 게시판 생성일, 네이버 질의당 1,000건
- v3 문서 §1·§2·§3·§5·§6 갱신, 템플릿 재내보내기

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 14:14:08 +09:00
Haewon Kam
dc0d233b58 feat(supporters): 홈페이지 이미지 단계 — 의료법 기준 이미지 규칙 문서, collect_images.mjs+img_process.py(법 조건부 이미지 자동 제외, 글자 이미지 검사), 워커 images 단계, 글별 히어로·갤러리 매칭. 원진 66장 반영 재배포
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 09:34:48 +09:00
Haewon Kam
eb873bc43a feat(supporters): 유튜브·로고 단계 — collect_youtube.mjs(API 집계·정보형 필터·쇼츠 판별), 워커 youtube 단계(로고·파비콘·homeVideos), 글↔영상 매칭, 카드 로고 대체, 기존 글 보존 재실행. 원진 재배포
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 09:09:00 +09:00
Haewon Kam
ee8c06f53b feat(supporters): haewon 결정 반영 — 채널 인기 전체 탭 제거, 단가 상한 $10 기본, 큐 폴러(poll.mjs)+launchd, /supporters/:id 페이지, URL 신청→빌드 큐, 수집기 UA·슬래시 수정, 기획 목록 일반화(원진 6편)
WIP: 원진성형외과 회귀는 글 생성 단계에서 네트워크 끊김(fetch failed)으로 중단. 다음 세션에 --from generate 로 재실행.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 18:54:01 +09:00
Haewon Kam
7e515b70ae feat(supporters): 승인 UI·입력 반영·워커 — §7-5 ClinicInputsPanel + supporter_builds/inputs 마이그레이션 + apply_inputs, §12-2 상단 고지·개설자 승인·색인 빌드 필터, §7-6 워커 E2E(뷰성형외과 URL → 5편 → 게이트 0오류)
- 게이트: DISCLOSURE_TOP_MISSING, APPROVED_*, UNAPPROVED_IN_INDEXABLE_BUILD, PRESS_VOICE 규칙화, 픽스처 추가 (16/16)
- 생성기: --site 옵션, 병원 확인 답변(clinicAnswers) 근거 [A]
- default_briefs.mjs 첫 배치 자동 기획, workers/supporters-build/run.mjs
- 랜딩: SAMPLE REPORT 라벨 "실제 사례"로 (haewon 지시)
- v2 §11-6·§11-7 기록, E2E 리포트

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 17:07:06 +09:00
Haewon Kam
622441e6aa feat(supporters): 템플릿화 — 병원 고유값을 데이터로 분리하고 templates/supporters-astro 내보내기
- src/data/site.json 신설(매체 이름·로고·홈 큐레이션), factSheet에 kind·areaLabel·transitShort·transitNote·reservationUrl·doctorsUrl·youtube.title 추가
- lib.ts: PENDING('확인 대기')·v()·has()·surface()·editor() 도입, 스키마는 빈 값을 빼고 생성
- 페이지·컴포넌트 15개에서 뷰성형외과 하드코딩 제거, 빈 데이터 empty-state (글 0편·영상 0건·뉴스 0건·이미지 없음)
- 검증: 28페이지 표시 텍스트·구조 지문 이전 빌드와 동일(홈 title 마침표 1건만 정정), JSON-LD 차이는 빈 sameAs 제거뿐
- scripts/export_template.mjs + scripts/template/data(빈 데이터 세트) → templates/supporters-astro (빈 데이터로 10페이지 0오류 빌드, 게이트 통과)
- 게이트 픽스처를 자체 authors로 분리해 템플릿에서도 테스트 통과, 구조 기준선의 글 상세 페이지는 사전순 첫 글로 동적 선택

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 14:07:14 +09:00