요청 수로는 LLM 비용을 통제할 수 없다
한 줄 요약
일반 API 는 요청 하나의 비용이 대체로 같지만, LLM 은 요청 하나가 100토큰일 수도 100,000토큰일 수도 있다. 그래서 게이트웨이는 요청이 아니라 토큰을 세야 한다.
왜 이게 필요했나
분당 60요청으로 레이트리밋을 걸었다고 합시다. 어떤 테넌트가 매 요청에 200,000토큰 컨텍스트를 넣으면, 요청 수는 제한 안이지만 비용과 GPU 점유는 다른 테넌트의 수백 배입니다. 그리고 그 요청들이 KV 캐시를 독식해 다른 요청들이 선점당합니다.
요청 수 제한은 이 상황을 전혀 막지 못합니다. 필요한 것은 토큰 단위의 회계입니다.
어떻게 동작하나
게이트웨이가 해야 할 일이 다섯 가지입니다.
첫째, 인증과 테넌트 식별. API 키를 테넌트에 매핑합니다.
둘째, 토큰 회계. 요청의 입력 토큰과 응답의 출력 토큰을 각각 세어 기록합니다. 스트리밍이면 출력 토큰은 스트림이 끝나야 확정되므로, 중간에 끊긴 경우도 그때까지의 토큰을 계상해야 합니다.
셋째, 두 층위의 제한. RPM(분당 요청)과 TPM(분당 토큰)을 함께 겁니다. RPM 은 남용을 막고 TPM 은 비용과 자원을 막습니다. TPM 에는 토큰 버킷이 잘 맞습니다 — 평소 조용하던 테넌트의 순간 버스트를 허용하면서 장기 평균은 지킵니다.
넷째, 429 와 Retry-After. 거절할 때 언제 다시 오면 되는지 알려 줍니다. 이게 없으면 클라이언트들이 각자 재시도해 파도를 만듭니다.
다섯째, 비용 산출. 모델별로 입력·출력 단가가 다르므로 토큰 수에 단가를 곱해 누적합니다. 출력 토큰이 입력보다 비싼 것이 보통이라 둘을 분리해 세야 합니다.
여기에 예산이 붙습니다. 테넌트별 일간 또는 월간 상한을 두고 초과하면 차단하거나 더 싼 모델로 강등합니다. 후자를 폴백이라고 하고, 서비스가 완전히 멈추는 것보다 나은 경우가 많습니다.
무엇을 기준으로 제한하나
RPM(분당 요청)만 걸면 LLM 서비스에서는 거의 소용이 없습니다. 요청 하나가 100 토큰일 수도 10만 토큰일 수도 있기 때문입니다. 그래서 두 축을 함께 겁니다.
| 축 | 무엇을 막나 | 단위 |
|---|---|---|
| RPM | 요청 폭주, 봇 | 분당 요청 수 |
| TPM | GPU 시간 소모 | 분당 토큰 수(입력+출력) |
| 동시성 | 큐 폭발, 메모리 | 진행 중 요청 수 |
셋 중 동시성이 GPU 를 가장 정확히 반영 합니다. vLLM 은 배치로 처리하므로 동시 요청 수가 곧 KV 캐시 사용량이고, 그것이 넘치면 OOM 이거나 선점(preemption) 입니다. RPM·TPM 은 비용 회계에, 동시성은 안정성에 씁니다.
토큰 버킷으로 구현하면 버스트를 허용하면서 평균을 지킬 수 있습니다.
용량 = 60,000 토큰, 채우는 속도 = 1,000 토큰/초 (= 60k TPM)
요청이 오면 max_tokens 만큼 버킷에서 뺀다(예약)
완료되면 실제 사용량과의 차이를 돌려준다(정산)
버킷이 비면 429 + Retry-After: <채워질 때까지의 초>
Retry-After 를 반드시 보냅니다. 없으면 클라이언트가 즉시 재시도해 상황이 더
나빠집니다.
429 를 잘 내는 것도 설계다
거절할 때 무엇을 알려 주는지가 클라이언트 코드의 품질을 정합니다.
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit-Tokens: 60000
X-RateLimit-Remaining-Tokens: 0
X-RateLimit-Reset-Tokens: 12s
그리고 어떤 한도에 걸렸는지 를 본문에 씁니다. RPM 인지 TPM 인지 예산인지에 따라 클라이언트가 할 일이 다릅니다 — 앞의 둘은 기다리면 되지만 예산 초과는 기다려도 안 됩니다.
캐시가 가장 큰 절감이다
제한보다 효과가 큰 것이 캐시입니다. 세 층이 있습니다.
- 완전 일치 캐시 — 같은 프롬프트·같은 파라미터면 저장된 답을 돌려줍니다. FAQ 성격의 트래픽에서 적중률이 30% 를 넘기도 합니다. 온도가 0 이 아니면 같은 입력에 같은 답을 주는 것이 맞는지 제품이 정해야 합니다.
- 의미 캐시 — 임베딩으로 비슷한 질문을 찾아 재사용합니다. 적중률은 높지만 틀린 재사용의 위험 이 있어 임계값을 보수적으로 잡습니다.
- 프리픽스 캐시 — 시스템 프롬프트처럼 앞부분이 같은 요청들의 KV 캐시를
재사용합니다. vLLM 의
enable_prefix_caching이 이것이고, 정확도에 영향이 없으면서 TTFT 를 크게 줄입니다. 가장 먼저 켤 것.
현장에서 만나는 모습
토큰 수를 미리 알 수 없다는 점이 이 설계를 어렵게 만듭니다. 입력 토큰은 요청 시점에 셀 수 있지만 출력 토큰은 생성이 끝나야 압니다. 그래서 실무는 max_tokens 를 상한으로 예약해 두고, 완료 후 실제 사용량으로 정산하는 방식을 씁니다. 예약을 안 하면 동시에 들어온 요청들이 모두 예산 안이라고 판단하고 통과한 뒤 함께 초과합니다.
비용 폭주 사고는 대개 재시도 루프에서 납니다. 클라이언트가 타임아웃마다 재시도하는데 서버는 계속 생성하고 있으면, 사용자 한 명이 같은 답을 다섯 번 만들게 합니다. 게이트웨이에서 멱등성 키를 받아 진행 중인 동일 요청을 합치면 이 낭비가 사라집니다.
다음 실습에서 할 것
앞에서 만든 모의 서버 앞에 게이트웨이를 세웁니다. API 키 인증, RPM 제한, 토큰 회계, TPM 토큰 버킷, Retry-After, 모델별 비용 산출, 그리고 테넌트 예산 초과 차단까지 붙입니다.