o2o-site-AEO/docs/DATA_SOURCE_RESEARCH.md
Mina Choi 4871e50327 문서: 앱을 가른 뒤 낡아진 서술을 고치고, 개발과 무관해진 기록을 지운다
지운 것 — 앞으로의 개발에 쓸 데가 없다.
- solution/backend/demo_site.html: 어떤 스크립트도 만들지 않는 고아 산출물이고,
  손으로 쓴 HTML 이라 "백엔드는 HTML 을 만들지 않는다" 와도 어긋난다.
- solution/README.md: front/ · admin/.env · 사이트당 rooms/index.html·sitemap.xml 처럼
  지금은 전부 틀린 서술이었다. 살아 있는 두 가지(빌더 CSR vs 발행물 SSG 대비표,
  하이드레이션 블롭에 미검증 값이 샜던 실측)는 ARCHITECTURE 로 옮겼다.
- docs/API_USAGE.md 의 Claude 개발비 집계: 2026-08-27 스냅샷과 재집계 스크립트는
  일회성 지출 기록이라 제품 원가와 성격이 다르다. 문서를 외부 API 원가 하나로 좁혔다.

고친 것 — 코드를 따라가지 못하던 서술.
- 코드 경로가 solution/backend 로 옮겨진 뒤 `backend/...` 로 남아 있던 포인터 전부.
  가리키는 자리가 없는 경로는 문서가 아니라 함정이다.
- ARCHITECTURE: 트리의 front→frontend, 컨테이너 표에 api-admin(:9801)·admin(:3002) 추가.
- ★ ARCHITECTURE·AGENTS 의 "admin 전용 라우터가 0개" 는 사실이 아니었다.
  /v1/admin/local-content 가 admin 전용인데 :9800 에도 마운트돼 있다 —
  포트를 가른 논리에 아직 남은 구멍이라 그렇게 적었다.
- DECISIONS: 결론난 것을 미결로 두면 함정이 된다. 작업 큐(2026-08-27 결론),
  날씨 캐시 TTL 1시간, jobs 테이블, media 조회 API, 수집 체인을 결론으로 옮기고
  네이버 플레이스 대 TourAPI 실측(2026-08-31)을 1-1 에 이었다.
- API_USAGE: 어댑터가 다 붙고 TourAPI 키도 나왔다. "실호출 0건" 은 낡은 서술이었다.
- backend/README: 16→17 테이블(jobs), 없어진 alters/, MockAdapter 만이라는 서술,
  cd backend 경로, media·local 라우터 누락.
2026-08-31 16:58:09 +09:00

33 KiB
Raw Permalink Blame History

콘텐츠 소스 리서치 — AEO/SEO/GEO 관점

조사일: 2026-08-28. 모든 HTTP 응답은 이 날짜에 이 개발 머신에서 직접 찍은 실측값이다. DECISIONS.md 1-1(크롤링 약관·법적 검토), 1-2(이미지 재게시 권리)에 대한 답을 포함한다.


0. 한 줄 결론

네이버·카카오 생태계는 AI 검색엔진에게 통째로 닫혀 있다. 그래서 우리가 네이버 플레이스를 복제할 이유가 없어졌고, 오히려 복제하면 안 된다. AI 인용을 노린다면 소스 전략은 "OTA에서 긁어오기" → "공공데이터(인용 가능한 1차 출처) + 사장님 인증 채널(고유 콘텐츠)" 로 바꿔야 한다.


1. ★ 결정적 발견 — AI 크롤러 차단 지도

각 사이트 robots.txt 실측. 전면차단 = Disallow: /.

소스 GPTBot OAI-SearchBot ClaudeBot PerplexityBot Google-Extended Googlebot Yeti(네이버)
m.place.naver.com (현재 어댑터) 전면차단 전면차단 전면차단 전면차단 전면차단 전면차단 전면차단
pcmap.place.naver.com 전면차단 전면차단 전면차단 전면차단 전면차단 전면차단 전면차단
map.naver.com 전면차단 전면차단 전면차단 전면차단 전면차단 거의차단 거의차단
blog.naver.com 전면차단 전면차단 전면차단 전면차단 전면차단 거의차단 전면차단
booking.naver.com 전면차단 전면차단 전면차단 전면차단 전면차단 전면차단 전면차단
smartstore.naver.com 전면차단 전면차단 전면차단 전면차단 전면차단 부분제한 부분제한
place.map.kakao.com 전면차단 전면차단 전면차단 전면차단 전면차단 거의차단 거의차단
app.catchtable.co.kr 전면차단 전면차단 전면차단 전면차단 전면차단 허용 허용
www.goodchoice.kr (여기어때) 전면차단 허용 전면차단 허용 전면차단 부분제한 부분제한
www.diningcode.com 전면차단 부분제한 허용 허용 전면차단 부분제한 부분제한
www.yanolja.com 별도규칙 없음(*만) " " " " " "

