내 컨텍스트의 87%는 쓰레기였다: Claude Code 토큰 사용량 최적화 방법
요약
Claude Code 사용 시 발생하는 과도한 토큰 소모의 원인이 대화 기록, 특히 도구 I/O(Tool I/O)에 있음을 분석합니다. 단순한 시간 기반 요약 방식인 /compact의 한계를 지적하며, 데이터 유형별 분류를 통한 최적화 필요성을 제안합니다.
핵심 포인트
- Claude Code 토큰의 87%가 대화 기록에서 발생
- 대화 기록 중 약 80%가 불필요한 도구 I/O 데이터임
- /compact 명령은 요약 과정에서 오히려 많은 토큰을 소비함
- 시간 기반 압축 대신 데이터 유형별 분류 방식이 필요함
MAX 플랜의 주간 할당량이 3일 만에 녹아내렸습니다.
20배의 할당량을 가지고 있어야 함에도 불구하고, 수요일이 되자 남은 양이 의심스러워 보였습니다. 평소라면 그냥 "뭐, 그럴 수도 있지"라며 넘겼겠지만, 갑자기 궁금증이 생겼습니다. 컨텍스트 윈도우 (Context Window) 내부에서는 실제로 어떤 일이 일어나고 있는 걸까요?
제 이전 기사에서는 CLAUDE.md를 다듬거나 MCP 도구 정의를 축소하는 등, AI 비서를 위한 토큰 절약 방법에 대해 썼습니다. 하지만 이번에는 AI 비서가 아니라 Claude Code 자체에 관한 것입니다. 알고 보니 도구 자체가 엄청난 먹보였습니다.
계기
4월 14일, 스페인어로 된 트윗 하나가 제 눈을 사로잡았습니다.
"Claude Code 토큰 낭비의 대부분은 사용자 측에서 발생한다."
무슨 뜻인지는 이해합니다. CLAUDE.md가 비대해졌거나, 프롬프트 (Prompt)가 중복될 수 있죠. 하지만 "대부분이 사용자 측 문제"라니—이게 실제 측정을 바탕으로 한 말일까요?
그래서 직접 측정해 보기로 했습니다.
직접 측정하기
저는 Claude Code의 내부 트랜스크립트 (Internal Transcript, 세션을 기록하는 JSONL 파일)를 분석했습니다.
턴당 188,000 토큰. 그중 164,000 토큰(87%)이 대화 기록 (Conversation History)이었습니다.
CLAUDE.md는 12,700 토큰이었고, MCP 도구 정의는 3,900 토큰이었습니다. 이 둘을 합쳐도 전체의 9%에 불과했습니다. 이것들을 절반으로 줄여봤자 5%도 채 아낄 수 없었을 것입니다.
진짜 범인은 대화 기록의 비대화였습니다. CLAUDE.md를 줄이려고 그토록 애썼던 제 자신이 조금 부끄러워졌습니다.
범인: 도구 I/O (Tool I/O)
그렇다면 기록 안에는 무엇이 들어있을까요?
기록을 열어보고 저는 충격을 받았습니다. 기록의 약 80%가 도구 I/O (Tool I/O)였습니다. 파일 읽기 결과, Bash 명령 출력, grep 결과 등—AI가 그 즉시 사용하고, 결정을 내린 뒤, 다음 단계로 넘어갈 때쯤이면 이미 역할을 마친 데이터들이었습니다.
하지만 그 데이터들은 컨텍스트 윈도우 (Context Window) 안에 영원히 머물며, 매 턴마다 토큰을 잡아먹고 있습니다.
50턴의 세션 동안, 맨 처음에 수행한 grep 결과는 다시는 볼 일이 없음에도 불구하고 여전히 컨텍스트 (Context) 안에 남아 있습니다.
/compact의 모순
여러분은 "그냥 /compact를 쓰면 되지 않나요?"라고 생각할지도 모릅니다. 저도 똑같이 생각했습니다.
하지만 /compact의 메커니즘은 AI가 전체 히스토리를 읽고 이를 요약하도록 만드는 것입니다.
토큰을 아끼기 위해 엄청난 양의 토큰을 소비하게 됩니다. 게다가 요약 과정에서 뉘앙스가 손실됩니다. "당시에 왜 이 디자인을 선택했는가"와 같은 컨텍스트는 반올림되어 사라질 수 있습니다.
요약 후에 작업을 계속하면 다시 컨텍스트가 부풀어 오르고, 그러면 다시 컴팩트(compact)를 수행하는... 반복적인 사이클이 발생합니다. 이는 근본적인 해결책이 아닙니다.
시간이 아닌 유형별로 분류하기
여기서 저는 관점을 바꾸었습니다.
MemGPT와 LangChain의 SummaryBufferMemory는 가장 오래된 데이터부터 요약합니다. 이는 시간 기반 압축 (Time-based compression)입니다. 하지만 문제는 "오래됨"이 아닙니다.
10턴 전의 "이 디자인의 이유"는 오늘날에도 여전히 가치가 있습니다. 방금 전의 grep 결과는 단 한 턴 전이라 할지라도 쓸모가 없습니다.
시간 대신, 유형별로 분류해야 합니다.
- 대화 본문 (사용자가 작성한 내용, AI의 답변) → 유지 (Keep)
- 도구 I/O (파일 내용, 명령 결과) → 제거 (Evict)
이 아이디어를 바탕으로 Throughline을 만들었습니다.
3계층 모델 (3-Layer Model)
Throughline은 대화를 세 가지 계층으로 나누어 SQLite에 저장합니다.
L1 (Skeleton, 골격) — 오래된 턴들에 대한 한 줄 요약. 경량 모델 (Lightweight model)에 의해 생성됩니다. 턴당 약 10토큰 정도를 사용합니다.
L2 (Body, 본문) — 최근 20턴의 대화 본문. 사용자 메시지와 AI 응답은 있는 그대로 유지됩니다. 압축 없이 손실 없는 (Lossless) 방식입니다.
L3 (Detail, 상세) — 도구 I/O, 시스템 메시지. SQLite로 제거(Evict)되며 컨텍스트에는 절대 유지되지 않습니다. 필요할 때 AI가 SQLite에서 직접 이를 가져옵니다.
/clear를 실행해도 안전합니다. SQLite 데이터베이스가 사라지지 않기 때문에, 다음 세션이 시작될 때 단일 트랜잭션(Single transaction) 내에서 이전 세션의 메모리를 상속받습니다. PID를 추적하거나 시간 창(Time windows)을 기준으로 판단할 필요가 없습니다. 확실하게 작동합니다.
수치상으로는 다음과 같습니다:
Throughline 미사용 시 (50턴, /clear 미사용):
컨텍스트 ≈ 125,000 토큰 (80%가 완료된 도구 I/O)
...
약 90%의 감소입니다.
실패한 설계에 대한 노트
처음부터 이런 형태였던 것은 아닙니다.
초기 설계에서 저는 L2를 "중요한 결정 사항의 구조화된 추출 (Structured extraction)"로 만들려고 시도했습니다. [DECISION] WebSocket 채택, [CONSTRAINT] 8080 포트 사용 불가와 같은 태그를 사용하여 대화에서 중요한 정보만 추출하는 것을 상상했습니다.
이론적으로는 아름다웠지만, 구현 후에 한 가지를 깨달았습니다: AI가 미래에 무엇을 필요로 할지 예측할 수 없다는 것입니다.
분류기(Classifier)가 "중요하지 않다"고 판단한 정보가 10턴 후에 필요해질 수도 있습니다. 그리고 당신은 그 정보가 사라졌다는 사실조차 알아차리지 못할 것입니다. 80%의 정확도는 나머지 20%가 보이지 않게 된다는 것을 의미합니다.
결국, 저는 L2가 대화의 전체 텍스트를 유지하는 방식으로 결론을 내렸습니다. 오직 차감만 하는 설계(Subtraction-only design)입니다. 저는 원래의 Claude Code 컨텍스트에서 도구 I/O(Tool I/O)만 제거합니다. 이렇게 하면 원칙적으로 "Throughline으로 인한 품질 저하"가 불가능해집니다.
세션 간의 상속 또한 초기에는 10초 이내에 /clear를 감지하려고 시도하는 파일 기반 방식이었으나, 이는 병렬 세션(Parallel sessions) 환경에서 문제가 발생했습니다. 결국, 단일 SQLite UPDATE 방식으로 정착되었습니다. 단순한 것이 더 견고합니다.
요약 비용이 거의 제로에 가까운 이유
L1 요약은 Haiku 4.5를 사용하여 생성되지만, 여기에는 요령이 있습니다.
지난 86번의 세션을 분석한 결과, 턴(Turn) 수의 중앙값은 13회였습니다. 세션의 절반 이상이 20턴 이내에 종료됩니다.
Throughline은 20턴까지의 L2(Level 2)를 유지하므로, 짧은 세션에서는 요약 모델(summarization model)이 절대 실행되지 않습니다. 요약은 21번째 턴부터만 필요합니다. 그리고 그 경우에도 한 번에 한 턴씩 지연 처리(lazily) 방식으로 진행합니다.
다시 말해, 요약 프로세스 자체의 토큰 소비량은 거의 제로에 가깝습니다. "아끼기 위해 엄청난 양을 소비하는" /compact 명령의 모순은 발생하지 않습니다.
보너스: 토큰 모니터 (Token Monitor)
개발 과정에서 부산물로, 멀티 세션이 가능한 토큰 모니터도 제작되었습니다.
▶ Throughline 2ed5039c ████░░░░░░░░░░░░░░░░ 205.1k / 21% Remaining 794.9k claude-opus-4-6
이 도구는 트랜스크립트(transcript)의 JSONL 파일에서 API 실제 값(message.usage)을 읽어오기 때문에, "글자 수 ÷ 4"와 같은 대략적인 추정치가 아닌 정확한 값을 제공합니다. 또한 1M 컨텍스트 제한(context limits)을 자동으로 감지합니다.
여러 세션을 실행할 때 각 세션이 얼마나 소비하고 있는지 실시간으로 확인할 수 있습니다. 은근히 편리합니다.
요약
할당량이 3일 만에 녹아내리는 문제의 실체는 컨텍스트 윈도우(context window)의 87%를 차지하고 있던 대화 기록이었습니다. 그중 대부분은 도구 I/O(tool I/O)에서 발생한 잔해였습니다.
CLAUDE.md를 최적화하거나 프롬프트(prompt)를 줄이는 것은 전체의 9%에만 영향을 미치는 조치이므로, 안 하는 것보다는 낫습니다. 하지만 그것이 주요 문제는 아니었습니다.
아마도 이런 종류의 문제는 플랫폼 측에서 해결해야 할 문제일 것입니다. 하지만 저는 당장 해결이 급했기에 직접 만들었습니다. Node.js 22.5+ 기반이며, 의존성(dependencies)이 없고, MIT 라이선스입니다. MAX 계약을 사용 중이라면 작동합니다.
혹시 다른 분들도 같은 문제로 어려움을 겪고 계신다면, 여유가 될 때 한 번 살펴보시기 바랍니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기