Codex가 컴팩션 문제를 다루는 방법
Claude Code와 비교해 Codex가 컨텍스트 오버플로를 어떻게 처리하는지 역분석했습니다. AES 암호화, 세션 핸드오버 패턴, KV 캐시 활용 방식이 핵심입니다.
Claude Code로 본격적인 코딩 세션을 진행해 보셨다면 한 번쯤은 목격하셨을 겁니다. 터미널에 “Compacting conversation…”이 뜨는 순간, 무언가 이상해지기 시작합니다. 10분 전에 나눴던 내용을 모델이 기억하지 못하고, 응답 속도는 느려지며, 방금 함께 리팩터링한 함수에 대해 물어보면 처음 듣는 듯이 대답합니다.
이런 현상이 생기는 이유는 Claude Code의 200K 토큰 컨텍스트 창이 생각보다 훨씬 빨리 채워지기 때문입니다. 대규모 리팩터링 세션 하나, 파일 읽기 몇 번, 출력이 긴 툴 호출 몇 개만으로도 한계에 도달합니다. 임계치(대략 창의 75~92%, 경우에 따라 65%에서 발동하기도 합니다)에 도달하면 Claude Code는 대화를 요약하고 원본 메시지를 버린 뒤 요약본만 남긴 채 계속 진행합니다. 요약에 포함되지 못한 정보는 그대로 사라집니다.
OpenAI의 Codex는 이 문제를 다르게 처리한다는 이야기를 계속 들었습니다. 그래서 관련 공개 분석 자료를 최대한 모아 직접 살펴봤습니다. 가장 흥미로운 작업은 Krafton의 CAIO인 Kangwook Lee가 프롬프트 인젝션을 이용해 실제 파이프라인을 역분석한 내용이었습니다.
컴팩션이 잃는 것들
핵심 문제는 단순합니다. 요약은 손실 압축입니다. Claude Code가 컴팩션을 실행하면 전체 대화를 백그라운드에서 요약한 뒤 컴팩션 블록을 생성하고, 그 이전의 모든 내용을 버립니다. CLAUDE.md 파일은 디스크에서 다시 읽히기 때문에 살아남지만, 대화 중에만 언급된 내용은 요약에 담기지 않는 이상 사라집니다.
가장 피해가 큰 부분은 툴 호출 결과입니다. Claude Code에게 파일을 읽으라고 요청하면 파일 전체 내용이 컨텍스트에 들어옵니다. 명령어를 실행하면 전체 출력이 컨텍스트에 들어옵니다. 이런 툴 결과는 대화에서 가장 정보 밀도가 높은 부분인데, 요약 과정에서 정확히 이 내용이 평탄화됩니다. 500줄짜리 파일 읽기가 “설정 파일을 읽어 데이터베이스 설정을 확인했습니다” 한 문장으로 줄어듭니다. 구체적인 값, 논의했던 엣지 케이스, 참조했던 줄 번호는 모두 사라집니다.
이 현상을 수십 번 직접 경험했습니다. 컴팩션 이후에 “아까 살펴본 헬퍼 함수의 반환 타입이 뭐였죠?”라고 물으면 자신 있게 틀린 답을 돌려줍니다. 통상적인 의미의 환각이 아닙니다. 모델은 해당 내용이 실제로 없는 요약본을 바탕으로 대답하고 있을 뿐입니다.
긴 세션에서 컴팩션이 9회 이상 발생하면 문제는 누적됩니다. 각 요약이 이전 요약을 더 압축합니다. 세션 초반의 의사결정 근거는 완전히 사라집니다. 10시간짜리 세션의 끝에서는, 20분을 들여 트레이드오프를 논의하며 접근 방식 A를 선택했더라도 그 이유를 모델이 전혀 기억하지 못합니다.
Codex의 암호화된 컴팩션 파이프라인
Kangwook Lee의 분석은 독창적이었습니다. 그는 두 개의 연쇄 프롬프트 인젝션을 사용해 Codex 컴팩션 시스템의 내부 동작을 추출했습니다.
첫 번째 인젝션은 컴팩터 LLM 자체를 대상으로 했습니다. Codex가 컴팩션을 트리거하면 로컬에서 요약하는 것이 아니라 OpenAI 서버의 별도 LLM에 대화를 전송하고 요약을 생성합니다. Lee의 인젝션은 이 컴팩터가 자신의 시스템 프롬프트를 요약 결과물에 포함하도록 속였습니다. 서버는 이 요약(유출된 프롬프트가 포함된)을 AES로 암호화해 불투명한 blob 형태로 반환했습니다.
두 번째 인젝션은 복호화 단계를 활용했습니다. 암호화된 blob과 조작된 사용자 메시지를 Responses API에 다시 전달하자 서버가 blob을 복호화하고 모델의 컨텍스트를 조합했습니다. 첫 번째 인젝션이 컴팩터의 시스템 프롬프트를 요약 안에 심어놨기 때문에, 복호화된 컨텍스트를 통해 파이프라인 전체 동작 방식이 드러났습니다.
밝혀진 내용은 다음과 같습니다. Codex의 compact() API를 호출하면 별도의 LLM이 대화를 요약하고, 결과는 AES로 암호화되어 반환됩니다. 다음 턴에서 서버는 이 blob을 복호화하고 “이전 대화 요약입니다”라는 핸드오프 프롬프트를 앞에 붙인 뒤 전체를 모델에 공급합니다. 암호화 키는 OpenAI 서버에 있습니다. 클라이언트는 평문 요약을 볼 수 없습니다.
컴팩션 프롬프트 자체는 비 Codex 모델용 오픈소스 Codex CLI의 컴팩션 템플릿과 거의 동일한 것으로 밝혀졌습니다. 프롬프트 엔지니어링에 특별한 비법은 없었습니다. 흥미로운 부분은 아키텍처입니다. 서버 측에서 요약을 암호화하고 복호화해 주입하며, 클라이언트는 내용을 확인하거나 수정할 수 없는 불투명한 blob을 주고받을 뿐입니다.
왜 암호화할까요? Lee의 분석도 이에 대한 결정적인 답을 내놓지 못했습니다. 한 가지 가설은 암호화된 blob에 텍스트 요약 이상의 내용이 담겨 있다는 것입니다. 툴 호출 복원 데이터, 내부 상태 마커, 혹은 OpenAI가 노출하고 싶지 않은 구조화된 메타데이터가 포함되어 있을 수 있습니다. 또 다른 가능성은 단순히 사용자가 요약을 조작해 모델 동작을 변경하는 것을 막기 위한 것이라는 설명입니다. 개인적으로는 후자가 더 그럴듯하다고 생각하지만, 둘 다 확인된 사실은 아닙니다.
OpenAI는 Responses API를 통해 서버 측에서도 이 기능을 지원합니다. compact_threshold 값을 설정하면 토큰 수가 임계치를 넘을 때 서버가 인라인으로 컴팩션을 실행합니다. 컴팩션 항목이 응답 스트림 안으로 돌아오고, 이후 요청에 추가하면 됩니다. 가장 최근 컴팩션 항목 이전의 항목들은 안전하게 삭제할 수 있습니다.
Claude Code의 접근 방식과 비교하면, 컴팩션 블록은 사람이 읽을 수 있습니다. 내용을 직접 확인할 수 있고, instructions 파라미터나 CLAUDE.md에 커스텀 컴팩션 지시문을 추가해 동작을 조정할 수 있습니다. 더 투명하지만, 정보 손실이라는 근본적인 문제는 동일합니다.
세션 핸드오버 패턴
컴팩션 메커니즘을 차치하고, 더 흥미로운 문제는 새 세션을 시작하면서도 컨텍스트를 잃지 않는 방법입니다. 이 문제에 대한 제 생각을 바꿔놓은 한 개발자의 자동화 방식을 접한 게 바로 여기서였습니다.
패턴은 이렇게 동작합니다. 컴팩션이 트리거되기 직전, pre-compact 훅이 모든 쓰기 툴을 차단합니다. 이는 모델이 부분적으로 인식하는 상태에서 코드를 변경하는 것을 막기 위한 조치입니다. 제가 여러 번 겪은 실패 패턴인데, 컴팩션이 리팩터링 도중 발동하면 모델이 어떤 파일을 이미 변경했는지 파악하지 못하고 충돌하는 수정을 가합니다.
쓰기가 차단된 상태에서 시스템은 JSONL 세션 로그에서 사용자 메시지와 thinking 블록만 추출합니다. 툴 호출, 파일 내용, 어시스턴트 응답은 모두 버립니다. 이로써 로그가 원래 크기의 약 2%로 줄어듭니다.
그다음 세 개의 서브 에이전트가 병렬로 실행되며 원본 비압축 JSONL 로그에서 추출 과정이 놓친 정보를 탐색합니다. 찾는 것은 빠진 부분입니다. 사용자 메시지에 담기지 않은 채 논의된 아키텍처 결정, 툴 출력에만 나타난 오류 패턴, 기각된 접근 방식의 근거 등입니다. 이 에이전트들은 세션 요약, 빠진 부분 분석 결과, 수정된 파일 목록을 담은 resume-prompt.md 파일로 결과를 정리합니다.
VS Code 파일 워처가 새로운 resume-prompt.md를 감지해 이를 초기 컨텍스트로 불러오는 새 세션을 엽니다. 새 세션은 이전 세션이 마무리된 시점을 명확하고 완전하게 파악한 채로 시작됩니다.
보고된 개선치는 빌드 효율 10배 향상이었습니다. 이 수치를 독립적으로 검증하기는 어렵지만, 아키텍처 자체는 타당합니다. 점점 열화되는 하나의 요약 대신, 빠진 부분까지 보완된 핸드오버 문서와 함께 새로운 컨텍스트 창으로 시작하는 방식입니다.
저도 단순화된 버전을 직접 구현해 봤습니다. 가치가 집중되는 단계는 빠진 부분 분석입니다. 이 단계 없이는 컴팩션이 이미 하는 것과 다를 바 없이 형식만 다른 요약에 불과합니다. 이 단계가 있으면 요약 과정에서 손실된 정보를 능동적으로 복원할 수 있습니다. 저는 세 개 대신 서브 에이전트 하나를 사용했는데, 결과는 순수한 컴팩션보다 눈에 띄게 낫지만 세 에이전트 방식만큼 철저하지는 않을 것입니다.
숨겨진 비용 레버, KV 캐시
대부분의 논의에서 완전히 빠뜨리는 성능 측면이 있습니다. KV 캐시(어텐션 연산 중 계산된 키-값 쌍)는 프롬프트 접두사가 동일할 때 요청 간에 재사용될 수 있습니다. 동일한 시작 토큰을 공유하는 두 요청은 해당 토큰의 재계산을 건너뜁니다.
수치가 상당합니다. 안정적인 시스템 프롬프트와 변경된 시스템 프롬프트를 비교한 통제 테스트에서, 안정적인 접두사는 캐시 히트율 85%에 중앙값 첫 토큰 응답 시간(TTFT) 953ms를 기록했습니다. 변경된 접두사는 캐시 히트율 0%, TTFT 2,727ms였습니다. 요청당 비용은 $0.033에서 $0.009로 줄었습니다. 프롬프트 접두사를 일관되게 유지하는 것만으로 레이턴시 65%, 비용 71%를 절감할 수 있습니다.
이는 세션 핸드오버 패턴에 직접적인 시사점을 갖습니다. resume-prompt.md가 항상 동일한 구조적 접두사(시스템 프롬프트, 핸드오프 지시문, 그다음 가변 콘텐츠)로 시작하면 고정 부분이 캐시됩니다. 새 세션의 이후 모든 요청이 그 캐시의 혜택을 받습니다. 접두사 구조를 무작위로 구성하거나 초반에 가변 콘텐츠를 주입하면 모든 요청이 처음부터 재계산됩니다.
저는 이 통찰을 바탕으로 세션 폴더 구조를 설계했습니다. 세션 ID 기반 아카이빙으로 핸드오버 문서를 체계적으로 관리하고, resume 프롬프트에 고정 접두사 규칙을 적용해 새 세션의 처음 40~50K 토큰이 KV 캐시에 히트하도록 했습니다. QMD(별도로 다룬 툴)로 세션 아카이브를 사전 인덱싱해두면 서브 에이전트가 과거 세션을 검색할 때 속도가 빨라집니다.
실제로 중요한 것
진짜 교훈은 Codex의 방식이 Claude Code보다 낫거나 못하다는 것이 아닙니다. 둘 다 컴팩션 중에 정보를 잃습니다. 둘 다 긴 세션에 어려움을 겪습니다. 아키텍처 차이(암호화된 불투명 blob 대 사람이 읽을 수 있는 컴팩션 블록)는 서로 다른 설계 철학을 반영하지만, 근본적인 한계는 동일합니다. 컨텍스트 창은 유한하고, 요약은 손실적입니다.
중요한 것은 그 한계 위에 무엇을 구축하느냐입니다. 세션 핸드오버 패턴, 빠진 부분 분석, JSONL 기반 검색, KV 캐시 최적화는 모델 개선만으로는 완전히 해결할 수 없는 문제에 대한 공학적 해결책입니다. 500K 또는 1M 토큰 컨텍스트 창은 문제를 미룰 뿐, 없애지는 않습니다.
AI 코딩 툴의 병목은 모델 지능이 아닙니다. 컨텍스트 관리입니다. 잊혀진 정보를 안정적으로 복원하는 시스템을 구축하는 것이, 더 정확하게 요약하는 시스템을 구축하는 것보다 가치 있습니다. 직접 경험으로 확인한 사실입니다. 검색 능력이 좋은 평범한 요약이 검색 없는 훌륭한 요약을 항상 이깁니다.
기술적 세부 사항의 출처: Kangwook Lee의 분석, OpenAI와 Anthropic의 공개 API 문서.
뉴스레터 구독하기
최신 프로젝트, 아티클, AI와 웹 개발 실험에 대한 소식을 받아보세요.