이게 왜 사업의 핵심인가

m.place.naver.com/robots.txt 원문 (2026-08-28, 3회 재시도 모두 동일):

User-agent: *
Disallow: /

네이버 플레이스는 예외 없이 모든 크롤러를 금지한다. 그 결과:

  • 사용자가 ChatGPT/Claude/Perplexity/Gemini에 "강릉 ○○펜션 반려동물 되나요?" 를 물으면 → 인용할 소스가 존재하지 않는다. 네이버 플레이스에 답이 있어도 AI는 그 페이지를 읽을 수 없다.
  • 네이버 블로그·카페 후기도 똑같이 막혀 있다. 사장님 블로그를 아무리 잘 써도 글로벌 AI 검색에는 안 잡힌다.
  • 캐치테이블·카카오맵도 AI 봇 전면 차단.
  • 한국 로컬 비즈니스 정보는 AI 검색 입장에서 통째로 빈 구멍이다.

→ 사장님 자체 도메인의 정적 사이트가 그 구멍을 채우면 경쟁자 없이 독점 인용된다. 이 제품의 해자가 데이터로 확인됐다.

채널 이원화가 필요하다

목표 인용 소스 특성 우리 전략
네이버 AI 브리핑 네이버 생태계 편중. 인용 272건 분석 시 블로그가 158건으로 1위. 검색순위와 별개(인용의 49.3%가 검색 10위 밖) 자체 도메인으로는 뚫기 어렵다. 사장님 블로그 채널이 필요 (현재 미구현 — §11)
Google AI Overview / ChatGPT / Perplexity 웹 전체 대상, 자사 도메인 유리. 경쟁 소스(네이버·카카오)가 전부 차단 상태 여기가 우리 주전장. 현재 구조(정적 HTML + JSON-LD + llms.txt)가 정확히 맞다

2. 현재 상태 실측

항목 결과
m.place.naver.com/{kind}/{id}/home HTTP 200, __APOLLO_STATE__ 존재 (restaurant 347KB / accommodation 612KB). 기능적으로는 정상 동작
pcmap.place.naver.com/restaurant/{id}/home HTTP 200, __APOLLO_STATE__ 존재
robots.txt 준수 여부 위반 중. Disallow: / 를 무시하고 있다
네이버 지역검색 API (openapi.naver.com/v1/search/local) 정상 (2026-08-28 확인). 단 telephone·description은 전 표본 100% 빈 문자열
TourAPI KorService2 HTTP 403 — 키는 유효하나 해당 API 미신청 상태 (같은 키로 축제 API는 200)
KAKAO_REST_API_KEY 미설정(빈 값)

기능이 되는 것과 해도 되는 것은 다르다. 지금 네이버 플레이스 어댑터는 200을 받지만 robots.txt를 명시적으로 위반한다. DECISIONS.md 1-1의 "약관·robots.txt 기준 허용 범위" 질문에 대한 답은 "robots.txt 기준으로는 명백히 불허" 다.


3. OTA 크롤링 — 이미 막혀 있다

풀 브라우저 헤더(Chrome 128 UA + sec-ch-ua + Sec-Fetch-* 전체)로 재시도한 결과:

URL 결과
www.yanolja.com/pension/3013306 HTTP 403 + Cloudflare 챌린지
nol.yanolja.com/hotels/{id} HTTP 404/403 + Cloudflare 챌린지
www.goodchoice.kr/product/detail?ano=... HTTP 403 + Cloudflare 챌린지 (홈페이지는 200)
place.map.kakao.com/{id} HTTP 200이나 3.6KB SPA 셸 — 내용 없음
place-api.map.kakao.com/places/panel3/{id} (내부 API) HTTP 406 차단

야놀자·여기어때 어댑터는 만들어도 못 쓴다. 뚫으려면 헤드리스 브라우저 + 봇 탐지 우회가 필요한데, 그건 registry.pyDECISIONS.md결론과 무관하게 영구 금지로 못 박은 영역이다.

법적으로도 하면 안 된다 — 야놀자 v 여기어때

같은 행위(OTA 숙박정보 크롤링)에 대한 국내 유일 대형 선례다. 형사·민사 결론이 갈렸다.

구분 결론
형사 (대법원 2022-05-12 확정) 무죄. 정보통신망침해·업무방해·저작권법 위반 모두 무죄. "수집한 정보 대부분이 이용자에게 공개된 것", DB의 통상적 이용을 방해한 정도가 아니라고 판단
민사 (서울중앙지법 2021-08) 10억 원 배상 + 사용·복제·저장 금지. 성과물 무단사용(부정경쟁행위) 인정. 야놀자가 영업조직 인건비만 26억 투입한 점을 배상액 산정에 반영

