Part I · CHAPTER 03

컨텍스트 윈도우 해부

정적 / 동적 / 휘발 레이어

모델이 "잘 답한다"와 "헛소리를 한다" 사이의 가장 큰 변수는 모델 크기가 아니에요. 그 순간 모델 앞에 어떤 글이 펼쳐져 있는가예요. 그 펼쳐진 책상이 컨텍스트 윈도우예요.

200K 토큰짜리 컨텍스트 윈도우는 균일한 공간이 아니에요. 레이어가 있어요.

비유를 하나 빌려 와볼게요. 컨텍스트 윈도우는 거대한 작업 책상이에요. 책상 한쪽에는 매뉴얼·도구 사용 설명서가 늘 깔려 있고(정적 레이어), 그 위에 작업 중에 꺼낸 부품과 도면이 쌓여 가요(동적 레이어). 책상이 다 차면 누군가가 정리해서 메모지로 압축해야 해요(Compaction).

flowchart TB Ceiling["천장: 200K 토큰 한계(200K token limit)"] subgraph Dynamic["동적 레이어(Dynamic layer)"] direction TB Compaction["압축 버퍼(Compaction Buffer)<br/>꽉 차면 핵심만 남기고 자동 요약"] Remaining["남은 공간(Remaining Space)"] History["대화 기록 / 도구 결과(Conversation History / Tool Results)<br/>채팅 로그, 파일 읽기, 셸 출력, 에러<br/>작업할수록 위로 쌓임"] Compaction --- Remaining Remaining --- History end Boundary["경계: 정적 / 동적(static / dynamic boundary)"] subgraph Static["정적 레이어(Static layer)"] direction TB ClaudeMd["지침 파일(CLAUDE.md / AGENTS.md)<br/>전역 → 프로젝트 순"] Skills["스킬 파일(Skills)<br/>/skill-name 입력 시 로드"] ToolSpecs["도구 명세(Tool Specs)<br/>하네스 정의"] SysPrompt["시스템 프롬프트(System Prompt)"] ClaudeMd --- Skills Skills --- ToolSpecs ToolSpecs --- SysPrompt end Floor["바닥: 컨텍스트 시작(context start)"] Ceiling --- Compaction History --- Boundary Boundary --- ClaudeMd SysPrompt --- Floor

정적 레이어 — 세션 시작부터 자리를 잡아요

  • 시스템 프롬프트 파일 (Claude Code: CLAUDE.md 전역 → 환경 → 프로젝트 / Codex: AGENTS.md 전역 → 프로젝트 → 하위 폴더, 양쪽 다 순서대로 누적)
  • 호출된 스킬 파일 (/skill-name 입력 시 로드)

이 레이어는 세션 내내 메모리에 고정돼요. 길수록 모든 작업에 메모리를 잡아먹어요. 가구처럼 생각하면 돼요 — 책상에 깔려 있는 매트 자체가 두꺼우면, 그 위에 올릴 수 있는 도면 면적이 그만큼 줄어들어요.

동적 레이어 — 작업할수록 위로 쌓여요

  • 현재 대화 히스토리
  • 도구 실행 결과 (파일 읽기, zsh 셸 출력, 에러 로그)
  • 코드 실행 결과, 검색 결과

동적 레이어의 핵심 문제는 정제되지 않은 원본 그대로 쌓인다는 점이에요. 파일 하나를 열면 파일 전체가, 에러가 나면 스택 트레이스 전체가, 테스트를 돌리면 모든 반복 로그가 그대로 들어와요. 대규모 작업에서는 파일 탐색만으로도 컨텍스트를 가득 채울 수 있고, 의미 없는 로그로 가득 찬 컨텍스트는 LLM의 실효 집중력을 떨어뜨려요.

세션 타임라인 — 같은 책상이 변해 가는 모습

세 단계로 정리하면 흐름이 한눈에 보여요.

