"Claude와 Codex의 세션 이어하기 — 누가 무엇을 재전송하는가"

세션 이어하기는 정말 공짜인가?

--continue 또는 --resume으로 이전 세션을 이어갈 때, 서버가 대화를 기억해두었다가 저렴하게 이어준다고 생각하기 쉽다. Claude Code도, OpenAI Codex CLI도 마찬가지다. 세션이 "이어진다"는 UX는 내부적으로 서버 상태 유지처럼 느껴지지만, 실제로는 다르다.

결론부터 말하면: 두 시스템 모두 서버 stateful이 아니다. 두 툴 모두 로컬에 저장된 대화 기록을 재생(replay)해 서버에 다시 전송하는 구조다. 차이는 그 위에서 작동하는 프롬프트 캐시 정책과, 재생 시 prefix가 얼마나 안정적으로 유지되느냐에 있다. 그리고 Claude Code v2.1.116에서는 resume 시 prefix가 안정적으로 재조합되지 않는 버그가 관측됐다.

Anthropic 프롬프트 캐시 공식 스펙

Anthropic의 프롬프트 캐싱은 opt-in 방식이다. 기본 TTL은 5분이며, 1시간 TTL 옵션을 별도로 설정할 수 있다(추가 비용 발생).

가격 구조는 다음과 같다:

  • cache_write (5분): 기본 입력 토큰 대비 1.25×
  • cache_write (1시간): 기본 입력 토큰 대비 2×
  • cache_read: 기본 입력 토큰 대비 0.1× (90% 할인)

히트율이 높으면 매우 저렴하지만, write 비용이 붙는다는 점이 핵심이다. cache_read는 저렴하지만, cache_write는 1시간 TTL 기준으로 기본 입력의 2배를 지불한다. cache_write가 발생한 뒤 cache_read로 보상받으려면 같은 prefix로 반복 요청이 들어와야 한다.

캐시는 계층적으로 무효화된다: tools → system → messages 순서로, 상위 블록이 변경되면 이하 전체가 무효화된다. lookback window는 20블록이며, 캐시가 실제로 사용되면 TTL이 자동 갱신된다(추가 비용 없음).

출처: Anthropic Prompt Caching 공식 문서

OpenAI 자동 프롬프트 캐시 스펙

OpenAI의 캐시는 opt-in이 필요 없다. 1024 토큰 이상의 요청에 자동으로 적용되며, 캐시 히트 시 50% 할인이 적용된다. TTL은 표준 5~10분이며, 오프피크 시간대에는 최대 1시간까지 유지될 수 있다. 개발자가 별도로 캐시 포인트를 마킹할 필요 없이 자동 prefix 매칭으로 동작한다.

Anthropic과의 주요 차이는 두 가지다. 첫째, cache_write 프리미엄이 없다. 캐시가 구성될 때 추가 비용이 발생하지 않으므로, 히트율이 낮은 경우에도 손해가 없다. 둘째, 할인율은 50%로 Anthropic의 90%(cache_read 기준)보다 낮다. 요약하면 OpenAI는 보수적인 할인폭 대신 리스크 없는 구조이고, Anthropic은 높은 할인폭 대신 write 비용을 먼저 지불하는 구조다.

Codex CLI의 resume 동작 — 통념 교정

Codex CLI의 resume 동작 — 통념 교정

"Codex는 서버가 대화를 기억해서 이어진다"는 통념이 있다. OpenAI의 Responses API가 store: true + previous_response_id를 통해 서버 사이드 대화 상태를 지원하기 때문에 생긴 오해다.

그러나 codex-rs 오픈소스 코드를 직접 확인하면 다른 동작을 볼 수 있다:

  • protocol.rs:2525ResumedHistory { conversation_id, history: Vec<RolloutItem>, rollout_path } 구조체가 로컬 rollout 전체를 재생해 history로 spawn한다. previous_response_id 필드는 없다.
  • thread_manager.rs:531resume_thread_from_rollout 함수가 RolloutRecorder::get_rollout_history(path)를 호출해 로컬 jsonl 파일을 읽어 전체 대화를 재구성한다.
  • client.rs:998previous_response_id는 같은 프로세스의 WebSocket 세션 내부에서만 재사용된다. 프로세스를 재시작하면 연결이 끊어진다.

즉, Codex CLI는 프로세스 재시작 후 resume 시 Responses API의 서버 상태를 활용하지 않는다. 로컬 rollout 재생 + 서버 prefix caching 구조로, Claude Code와 본질적으로 동일하다.

이 구조는 중요한 함의를 가진다. 재개 시 전달되는 토큰 수는 이전 대화 전체에 비례해 늘어나고, prefix가 서버 캐시에 히트하지 못하면 그 비용을 고스란히 부담한다. "이어하기는 서버가 기억해서 공짜"라는 통념은 두 시스템 모두에서 성립하지 않는다.

증거 — TTL-matched 측정 (Claude Code v2.1.116)

Claude Code에서 --resume을 사용했을 때 캐시 히트율이 얼마나 달라지는지를 같은 idle gap 조건에서 비교 측정했다. 같은 idle gap에서 same-session 연속 턴(베이스라인)과 cross-session 재개 턴(테스트)을 대조해 TTL 변수를 수학적으로 제거했다.

샘플: 14개 jsonl, 197턴, 11시간 52분 세션.

구분 4분 gap 28분 gap
Same session (베이스라인) 97~99% hit 99% hit
Cross session (재개) 41.2% hit 0% hit
Δ −56pp −99pp

