/** * INFINITH AI Discovery AEO/GEO 채점 기준표 v1.0 * * 근거 기반: ado2-aeo-geo 스킬 (2026-08-26 한국 실측 · Princeton GEO · C-SEO Bench) * * 배점 철학 * - "차단되면 절대 인용될 수 없다"는 논쟁의 여지가 없다 → A(접근성)·B(엔티티) 배점이 높다 * - GEO 콘텐츠 전술의 효과는 학술적으로 논쟁 중 → C는 배점이 크지만 "좋은 콘텐츠 원칙"으로만 설명한다 * - llms.txt는 항목에서 제외한다 (Google 공식 효과 0, 채택률 0.13%) * - 병의원 업종은 강남언니(ai-input=yes)가 AI 인용을 장악 → D(표면)에서 최고 배점 */ import type { Rubric } from '../types/discovery'; const L = (l0: string, l1: string, l2: string, l3: string) => [ { level: 0 as const, label: l0 }, { level: 1 as const, label: l1 }, { level: 2 as const, label: l2 }, { level: 3 as const, label: l3 }, ]; export const AEO_GEO_RUBRIC: Rubric = { version: '1.0', updatedAt: '2026-08-28', categories: [ { id: 'access', code: 'A', name: 'AI 크롤러 접근성', nameEn: 'Access & Crawlability', description: 'AI 검색엔진이 사이트를 읽을 수 있는가. 크롤러 접근이 막히면 나머지 항목은 전부 무효다.', weight: 20, }, { id: 'entity', code: 'B', name: '엔티티 식별·구조화', nameEn: 'Entity & Structured Data', description: 'AI가 "이 병원이 누구인지"를 기계적으로 확정할 수 있는가. NAP 일관성과 스키마 위생.', weight: 20, }, { id: 'content', code: 'C', name: '답변 인용 가능 콘텐츠', nameEn: 'Answer-Ready Content', description: '패시지 단위로 잘라내도 자기완결적인 사실 덩어리가 있는가. 정의문·FAQ·수치·표·의료진 근거.', weight: 25, }, { id: 'surface', code: 'D', name: 'AI 인용 표면 확보', nameEn: 'Off-site Surfaces', description: '실제로 AI가 인용하는 외부 표면(강남언니·GBP·오픈웹)에 존재하는가. 네이버 자산은 글로벌 AI에 기여도 0.', weight: 20, }, { id: 'measure', code: 'E', name: 'AI 가시성 측정 인프라', nameEn: 'Measurement', description: '개선을 증명할 수 있는가. 소유 신호(크롤러 로그·GA4 AI 유입) 계측 여부.', weight: 10, }, { id: 'hygiene', code: 'F', name: '기술 위생', nameEn: 'Technical Hygiene', description: '이미지 대체텍스트·CMS 보안·페이지 무게·모바일. 보조 지표.', weight: 5, }, ], criteria: [ // ───────────── A. 접근성 (20) ───────────── { id: 'A1', category: 'access', name: '검색용 AI 봇 허용', rationale: 'OAI-SearchBot·PerplexityBot·Claude-SearchBot·bingbot·Googlebot이 막히면 인용 자체가 불가능. 학습봇(GPTBot·ClaudeBot)과 구분해 평가.', weight: 5, evidenceGrade: 'measured', control: 'direct', method: 'auto', howToCheck: 'robots.txt 파싱 + 각 UA로 실제 GET 200 확인 (crawler_audit.py)', levels: L('검색봇 2개 이상 차단', '검색봇 1개 차단', '모두 허용, 학습봇 일부 차단', '검색봇 전부 허용 + 200 응답'), }, { id: 'A2', category: 'access', name: 'CDN/WAF AI 차단 없음', rationale: 'Cloudflare는 2025-07부터 신규 도메인에 AI 크롤러 기본 차단. 업체가 인지하지 못한 채 AI 검색에서 사라지는 주원인.', weight: 3, evidenceGrade: 'measured', control: 'direct', method: 'auto', howToCheck: '응답 헤더(server/cf-ray) + AI UA 요청 시 403/challenge 여부', levels: L('AI UA 403/챌린지', '일부 UA 차단', 'CDN 사용, 차단 없음', 'CDN 차단 없음 확인'), }, { id: 'A3', category: 'access', name: '서버 렌더링 본문', rationale: 'AI 크롤러는 JS를 실행하지 않거나 지연시킨다. 초기 HTML에 본문이 없으면 빈 페이지로 읽힌다.', weight: 3, evidenceGrade: 'measured', control: 'direct', method: 'auto', howToCheck: 'JS 미실행 HTML의 텍스트 길이 / 렌더 후 텍스트 길이 비율', levels: L('본문 전무 (SPA 빈 셸)', '30% 미만', '30~80%', '80% 이상 (SSR/정적)'), }, { id: 'A4', category: 'access', name: 'HTTPS·리다이렉트 정합', rationale: 'http/www 변형이 하나의 정본 URL로 301 수렴해야 엔티티가 분산되지 않는다.', weight: 2, evidenceGrade: 'measured', control: 'direct', method: 'auto', howToCheck: 'http://, http://www, https:// 4변형 → 최종 URL 동일 여부', levels: L('HTTPS 없음', '변형 간 200 중복', '일부 302', '전부 301로 정본 수렴'), }, { id: 'A5', category: 'access', name: 'sitemap.xml 제공', rationale: '크롤러 발견 경로. robots.txt에 선언되어야 한다.', weight: 3, evidenceGrade: 'measured', control: 'direct', method: 'auto', howToCheck: 'robots.txt Sitemap: 선언 + 200 + lastmod 존재', levels: L('없음', '있으나 robots 미선언', '선언·200', '선언·200·lastmod 최신'), }, { id: 'A6', category: 'access', name: 'Bing 색인 (ChatGPT 검색 경로)', rationale: 'ChatGPT 검색은 Bing 인덱스에 상당 부분 의존. Bing Webmaster + IndexNow 등록 여부.', weight: 3, evidenceGrade: 'vendor', control: 'direct', method: 'manual', howToCheck: 'Bing Webmaster Tools 등록 확인 (오너 접근 필요) 또는 site: 검색 결과 수', levels: L('Bing 미색인', '일부 색인', '색인됨', '색인 + IndexNow 연동'), }, { id: 'A7', category: 'access', name: '응답 속도·크롤 예산', rationale: 'TTFB가 느리거나 HTML이 비대하면 크롤러가 페이지를 덜 가져간다.', weight: 1, evidenceGrade: 'estimate', control: 'direct', method: 'auto', howToCheck: 'TTFB < 0.5s, HTML < 300KB', levels: L('TTFB > 2s', 'TTFB > 1s 또는 HTML > 1MB', 'TTFB < 0.5s, HTML > 300KB', 'TTFB < 0.5s, HTML < 300KB'), }, // ───────────── B. 엔티티 (20) ───────────── { id: 'B1', category: 'entity', name: 'JSON-LD 문법 유효성', rationale: '문법이 깨진 JSON-LD는 파서가 통째로 버린다. 있는데 무효인 것은 없는 것과 같다.', weight: 3, evidenceGrade: 'measured', control: 'direct', method: 'auto', howToCheck: '모든 ld+json 블록 JSON.parse 성공 여부', levels: L('전부 무효 또는 없음', '일부 무효', '전부 유효', '전부 유효 + @graph 연결'), }, { id: 'B2', category: 'entity', name: '업종 엔티티 스키마 (MedicalClinic / LegalService / LocalBusiness)', rationale: '엔티티 식별을 돕는 위생 조치. 인과 증거는 없으나 name·address·telephone·openingHours·전문분야(medicalSpecialty / areaServed)를 기계가 읽게 한다.', weight: 5, evidenceGrade: 'academic', control: 'direct', method: 'auto', howToCheck: '@type MedicalClinic|LegalService|LocalBusiness 존재 + 필수 속성 4개', levels: L('없음', 'Organization만', '업종 타입 + 주소·전화', '+ openingHours·전문분야·geo'), }, { id: 'B3', category: 'entity', name: '전문가 스키마 (Physician / Attorney)', rationale: '"누가 담당하나"는 병의원·로펌 질의의 핵심. 의사·변호사 이름과 자격·경력을 구조화하면 E-E-A-T 엔티티가 된다.', weight: 3, evidenceGrade: 'academic', control: 'direct', method: 'auto', howToCheck: '@type Physician|Attorney + name·전문분야·worksFor', levels: L('없음', '이름만', '전문분야 포함', '자격·소속·sameAs 포함'), }, { id: 'B4', category: 'entity', name: 'NAP 사이트 내 일관성', rationale: '같은 사이트 안에서 주소 표기가 다르면 외부 표면과 대조할 기준이 없다. 한 글자까지 동일해야 한다.', weight: 3, evidenceGrade: 'measured', control: 'direct', method: 'auto', howToCheck: '주소·전화 정규식 추출 → 고유 표기 수', levels: L('주소/전화 미표기', '3개 이상 표기 변형', '2개 변형', '단일 표기'), }, { id: 'B5', category: 'entity', name: 'sameAs 연결', rationale: 'Organization.sameAs로 YouTube·Instagram·강남언니·GBP를 연결해야 AI가 흩어진 언급을 한 엔티티로 묶는다.', weight: 2, evidenceGrade: 'academic', control: 'direct', method: 'auto', howToCheck: 'Organization.sameAs 배열 길이', levels: L('비어 있음', '1~2개', '3~4개', '5개 이상 (SNS + 버티컬 + 지도)'), }, { id: 'B6', category: 'entity', name: 'canonical·중복 제거', rationale: '같은 내용이 여러 URL이면 인용 신호가 분산된다.', weight: 2, evidenceGrade: 'measured', control: 'direct', method: 'auto', howToCheck: 'rel=canonical 존재 + self-referencing', levels: L('없음', '있으나 불일치', '있음', '있음 + 파라미터 정리'), }, { id: 'B7', category: 'entity', name: '다국어 hreflang', rationale: '외국인 환자 질의(영·중·일)는 별도 엔진 표면. 언어 버전이 있다면 hreflang으로 연결해야 한다.', weight: 2, evidenceGrade: 'vendor', control: 'direct', method: 'auto', howToCheck: 'link hreflang 태그 수', levels: L('다국어 페이지 있으나 미연결', '단일 언어', '2개 언어 연결', '3개 이상 연결'), }, // ───────────── C. 콘텐츠 (25) ───────────── { id: 'C1', category: 'content', name: '정의문형 첫 문단', rationale: 'Google AI Mode의 query fan-out은 패시지 단위로 검색한다. "○○은 강남 신논현에 있는 가슴성형 전문 성형외과다"처럼 답을 먼저 써야 청크가 자기완결적이 된다.', weight: 4, evidenceGrade: 'academic', control: 'direct', method: 'semi', howToCheck: '메인·시술 페이지 첫 200자에 [상호+지역+전문분야] 정의문 존재', levels: L('없음 (슬로건·이미지만)', '슬로건형', '일부 페이지에 정의문', '전 핵심 페이지 정의문'), }, { id: 'C2', category: 'content', name: 'FAQ 섹션', rationale: '실제 질문 형태 그대로의 H2/H3 + 답변은 답변엔진이 가장 잘 가져가는 구조. FAQPage 스키마와 결합.', weight: 4, evidenceGrade: 'academic', control: 'direct', method: 'auto', howToCheck: 'FAQ/Q&A 섹션 탐지 + FAQPage 스키마', levels: L('없음', '게시판형 Q&A만', 'FAQ 섹션 존재', 'FAQ + FAQPage 스키마 + 시술별'), }, { id: 'C3', category: 'content', name: '제목 계층 의미성', rationale: 'H1은 페이지당 1개, 주제를 서술해야 한다. "VIEW SELFIE" 같은 라벨형 H1은 AI에게 아무 정보도 주지 않는다.', weight: 3, evidenceGrade: 'measured', control: 'direct', method: 'auto', howToCheck: 'H1 개수 = 1, H1/H2 텍스트가 명사구 서술형인지, 중복 H2 비율', levels: L('H1 없음 또는 5개 이상', 'H1 다수·라벨형', 'H1 1개, H2 일부 라벨형', 'H1 1개 서술형, H2 논리 계층'), }, { id: 'C4', category: 'content', name: '구체 수치 (가격·시간·회복)', rationale: '가격대·수술시간·회복기간·재수술률 같은 수치는 인용되는 콘텐츠의 공통 특징. (Princeton GEO 정합, 단 C-SEO Bench 반박 있음 → 효과 보장 아님)', weight: 4, evidenceGrade: 'academic', control: 'direct', method: 'semi', howToCheck: '시술 페이지에서 원/분/일/% 단위 수치 밀도', levels: L('수치 없음', '마케팅 문구 속 수치만', '시술별 일부 수치', '시술별 가격대·시간·회복 표기'), }, { id: 'C5', category: 'content', name: '표·비교 구조', rationale: 'AI가 인용하는 콘텐츠는 구조화·표 중심 (인블로그 조사). 보형물 비교표·시술 비교표가 대표 예.', weight: 2, evidenceGrade: 'vendor', control: 'direct', method: 'auto', howToCheck: ' 또는 정의 리스트 존재 (이미지 표 제외)', levels: L('없음', '이미지로만 된 표', 'HTML 표 1~2개', 'HTML 표 다수'), }, { id: 'C6', category: 'content', name: '신선도 신호', rationale: 'dateModified·최종 수정일 명시. 1년 이상 갱신 없는 사이트는 "폐업했나?" 오정보 위험.', weight: 3, evidenceGrade: 'academic', control: 'direct', method: 'auto', howToCheck: 'sitemap lastmod 최신값, article:modified_time, 본문 갱신일', levels: L('2년 이상 갱신 없음', '1년 이상', '6개월 이내', '3개월 이내 + dateModified 표기'), }, { id: 'C7', category: 'content', name: '전문가 E-E-A-T (의료진 / 변호사)', rationale: '전문의 자격·경력·학회·수상, 변호사의 전관 경력·전문분야 등록이 텍스트로 존재해야 AI가 "믿을 만한 출처"로 판단할 근거가 생긴다. 이미지 안의 글자는 안 읽힌다.', weight: 3, evidenceGrade: 'academic', control: 'direct', method: 'semi', howToCheck: '전문가 페이지 텍스트에 자격·경력 명시 여부', levels: L('전문가 정보 없음', '이름·사진만', '자격·경력 텍스트', '+ 논문·학회·수상 + 전문가 스키마'), }, { id: 'C8', category: 'content', name: '패시지 자기완결성', rationale: '한 단락만 떼어내도 "누가·어디서·무엇을"이 들어 있어야 인용된다. 대명사·"저희"로 시작하는 단락은 맥락을 잃는다.', weight: 2, evidenceGrade: 'academic', control: 'direct', method: 'manual', howToCheck: '핵심 페이지 단락 샘플 10개 중 상호·시술명이 포함된 비율', levels: L('0~20%', '20~50%', '50~80%', '80% 이상'), }, // ───────────── D. 표면 (20) ───────────── { id: 'D1', category: 'surface', name: '업종 버티컬 프로필 (병의원: 강남언니 / 법률: 로톡)', rationale: '2026-08 실측: 병의원 질의는 강남언니(ai-input=yes 명시)·닥터나우가 AI 인용을 장악. 법률은 로톡·로앤굿 등 변호사 디렉터리가 같은 역할. 여기 없으면 AI에겐 없는 업체.', weight: 6, evidenceGrade: 'measured', control: 'direct', method: 'manual', howToCheck: '버티컬 프로필 존재·등록 항목 수·후기 수·최근 갱신 (병원 페이지 / 로펌·변호사 프로필)', levels: L('미등록', '등록, 항목 3개 미만', '항목 다수·후기 활성', '+ 전문가·가격 갱신 3개월 이내'), }, { id: 'D2', category: 'surface', name: 'Google 비즈니스 프로필', rationale: 'Gemini·Google AI Mode의 구조화 데이터 원천. 국내 성형외과 등록률이 낮아 선점 여지가 크다.', weight: 4, evidenceGrade: 'measured', control: 'direct', method: 'manual', howToCheck: 'Google Maps 검색 → 소유권 확인·카테고리·사진·리뷰·NAP 일치', levels: L('없음', '미소유 자동 생성', '소유·기본 정보', '소유·리뷰 응답·게시물 활성'), }, { id: 'D3', category: 'surface', name: '오픈웹 언급 (티스토리·유튜브·언론)', rationale: 'Ahrefs 7.5만 브랜드: 웹 언급 상관 r=0.664 (백링크 0.218). 네이버 블로그·카페는 AI 차단이라 제외.', weight: 4, evidenceGrade: 'academic', control: 'influence', method: 'semi', howToCheck: '검색 그라운딩으로 상호 언급 도메인 집계 (naver.com 제외)', levels: L('자체 사이트 외 없음', '유튜브만', '유튜브 + 언론/티스토리', '다수 오픈 도메인에서 일관된 언급'), }, { id: 'D4', category: 'surface', name: '표면 간 NAP 완전 일치', rationale: '홈페이지·강남언니·GBP·플레이스의 주소·전화가 한 글자라도 다르면 답변엔진이 동일 업체로 묶지 못한다.', weight: 3, evidenceGrade: 'measured', control: 'direct', method: 'manual', howToCheck: '표면별 NAP 표를 만들어 대조', levels: L('전화번호 상이', '주소 표기 상이', '경미한 표기 차이', '완전 일치'), }, { id: 'D5', category: 'surface', name: '네이버 플레이스 (AI 브리핑 전용)', rationale: '글로벌 AI에는 기여도 0. 단 네이버 AI 브리핑(검색의 20%→40%)은 별도 게임이며 플레이스↔홈페이지 연결이 중요.', weight: 2, evidenceGrade: 'measured', control: 'direct', method: 'semi', howToCheck: '플레이스 존재·홈페이지 링크·리뷰 수', levels: L('없음', '있음, 홈페이지 미연결', '연결·리뷰 활성', '연결·리뷰·소식 활성'), }, { id: 'D6', category: 'surface', name: '위키데이터/위키백과 엔티티', rationale: 'LLM의 엔티티 앵커. 대형 병원만 해당하나 있으면 확정적.', weight: 1, evidenceGrade: 'estimate', control: 'influence', method: 'auto', howToCheck: 'Wikidata API 상호 검색', levels: L('없음', '언급만', '항목 존재', '항목 + 공식 사이트 연결'), }, // ───────────── E. 측정 (10) ───────────── { id: 'E1', category: 'measure', name: 'AI 크롤러 로그 (IP 검증)', rationale: 'AI가 우리를 읽었는가(선행 지표). 크롤러 UA의 최대 81.8%가 위조 → IP 검증 히트만 인정.', weight: 3, evidenceGrade: 'measured', control: 'direct', method: 'manual', howToCheck: 'Cloudflare AI Crawl Control 또는 서버로그 + bot_ip_verify.py', levels: L('로그 접근 불가', '로그 있음, 미분석', 'UA 기준 집계', 'IP 검증 히트 집계'), }, { id: 'E2', category: 'measure', name: 'GA4 AI 유입 채널', rationale: 'AI에서 사람이 왔는가(후행·매출 귀속). chatgpt.com·perplexity.ai 등 커스텀 채널그룹. 35~70%가 Direct로 유실되므로 하한값.', weight: 3, evidenceGrade: 'measured', control: 'direct', method: 'manual', howToCheck: 'GA4 커스텀 채널그룹 존재 여부 (오너 접근 필요)', levels: L('GA4 없음', 'GA4 있음, AI 채널 없음', '기본 AI Assistant 채널', '커스텀 채널 (Perplexity 포함)'), }, { id: 'E3', category: 'measure', name: 'GSC AI Overviews 노출', rationale: 'Google AI 표면 노출 (2026-06~ Search Console UI 전용).', weight: 2, evidenceGrade: 'vendor', control: 'direct', method: 'manual', howToCheck: 'GSC 등록 + AI Overviews 필터 확인', levels: L('GSC 미등록', '등록만', '데이터 확인', '월간 추적'), }, { id: 'E4', category: 'measure', name: '기준선 스팟체크', rationale: '질의 10개 × 5~7회 반복 수동 측정 → 언급률+신뢰구간. 단발 조회는 무효(분산 10~34%).', weight: 2, evidenceGrade: 'academic', control: 'direct', method: 'manual', howToCheck: '04_baseline 진단 리포트 존재 여부', levels: L('없음', '단발 조회만', '반복 측정 1회', '분기 반복 측정'), }, // ───────────── F. 위생 (5) ───────────── { id: 'F1', category: 'hygiene', name: '이미지 대체텍스트', rationale: '성형외과 사이트는 정보의 대부분이 이미지 안 글자. alt가 없으면 AI에게 그 정보는 없다.', weight: 2, evidenceGrade: 'measured', control: 'direct', method: 'auto', howToCheck: 'alt 누락 이미지 비율', levels: L('50% 이상 누락', '20~50% 누락', '5~20% 누락', '5% 미만 누락'), }, { id: 'F2', category: 'hygiene', name: 'CMS 보안·최신성', rationale: '지원 종료 CMS는 해킹 시 스팸 페이지가 주입되어 AI가 엉뚱한 내용을 인용할 위험.', weight: 1, evidenceGrade: 'estimate', control: 'direct', method: 'auto', howToCheck: 'meta generator 버전 vs 최신 안정 버전', levels: L('3년 이상 구버전', '1~3년', '1년 이내', '최신'), }, { id: 'F3', category: 'hygiene', name: '페이지 무게', rationale: 'HTML 크기가 크면 크롤 예산 소모·요약 품질 저하.', weight: 1, evidenceGrade: 'estimate', control: 'direct', method: 'auto', howToCheck: '초기 HTML 바이트', levels: L('> 2MB', '> 800KB', '300~800KB', '< 300KB'), }, { id: 'F4', category: 'hygiene', name: '모바일 대응', rationale: '모바일 인앱 브라우저 유입(ChatGPT 앱)이 주 경로.', weight: 1, evidenceGrade: 'measured', control: 'direct', method: 'auto', howToCheck: 'viewport meta + 반응형', levels: L('viewport 없음', '별도 m. 도메인', '반응형', '반응형 + CWV 양호'), }, ], }; export const getCriterion = (id: string) => AEO_GEO_RUBRIC.criteria.find((c) => c.id === id); export const getCategory = (id: string) => AEO_GEO_RUBRIC.categories.find((c) => c.id === id);