"형사는 무죄니까 괜찮다"가 아니라 "민사로 10억 물었다"가 우리에게 유효한 신호다. 우리는 그 정보를 재게시까지 하므로 여기어때보다 노출이 크다.


4. Google Places API — 우리 제품 구조와 약관이 충돌한다

항목 내용
캐싱 place_id만 무기한 저장 가능. 좌표는 최대 30일. 이름·영업시간·사진·리뷰·전화번호는 캐싱 금지 — 실시간 조회 후 표시해야 한다
표시 조건 Google 지도 위에 표시 + Google 로고 및 제3자 제공자 귀속 표기 의무

우리는 DB를 실시간 조회하지 않는 정적 HTML을 발행한다(§7). 정적 빌드에 값을 굽는 순간 캐싱 금지 조항 위반이다. → Places API는 콘텐츠 소스로 배제. (Place ID를 엔티티 식별자로만 쓰는 건 가능)


5. 업종별 실측: 사장님이 실제로 어디에 콘텐츠를 두는가

네이버 지역검색 API link 필드 충전율. 업종별 5개 쿼리 × 상위 5건 = 표본 25건씩 (작은 표본이니 방향만 참고).

업종 link 있음 인스타그램 자체 도메인·기타 시사점
카페 23/25 (92%) 17 6 (자체 도메인 2, 스마트스토어 2, 캐치테이블 1) 카페는 사실상 인스타가 공식 홈페이지
숙박 24/25 (96%) 5 19 (개별 도메인 다수: gangmunstay.kr, offinghouse.com 등) 숙박은 자체 홈페이지 보유율이 압도적
음식점 16/25 (64%) 8 8 (네이버블로그 4, 자체 도메인 4) 절반씩 갈린다. 충전율도 가장 낮다

이게 어댑터 우선순위를 정해준다:

  • 숙박 → 사장님 자체 홈페이지 크롤링(StaticHtmlAdapter)이 최고 효율. 사장님이 URL을 확정해주므로 법적으로도 가장 깨끗하다.
  • 카페 → 인스타그램이 유일한 실질 콘텐츠 소스.
  • 음식점 → 커버리지가 낮아 공공데이터·사장님 직접입력 비중을 높여야 한다.

5-1. ★ 업종별로 결론이 다르다

세 업종은 경쟁 상황이 서로 다르다. "AI가 인용할 대체 소스가 이미 있는가"가 갈림점이다.

숙박 — 야놀자·여기어때는 답이 아니다

소스 실측 판정
야놀자 HTTP 403 + Cloudflare 챌린지 (풀 브라우저 헤더로도 동일) ✗ 기술적 차단 + 민사 10억 선례
여기어때 HTTP 403 + Cloudflare 챌린지 ✗ 동일
TourAPI 숙박(32) 무료·재게시 자유. 체크인/아웃, 객실 수·유형, 취사, 주차, 픽업, 부대시설 1순위
사장님 자체 홈페이지 표본 보유율 76% (숙박이 3업종 중 최고) 2순위
LOCALDATA 숙박업 객실 수, 시설면적, 영업상태(인허가 기준) ○ 신뢰 근거
스테이폴리오 SSR + JSON-LD 있으나 감성숙소 한정 △ 커버리지 좁음

덧붙일 중요한 사실: 야놀자·여기어때는 AI 크롤러에게도 닫혀 있다. 즉 긁어봐야 "AI가 못 읽는 정보를 위법하게 옮겨오는" 것에 불과하다. GEO 관점에서 얻는 게 없다.

단, 숙박만 예외가 하나 있다. 해외 OTA는 AI에 열려 있다:

소스 상세페이지 GPTBot ClaudeBot PerplexityBot
에어비앤비 HTTP 200 (446KB, SSR) 허용 허용 허용
부킹닷컴 접근 가능 허용 허용 허용
아고다 접근 가능 허용 허용 허용

호텔·리조트급은 AI 검색 공백이 아니다. 부킹닷컴·아고다 페이지가 이미 인용된다. 우리가 긁을 대상이 아니라 경쟁 소스로 인식해야 한다. → 반대로 펜션·게스트하우스·독채는 해외 OTA 커버리지가 낮고 국내 채널은 전부 차단공백이 그대로 남아 있다. 여기가 숙박의 진짜 기회다.

카페 — AI 검색 공백은 최대. 소스는 생각보다 있다

정정 (2026-08-31). 초판에서 "TourAPI 카페 커버리지 거의 없음"이라고 썼는데 틀렸다. 카페는 별도 콘텐츠 타입이 아니라 음식점(39)의 소분류(cat3=A05020900 카페/전통찻집)로 들어 있고, 전국 1,979건이 등록돼 있다. 실측으로 확인했다.

