AI & LLM/Claude & Anthropic

Claude 프롬프트 캐싱으로 API 비용 90% 줄인 실전 후기

Lumin 2026. 5. 21. 10:22
반응형
Claude API 프롬프트 캐싱을 실제 서비스에 적용해 월 비용을 약 90% 줄인 과정을 정리합니다. 캐싱 동작 원리, 코드 적용 방법, 자주 빠지는 함정과 비용 계산까지 한 번에 다룹니다.

Claude API를 본격적으로 쓰기 시작하면 가장 먼저 부딪히는 벽이 비용입니다. 특히 긴 시스템 프롬프트나 문서를 매 요청마다 통째로 보내야 하는 RAG·에이전트 구조에서는 토큰 비용이 정말 빠르게 불어납니다. 이 글에서는 제가 운영하던 사이드 프로젝트에서 Claude 프롬프트 캐싱(Prompt Caching)을 적용해 월 청구액을 1/10 수준까지 떨어뜨린 과정을 풀어봅니다. 심화 주제이긴 하지만, API를 한 번이라도 호출해본 분이라면 따라올 수 있게 개념부터 천천히 짚어보려 합니다.

프롬프트 캐싱이 뭐길래

Claude 프롬프트 캐싱은 반복적으로 보내는 프롬프트의 일부를 Anthropic 서버에 5분간 저장해두고, 같은 내용은 다시 처리하지 않게 하는 기능입니다. 쉽게 말해 카페에서 매번 메뉴판을 처음부터 다시 읽지 않고 "어제 그거요"로 끝내는 것과 비슷합니다.

LLM(거대 언어 모델, ChatGPT·Claude 같은 AI의 본체)은 입력된 모든 토큰을 매번 다시 계산합니다. 시스템 프롬프트가 1만 토큰짜리라면, 사용자가 한 줄짜리 질문을 던져도 1만 토큰을 통째로 다시 처리하는 구조입니다. 캐싱은 이 중복 계산을 건너뛰게 해줍니다.

Anthropic 공식 문서 기준으로 캐싱된 토큰의 가격 구조는 다음과 같습니다 (글 작성 시점, Claude Sonnet 기준).

토큰 종류 가격 비율 (일반 입력 = 1배 기준)
일반 입력 토큰 1.0배
캐시 쓰기 (최초 저장) 1.25배
캐시 읽기 (재사용) 0.1배
출력 토큰 5.0배 (모델별 상이)

핵심은 캐시 읽기가 일반 입력의 10분의 1이라는 점입니다. 같은 시스템 프롬프트를 100번 재사용한다면, 99번은 1/10 가격으로 처리된다는 뜻입니다.

어떤 상황에서 효과가 큰가

캐싱은 만능이 아닙니다. 효과가 폭발적인 케이스와 거의 의미 없는 케이스가 명확히 갈립니다.

효과가 큰 케이스

  • 시스템 프롬프트가 길고 (대략 2,000 토큰 이상), 자주 호출되는 챗봇
  • RAG에서 같은 문서를 여러 번 참조하는 구조
  • 긴 코드베이스를 컨텍스트로 넣고 여러 질문을 던지는 코드 어시스턴트
  • 대화 히스토리가 길어지는 멀티턴 챗봇

효과가 거의 없는 케이스

  • 매번 프롬프트가 완전히 달라지는 일회성 요청
  • 시스템 프롬프트가 짧은 (수백 토큰) 단순 호출
  • 호출 간격이 5분을 넘는 저빈도 서비스 (캐시는 5분 후 만료)

제 경우는 첫 번째에 해당했습니다. 약 8,000 토큰짜리 시스템 프롬프트(역할 정의 + 예시 20개 + 출력 포맷 규칙)를 사용하는 분류용 봇이었고, 분당 수십 회씩 호출되고 있었습니다. 캐싱을 안 쓸 이유가 없었습니다.

적용 전후 비용 비교

실측치를 공유합니다. 한 달간 동일 트래픽 기준이며, 모델은 Claude Sonnet, 시스템 프롬프트는 약 8,000 토큰으로 고정입니다.

항목 캐싱 적용 전 캐싱 적용 후
월 호출 수 약 12만 회 약 12만 회
시스템 프롬프트 토큰 처리 비용 100% (기준) 약 11%
출력 토큰 비용 변화 없음 변화 없음
월 청구액 (대략) $X 약 $X × 0.1
평균 응답 지연 약 2.4초 약 1.6초

비용뿐 아니라 응답 속도도 빨라집니다. 캐시된 토큰은 재계산이 없으니 첫 토큰까지의 지연(TTFT)이 짧아지거든요. 저는 이게 비용 절감보다 더 체감되는 변화였습니다.

💡 정확한 절감률은 "시스템 프롬프트 길이 ÷ 전체 입력 길이"에 비례합니다. 시스템 프롬프트가 입력의 95%를 차지하던 제 케이스에서 90% 절감이 나온 거고, 절반 정도라면 절감폭도 절반 수준이 됩니다.

코드로 적용하는 법

Python SDK 기준으로 보겠습니다. 적용은 생각보다 단순합니다. 캐싱하고 싶은 블록에 cache_control 필드를 추가하기만 하면 됩니다.

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": "당신은 고객 문의를 분류하는 전문가입니다...",
        },
        {
            "type": "text",
            "text": LONG_INSTRUCTIONS,  # 8,000 토큰짜리 긴 규칙
            "cache_control": {"type": "ephemeral"}  # 이 줄이 핵심
        }
    ],
    messages=[
        {"role": "user", "content": "환불 가능한가요?"}
    ]
)

