가장 손쉬운 절감 지점 — 프롬프트 캐시

claudeprompt-cachingcost-optimization

안녕하세요! 재니미입니다.

지난 글에서 Anthropic이 정리한 비용·성능 최적화 세 축을 소개했습니다. 이번 편은 그중 가장 손쉬운 지점인 프롬프트 캐시부터 보겠습니다.

캐시는 자주 쓰는 데이터나 값을 미리 복사해 두는 임시 저장 공간으로, 한 번 처리한 내용을 저장해뒀다가 똑같은 요청이 다시 오면 처음부터 다시 처리하지 않고 저장해둔 걸 그대로 재사용해서 토큰 소모를 줄입니다. 똑같은 요청이 아니면, 토시 하나라도 틀리면 캐시를 사용할 수 없습니다. 예를 들어 시스템 프롬프트에 타임스탬프 하나만 끼어 있어도, 도구 정의 순서 하나만 바뀌어도 그 지점부터 캐시가 깨집니다.

이 글은 Anthropic 공식 글 Reducing cost and improving performance with the Claude Platform을 읽고 정리한 것입니다. 벤치마크 수치는 전부 원문 출처이며, 별도 표기 없는 한 직접 재현한 결과가 아닙니다.

원리

모델은 답을 생성하기 전에 받은 프롬프트 전체를 먼저 한 번 훑어서 내부 상태로 바꾸는 과정을 거치는데, 이 단계를 prefill이라고 부릅니다. 입력을 처리하는 과정에서 비용이 가장 많이 드는 부분이 바로 이 prefill입니다. 프롬프트 캐시는 이 prefill 단계에서 만든 상태(KV 캐시)를 저장해두고, 같은 입력이 다시 오면 그 상태를 재사용합니다. 캐시 읽기는 전체 입력을 새로 처리하는 것보다 훨씬 싸게 청구됩니다. 조건은 하나, 완전히 동일한 바이트여야 한다는 것입니다. 이렇게 캐시가 실제로 재사용되는 경우를 히트(hit), 바이트가 하나라도 달라서 재사용되지 못하고 처음부터 다시 처리하는 경우를 미스(miss)라고 부릅니다.

캐시를 깨는 흔한 실수

  • 시스템 프롬프트에 타임스탬프·요청 ID 같은 동적 값을 끼워 넣습니다. 예를 들어 시스템 프롬프트 맨 위에 “현재 시각: 2026-09-14 21:30:00”처럼 매번 바뀌는 값을 넣으면, 뒤에 오는 내용이 전부 똑같아도 이 한 줄 때문에 시스템 프롬프트 전체가 매번 다른 바이트가 되어 캐시가 히트하지 않습니다.
  • 도구 정의 순서를 매 요청 바꿉니다. 첫 요청에서 도구를 [검색, 파일 읽기, 파일 쓰기] 순서로 보냈다가 다음 요청에서 [파일 읽기, 검색, 파일 쓰기]처럼 순서만 바꾸면, 도구 하나하나의 내용은 같아도 순서가 달라져 캐시가 깨집니다.
  • 대화를 분기시키면서 모델이나 effort를 슬쩍 바꿉니다. 세션 초반엔 Opus로 시작했다가 중간에 비용을 아끼려고 Sonnet으로 바꾸면, 그 지점 이후의 캐시는 이전 모델 기준으로 쌓인 것이라 재사용할 수 없습니다.

캐시를 살리는 여섯 가지 방법

  1. 프롬프트 구조를 정적 → 동적 순서로 고정합니다. 도구·시스템 프롬프트처럼 안 변하는 걸 앞에, 대화처럼 매번 바뀌는 걸 뒤에 둡니다.
  2. 드물게 쓰는 도구는 defer_loading으로 캐시 밖으로 뺍니다. 도구 정의에 이 표시를 붙이면 그 도구는 캐시되는 프롬프트 앞부분에서 빠지고, 실제로 그 도구를 써야 할 때만 따로 불러옵니다. 안 쓰는 도구까지 매번 캐시 대상에 끼워 넣지 않아도 됩니다.
  3. 시스템 프롬프트를 직접 고치는 대신 mid-conversation 메시지로 지시를 추가합니다. 시스템 프롬프트 자체는 그대로 두고, 대화 중간에 새 메시지 하나를 끼워 넣어 지시를 전달하는 방식입니다. 시스템 프롬프트를 고치면 그 위의 캐시가 통째로 깨지지만, 이 방식은 캐시를 건드리지 않습니다.
  4. 캐시 재작성이 필요한 시점에 모델·effort 변경을 몰아서 합니다. 예를 들어 대화가 길어져서 그동안의 내용을 요약해 컨텍스트를 줄이는 작업(compaction)을 하면 캐시가 어차피 새로 쌓여야 하는데, 이런 시점에 맞춰서 모델이나 effort도 같이 바꾸면 캐시가 깨지는 횟수를 줄일 수 있습니다. 흩어놓으면 그때마다 따로 깨집니다.
  5. 장시간 세션은 TTL(캐시가 살아있는 시간)을 5분에서 1시간으로 늘립니다. 기본값 5분은 짧은 왕복에만 맞습니다.
  6. max_tokens: 0과 함께, 프롬프트에서 어디까지를 캐시할지 표시하는 캐시 브레이크포인트를 명시해서 요청을 보냅니다. 이러면 실제 응답은 생성하지 않고 그 지점까지만 캐시를 미리 채워둘 수 있습니다.

다른 모델에서는

캐시 방식 자체는 서비스마다 다릅니다. 이 부분은 나중에 따로 다뤄보겠습니다.

캐시 미스 진단

Claude Console이나, 요청마다 캐시가 얼마나 적중했는지 알려주는 cache diagnostics API로 요청 간 프롬프트 차이를 찾을 수 있습니다. “왜 캐시가 안 먹지”의 답은 대부분 위 여섯 가지 중 하나입니다.

다음 편은 캐시가 아니라 프롬프트 자체에 낀 군더더기, 구모델 시대의 안티패턴 이야기입니다.

감사합니다.


참고 자료