소스 실측 판정
TourAPI (39 / A05020900) 전국 1,979건. 영업시간·휴무일·대표메뉴·취급메뉴·주차·사진 관광지 인근 편중이지만 실재한다
인스타그램 표본의 **68%**가 인스타를 공식 채널로 등록 (3업종 중 최고) ◎ 그 외 카페의 주 소스. 사장님 OAuth 필요
LOCALDATA 휴게음식점 영업상태·시설면적만 △ 검증용
네이버 플레이스 robots 전면 금지

→ 카페는 AI 검색 공백이 가장 크다(네이버·카카오·인스타 전부 AI 차단). TourAPI에 등록된 카페는 바로 채워지고, 나머지는 인스타 OAuth + 사장님 입력으로 간다.

음식점 — 소스는 가장 많은데 쓸 만한 게 가장 적다

소스 실측 판정
배민 / 요기요 / 쿠팡이츠 공식 API 없음, 앱 중심
캐치테이블 SPA 셸(8KB) + AI봇 전면차단
다이닝코드 SPA 셸(23KB, JS 필요). 단 ClaudeBot·PerplexityBot 허용 경쟁 소스
식신 SSR + LocalBusiness JSON-LD (상호·전화·주소) △ 얕음
미쉐린가이드 AI봇 허용 △ 경쟁 소스(해당 업소 한정)
블루리본서베이 AI봇 전면차단
TourAPI 음식점(39) 대표메뉴·취급메뉴·영업시간·쉬는날·포장·예약·좌석 수 단 관광지 인근 편중 우려
LOCALDATA 일반음식점 업태(한식/중식…), 시설면적, 영업상태
식품안전나라 위생등급 공공 인증 E-E-A-T 신호로 유용
사장님 자체 홈페이지 표본 보유율 32%, link 충전율 64%로 3업종 최저

→ 음식점은 자체 채널 보유율이 가장 낮아 공공데이터와 사장님 직접 입력 비중을 가장 크게 잡아야 한다.

요약

대체 소스 AI 검색 공백 주력 소스 난이도
숙박(펜션·독채) 야놀자·여기어때 모두 차단 완전 공백 TourAPI + 자체 홈페이지(76%) 낮음 ← 먼저 공략
숙박(호텔) 해외 OTA가 AI에 노출 중 부분 공백 TourAPI + 자체 홈페이지 중간
카페 사실상 없음 완전 공백 인스타 OAuth + 사장님 입력 높음
음식점 다이닝코드·미쉐린 일부 노출 대부분 공백 LOCALDATA + TourAPI + 사장님 입력 높음

6. 소스 카탈로그

Tier 1 — 공공 API (재게시 자유, GEO에 가장 강함)

소스 상태 숙박 카페 음식점 라이선스
TourAPI (한국관광공사 KorService2) 키는 있으나 활용신청 필요(무료·즉시) ◎ 체크인/아웃, 객실 수·유형, 취사, 주차, 픽업, 부대시설 ✕ 커버리지 거의 없음 ○ 대표메뉴, 취급메뉴, 영업시간, 쉬는날, 포장·예약, 좌석 수 이용허락범위 제한없음 / 무료. 개발계정 1,000건/일 → 운영계정 확장 가능
LOCALDATA (지방행정 인허가) 미도입. 무료 신청 ○ 숙박업 인허가 — 객실 수, 시설면적, 영업상태 ○ 휴게음식점 ○ 일반음식점 — 업태(한식/중식…), 시설면적, 영업상태 공공데이터. 208개 업종 전체, 좌표 포함(EPSG:5174)
네이버 지역검색 API 사용 중 상호·주소·좌표·카테고리·link만. 전화·설명 필드는 빈 값
카카오 로컬 API 키 미설정 네이버 지역검색과 동급. 콘텐츠 없음

TourAPI의 필드 목록은 활용신청 후 detailIntro2로 실측 확정 필요. 현재 403이라 응답 스키마를 직접 확인하지 못했다.

Tier 2 — 사장님 인증 채널 (고유 콘텐츠, 법적으로 가장 안전)

소스 방식 숙박 카페 음식점 비고
사장님 자체 홈페이지 사장님이 URL 확정 → StaticHtmlAdapter ◎ (보유율 76%) 가장 권장. 사장님 동의 = 법적 리스크 0. 이미 LinkChannel.OFFICIAL_SITE 존재
인스타그램 Graph API 사장님 OAuth (Business/Creator 계정 필요) ◎ (68%) 사진 + 캡션. 저작권자가 사장님이라 1-2 미결이 해소된다
Google Business Profile API 사장님 OAuth + Google 접근 승인 필요 영업시간·속성·사진 전부. 단 국내 소상공인 GBP 관리율이 낮아 커버리지 문제. 신규 프로젝트는 쿼터 0에서 시작, 별도 심사 필요
사장님 직접 입력 관리자 화면 커버리지 100%. 이탈률이 유일한 비용

