
어느 날 API 청구서 금액이 평소보다 두세 배로 찍혀 있으면 제일 먼저 뭘 봐야 할까요? 로그를 무작정 뒤지기보다 최근 배포 내역, 날짜·모델별 토큰 사용량 분해, 재시도·루프 여부, 조용히 바뀐 설정, 캐싱 적용 여부 순서로 짚어보면 원인이 훨씬 빨리 좁혀집니다. LLM 비용 급증은 대부분 한 가지 원인이 아니라 이 중 두 지점 이상이 겹쳐서 일어나기 때문에, 순서를 정해두고 하나씩 배제해나가는 편이 감으로 뒤지는 것보다 확실히 빠릅니다.
비용 그래프가 꺾인 날짜부터 확인하세요
OpenAI나 Anthropic 콘솔의 사용량 대시보드를 열면 일별 비용이 그래프로 나옵니다. 여기서 급증이 시작된 정확한 날짜를 먼저 특정해야, 그 시점 전후로 배포된 커밋만 골라서 볼 수 있습니다. git log --since="2026-08-20" --until="2026-08-25" 같은 명령으로 범위를 좁히고, 프롬프트 템플릿이나 max_tokens, 모델명 문자열을 건드린 커밋이 있는지 diff를 확인하는 게 가장 먼저 할 일입니다.
추적 순서를 표로 정리하면 다음과 같습니다.
| 확인 순서 | 원인 후보 | 확인 방법 | 의심 신호 |
|---|---|---|---|
| 1 | 최근 배포 변경 | git log, 배포 로그 대조 | 급증 시작일과 배포일이 겹침 |
| 2 | 토큰 사용량 분포 | 날짜·모델·기능별 집계 | 특정 기능 하나가 전체 비용의 대부분 차지 |
| 3 | 재시도·루프 | 요청 로그의 상태코드 분포 | 429·5xx 뒤에 동일 요청 반복 |
| 4 | 설정값 변경 | 코드 diff, 환경변수 이력 | 모델명·컨텍스트 길이가 바뀌어 있음 |
| 5 | 캐싱 적용 여부 | 프롬프트 프리픽스 비교 | 캐시 히트율이 갑자기 0에 가까움 |
이 표의 각 줄을 순서대로 훑는 게 이번 글의 뼈대이고, 아래에서 하나씩 더 깊게 들어갑니다.
날짜·모델별로 토큰 사용량을 쪼개보는 법

배포 diff에서 딱히 원인이 안 보이면 토큰 사용량을 잘게 쪼개는 단계로 넘어갑니다. 요청마다 날짜, 모델명, 어떤 기능에서 호출했는지 태그를 붙여 JSONL로 저장해두면, 나중에 집계할 때 범인을 특정하기 훨씬 쉽습니다. 이미 이런 로그가 없다면 지금부터라도 남기는 게 다음 급증 때 시간을 크게 줄여줍니다.
import pandas as pd
import json
records = []
with open("usage_log.jsonl", "r", encoding="utf-8") as f:
for line in f:
records.append(json.loads(line))
df = pd.DataFrame(records)
df["date"] = pd.to_datetime(df["timestamp"]).dt.date
summary = (
df.groupby(["date", "model", "feature_tag"])
.agg(total_tokens=("total_tokens", "sum"), calls=("total_tokens", "count"))
.sort_values("total_tokens", ascending=False)
.head(10)
)
print(summary)
total_tokens calls
date model feature_tag
2026-08-23 gpt-4o rag_search 912340 1840
2026-08-23 gpt-4o-mini chat_reply 118220 2210
2026-08-22 gpt-4o-mini chat_reply 96410 1990
이 출력에서 rag_search 태그 하나가 전체 토큰의 대부분을 차지하는 게 보이면, 문제는 챗봇 전체가 아니라 검색 결과를 프롬프트에 얼마나 많이 붙이는지 하나로 좁혀집니다. 다만 OpenAI 응답은 prompt_tokens·completion_tokens·total_tokens 필드를 쓰고 Anthropic 응답은 input_tokens·output_tokens로 이름이 다르므로, 여러 공급자를 함께 쓰는 경우 로깅 스키마 단계에서 이 차이를 먼저 통일해둬야 위 코드가 그대로 맞습니다.
재시도 로직이 도는 사이 요청 수가 배로 늘어 있습니다
토큰 사용량 집계에서 calls 숫자가 평소보다 유난히 많다면 재시도 루프를 의심할 차례입니다. 요청이 429(레이트 리밋 초과) 오류를 받았을 때 자체 코드에서 무한 while 루프로 즉시 재요청을 걸면, 같은 프롬프트가 여러 번 과금됩니다. openai-python SDK(v1.x 기준)는 연결 오류나 일부 5xx 응답을 기본적으로 최대 2회까지 자동 재시도하는데, 이 위에 직접 만든 재시도 래퍼를 또 씌우면 재시도 횟수가 곱으로 늘어나는 경우가 흔합니다.
이 문제는 레이트 리밋 오류가 났을 때 대기 시간 없이 바로 재요청하는 코드에서 특히 잘 나타납니다. 지수 백오프 없이 짧은 간격으로 계속 재시도하면 요청 수만 늘어나고 정작 성공률은 크게 개선되지 않는 경우가 많으므로, 로그에서 상태코드별 분포를 뽑아 429 뒤에 동일한 프롬프트 해시가 여러 번 찍히는지 확인해보는 게 확실한 검증 방법입니다.

