Claude API 비용 절감 사용법 — Prompt Caching + Pre-warming 5분 세팅
Photo by Alex Shuper on Unsplash
뭘 만들 수 있나요?
Claude API의 Prompt Caching은 프롬프트의 특정 접두사(prefix)를 캐시에 저장해 두고 다음 요청에서 재사용하는 기능입니다. 같은 시스템 프롬프트나 큰 문서를 반복해서 입력 토큰으로 보내는 대신, 한 번 캐시해 두면 그 다음부터는 정가의 10% 비용으로 읽어 쓸 수 있습니다.
여기에 Pre-warming(사전 워밍)을 더하면, 실제 사용자 요청이 들어오기 전에 캐시를 미리 채워 두어 첫 응답의 캐시 미스 페널티까지 제거할 수 있습니다. 결과적으로 비용은 최대 약 83% 절감되고, 첫 응답 레이턴시(TTFT)도 빨라집니다.
이 글에서는 Claude 공식 문서의 Pre-warming 섹션을 기준으로 5분 안에 적용할 수 있는 세팅 방법을 정리했습니다.
준비물
- Anthropic API 키 (ANTHROPIC_API_KEY 환경변수 등록)
- Python 3.8+ 또는 Node.js 18+ (예시는 Python 기준)
anthropicSDK (pip install anthropic)- 캐시할 시스템 프롬프트 또는 도구 정의 (모델별 최소 토큰 요건 충족 필요)
모델별 최소 캐시 가능 토큰
최소값보다 짧은 프롬프트는 cache_control을 붙여도 캐시되지 않고, 오류 없이 그대로 일반 처리됩니다.
Step 1: cache_control로 캐시 중단점 정하기
가장 먼저 할 일은 캐시 중단점을 결정하는 것입니다. 자주 변하지 않는 콘텐츠(시스템 프롬프트, 도구 정의, 대용량 문서)의 마지막 블록에 cache_control을 붙입니다.
import anthropic
client = anthropic.Anthropic()
SYSTEM_PROMPT = [
{
"type": "text",
"text": "You are an expert software engineer with deep knowledge of distributed systems...",
"cache_control": {"type": "ephemeral"},
}
]
"type": "ephemeral"이 캐시를 만든다는 표시입니다. TTL을 명시하지 않으면 기본 5분이 적용됩니다.
자동 캐싱 vs 명시적 중단점
활성화 방식은 두 가지입니다.
자동 캐싱 (다중 턴 대화 권장):
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=1024,
cache_control={"type": "ephemeral"},
system="You are a helpful assistant...",
messages=[...],
)
요청 최상위에 cache_control을 한 번 넣으면, 시스템이 알아서 마지막 캐시 가능 블록에 중단점을 적용합니다. 대화가 길어지면 중단점이 앞으로 이동합니다.
명시적 중단점 (세밀한 제어):
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=1024,
system=[
{
"type": "text",
"text": "Static system prompt",
"cache_control": {"type": "ephemeral"}
},
{
"type": "text",
"text": "Updated daily context..."
}
],
messages=[...]
)
정적 블록 끝에만 캐시 중단점을 두고, 자주 바뀌는 블록은 그 뒤에 배치합니다. Pre-warming에서는 반드시 명시적 중단점을 사용해야 합니다.
Step 2: max_tokens=0으로 캐시 사전 워밍
이제 핵심입니다. 실제 사용자 요청이 들어오기 전에 캐시를 채워 두는 호출을 보냅니다.
prewarm = client.messages.create(
model="claude-opus-4-7",
max_tokens=0,
system=SYSTEM_PROMPT,
messages=[{"role": "user", "content": "warmup"}],
)
print(prewarm.stop_reason) # "max_tokens"
print(prewarm.content) # []
print(prewarm.usage) # cache_creation_input_tokens > 0 확인
max_tokens=0을 설정하면 Claude는 전체 prefill 단계(프롬프트 읽기 + cache_control 중단점에서 캐시 쓰기)를 수행한 다음, 출력을 생성하지 않고 즉시 반환합니다. 응답은 다음과 같은 모양이 됩니다.
content배열: 빈 배열[]stop_reason:"max_tokens"usage: 완전히 채워진 상태로 반환
비용은 캐시 쓰기 요금만 청구되고 출력 토큰 비용은 0원입니다.
TypeScript도 동일한 패턴
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const prewarm = await client.messages.create({
model: "claude-opus-4-7",
max_tokens: 0,
system: [
{
type: "text",
text: "You are an expert software engineer...",
cache_control: { type: "ephemeral" }
}
],
messages: [{ role: "user", content: "warmup" }]
});
console.log(prewarm.stop_reason); // "max_tokens"
console.log(prewarm.content); // []
cURL로 호출하는 경우
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-opus-4-7",
"max_tokens": 0,
"system": [
{
"type": "text",
"text": "You are an expert software engineer...",
"cache_control": {"type": "ephemeral"}
}
],
"messages": [{"role": "user", "content": "warmup"}]
}'