Tier 3 — 기술적으로 가능하나 권장하지 않음

소스 상태
식신 (siksinhot.com) HTTP 200, SSR, LocalBusiness JSON-LD 제공(상호·전화·주소). 콘텐츠 깊이는 네이버보다 얕고 업소 매칭이 어렵다
다이닝코드 HTTP 200이나 SPA 셸(23KB) — JS 렌더링 필요. rid 매칭 문제

Tier 4 — 배제

야놀자 / 여기어때 (Cloudflare 403 + 민사 10억 선례) · 카카오맵 (406, AI봇 전면차단) · 캐치테이블 (SPA + AI봇 전면차단) · Google Places API (캐싱 금지) · 네이버 블로그·카페 후기 (이미 정책상 배제, robots 전면차단)


7. GEO 관점에서 소스를 다시 줄 세우면

Princeton·Georgia Tech·IIT Delhi 공동연구(KDD 2024)가 확인한 AI 인용률을 최대 40% 끌어올리는 요인은 키워드가 아니라 이 셋이다:

  1. 통계·수치 추가 (Statistics Addition)
  2. 출처 인용 (Cite Sources)
  3. 인용문 추가 (Quotation Addition)

그리고 ChatGPT·Gemini·Copilot이 인용한 소스의 10% 미만만 해당 질의 구글 상위 10위에 든다. 랭킹 신호와 인용 신호는 다른 게임이다.

이 기준으로 소스를 평가하면 순위가 뒤집힌다:

소스 수치 밀도 출처로 명시 가능? GEO 가치
LOCALDATA 인허가 높음 (객실 수, 시설면적, 인허가일) "○○시 인허가 정보 기준" 최상
TourAPI 높음 (체크인 시각, 객실 수, 좌석 수) "한국관광공사 TourAPI 기준" 최상
사장님 확인 정보 중간 "사업주 확인, 2026-08-28" 높음
네이버 플레이스 크롤링 높음 밝힐 수 없다 (robots 위반 + 재게시 근거 없음) 낮음

핵심 역설: 지금 유일한 소스인 네이버 플레이스가 GEO 관점에서 가장 가치가 낮다. 출처를 명시할 수 없는 사실은 AI가 신뢰하지 않고, 우리 AnswerBlock·llms.txt의 "출처와 검증 시각" 신호도 채우지 못한다. 반면 공공데이터는 그 자체가 인용 가능한 1차 출처라 GEO 신호를 직접 만든다.

여기에 하나 더 — 복제 콘텐츠는 GEO에서 마이너스다. 네이버 플레이스와 같은 문장이 실린 페이지는 PUBLISH_NO_UNIQUE_CONTENT 게이트가 이미 경계하는 그 상태다. AI가 굳이 인용할 이유가 없다.


8. 권고 로드맵

순위 작업 근거 비용
1 TourAPI KorService2 활용신청tour_api.py 확장 (searchStay2, detailIntro2) 숙박·음식점 fact를 출처 명시 가능한 형태로 확보. 무료, 신청 즉시 승인 1일
2 StaticHtmlAdapter 등록 (사장님 확정 URL 전용) 숙박 76%·음식점 32%가 자체 도메인 보유. 사장님 동의 기반이라 법적 리스크 0. DECISIONS.md 1-1이 "결론 후 등록"으로 남겨둔 바로 그 어댑터 3일
3 LOCALDATA 연동 인허가 기반 영업상태·객실수·시설면적. PLACE_NOT_VERIFIED 검증 근거로도 사용 가능 2일
4 인스타그램 Graph API (사장님 OAuth) 카페 68%의 유일한 콘텐츠·사진 소스. 1-2(이미지 재게시 권리) 미결을 구조적으로 해소 5일
5 네이버 플레이스 어댑터 역할 축소 — 사장님 화면의 "대조용 참고값"으로만 사용하고 발행 payload에서 제외 robots 위반 해소 + 복제 콘텐츠 제거. COLLECT_ADAPTERS 환경변수로 코드 수정 없이 즉시 가능 0.5일
6 네이버 블로그 채널 (§11 미구현분) 네이버 AI 브리핑 인용의 58%가 블로그. 글로벌 AI와 별개 전장 별도 검토

registry.py가 이미 COLLECT_ADAPTERS 환경변수로 채널별 on/off를 지원하므로, 5번은 배포 없이 즉시 실행 가능하다.

업종 출시 순서 제안

