이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- LLM 비용은 어떤 지점에서 폭증하는가?
- token budget은 사용자별, 기능별, 테넌트별로 어떻게 나눠야 하는가?
- prompt caching을 쓰기 좋은 입력 구조는 무엇인가?
- API key와 prompt/context 로그는 어떻게 보호해야 하는가?
개요
LLM 비용은 요청 수만으로 결정되지 않는다. input token, output token, retry, fallback, eval, embedding, vector search, logging, prompt cache hit rate가 모두 비용에 영향을 준다.
운영 AI 기능은 품질과 비용을 함께 설계해야 한다. “좋은 답변”만 보고 긴 context와 무제한 output을 허용하면 개인 프로젝트도 금방 비용이 튀고, 기업 서비스에서는 tenant 간 비용 분쟁이 생긴다.
왜 백엔드 개발자에게 중요한가
백엔드 개발자는 API 사용량과 비용을 사용자, 조직, 기능 단위로 설명할 수 있어야 한다. 인프라팀과 재무/운영팀은 다음을 묻는다.
- 어떤 기능이 비용을 가장 많이 쓰는가?
- 어떤 tenant가 quota를 초과했는가?
- retry 때문에 비용이 늘었는가?
- prompt 변경 후 input token이 증가했는가?
- eval batch가 production quota를 잡아먹었는가?
이 질문에 답하려면 token usage를 기능 메타데이터와 함께 기록해야 한다.
핵심 원리
LLM 비용을 관리하는 기본 단위는 request가 아니라 usage event다.
{
"event": "llm.usage",
"feature": "ticket-summary",
"tenant_id": "tenant-a",
"user_id": "user-42",
"prompt_version": "ticket-summary-v3",
"model": "gpt-4.1-mini",
"input_tokens": 1840,
"output_tokens": 320,
"retry_count": 1,
"cached_input_tokens": 1200
}이 정보가 있어야 기능별 원가, tenant별 quota, prompt 변경 영향, retry 비용을 분석할 수 있다.
Token Budget 설계
token budget은 여러 층으로 둔다.
- 요청당 최대 input token
- 요청당 최대 output token
- 사용자별 분당 요청 수
- 사용자별 일일 token
- tenant별 월간 token
- 기능별 모델 profile 제한
- eval/batch 별도 budget
요청 전에는 대략적인 token을 추정해 너무 큰 요청을 막고, 요청 후에는 provider usage를 기록해 실제 비용을 차감한다.
Prompt Caching 설계
prompt caching은 provider가 동일하거나 유사한 prompt prefix를 재사용할 수 있을 때 효과적이다. 캐시를 기대하려면 입력 구조를 안정적으로 만들어야 한다.
좋은 구조:
[고정 system instruction]
[고정 output schema]
[고정 few-shot examples]
[자주 바뀌는 user input]
[자주 바뀌는 retrieved context]나쁜 구조:
[매번 바뀌는 timestamp]
[매번 바뀌는 user input]
[고정 instruction]
[고정 examples]자주 바뀌는 값이 앞에 오면 고정 prefix 캐시 효율이 떨어질 수 있다. prompt cache는 provider별 정책이 다르므로 공식 문서를 확인하고, 실제 cached token 지표를 봐야 한다.
Secret 관리
API key는 다음 위치에 있으면 안 된다.
- Git 저장소
- Obsidian 노트
- 프론트엔드 번들
- 일반 애플리케이션 로그
- 에러 메시지
- 테스트 fixture
개인 프로젝트는 환경 변수와 .env gitignore로 시작할 수 있다. 기업 운영은 secret manager, rotation, least privilege, audit log가 필요하다.
코드로 이해하기
다음은 요청 전 budget을 확인하고, 호출 후 usage를 기록하는 단순 Java 구조다.
@Service
public class BudgetedLlmService {
private final TokenBudgetService budgetService;
private final LlmClient llmClient;
private final UsageRecorder usageRecorder;
public BudgetedLlmService(
TokenBudgetService budgetService,
LlmClient llmClient,
UsageRecorder usageRecorder
) {
this.budgetService = budgetService;
this.llmClient = llmClient;
this.usageRecorder = usageRecorder;
}
public LlmGenerateResult generate(User user, LlmGenerateCommand command) {
int estimatedTokens = TokenEstimator.estimate(command.userInput());
budgetService.checkAvailable(user.tenantId(), estimatedTokens);
LlmGenerateResult result = llmClient.generate(command);
usageRecorder.record(new LlmUsageEvent(
user.tenantId(),
user.id(),
command.promptVersion(),
result.model(),
result.inputTokens(),
result.outputTokens()
));
budgetService.consume(
user.tenantId(),
result.inputTokens() + result.outputTokens()
);
return result;
}
}실제 구현에서는 동시성 문제가 있으므로 budget 차감은 원자적으로 처리해야 한다. 사용량 기록과 quota 차감의 일관성도 고려해야 한다.
장애 상황과 대응
비용 폭증이 발생하면 다음을 확인한다.
- prompt version 변경 후 input token이 늘었는가?
- output token max가 풀렸는가?
- retry count가 증가했는가?
- 특정 tenant가 batch 작업을 돌렸는가?
- eval이 production key를 사용했는가?
- RAG가 top-k를 너무 크게 가져오는가?
- prompt cache hit rate가 떨어졌는가?
비용 장애는 장애로 취급해야 한다. 알림, 차단, degrade mode가 필요하다.
인프라 협업 포인트
인프라팀과는 다음을 합의한다.
- secret manager와 key rotation
- tenant별 budget metric
- usage event 보존 기간
- prompt/context 원문 저장소 분리
- 비용 알림 threshold
- eval/batch workload 격리
특히 observability 비용도 무시하면 안 된다. prompt나 context를 큰 로그로 남기면 모델 비용 외의 로그 비용이 폭증할 수 있다.
개인 프로젝트 최소 기준
.env를 gitignore한다.- 요청당 max output token을 둔다.
- 일일 호출 제한을 둔다.
- token usage를 간단히 기록한다.
- prompt와 context 전문을 공개 로그에 남기지 않는다.
기업 운영 수준 기준
- tenant별 quota와 비용 리포트를 제공한다.
- key rotation과 접근 감사 로그를 둔다.
- prompt cache hit rate와 token usage를 추적한다.
- eval/batch와 production key를 분리한다.
- 비용 폭증 시 자동 차단 또는 degrade mode를 둔다.
실전 팁
- 비용 최적화는 모델 단가보다 context 길이와 retry부터 본다.
- max output token을 기능별로 명시한다.
- 긴 문서는 요약 후 넣을지, 검색 후 관련 부분만 넣을지 먼저 판단한다.
- prompt cache를 기대한다면 고정 instruction을 앞쪽에 안정적으로 둔다.
위험 신호!
- token usage를 저장하지 않는다.
- 모든 기능이 같은 API key와 같은 quota를 쓴다.
- eval 작업이 production quota를 쓴다.
- prompt/context 전문을 무제한 로그로 남긴다.
- 사용자별 또는 tenant별 비용 제한이 없다.
확인 질문
확인 질문
- LLM 비용 폭증의 대표 원인은 무엇인가?
- 긴 context, 무제한 output, retry storm, batch/eval, 중복 indexing, 낮은 cache hit rate다.
- prompt caching이 잘 동작하려면 입력 구조가 어떻게 되어야 하는가?
- 고정 instruction과 schema, 예시가 앞쪽에 안정적으로 반복되고, 자주 바뀌는 입력은 뒤쪽에 배치되는 편이 좋다.
- secret 관리에서 개인 프로젝트와 기업 운영의 차이는 무엇인가?
- 개인 프로젝트는 환경 변수와 gitignore가 최소 기준이고, 기업 운영은 secret manager, rotation, audit log가 필요하다.
Spring AI 적용 연결
- Spring AI 적용 시 비용 관리는 model profile, token budget, usage metric을 gateway에서 함께 남기는 방식으로 시작한다. 호출 경계는 03. Spring Boot 계층 설계와 AI Gateway를 함께 본다.
- 비용 폭증과 quota 대응은 12. Production Readiness와 인프라 협업에서 운영 계약 관점으로 이어진다.