AEO(Agentic Engine Optimization): AI 에이전트를 위한 문서 최적화 전략

서론

개발자 A가 최근 이상한 현상을 발견했습니다. GitHub 트래픽 분석 도구에는 방문자 수가 절반으로 줄어들었는데, 실제로는 API 레퍼런스 페이지를 참조하는 프로젝트가 두 배나 늘었습니다. 어디서 온 트래픽인지 추적도 안 되고, 검색 엔진 순위도 그대로인데 사용자는 늘어난 겁니다.

이 현상의 원인은 바로 AI 코딩 에이전트입니다. Cursor, Devin, GitHub Copilot 같은 에이전트들이 문서를 소비하는 방식은 인간과 근본적으로 다릅니다. 브라우저를 열고 페이지를 스크롤하며 읽는 대신, 에이전트는 단일 HTTP 요청으로 raw 텍스트를 가져와 토큰 수를 계산하고, 컨텍스트 윈도우에 맞지 않으면 조용히 폐기합니다. Google Analytics도, Mixpanel도, 어떤 분석 도구도 이 과정을 포착하지 못합니다.

문제는 단순히 트래픽 측정뿐 아닙니다. AI 에이전트가 문서를 이해하지 못하면, 여러분이 만든 훌륭한 API가 에이전트의 추천에서 영원히 제외될 수 있습니다. 이것이 바로 **AEO(Agentic Engine Optimization)**가 필요한 이유입니다.

본론

1. AI 에이전트의 문서 소비 메커니즘

전통적인 SEO는 인간의 브라우징 패턴에 최적화되어 있습니다. 사용자가 검색 결과를 클릭하고, 페이지에 머무는 시간(dwell time)을 측정하며, 클릭 패턴을 분석합니다. 하지만 AI 에이전트는 완전히 다른 경로를 따릅니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
graph LR
    A[에이전트 작업 시작] --> B[검색 API 호출]
    B --> C[후보 문서 URL 리스트 확보]
    C --> D[HTTP GET - Raw 텍스트]
    D --> E[토큰 카운트 계산]
    E --> F[컨텍스트 윈도우 초과?]
    F -->|Yes| G[문서 폐기]
    F -->|No| H[RAG 청크 분할]
    H --> I[임베딩  검색]
    I --> J[컨텍스트 윈도우에 삽입]
    J --> K[LLM 응답 생성]

핵심은 에이전트가 문서를 ‘읽지’ 않는다는 점입니다. 에이전트는 문서를 데이터로 처리합니다. 이 과정에서 몇 가지 결정적인 차이가 발생합니다:

| 비교 항목 | 인간 개발자 | AI 코딩 에이전트 | | :— | :— | :— | | 접근 방식 | 브라우저 렌더링 후 시각적 탐색 | HTTP GET으로 raw 텍스트 수신 | | 분석 단위 | 시각적 섹션, 헤딩 | 토큰 (Token) | | 필터링 기준 | 관련성, 가독성 | 토큰 수 ≤ 컨텍스트 윈도우 | | 폐기 시 signal | 404/500 에러 | 없음 (조용히 무시) | | 분석 추적 | JS 기반 분석 도구 | 추적 불가 (JS 미실행) | | 소비 시간 | 수 초 ~ 수 분 | 수 밀리초 |

2. 컨텍스트 윈도우의 물리적 한계

현재 주요 LLM의 컨텍스트 윈도우는 다음과 같습니다:

| 모델 | 컨텍스트 윈도우 | 실제 사용 가능 토큰 | | :— | :— | :— | | GPT-4o | 128K tokens | ~90K (시스템 프롬프트 제외) | | Claude 3.5 Sonnet | 200K tokens | ~150K | | Gemini 1.5 Pro | 1M tokens | ~800K | | Qwen2.5-72B | 128K tokens | ~100K |

에이전트는 단일 API 문서를 가져올 때, 보통 다음과 같은 토큰 예산을 가집니다:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# AI 에이전트의 내부 의사결정 과정 (의사코드)
def should_consume_document(url: str, context_budget: int) -> bool:
    """
    에이전트가 문서를 컨텍스트에 포함할지 결정하는 로직
    """
    # 1. HTTP 요청으로 raw 텍스트 가져오기
    raw_text = http_get(url)
    
    # 2. HTML → 마크다운/텍스트 변환
    clean_text = html_to_markdown(raw_text)
    
    # 3. 토큰 수 계산
    token_count = count_tokens(clean_text, model="gpt-4o")
    
    # 4. 현재 컨텍스트 예산 확인
    remaining_budget = context_budget - token_count
    
    # 5. 예산이 부족하면 조용히 폐기
    if remaining_budget < SAFETY_MARGIN:
        # 로그도 없이, 에러도 없이 그냥 스킵
        return False
    
    return True