위 코드에서 cache_control 한 줄이 전부입니다. 이 표시가 붙은 블록과 그 앞에 있는 모든 내용이 캐시 대상이 됩니다. 즉 캐시는 "프롬프트의 시작부터 이 지점까지"를 단위로 저장됩니다.

응답 객체에는 캐시 사용량이 함께 돌아옵니다.

print(response.usage)
# CacheCreationInputTokens: 8000  (최초 호출 시)
# CacheReadInputTokens: 0

두 번째 호출부터는 이렇게 바뀝니다.

# CacheCreationInputTokens: 0
# CacheReadInputTokens: 8000  (캐시에서 읽음)

이 숫자가 안 올라오면 캐싱이 동작하지 않은 겁니다. 디버깅의 출발점이 여기입니다.

자주 빠지는 함정

처음 적용했을 때 저도 몇 번 헛발질했습니다. 비슷하게 막힐 가능성이 높은 지점들을 정리합니다.

1. 캐시는 "정확히 같은 텍스트"여야 적중합니다

시스템 프롬프트 안에 현재 시각이나 사용자 ID 같은 가변 값을 넣어두면 매번 캐시가 새로 만들어집니다. 결과적으로 캐시 쓰기(1.25배) 비용만 계속 내는 셈이 됩니다.

[안 좋은 예]
"현재 시각: 2025-01-15 14:32:01
 당신은 ..."  ← 매 요청마다 시각이 바뀌어서 캐시 미스

[좋은 예]
"당신은 ...
 (가변 값은 messages 쪽 user 메시지에 넣기)"

2. 5분 TTL을 잊지 말기

기본 캐시 유효시간은 마지막 적중으로부터 5분입니다. 호출이 5분 이상 끊기면 캐시가 사라지고, 다음 호출은 다시 캐시 쓰기 비용이 발생합니다. 트래픽이 띄엄띄엄 들어오는 서비스라면 효과가 줄어듭니다. 1시간 TTL 옵션도 있지만 추가 비용이 붙으니 트래픽 패턴을 보고 결정하세요.

3. 최소 토큰 제한이 있습니다

공식 문서 기준 모델별로 캐시 가능한 최소 토큰이 정해져 있습니다 (Sonnet 기준 약 1,024 토큰). 그보다 짧은 블록은 cache_control을 붙여도 캐싱이 안 됩니다. 짧은 프롬프트는 굳이 시도할 필요가 없다는 뜻이기도 합니다.

4. cache_control은 최대 4개까지

한 요청에 캐시 브레이크포인트는 최대 4개입니다. 시스템 프롬프트 + 도구 정의 + 긴 문서 + 대화 히스토리, 이렇게 4단 구조까지는 가능하지만 그 이상은 합쳐야 합니다.

흐름으로 한 번 더 정리

말로만 하면 헷갈리니 호출 흐름을 단순화해서 그려봤습니다.

[1번째 요청]
사용자 → [시스템 프롬프트 8K 토큰] + [질문] → Claude
                ↓
        캐시 저장 (1.25배 비용)
        → 응답 반환

[2번째 요청 - 5분 이내]
사용자 → [시스템 프롬프트 8K 토큰] + [질문] → Claude
                ↓
        캐시 적중! (0.1배 비용)
        → 응답 반환 (속도도 빠름)

[7분 후 요청]
        캐시 만료 → 다시 1번째 흐름으로

서비스 입장에서는 트래픽이 꾸준할수록 캐시가 살아있는 시간이 길어지고, 비용 곡선이 더 완만해집니다.

실제 적용할 때 체크리스트

심화 주제이긴 해도 단계 자체는 어렵지 않습니다. 적용 전에 한 번 훑어보세요.

  • [ ] 시스템 프롬프트가 1,024 토큰 이상인지 확인
  • [ ] 시스템 프롬프트 안에 가변 값(시각, 사용자 ID)이 섞여있지 않은지 점검
  • [ ] 호출 간격이 평균 5분 이내인지 트래픽 로그 확인
  • [ ] cache_control은 가장 긴 정적 블록 끝에 붙이기
  • [ ] 적용 후 첫 호출과 두 번째 호출의 usage 객체 비교
  • [ ] 1주일 동안 캐시 적중률(CacheRead / 총 입력 토큰) 모니터링

저는 적중률 80%를 목표로 잡았고 실측 92%까지 나왔습니다. 가변 값을 시스템 프롬프트에서 모두 빼낸 덕입니다.

마무리

Claude 프롬프트 캐싱은 코드 한두 줄 추가로 비용을 극적으로 줄일 수 있는 흔치 않은 기능입니다. 다만 모든 케이스에 마법처럼 듣지는 않습니다. 시스템 프롬프트가 길고, 호출이 자주 발생하며, 프롬프트 안에 가변 값이 없는 — 이 세 조건이 맞을 때 90% 절감 같은 숫자가 가능합니다.

오늘 당장 해볼 만한 일은 두 가지입니다. 운영 중인 Claude API 호출의 시스템 프롬프트 길이를 확인하고, 그 안에 가변 값이 섞여있는지 살펴보는 것. 이 두 가지만 정리해도 캐싱 적용을 위한 90% 준비는 끝난 셈입니다. 다음 글에서는 캐시 적중률을 모니터링하는 대시보드를 직접 만들어본 후기를 공유해보려 합니다.

반응형