hayou
dev13min read9

사이트가 AI에게 답할 수 있게 — 홈페이지 AEO 적용하기

홈페이지 AEO 적용기

학부모들이 ChatGPT에 학원을 묻는다.

"대치동 초등 논술 학원 추천해줘", "포도밭학원 수강료 얼마야?"

내가 개발하고 운영하고 있는 학원 홈페이지(podobat.com)의 SEO는 잘 돼있다.
메타데이터, sitemap, robots.txt도 박혀있고 구글 검색에도 노출이 잘 된다.
근데 AI에 물어보면 학원이 거의 언급되지 않았다.

SEO는 됐는데 AEO는 안 돼 있던 거다.
AEO(Answer Engine Optimization).
AI가 답을 만들 때 사이트의 정보를 정확히 가져가게 하는 작업.

그 격차를 메우는 작업을 진행했고, 간략한 기록을 남겨본다.

47%짜리 답변력

가장 먼저 AI가 사이트의 정보를 못 긁어오는 건지, 아니면 제공할 정보가 없는 건지 확인해야 했다.
원인이 다르면 해결도 다르기 때문이다.

학부모가 AI에 물을 만한 질문 19개를 뽑아서 카테고리화했다.
커리큘럼/대상, 시간표, 가격/등록, 강사/철학, 위치, 성과. 6개 카테고리, 19개 질문.
그리고 사이트가 각 질문에 답할 수 있는지 점검했다.

결과는 47%였다.

분류개수예시
✅ 답 명확5강사진, 위치, 교육 철학
🟡 흐릿함6학년 범위, 시즌 운영
❌ 어디에도 없음8수강료, 정원, 보강, 환불, 레벨테스트, 시간표

흐릿한 6개는 DB엔 있는데 정적 페이지엔 없거나, 페이지 어딘가에 묻혀있는 케이스.
없는 8개는 그냥 사이트에 정보가 없는 거였다.

원인은 후자(제공할 정보 부족) 쪽이 컸다.
사이트에 답이 없으면 어떤 기술로도 못 푼다.

정보의 실재

진단을 마치니 코드를 짜는 것보다 부족한 정보를 메꾸는게 최우선 순위의 업무가 되었다. 클로드와 소통하면서 필요한 정보들을 채워나갔다.

  • 한 반 정원은 몇 명까지 받나
  • 수강료를 사이트에 그대로 노출할 건가
  • 결석 보강 정책은 어디까지 공개할 건가
  • 교재비는 노출할 건가
  • 합격 실적을 사이트에 올릴 건가

이런 건 코드로 풀 수 있는 게 아니다.
운영자만 알 수 있고, 운영자만 결정할 수 있는 항목들.

기술보다 앞서 필요한 건 '정보의 실재'였다.
구조화 데이터든 llms.txt든, 박을 정보가 없으면 빈 그릇이다.
AI한테 잘 보이는 형태로 정렬하기 전에, 정렬할 정보가 사이트에 실재해야 한다.

이 단계가 가장 오래 걸렸다. 항상 가장 중요한 것은, 기술이 아닌 '실재'이다.

새로운 접근성

웹 접근성은 익숙한 작업이다.
시각장애인을 위한 alt text, 스크린 리더가 읽을 수 있는 시맨틱 마크업, 키보드만으로도 가능한 네비게이션.
사용자의 다양성을 인정하고, 같은 콘텐츠가 각자에게 닿을 수 있도록 형태를 다르게 내보내는 일.

AI도 이제 사이트를 읽는 사용자 중 하나다.
다만 시각이나 청각이 아니라 구조화된 데이터로 읽는다.
사람에게는 풍부한 마크업이, AI에게는 추출하기 쉬운 plain text가 잘 닿는다.

이 관점으로 첫 PR을 올렸다.
/faq 라우트를 새로 파고, 학년별 수강 안내 컴포넌트를 만들고, 루트 layout에 Organization JSON-LD를 박았다.

FAQ를 만들 때 같은 답을 두 가지 형태로 저장했다.

const FAQ_ITEMS = [
  {
    question: '수강료는 얼마인가요?',
    answerNodes: (<>
      <p>주 1회 120분 수업, 1회당 기준입니다.</p>
      <ul>
        <li>초등 논술: 55,000원</li>
        <li>중등 국어 단독: 60,000원</li>
        ...
      </ul>
    </>),
    answerPlain: '주 1회 120분 수업 1회당 기준. 초등 논술 55,000원, 중등 국어 단독 60,000원...'
  }
]

answerNodes는 화면 렌더링용. <ul>, <p> 같은 마크업이 들어간다. answerPlain은 JSON-LD에 들어가는 텍스트.

const faqJsonLd = {
  '@context': 'https://schema.org',
  '@type': 'FAQPage',
  mainEntity: FAQ_ITEMS.map((item) => ({
    '@type': 'Question',
    name: item.question,
    acceptedAnswer: { '@type': 'Answer', text: item.answerPlain },
  })),
};

같은 데이터를 두 번 쓰는 게 아니다.
같은 답을 두 채널에 맞게 다르게 내보내는 것.

AI 친화적 콘텐츠는 사람용 콘텐츠와 따로 만드는 게 아니다.
접근성의 폭을 한 차원 더 넓히는 것일 뿐임을 깨달았다.

쿼리스트링에서 정적 URL로

기존 사이트에서는 /about, /courses, /notices와 같은 정보성 페이지의 탭을 ?tab=xxx 쿼리스트링으로 관리하고 있었다.

/about?tab=philosophy   (교육 철학)
/about?tab=methods      (교육 방법)
/about?tab=teachers     (강사 소개)
/about?tab=location     (오시는 길)