소스 확보 난이도와 AI 검색 공백 크기가 같은 방향을 가리킨다.

  1. 숙박(펜션·게스트하우스·독채) — TourAPI 32 + 자체 홈페이지(보유율 76%)로 fact가 가장 잘 차고, 경쟁 소스가 전부 차단 상태다. 1·2번 작업만으로 출시 가능.
  2. 음식점 — LOCALDATA + TourAPI 39 + 위생등급으로 공공 fact를 채우고 나머지는 사장님 입력. 3번 작업 필요.
  3. 카페 — 인스타 OAuth(4번)가 선행되지 않으면 사장님 입력 100% 의존이라 이탈률이 높다. 가장 마지막.

8-1. 구현됨 (2026-08-28) — 사이트별 파서 대신 LLM 추출

조사 도중 방향을 바꿨다. 사장님 홈페이지마다 파서를 짜는 것은 끝이 없다 — 실측으로 확인됐다:

사이트 결과
mulhoe.co.kr 본문 2,841자 + JSON-LD 1블록 — 쓸 만함
gangmunstay.kr 8.5KB HTML인데 본문 9자 — SPA, JS 렌더링 필요
cheomdangukbap.com 834바이트, 본문 0자 — SPA
offinghouse.com SSL 자체서명 인증서 오류

4곳 중 1곳만 정적 HTML로 내용이 나온다. 그래서 입력이 무엇이든(URL 크롤링 본문 / 사장님 붙여넣기 텍스트) 원문 문자열 하나로 모아 Gemini가 업종 스키마로 추출하는 구조로 만들었다.

URL       → StaticHtmlAdapter (robots 준수) ─┐
붙여넣기  ────────────────────────────────────┴→ 원문 텍스트 → Gemini 구조화 추출
                                                    → evidence 원문 대조 → fact 후보(UNVERIFIED)

핵심 안전장치는 프롬프트가 아니라 코드다. 모델이 적어 낸 근거 문장이 원문에 실제로 있는지 대조하고, 없으면 버린다. 실측 검증:

공격 케이스 결과
원문에 없는 문장을 근거로 지어냄 🚫 차단
근거는 "반려동물 불가"인데 pet_allowed=true 🚫 차단 (부호 뒤집기)
원문 "오후 3시"를 "15:00"으로 환산 🚫 차단 (표기 변경)
업종 스키마에 없는 key 🚫 차단
unit 스코프인데 unit_name 없음 🚫 차단

실제 결과 — 파서 한 줄 없이:

  • mulhoe.co.kr (음식점): 영업시간·주차·포장·예약채널 + 메뉴 5종의 이름/가격/설명 = fact 19건
  • 붙여넣기 238자 (펜션): 체크인/아웃·주차·와이파이·반려동물(false)·바비큐요금 + 객실 2종의 인원/침대/주중·주말요금 = fact 15건
파일 역할
solution/backend/services/collector/static_html_adapter.py robots.txt 준수 fetch + JSON-LD/OG 추출. _DENY_HOSTS 로 플랫폼 차단
solution/backend/services/prompts/extract.py 추출 프롬프트·응답 스키마 (업종 스키마에서 자동 생성)
solution/backend/services/grounding/extract.py evidence 원문 대조 게이트
solution/backend/services/external/gemini_extract.py 엮는 자리
solution/backend/tests/test_extract_grounding.py 게이트 회귀 테스트 17건

비용: 호출당 약 $0.006 (gemini-3.7-flash, temperature 0.0).

남은 작업: collect_service 에서 이 추출 경로를 호출하도록 배선 + 관리자 화면에 "텍스트 붙여넣기" 입력창. 컨테이너는 소스 마운트가 없으므로 docker compose up -d --build 로 이미지를 다시 빌드해야 반영된다.


8-2. 지금 실제로 가져올 수 있는 정보 (2026-08-31 실측)

TourAPI 활용신청이 승인돼 어댑터를 붙였다. 아래는 표본 8곳씩 실제로 돌려서 센 것이지 문서 기준 추정이 아니다.

숙박 — 객실 단위까지 채워진다

필드 TourAPI 충전율 비고
★ 체크인 / 체크아웃 8/8곳 critical
숙소 소개 8/8곳
바비큐 이용 8/8곳
주차 6/8곳
★ 취사 가능 4/8곳 critical
★ 취소·환불 규정 3/8곳 critical
픽업 서비스 3/8곳
객실 단위 — 타입·★기준인원·★최대인원·★주중요금·★주말요금·에어컨 객실 20개분 표본 8곳에서 객실 20개
객실 면적(㎡) 객실 17개분
★ 성수기 요금 객실 14개분
★ 주방 여부 객실 13개분
사진 4/8곳 객실별로 붙는다

TourAPI가 못 채우는 13개: pet_allowed smoking extra_person_fee reception_hours parking_capacity wifi bbq_fee breakfast baby_amenities bed_type bathroom_count view room_intro → 자체 홈페이지 추출 또는 사장님 입력.