flowchart LR subgraph Phase1["Phase 1 — 약 10% 사용(~10% used)"] direction TB D1["동적 레이어: 거의 비어 있음<br/>(dynamic layer thin)"] S1["정적 레이어<br/>(static: CLAUDE.md, skills, tool spec)"] D1 --- S1 end subgraph Phase2["Phase 2 — 약 70% 사용(~70% used)"] direction TB D2["동적 레이어: 히스토리 누적<br/>도구 출력이 무거워짐<br/>attention 희석"] S2["정적 레이어 (불변)<br/>(static, unchanged)"] D2 --- S2 end subgraph Phase3["Phase 3 — Compaction 직후(post-Compaction)"] direction TB D3["동적 레이어: 히스토리 → 요약본<br/>도구 로그 폐기<br/>새 흐름 이어감"] S3["정적 레이어 (불변)<br/>(static, unchanged)"] D3 --- S3 end Phase1 --> Phase2 --> Phase3

핵심: 정적 레이어는 세 단계 내내 고정 비용이에요. 줄어드는 건 오직 동적 레이어뿐이고, Compaction이 손대는 곳도 동적 레이어예요. 그래서 시스템 프롬프트 파일 한 줄이 반복 비용에 매번 곱셈으로 들어가요.

Attention 분산 — 책상이 넓어진다고 시야가 똑같이 좋아지진 않아요

여기에 깜짝 사실이 하나 더 있어요. 컨텍스트 윈도우가 200K라고 해서 모델이 그 200K 전체를 똑같이 잘 보는 건 아니에요. 트랜스포머의 self-attention(자기 주의) 메커니즘은 모든 토큰 쌍 사이의 가중치를 계산하는데, 토큰이 많아질수록 한 토큰에 분배되는 attention 가중치는 그만큼 얕아져요. 영어권 연구에서는 이를 "lost in the middle" — 양 끝(시작과 끝)의 정보는 잘 챙기지만 가운데가 흐려진다 — 이라고 불러요. 직관적으로 사람이 긴 회의록의 처음과 끝만 기억하는 현상과 닮았어요. 본문에서 "attention 분산" 으로 줄여 부르는 건 직관적 요약이고, 실제로는 위치 임베딩 방식(RoPE·YaRN 등) · 학습 데이터의 길이 분포 · 어텐션 변형(SWA·GQA·MLA 등) 이 복합적으로 작용해요. 자세한 메커니즘은 어텐션 변형 부록 에서 다뤄요.

비유에는 한계가 있어요. "책상" 비유는 컨텍스트의 양적 한계와 레이어 구조를 설명하지만, 실제 모델 안쪽에서 일어나는 일은 종이를 펴는 일이 아니라 모든 토큰 쌍 사이의 관계를 계산하는 일이에요. 책상이 두 배 넓어지면 종이가 두 배 들어가는 게 아니라, 처리해야 할 짝의 개수가 네 배로 늘어나요. Prefill 비용이 길이의 제곱에 비례하는 이유예요.

이 문제의 해법이 서브에이전트(Sub-agent) 위임이에요. 탐색·반복 실행·에러 테스트처럼 컨텍스트를 많이 소모하는 작업은 서브에이전트에게 맡기고, 메인 에이전트는 깔끔하게 요약된 보고만 받는 구조예요. 다음 섹션에서 바로 이어가요.

Compaction: LLM의 자기 압축

컨텍스트가 한도에 가까워지면 Compaction이 일어나요.

Compaction은 하네스가 LLM을 한 번 더 호출해 대화 히스토리 자체를 요약하게 시키는 과정이에요. 결과적으로 정적 레이어는 그대로 유지되고, 동적 레이어의 세부 내용만 요약본으로 대체돼요.

Compaction 후에는 원래 대화 히스토리가 사라져요. 오래된 작업을 다시 언급하면 LLM이 기억 못하는 이유예요.

실천 원칙: 시스템 프롬프트 파일은 짧게 유지하고, 작업은 세션 단위로 쪼개세요. 정적 레이어에 낭비되는 메모리를 최소화하는 것이 곧 지능의 실효 컨텍스트를 늘리는 일이에요.

컨텍스트 윈도우의 구조가 왜 이렇게 생겼는지, 그리고 트랜스포머의 Prefill/Decode 단계가 컨텍스트 길이에 어떤 비용을 매기는지는 트랜스포머 추론의 두 단계: Prefill과 Decode에서 다뤄요.