조용히 바뀐 설정값부터 의심해 보세요
재시도도 정상이라면 남는 건 설정값입니다. 모델명을 gpt-4o-mini에서 gpt-4o로 바꾼 커밋이 리뷰 없이 병합됐거나, max_tokens를 테스트하려고 늘려둔 값을 되돌리지 않은 채 배포된 경우가 실무에서 자주 나옵니다. 대화 이력을 매번 프롬프트 앞에 통째로 붙이는 구조라면 대화가 길어질수록 요청 하나의 토큰 수 자체가 계속 불어나므로, 사용자당 평균 대화 턴 수가 늘었는지도 함께 봐야 합니다.
RAG를 쓰는 서비스라면 검색 결과 청크 개수를 늘린 설정 변경이 가장 흔한 원인입니다. 청크를 3개에서 8개로 늘리면 검색 품질은 조금 나아질 수 있어도 프롬프트 길이는 그 이상으로 늘어나기 때문에, 환경변수나 설정 파일에서 top_k 값의 변경 이력을 git blame으로 확인해보는 게 빠릅니다.
캐싱을 켰는데 왜 요금이 그대로인가요?
캐싱은 프롬프트 앞부분이 완전히 동일할 때만 작동합니다. 시스템 프롬프트 맨 앞에 현재 시각이나 요청 ID, 세션별 사용자 정보를 넣어두면 매 요청마다 프리픽스가 달라져서 캐시가 매번 새로 쌓였다가 버려지는 상태가 됩니다. 정적인 지시문과 예시는 앞쪽에, 매번 바뀌는 사용자 입력이나 검색 결과는 뒤쪽에 배치하는 순서만 바꿔도 캐시 히트율이 달라지는 경우가 많습니다.
또한 공급자마다 캐시가 적용되는 최소 조건이 다릅니다. 프롬프트가 너무 짧으면 캐싱 대상 자체가 안 될 수도 있고, 명시적으로 캐시 구간을 지정해줘야 하는 방식도 있으므로 캐싱을 켠 뒤에는 반드시 로그에서 캐시 히트율 수치를 직접 확인하는 편이 안전합니다. 켰다고 표시만 해두고 실제로는 매번 미스가 나는 상태를 몇 주씩 방치하는 사례도 드물지 않습니다.
다음 청구서에서 같은 일을 반복하지 않으려면
위 다섯 단계를 순서대로 거치면 대부분의 LLM 비용 급증은 배포 diff, 특정 기능의 토큰 사용량, 재시도 루프, 설정값 변경, 캐시 미스 중 한두 곳에서 원인이 잡힙니다. 원인을 찾은 뒤에는 같은 문제를 다음에도 사람이 뒤늦게 발견하는 구조로 남겨두지 않는 게 중요합니다. 콘솔의 예산 알림(budget alert) 기능으로 일정 금액을 넘으면 이메일이나 슬랙으로 통보되게 걸어두고, 위 집계 스크립트를 하루 한 번 cron으로 돌려 전날 합계가 평소 평균의 일정 배수를 넘으면 알림이 오게 만들어두면 다음 급증은 청구서가 아니라 알림으로 먼저 알게 됩니다.