탭을 누르면 쿼리스트링이 바뀌고, 활성 탭의 콘텐츠만 화면에 보인다.
사람이 보기엔 멀쩡하다.

근데 크롤러 입장은 다르다.
탭 컨테이너가 클라이언트 컴포넌트라 useState로 활성 탭을 추적한다.
활성 탭의 콘텐츠만 DOM에 렌더링되고, 비활성 탭은 DOM에 아예 없다.

크롤러와 AI는 JavaScript를 실행하지 않는다.
그래서 /about을 크롤하면 기본 탭 "교육 철학"만 본다.
강사 소개, 교육 방법, 오시는 길은 존재하지 않는 셈이다.

페이지크롤러가 보는 것못 보는 것
/about교육 철학 (기본 탭)교육 방법·강사·오시는 길
/courses탭 버튼만커리큘럼 테이블 전체
/notices로딩 스켈레톤만공지사항 목록 전체

사이트의 절반 이상이 AI한테는 안 보이고 있었다.

그래서 각 탭을 별도 정적 라우트로 분리했다.

/about/philosophy
/about/methods
/about/teachers
/about/location

이유는 한 가지다. URL이 AI 인용 단위이기 때문이다.

AI가 답변할 때 출처를 URL로 단다.
"포도밭학원 강사진은 누구야?"에 답할 때, AI는 /about/teachers를 인용하지 /about을 인용하지 않는다.
콘텐츠 단위와 URL 단위가 일치해야 정확한 답이 나온다.

탭 컴포넌트를 정적 라우트 구조에 맞게 다시 짜고, 기존 ?tab=xxx URL은 redirect로 호환성을 챙겼다.
결과적으로 12개의 독립된 정적 라우트가 새로 생겼고, 모두 prerender됐다.
이제 AI가 "포도밭학원 수업 방식이 뭐야?"라고 물으면 /about/methods를 인용할 수 있다.

페이지별 JSON-LD

라우트가 쪼개진 김에, 페이지마다 다른 스키마를 박았다.

페이지스키마
/WebSite
/about/teachersEducationalOrganization + Person × 10
/courses/elementary-writingCourse + Offer (55,000원, 9명, 120분)
/courses/middle-writingCourse + Offer (60,000원, 12명, 120분)
/courses/middle-koreanCourse + Offer (60,000원, 15명, 120분)
/notices/[id]NewsArticle
/faqFAQPage

페이지마다 답하는 질문이 다르니, 박는 스키마도 다르다.
강사 페이지는 강사가 누군지, 수업 페이지는 수업의 가격과 정원이 얼마인지.
AI는 페이지에 박힌 스키마를 그대로 사실로 추출한다.

llms.txt 인덱싱

마지막은 llms.txt였다.
llmstxt.org에서 제안된 표준으로, AI 크롤러가 사이트의 핵심 정보를 빠르게 인덱싱할 수 있게 well-known 위치에 마크다운 파일을 두는 방식이다.
robots.txt의 AI 버전이라고 생각하면 된다.

만드는 데 10분 밖에 걸리지 않았지만, 이것만으로도 충분히 ROI를 높일 수 있다.

# 포도밭 국어논술 (포도밭학원)

> 서울 강남구 대치동에 위치한 초·중등 국어·논술 전문 학원...

## 학원 기본 정보
- 학원명: 포도밭 국어논술
- 대표: 유경하
- 주소: 서울특별시 강남구 역삼로 460, 3·4층
- 전화: 02-568-3612
- 대상 학년: 초등 3학년 ~ 중등 3학년 (고등부 미운영)

## 수업 과정 및 수강료
주 1회 120분 수업, 1회당 기준.
- **초등 논술** (초3~초6): 정원 9명, 1회당 55,000원
- **중등 논술** (중1~중3): 정원 12명, 단독 60,000원, 동시 수강 100,000원
...

정적 파일로 둘지, 라우트 핸들러(app/llms.txt/route.ts)로 만들어서 DB에서 자동 생성할지 잠깐 고민했다.
지금은 수강료가 코드 상수에 박혀있어서 자동화 효과가 크지 않다고 판단해서 정적으로 갔다.
DB 이관 시점에 라우트 핸들러로 바꿀 예정.

사이트의 핵심 사실이 한 파일에 모여있고, AI가 가장 빠르게 인덱싱한다.

결국, 콘텐츠

이번 작업을 돌아보고 남는 한 가지.

AEO는 기술이 30%, 콘텐츠가 70%다.

JSON-LD를 박고, 라우트를 쪼개고, llms.txt를 만드는 건 답을 추출하기 쉬운 형태로 정렬하는 일이다.
정작 진짜 일은 그 앞에 있다.
"이 사이트는 무엇을 답해주는 곳인가"를 정하는 단계.

내가 47% 진단을 한 뒤에 정책들을 고민했던 이유다.
그 시간이 없었다면 빈 페이지에 JSON-LD를 박는 의식 같은 작업이 됐을 거다.

URL이 AI 인용 단위라는 사실도 사이트 구조 설계 단계에서부터 의식해야 한다.
탭이 편해서 한 URL에 묶었던 콘텐츠가, AEO 관점에서는 묶이지 말았어야 할 콘텐츠였다.
콘텐츠 단위가 URL 단위와 일치해야 한다.

SEO와 AEO는 인접하지만 결이 다르다.
SEO는 키워드 매칭이 중심이라면, AEO는 사실 추출이 중심이다.
JSON-LD가 SEO에선 보너스지만 AEO에선 가장 기초적인 데이터이다.

학부모는 이제 AI에 묻는다.
사이트가 그 질문에 답할 수 있어야 한다.
답할 수 있으려면, 답이 사이트에 있어야 하고, AI가 추출할 수 있는 형태여야 한다.

share

comments

loading…