문제의 핵심: 여러분의 API 문서가 50,000 토큰인데, 에이전트의 남은 예산이 40,000 토큰이라면? 에이전트는 문서를 읽지 않습니다. 에러도, 로그도, 어떤 추적 가능한 signal도 남기지 않습니다. 그냥 조용히 사라집니다.

3. AEO 적용을 위한 문서 최적화 전략

AEO의 핵심 원칙은 에이전트가 이해하기 쉬운 구조로 문서를 재구성하는 것입니다. 사람의 가독성을 해치지 않으면서, 에이전트의 파싱 효율을 극대화해야 합니다.

Step 1: 문서 구조의 계층화

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 나쁜 예: 하나의 긴 페이지
## API Reference
... (50,000 토큰의 내용이 연속적으로 배치)

# 좋은 예: 모듈식 구조
## API Reference Overview (500 토큰)
- 핵심 개념 요약
- 모듈별 링크

## Authentication Module (2,000 토큰)
- 별도 엔드포인트

## User Management Module (3,000 토큰)
- 별도 엔드포인트

Step 2: 메타데이터 강화

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<!-- 기존 SEO 메타데이터 -->
<meta name="description" content="API documentation for developers">
<meta name="keywords" content="API, REST, documentation">

<!-- AEO 메타데이터 추가 -->
<meta name="x-ai-tokens" content="2500">
<meta name="x-ai-language" content="python">
<meta name="x-ai-complexity" content="intermediate">
<meta name="x-ai-updated" content="2024-12-01">
<link rel="ai-index" href="/api/ai-index.json">

Step 3: AI 인덱스 파일 생성

에이전트가 한 번에 전체 문서 구조를 파악할 수 있도록 ai-index.json 파일을 제공합니다:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
  "schema_version": "1.0",
  "documentation": {
    "total_modules": 5,
    "estimated_tokens": 12000,
    "modules": [
      {
        "name": "authentication",
        "url": "/api/docs/auth.md",
        "tokens": 1500,
        "keywords": ["OAuth", "JWT", "API key"],
        "complexity": "beginner"
      },
      {
        "name": "users",
        "url": "/api/docs/users.md",
        "tokens": 2800,
        "keywords": ["CRUD", "pagination", "filtering"],
        "complexity": "intermediate"
      }
    ]
  },
  "quick_start": {
    "url": "/api/docs/quickstart.md",
    "tokens": 800,
    "language_examples": ["python", "javascript", "curl"]
  }
}

Step 4: 토큰 효율적 콘텐츠 작성

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 토큰 효율성 검증 도구
import tiktoken

def analyze_document_efficiency(filepath: str) -> dict:
    """
    문서의 토큰 효율성을 분석합니다.
    """
    with open(filepath, 'r') as f:
        content = f.read()
    
    # 토크나이저 초기화
    encoding = tiktoken.encoding_for_model("gpt-4o")
    tokens = encoding.encode(content)
    token_count = len(tokens)
    
    # 정보 밀도 계산 (코드 블록, 예제 포함)
    code_blocks = content.count('```')
    api_endpoints = content.count('GET') + content.count('POST') + \
                    content.count('PUT') + content.count('DELETE')
    
    efficiency_score = (api_endpoints * 50 + code_blocks * 20) / token_count
    
    return {
        "token_count": token_count,
        "efficiency_score": round(efficiency_score, 4),
        "recommendation": "split" if token_count > 4000 else "optimal",
        "code_blocks": code_blocks // 2,
        "api_endpoints": api_endpoints
    }

# 실행 예시
result = analyze_document_efficiency("api_docs.md")
print(f"토큰 수: {result['token_count']}")
print(f"효율성 점수: {result['efficiency_score']}")
print(f"권장 사항: {result['recommendation']}")