음식점 · 카페 — 영업정보와 메뉴 이름까지

필드 TourAPI 충전율
가게 소개 · ★영업시간 · ★휴무일 · 대표메뉴 · ★주차 8/8곳
메뉴 이름(단위) 메뉴 25건
포장 4/8곳
★ 예약 필수 2/8곳
사진 8/8곳

못 채우는 16개: break_time last_order reservation_channel pet_allowed kids_allowed corkage room_available group_seat_max parking_capacity delivery payment_methods price_range wheelchair_accessible menu_price menu_min_order menu_intro메뉴 가격이 없는 것이 가장 아프다. 자체 홈페이지 추출(속초항아리물회에서 메뉴 5종 가격 확보)이나 사장님 입력으로 메운다.

네이버 플레이스와 비교 (같은 날 실측)

네이버 플레이스 TourAPI
숙박 사업장 fact 2건 (intro, wifi) 8종 (체크인/아웃·취사·주차·픽업·바비큐·환불규정·소개)
숙박 객실 정보 객실명(room_type) 18개 객실 20개의 인원·요금·면적·주방·에어컨
음식점 수집 실패 (PlaceDetailBase 없음) 영업시간·휴무일·대표메뉴·주차·메뉴명·사진
출처 표기 불가 (robots 위반) 가능 — 대한민국구석구석 공개 URL

네이버 플레이스를 유지할 이유가 사실상 사라졌다. 정보량도 적고 출처도 못 밝힌다.

이미지 — 저작권까지 확정

TourAPI 숙박 사진 167장 표본의 공공누리 유형: Type3 161장 · Type1 6장 → 100% 상업적 이용 가능. 다만 Type3가 96%이고 제3유형은 '변경금지' 다.

⚠️ 렌더러 제약: Type3 사진은 크롭·리사이즈하면 조건 위반이다. 썸네일 생성 경로가 이 사진들을 건드리면 안 된다. CollectedMedia.license 에 유형을 담아 내보내므로 발행 단계에서 판정할 수 있다. 상업적 이용이 막힌 Type2·Type4와 유형 미상은 수집 단계에서 버린다.


8-3. API별 수집 카탈로그 — 어디서 무엇을 가져오는가

코드에서 뽑은 것이다(2026-08-31). 왼쪽이 API 원본 필드, 오른쪽이 우리 fact key.

A. 한국관광공사 TourAPI · apis.data.go.kr/B551011/KorService2

무료 · 이용허락범위 제한없음 · 개발계정 1,000건/일. fact 의 주력 공급원.

오퍼레이션 원본 → fact key
detailCommon2 overviewintro · firstimage+cpyrhtDivCd → 대표사진
참고용으로만 읽음: title addr1 mapx/mapy homepage cat1/2/3 modifiedtime
detailIntro2 숙박(32) checkintimecheck_in_time · checkouttimecheck_out_time
refundregulationcancel_policy · chkcookingcooking_allowed
parkinglodgingparking · pickuppickup_service · barbecuebbq_available
detailIntro2 음식점·카페(39) opentimefoodbusiness_hours · restdatefoodclosed_days
firstmenusignature_menu · treatmenumenu_name(단위, /로 분리)
parkingfoodparking · packingtakeout · reservationfoodreservation_required
detailInfo2 숙박(32) = 객실 행 roomtitleroom_type(+ unit_name) · roombasecountstandard_capacity
roommaxcountmax_capacity · roomsize2room_size(㎡)
roomoffseasonminfee1/2weekday_price / weekend_price
roompeakseasonminfee1peak_price · roomcookhas_kitchen · roomairconditionhas_aircon
roomimg1~5 + cpyrhtDivCd1~5 → 객실 사진
detailImage2 originimgurl imgname cpyrhtDivCd → 추가 사진

일부러 안 쓰는 필드 — 스키마에 자리가 없거나 뜻이 다르다: roomcount scalelodging accomcountlodging foodplace reservationlodging infocenter* subfacility seminar/sports/sauna/beauty/beverage/karaoke/campfire/bicycle/fitness/publicpc/publicbath kidsfacility(놀이방 유무 ≠ 아동 입장) · chkcreditcardfood(가능/없음 ≠ 결제수단 목록) · smoking · lcnsno roomsize1(평 — ㎡와 섞으면 3배 틀린다) · roombathfacility/tv/cable/internet/refrigerator/toiletries/sofa/table/hairdryer

B. 네이버 지역검색 API · openapi.naver.com/v1/search/local.json

fact 를 만들지 않는다. 사업장 후보 검색과 채널 URL 발견에만 쓴다.

title→상호 · roadAddress/address→주소 · mapx/mapy→좌표(1e7 스케일) · category→업종명 · link공식 채널 URL 후보 telephone·description실측 100% 빈 문자열. 있는 셈 치면 안 된다.