같은 TTL 조건에서도 세션을 재개한 경우 캐시 히트율이 극단적으로 낮아졌다. Claude Code 내부에서 사용하는 캐시 타입을 확인하면 ephemeral_1h(1시간 캐시)만 사용되고, ephemeral_5m은 0이다. TTL이 넉넉한 상태인데도 히트율이 낮다는 것은 TTL 문제가 아니라 prefix 재조합 문제임을 시사한다.

원인으로 지목된 것은 v2.1.69에서 도입된 deferred_tools_delta 및 attachment reordering이다. resume 시 skill_listing, todo_reminders, nested_memory 주입 순서가 달라지면서 prefix hash가 깨진다. 이 분석은 simpolism의 gist를 통해 확인됐다.

영향 — 실제 quota 소모

위 세션(197턴, 11시간 52분)에서 관측된 수치:

  • cache_creation_input_tokens / output_tokens 비율(cc/output ratio): 4.52× (정상 범위 1~2×)
  • 1시간 TTL 이내 재개 구간에서만 40,260 토큰의 cache_creation 발생. prefix가 보존됐다면 회피 가능했던 비용이다.
  • Claude Pro/Max 구독자의 5시간 rolling quota 중 약 20%가 resume 재생성으로 소모되는 사례가 관측됐다.

cache_creation은 1시간 TTL 기준 기본 입력 대비 2×다. cache_read의 0.1×와 비교하면 20배 비싼 경로다. 캐시가 정상 히트하지 않고 매번 새로 구성된다면, 경제성을 기대하고 사용하던 caching이 오히려 비용을 키우는 상황이 된다.

대응

GitHub Issue #51764(v2.1.116 재현)가 제출된 상태이며, 이전에 닫힌 #42338의 뒤를 잇는 트래킹이다. v2.1.90~92에서 패치된 일부 경로는 커버되었을 수 있으나, 커스텀 에이전트 + MCP + 스킬 + 훅 조합 경로에서는 v2.1.116에서도 재현이 확인된다.

직접 확인 방법: ~/.claude/projects/<slug>/*.jsonl에서 각 턴의 cache_read_input_tokenscache_creation_input_tokens를 집계한다. cc/output ratio가 2×를 넘기 시작하면 prefix 재조합이 반복되고 있을 가능성이 높다. ArkNill의 claude-code-cache-analysis 스크립트를 활용하면 세션별 집계가 수월하다. taekim34는 해당 이슈에서 환경 재현 코멘트를 통해 커스텀 CLAUDE.md + 다단계 훅 조합에서의 재현 경로를 상세히 기록했다.

실무 권장사항: auto-restart watchdog 패턴에서 claude --continue 자동 재개 대신, stop 후 수동 세션 시작으로 전환하는 것을 고려할 수 있다. CLAUDE.md 재로드 비용만 지불하고, prefix는 새로 안정적으로 구축된다. 불안정한 prefix를 반복 재생하는 것보다 예측 가능한 비용 구조가 낫다.

정리

정리

두 시스템의 resume 구조를 정리하면:

  • Claude Code: 로컬 jsonl 재생 + 서버 prefix caching (opt-in, 계층적 무효화)
  • Codex CLI: 로컬 rollout 재생 + 서버 prefix caching (자동, 프리미엄 없음)

서버가 상태를 유지해 "공짜로 이어지는" 시스템은 없다. resume은 항상 전체 대화를 다시 전송하는 비용을 수반한다.

비용 구조 차이: Anthropic은 cache_read 0.1×로 할인 폭이 크지만 cache_write 프리미엄(최대 2×)이 붙는다. OpenAI는 50% 할인이지만 write 프리미엄이 없다. 어느 쪽이 더 저렴한지는 워크로드와 TTL 히트율에 따라 달라지며, 단순 비교보다 실제 히트율 측정이 먼저다.

Claude Code v2.1.116의 resume 버그는 "cache_write 비용을 불필요하게 중복 부담"하는 실재하는 문제다. TTL이 충분한 조건에서도 prefix 재조합 실패로 인해 cache_read 대신 cache_write가 반복 발생한다. 현재 Issue #51764로 트래킹 중이며, 위에 소개한 직접 측정 방법으로 본인 환경에서 재현 여부를 확인할 수 있다.

참조

  • Anthropic Prompt Caching 문서: https://docs.claude.com/en/docs/build-with-claude/prompt-caching
  • Codex 소스: https://github.com/openai/codex
  • Issue #51764: https://github.com/anthropics/claude-code/issues/51764
  • Issue #42338 (closed/locked): https://github.com/anthropics/claude-code/issues/42338
  • Issue #34629, #46829 (관련)
  • ArkNill 캐시 분석: https://github.com/ArkNill/claude-code-cache-analysis
  • simpolism gist: https://gist.github.com/simpolism/302621e661f462f3e78684d96bf307ba
  • taekim34 환경 재현 코멘트: https://github.com/anthropics/claude-code/issues/42338#issuecomment-4174599756

댓글

이 블로그의 인기 게시물

"LLM 핵심 학습 (1/6) — 기본: 토큰화·임베딩·어텐션·위치 인코딩"

"LLM 핵심 학습 (2/6) — 파인튜닝: LoRA·QLoRA·증류·Adapter"

"ML 기초 학습 (1/9) — 머신러닝과 sklearn: 학습의 좌표계"