프로덕션의 Anthropic 프롬프트 캐싱: 실제 절약 효과와 함정
- Claude
- Prompt Caching
- Performance
- TypeScript
- Anthropic
- Cost
4월 Anthropic 청구서를 열어봤다. 3월 대비 3배. 사용량이 증가했으니 당연한 면도 있지만, 자세히 들여다보니 비용의 60%가 피할 수 있는 것이었다. 원인은 8,000 토큰짜리 system prompt를 캐시 없이 매 에이전트 호출마다 반복 전송한 것. 페이로드를 세 군데 손보니, 다음 달 청구서가 40% 줄었다.
Anthropic의 프롬프트 캐싱(prompt caching)은 출시된 지 거의 2년이 됐다. 그런데도 프로덕션 통합 사례를 보면 여전히 활성화하지 않은 곳이 많다. 익숙하지 않거나, 이득이 잘못 이해되어서다. 클라이언트 프로젝트 6-7건에 적용한 후 정리한 내용이다.
30초 요약
프롬프트의 특정 세그먼트에 cache_control: { type: "ephemeral" } 속성을 붙인다. Anthropic은 그 세그먼트들을 서버 측 임시 캐시에 저장한다. 다음 호출 때 cache_control 이전의 모든 토큰이 정확히 일치하면, 그 세그먼트들은 재처리 없이 캐시에서 읽힌다.
비용: 첫 쓰기는 일반 입력 대비 +25%. 캐시 읽기는 일반 입력의 10%. 기본 TTL은 5분 (옵션으로 1시간 연장 가능). 캐싱 가능한 최소 크기는 Sonnet/Opus 1,024 토큰, Haiku 2,048 토큰.
계산은 간단하다. 5,000 토큰 세그먼트를 5분 안에 4번 재생하면, 일반 비용 4배가 아니라 1.25 + 0.10 × 3 = 1.55배만 낸다. -61%.
캐싱이 효과적인 경우
- 길고 안정적인 system prompt (2,000 토큰 이상): tone of voice, 작업 지침, few-shot 예시. 가장 수익성 높은 케이스.
- 에이전트의 도구(tool) 정의: 10-20개의 tool = 3,000-8,000 토큰의 JSON schema, 매 루프 턴마다 동일. 막대한 절약.
- 고정된 RAG 컨텍스트: 법률 문서, 약관, 사내 정책. 매 사용자 요청마다 동일한 50개 청크를 보낸다면 캐시할 것.
- 반복적 에이전트 워크플로: 같은 짐을 안고 도는 10번의 tool call 루프 = 캐시 읽기 9번, 각 10%.
도움이 안 되는 (또는 더 비싼) 경우
- 짧은 세션: 사용자당 단일 호출, 재사용 없음. 25% 쓰기 추가 비용만 내고 읽을 일이 없다.
- 1,024 토큰 미만: 임계치 아래에서는 cache_control이 조용히 무시된다. API는 경고하지 않는다.
- 앞부분에 동적 변수: 캐시 영역 이전의 변수가 하나라도 바뀌면 전체 prefix가 무효화된다. 동적 세그먼트는 캐시된 부분 뒤에 둘 것, 절대 앞에 두지 말 것.
- TTL 초과: 5분 동안 호출이 없으면 캐시가 비워진다. 사용자가 다시 오면 괜찮지만, 트래픽이 산발적이면 계산이 무너진다.
작동하는 패턴
순서대로 4개 세그먼트:
- System rule (거의 변하지 않음) → cache_control
- Tool 정의 (릴리스마다 변경) → cache_control
- 고정 컨텍스트 (RAG, 사용자 프로필, 세션 데이터) → 1,024 토큰 초과 시 cache_control
- User turn (호출마다 변경) → cache_control 없음
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
system: [
{ type: "text", text: SYSTEM_RULE, cache_control: { type: "ephemeral" } },
{ type: "text", text: TOOL_INSTRUCTIONS, cache_control: { type: "ephemeral" } },
{ type: "text", text: USER_PROFILE, cache_control: { type: "ephemeral" } },
],
tools: TOOLS,
messages: [{ role: "user", content: userInput }],
});
console.log(response.usage);
// cache_creation_input_tokens vs cache_read_input_tokens
usage.cache_read_input_tokens 필드가 핵심이다. 모니터링하지 않으면 캐시가 hit 됐는지 알 수 없다. 캐싱은 활성화돼 있지만 실제로는 한 번도 사용되지 않은 프로젝트를 셋이나 봤다 — system prompt에 매번 한 글자 차이(타임스탬프, UUID)가 들어가 매번 무효화되고 있었다.
프로덕션의 함정
- 보이지 않는 디버깅: cache miss는 에러를 발생시키지 않는다. 청구서가 늘어나는 걸 지켜볼 뿐. 매 호출마다
cache_read_input_tokens를 로그하고, 비율이 떨어지면 알림을 띄울 것. - Hit rate 측정: 캐싱을 켜기 전에 프로덕션 호출 하루치를 캡처한다. 재사용 가능했던 prompt가 몇 %인지 센다. 30% 미만이면 캐싱 비용이 절약보다 크다.
- 릴리스가 모든 걸 무효화: system prompt를 수정하는 배포마다 활성 사용자 전원에 대해 전체 write를 다시 낸다. 동시 사용자 1만 명 서비스에서는 따갑다. 피크 시간대 외에 atomic release 권장.
- 멀티 테넌시: 테넌트마다 system prompt가 미세하게 다르면 N개의 별도 캐시가 생긴다. 공통 부분을 cache_control로 분리하고, 테넌트별 부분은 그 뒤에 둘 것.
사용하지 말아야 할 때
하루 100회 미만 호출이라면 캐싱에 시간 쓰지 말 것. 복잡도가 가치보다 크고, 첫 write 추가 비용이 결코 회수되지 않는다. 에이전트가 루프를 돌고 같은 짐이 5번 반복되는 게 보일 때 켤 것 — 그 전에는 아니다.
그 외에는, 프로덕션 LLM 앱에서 노력 대비 가장 큰 효과를 내는 최적화일 것이다.