Claude Code API 비용을 90% 줄이는 캐시 설계 원칙
프로덕션에서 캐시가 깨지니 API 비용이 10배로 뛰었다. 같은 날 Anthropic 엔지니어들이 그 이유를 정확히 설명해줬다.
어제 급한 프로덕션 작업이 있었습니다. 작업 중간에 프롬프트 캐시가 깨졌습니다. 그 한 시간 동안 나간 API 비용이 지난 3일치를 합친 것보다 많았습니다.
타이밍이 묘했습니다. 같은 날 저녁, Claude Code를 만든 Anthropic의 Thariq과 Lance Martin이 나란히 프롬프트 캐싱 설계에 대한 글을 올렸습니다. 읽어보니 내 캐시가 우연히 깨진 게 아니었습니다. 설계부터 깨지기 쉬운 구조였습니다.
두 글에서 뽑아낸 내용을, 당일 겪은 프로덕션 사고의 경험과 함께 정리합니다.
접두사 매칭이라 순서가 전부입니다
Anthropic API의 프롬프트 캐싱은 요청 시작 지점부터 토큰 단위로 순서대로 매칭합니다. 캐시된 버전과 한 글자라도 달라지는 순간, 그 뒤는 전부 캐시 미스가 됩니다. 부분 매칭도 없고, 건너뛰기도 없습니다.
Claude Code 팀은 프롬프트 순서를 인프라처럼 다룹니다. 정적 시스템 프롬프트가 맨 앞에 오고, 그 다음이 CLAUDE.md, 세션 컨텍스트 순입니다. 대화 메시지는 매 턴마다 바뀌기 때문에 맨 뒤에 놓습니다. 이 순서 덕분에 비용이 큰 고정 접두사가 세션 내내 캐시되어 재사용됩니다.
캐시된 토큰은 일반 입력 토큰 비용의 10%만 청구됩니다. 이 차이가 캐시 깨짐이 곧 10배 비용 폭증인 이유입니다.
내 실수는 시스템 프롬프트에 타임스탬프를 넣은 것이었습니다. 매 요청마다 타임스탬프가 새로 생성되니 맨 첫 토큰부터 달라졌습니다. 그 뒤의 어떤 것도 캐시를 탈 수 없었습니다. 디버그 로그 하나를 잘못된 위치에 넣은 대가로, 매 요청 10만 토큰 이상을 전액 결제하고 있었습니다.
Claude Code 팀도 도구 정의의 순서가 비결정적으로 바뀌면 캐시 미스가 발생한다고 밝혔습니다. 도구 자체는 달라진 게 없어도, 직렬화 순서만 바뀌면 그 지점에서 캐시가 끊깁니다.
시스템 프롬프트를 고치지 말고 메시지로 업데이트하라
세션 중에 컨텍스트가 바뀔 때(파일 수정, 시간 변경, 모드 전환) 시스템 프롬프트를 직접 고치고 싶은 충동이 듭니다. 하지 마십시오. 시스템 프롬프트를 한 글자라도 고치면 캐시 접두사 전체가 무효화됩니다.
Claude Code는 첫 요청 이후 시스템 프롬프트를 건드리지 않습니다. 바뀐 컨텍스트는 다음 사용자 메시지에 system-reminder 태그로 감싸서 전달합니다. 모델은 동일하게 읽지만, 캐시 접두사는 그대로 유지됩니다.
Plan Mode가 좋은 예입니다. Plan Mode로 전환하려면 도구 정의를 교체해야 할 것 같지만, 그러면 캐시가 깨집니다. 대신 Claude Code는 Plan Mode를 EnterPlanMode라는 도구 호출로 구현했습니다. 모델이 어려운 문제를 감지하면 스스로 Plan Mode에 진입할 수 있습니다. 도구 세트는 처음부터 끝까지 바뀌지 않습니다.
모델 전환도 같은 논리입니다. 대화 중간에 모델을 바꾸면 캐시가 통째로 깨집니다. Claude Code는 다른 모델을 별도 컨텍스트의 서브에이전트로 실행해서, 부모 대화의 캐시를 보호합니다.
도구가 많으면 빼지 말고 숨겨라
MCP 서버가 수십 개의 도구를 로딩할 수 있습니다. 매 요청에 전부 포함시키면 비용이 큽니다. 하지만 요청 사이에 도구를 빼면 캐시가 깨집니다. 도구 정의도 캐시 접두사의 일부이기 때문입니다.
Claude Code 팀이 찾은 해법은 defer_loading입니다. 전체 도구 스키마 대신, 도구 이름과 defer_loading: true 플래그만 담은 가벼운 스텁을 넣습니다. 스텁은 항상 같은 순서로 유지되어 캐시 접두사를 동일하게 보존합니다. 모델이 실제로 도구의 전체 스키마가 필요할 때는 ToolSearch 도구를 호출해서 그때그때 불러옵니다.
이 패턴은 현재 Anthropic API에서 직접 사용할 수 있습니다. 자체 에이전트에 같은 스텁-앤-서치 방식을 구현하면 됩니다.
Manus의 peakji는 캐시 히트율을 프로덕션 에이전트에서 가장 결정적인 지표라고 말했습니다. 어제 이후로 동의합니다.
컨텍스트 압축에도 캐시 함정이 있습니다
대화가 컨텍스트 윈도우를 가득 채우면 압축이 필요합니다. 히스토리를 요약하고 줄인 형태로 이어가는 것입니다. 보통은 요약 프롬프트로 API를 호출하려 합니다. 하지만 그 요약 호출이 다른 시스템 프롬프트나 다른 도구 정의를 사용하면, 기존 캐시와 전혀 매칭되지 않습니다. 비용이 가장 높은 순간에, 10만 토큰 이상의 전체 대화를 캐시 혜택 없이 처리하게 됩니다.
Claude Code는 압축 호출에도 부모 대화의 시스템 프롬프트와 도구 정의를 그대로 재사용합니다. 바뀌는 것은 마지막 사용자 메시지뿐입니다(압축 지시문으로). 부모 대화의 캐시 접두사가 여전히 매칭되므로, 새 메시지와 요약 출력분만 전액 결제하면 됩니다.
Anthropic은 이 패턴을 API에 컴팩션 기능으로 내장했습니다. 여기에 auto-caching도 출시해서, 요청 본문에 cache_control을 한 번만 설정하면 캐시 브레이크포인트가 자동 적용됩니다.
캐시 히트율은 운영 지표입니다
Claude Code 팀은 캐시 히트율을 서버 가동률처럼 모니터링합니다. 수치가 떨어지면 장애로 선언합니다.
이 관점이 프롬프트 설계에 대한 내 생각을 바꿨습니다. 시스템 프롬프트 수정, 도구 순서 변경, 세션 중 모델 전환, 모두 잠재적 장애 요인입니다. 가장 저렴한 토큰은 캐시를 타는 토큰이고, 어제 나는 그 반대가 얼마나 비싼지 정확히 배웠습니다.
뉴스레터 구독하기
최신 프로젝트, 아티클, AI와 웹 개발 실험에 대한 소식을 받아보세요.