Step 5: 구조화된 데이터 마크업

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "TechArticle",
  "name": "User API Documentation",
  "description": "RESTful API for user management",
  "programmingLanguage": "Python",
  "codeSampleType": "API",
  "hasPart": [
    {
      "@type": "HowTo",
      "name": "Create User",
      "step": [
        {
          "@type": "HowToStep",
          "text": "POST /api/v1/users with JSON body"
        }
      ]
    }
  ]
}
</script>

4. AEO vs SEO 비교

| 최적화 영역 | 전통적 SEO | AEO (Agentic Engine Optimization) | | :— | :— | :— | | 주요 소비자 | 인간 사용자 | AI 에이전트 | | 핵심 지표 | 클릭률, 체류 시간 | 토큰 효율성, 정보 밀도 | | 문서 구조 | 시각적 계층 (CSS) | 논리적 계층 (Markdown) | | 메타데이터 | Open Graph, Schema.org | AI Index, 토큰 카운트 | | 분석 방법 | JS 기반 추적 | 서버 로그, API 호출 패턴 | | 최적화 단위 | 페이지 | 모듈/섹션 | | 실패 signal | 404, 이탈률 | 없음 (조용한 무시) | | 콘텐츠 형식 | HTML + 이미지 | Markdown + JSON + 코드 |

5. 실무 적용: AEO 체크리스트

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
## AEO 문서 최적화 체크리스트

### 구조 최적화
- [ ] 각 섹션이 4,000 토큰 이하인가?
- [ ] 모듈별로 독립 접근 가능한가?
- [ ] 계층 구조가 명확한가?

### 메타데이터
- [ ] AI 인덱스 파일이 있는가?
- [ ] 각 문서의 토큰 수가 명시되었는가?
- [ ] 구조화된 데이터 마크업이 포함되었는가?

### 내용 최적화
- [ ] 코드 예제가 실행 가능한가?
- [ ] API 엔드포인트가 명확히 표시되었는가?
- [ ] 불필요한 장식적 텍스트가 제거되었는가?

### 추적 및 분석
- [ ] 서버 로그에 User-Agent별 접근 기록이 있는가?
- [ ] AI 에이전트 트래픽을 식별할 수 있는가?
- [ ] 문서별 API 호출 빈도를 모니터링하는가?

6. 서버 측 AI 트래픽 감지

AI 에이전트 트래픽을 식별하기 위한 서버 미들웨어 예시입니다:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from fastapi import FastAPI, Request, Response
from starlette.middleware.base import BaseHTTPMiddleware
import time
import json

app = FastAPI()

class AIAgentDetectionMiddleware(BaseHTTPMiddleware):
    """
    AI 에이전트의 트래픽을 감지하고 로깅하는 미들웨어
    """
    
    KNOWN_AI_AGENTS = [
        "cursor", "github-copilot", "deeplearning-ai",
        "openai", "anthropic", "google-ai"
    ]
    
    async def dispatch(self, request: Request, call_next):
        start_time = time.time()
        
        # 응답 처리
        response = await call_next(request)
        
        # 처리 시간 계산
        process_time = (time.time() - start_time) * 1000
        
        # AI 에이전트 감지
        user_agent = request.headers.get("user-agent", "").lower()
        is_ai_agent = any(
            agent in user_agent for agent in self.KNOWN_AI_AGENTS
        )
        
        # HTTP 클라이언트 패턴 감지
        # (에이전트는 보통 python-requests, httpx 등 사용)
        http_client_patterns = [
            "python-requests", "httpx", "aiohttp", "node-fetch"
        ]
        is_automated = any(
            pattern in user_agent for pattern in http_client_patterns
        )
        
        # AI 트래픽 로깅
        if is_ai_agent or is_automated:
            log_entry = {
                "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
                "path": str(request.url.path),
                "method": request.method,
                "user_agent": user_agent,
                "process_time_ms": round(process_time, 2),
                "is_ai_agent": is_ai_agent,
                "is_automated": is_automated
            }
            
            # 별도 로그 파일에 기록
            with open("ai_traffic.log", "a") as f:
                f.write(json.dumps(log_entry) + "
")
            
            # 응답 헤더에 AI 최적화 정보 추가
            if is_ai_agent:
                response.headers["X-AI-Optimized"] =

출처: https://news.hada.io/topic?id=28588

Hugo로 만듦
JimmyStack 테마 사용 중