C. 카카오 로컬 API · dapi.kakao.com (키 미설정 — 현재 미사용)

id→kakao_place_id · place_name · road_address_name · address_name · phone · x/y→좌표 · category_name · place_url 행정구역: code · region_1~3depth_name · region_type지역 콘텐츠 캐시 키

D. 네이버 플레이스 크롤링 · m.place.naver.com __APOLLO_STATE__

⚠️ robots.txt Disallow: / 위반 상태. 출처를 밝힐 수 없어 GEO 가치가 없다. 대조용으로만 남긴다.

description/microReviewsintro · Naverhotel3HotelData.checkInStr/checkOutStrcheck_in_time/check_out_time BusinessHour*business_hours · operating_hours · reception_hours conveniences + InformationFacilities*parking wifi bbq_available cooking_allowed breakfast pet_allowed pickup_service baby_amenities has_aircon terrace takeout delivery power_outlet wheelchair_accessible Menu:*room_type(단위) · PlaceDetailTopPhotoItem* → 사진

E. 사장님 자체 홈페이지 · static_html 어댑터 (사장님 확정 URL 한정, robots 준수)

① JSON-LD(schema.org) / OpenGraph 직접 매핑 description/og:descriptionintro · openingHours(Specification)business_hours 등 checkinTime/checkoutTimecheck_in_time/check_out_time · priceRangeprice_range · paymentAcceptedpayment_methods petsAllowedpet_allowed · smokingAllowedsmoking hasMenu.hasMenuItemmenu_name · menu_price · menu_intro(단위) amenityFeature → 편의시설 bool 15종 · image/og:image → 사진

② Gemini 추출 — ①로 안 잡히면 본문 텍스트에서 업종 스키마 전 필드를 뽑는다(근거 원문 대조 통과분만)

F. 사장님 붙여넣기 텍스트 · gemini_extract

원문 문자열 하나 → 업종 스키마 전 필드. E-②와 같은 엔진, 입력만 다르다.

G. Perplexity Sonar

채널 URL 후보만 (title, url). ★ 응답 본문의 영업시간·가격 같은 정보는 쓰지 않는다.

H·I. Gemini

산출
Vision ref · label(업종별 사진 라벨) · alt_text · confidence
Text intro · meta_description · FAQ(question/answer/fact_keys) — 확보된 fact 로만

J·K. 지역 콘텐츠 (사업장 fact 아님 — 주변 정보 블록용)

API 산출
Open-Meteo api.open-meteo.com/v1/forecast temperature weather_code wind_speed observed_at timezone
공공데이터포털 축제 tn_pubr_public_cltur_fstvl_api title eventstartdate eventenddate mapx/mapy tel homepage organizer overview

9. DECISIONS.md 미결 항목에 대한 답

1-1. OTA·플레이스 크롤링의 약관·법적 검토 → 결론 가능

대상 판정 근거
야놀자·여기어때 불가 Cloudflare 403 실측 + 민사 10억 배상 선례. 기술적으로도 법적으로도 막혔다
네이버 플레이스 robots.txt 기준 불허 Disallow: / 실측. 기능은 되지만 준수 상태가 아니다
카카오맵 불가 내부 API 406 차단
사장님 확정 URL(자체 홈페이지) 가능 사장님 동의 기반. StaticHtmlAdapter 등록 권고

폴백 3단계(① 공식 API → ② 사장님 직접 붙여넣기 → ③ 최소 정보 생성 + 보완 요청)로 간다는 기존 설계가 맞았다. 이번 조사는 ①의 실체를 TourAPI·LOCALDATA로 특정한 것이다.

1-2. 크롤링 이미지 재게시 권리 → 회피 경로 확보

권리 관계를 다투는 대신 입력원을 바꾸면 문제가 사라진다.

  • 인스타그램 Graph API (사장님 OAuth) → 저작권자 = 사장님
  • 사장님 직접 업로드 → 동일
  • TourAPI 이미지 → 공공누리

media.source_type(owner/crawl) 컬럼이 이미 있으므로, 발행 시 CRAWL 제외 전환은 준비되어 있다.


10. 확인하지 못한 것

  • TourAPI detailIntro2 실제 응답 필드 — 403이라 미확인. 활용신청 후 실측 필요. §6의 필드 목록은 공개 문서 기준 추정치다.
  • 업종별 link 충전율 표본이 25건씩이라 통계적 신뢰구간이 넓다. 방향성 판단용으로만 사용할 것.
  • GBP 국내 소상공인 실제 관리율 — 정량 자료를 찾지 못했다. 도입 전 표본 조사 필요.
  • TourAPI 음식점(39) 커버리지 — 관광지 인근 위주라 도심 일반 음식점 커버리지가 낮을 것으로 보이나 미측정.