Step 3: 실제 사용자 요청에서 캐시 히트 확인
Pre-warming이 끝났으면, 같은 시스템 프롬프트로 실제 요청을 보냅니다.
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=1024,
system=SYSTEM_PROMPT, # 워밍과 100% 동일해야 함
messages=[{"role": "user", "content": "How do I implement a binary search tree?"}],
)
usage = response.usage
print(f"Cache read tokens: {usage.cache_read_input_tokens}")
print(f"Cache creation tokens: {usage.cache_creation_input_tokens}")
print(f"Regular input tokens: {usage.input_tokens}")
cache_read_input_tokens가 0보다 크면 캐시 히트입니다. 이 시점부터 시스템 프롬프트는 정가의 10% 비용으로 처리됩니다.
응답 usage 필드 해석
요청 흐름:
┌─────────────────────────────────────────────────┐
│ cache_read_input_tokens │
│ (이미 캐시된 중단점 이전의 토큰) │
├─────────────────────────────────────────────────┤
│ cache_creation_input_tokens │
│ (지금 캐시되는 중단점 이전의 토큰) │
├─────────────────────────────────────────────────┤
│ input_tokens │
│ (캐시 중단점 이후의 토큰 - 캐시 불가) │
└─────────────────────────────────────────────────┘
total_input_tokens = cache_read_input_tokens
+ cache_creation_input_tokens
+ input_tokens
Step 4: TTL 유지로 캐시 계속 따뜻하게 두기
기본 TTL은 5분입니다. 캐시가 사용될 때마다 추가 비용 없이 자동으로 갱신되지만, 5분 동안 아무도 호출하지 않으면 만료됩니다. 트래픽이 띄엄띄엄 들어오는 서비스라면 주기적으로 워밍 호출을 보내는 것이 좋습니다.
import schedule
import time
def warm_cache():
"""5분 TTL을 유지하기 위해 4.5분마다 호출"""
client.messages.create(
model="claude-opus-4-7",
max_tokens=0,
system=SYSTEM_PROMPT,
messages=[{"role": "user", "content": "warmup"}]
)
schedule.every(4.5).minutes.do(warm_cache)
while True:
schedule.run_pending()
time.sleep(30)
1시간 TTL이 더 적합한 경우
ttl: "1h"을 명시하면 1시간 캐시를 쓸 수 있습니다. 쓰기 비용은 5분 캐시의 1.6배(기본 입력의 2배)로 비싸지지만, 다음과 같은 시나리오에서는 더 경제적입니다.
- 프롬프트를 5분보다는 자주, 1시간보다는 드물게 사용하는 경우
- 에이전트 부작업(subtask)이 5분 이상 걸리는 경우
- 사용자가 5분 이상 대화 간격을 두는 긴 채팅 세션
- 레이트 리밋 사용량을 줄이고 싶은 경우 (캐시 히트는 레이트 리밋에서 차감되지 않음)
SYSTEM_PROMPT_1H = [
{
"type": "text",
"text": "...",
"cache_control": {"type": "ephemeral", "ttl": "1h"}
}
]
5분과 1시간 캐시를 같은 요청에 섞을 수도 있습니다. 단, 1시간 캐시 블록이 5분 캐시 블록보다 먼저 와야 합니다.
결과 — 얼마나 절약되나
Claude Opus 4.7 기준으로 100,000 토큰짜리 시스템 프롬프트를 사용하는 법률 문서 분석 도구를 예로 들어봅시다.
가격 표를 다시 보면 비율이 명확합니다.
즉 같은 콘텐츠를 두 번 이상 재사용한다면 거의 항상 캐싱이 이득입니다.
⚠️ 주의할 점
Pre-warming은 강력하지만 함정도 분명합니다. 공식 문서가 명시한 제약과 흔한 실수를 정리했습니다.
1) cache_control을 플레이스홀더 메시지에 두지 말 것
가장 흔한 실수입니다. messages 배열의 워밍용 사용자 메시지에 cache_control을 붙이면 캐시 항목이 그 플레이스홀더 메시지에 키가 잡혀, 실제 사용자 요청은 캐시 히트를 받지 못합니다.
# ❌ 나쁨: 플레이스홀더 메시지에 cache_control
messages=[
{
"role": "user",
"content": "warmup",
"cache_control": {"type": "ephemeral"}
}
]
# ✅ 좋음: 후속 요청과 공유되는 시스템 프롬프트에 cache_control
system=[
{
"type": "text",
"text": "...",
"cache_control": {"type": "ephemeral"}
}
]
2) max_tokens=0이 거부되는 조건
다음 옵션을 함께 쓰면 워밍 호출이 거부됩니다.
stream=Truethinking={"type": "enabled"}(확장 사고)output_config={"format": "json"}(구조화 출력)tool_choice={"type": "tool", "name": "..."}- 배치 API 내부에서의
max_tokens=0
3) 캐시 히트는 100% 정확히 일치해야 함
tool_choice, 이미지 첨부 여부, 시스템 프롬프트 한 글자만 달라져도 캐시 미스가 됩니다. 워밍 호출과 실제 호출의 SYSTEM_PROMPT를 같은 변수에서 가져오게 하는 것이 안전합니다.
4) 캐시 무효화 계층
tools → system → messages 순서로 위쪽이 바뀌면 그 아래 캐시도 무효화됩니다.
5) Lookback 윈도우 20블록
캐시 시스템은 마지막 중단점부터 최대 20개 블록을 역순으로 확인합니다. 대화가 20블록 이상으로 커지면 중간에 두 번째 중단점을 두는 것이 안전합니다.
if len(messages) > 20:
messages[10]["cache_control"] = {"type": "ephemeral"}
자주 묻는 질문
Claude Prompt Caching은 무료인가요?
Prompt Caching 기능 자체는 별도 구독료 없이 사용할 수 있습니다. 다만 캐시 쓰기 비용(기본 입력의 1.25배 또는 2배)과 캐시 읽기 비용(0.1배)이 추가됩니다. 같은 콘텐츠를 두 번 이상 재사용한다면 캐싱이 더 저렴해집니다.
Pre-warming이 정확히 어떻게 비용을 줄이나요?
Pre-warming 호출 자체는 캐시 쓰기 비용을 그대로 지불합니다. 절약되는 것은 출력 토큰 비용(0원) 과 첫 사용자 요청의 캐시 미스 페널티입니다. 사용자가 도착했을 때 이미 캐시가 따뜻한 상태라 즉시 0.1배 비용으로 응답이 시작됩니다.
캐시가 실제로 적중했는지 어떻게 확인하나요?
응답의 usage.cache_read_input_tokens 값을 확인하면 됩니다. 이 값이 0보다 크면 캐시 히트입니다. 0이고 cache_creation_input_tokens만 채워져 있으면 이번에 새로 캐시를 쓴 상태입니다.
5분 TTL이 너무 짧으면 어떻게 하나요?
두 가지 옵션이 있습니다. 첫째는 4.5분마다 워밍 호출을 보내 TTL을 갱신하는 것입니다(schedule 라이브러리 등 사용). 둘째는 cache_control에 "ttl": "1h"을 추가해 1시간 TTL을 쓰는 것입니다. 1시간 TTL은 쓰기 비용이 더 비싸지만, 트래픽이 듬성듬성 들어오는 서비스라면 자주 다시 쓰는 것보다 경제적입니다.
Claude Code 같은 도구는 이걸 이미 쓰고 있나요?
네, Claude Code를 비롯한 Anthropic 공식 도구는 내부적으로 Prompt Caching을 활용합니다. 직접 API를 호출하는 자체 애플리케이션이라면 이번 가이드처럼 명시적으로 설정해 주어야 같은 비용 절감 효과를 얻을 수 있습니다.
💡 한 발 더 — 한국 개발자가 놓치고 있는 활용 패턴
공식 문서 어디에도 강조되지 않지만, 한국에서 Claude API를 쓰는 분들이 특히 놓치기 쉬운 활용 패턴 세 가지를 정리합니다.
첫째, 한국어 시스템 프롬프트는 토큰 효율이 영어보다 낮습니다. 같은 의미라도 한국어 시스템 프롬프트가 영어보다 1.5~2배 더 많은 토큰을 차지합니다. 즉 한국어 프롬프트일수록 캐싱의 절감 효과가 더 큽니다. 영어로 시스템 프롬프트를 쓰고 사용자 응답만 한국어로 받는 구조라면, 캐싱 없이 운영하는 것이 사실상 손해입니다.
둘째, 회사 내부 데이터(약관, 매뉴얼, 가이드)를 시스템 프롬프트에 넣는 RAG 대안 패턴입니다. 50,000~150,000 토큰 정도의 문서라면 매번 검색해서 컨텍스트로 주입하는 RAG 파이프라인보다, 그냥 통째로 시스템 프롬프트에 넣고 캐싱해 두는 편이 더 단순하고 저렴할 수 있습니다. 인덱싱 인프라 운영 비용까지 더하면 차이가 더 벌어집니다. 단, 문서가 자주 바뀌면 캐시 무효화가 잦아 효과가 떨어지니 정적인 문서에 적합합니다.
셋째, 스케줄러 기반 Pre-warming은 한국 시간대 트래픽 패턴과 잘 맞습니다. 한국 사용자 트래픽은 출근(오전 9시), 점심(12시), 퇴근 후(저녁 8시) 구간에 집중됩니다. 트래픽이 들어오기 5분 전에 워밍 호출을 시작하는 것만으로 첫 사용자의 응답 속도가 눈에 띄게 개선됩니다. AWS Lambda + EventBridge나 GitHub Actions의 cron 트리거로 무료로 구현할 수 있는 패턴입니다.
지금까지 Claude API를 캐싱 없이 써온 분이라면, 이번 한 번만 시간을 내어 적용해 보세요. 5분 세팅으로 한 달 청구서가 절반 이하로 줄어드는 경험을 할 수 있습니다.
원문: Prompt caching | Claude API Docs
참고: