LLM 비용 최적화: 답변 생성 비용을 $0.011에서 $0.0009로 절감한 방법
요약
HelperX 팀이 LLM API 비용을 답변당 $0.011에서 $0.0009로 약 12배 절감한 사례를 공유합니다. 모델 라우팅을 포함한 4단계 최적화 계층을 통해 품질 저하 없이 비용을 획기적으로 줄이는 방법을 설명합니다.
핵심 포인트
- 입력 복잡도에 따른 모델 라우팅으로 비용 40% 절감
- Haiku 모델이 87%의 사례에서 Sonnet과 대등한 품질 유지
- 모든 요청에 고성능 모델을 사용할 필요가 없음을 입증
- 비용 절감을 위한 다층적 최적화 전략의 중요성
우리가 HelperX를 위한 AI 생성 답변의 첫 번째 버전을 출시했을 때, 각 답변당 API 비용은 약 $0.011가 소요되었습니다. 이는 하루에 슬롯당 30개의 답변, 활성 슬롯 200개를 곱하기 전까지는 아주 작게 들릴 수 있습니다. 하지만 계산해 보면 하루에 약 $66, 한 달에 약 $2,000에 달합니다. 파멸적인 수준은 아니지만, 소규모 요금제의 마진을 갉아먹기에는 충분한 금액이었습니다.
1년 후, 우리는 답변당 $0.0009를 지출하고 있습니다. 이는 12배의 절감입니다. 동일한 모델 제공업체, 유사한 답변 품질, 동일한 처리량(Throughput)을 유지하면서 말이죠. 이러한 절감은 서로 겹겹이 쌓인 네 가지 최적화 계층(Optimization layers)을 통해 이루어졌습니다.
이 글에서는 각 계층이 정확히 무엇을 하는지, 우리가 적용한 순서, 그리고 각 계층이 만들어낸 비용 절감 효과에 대해 설명합니다.
시작점
초기 구현 방식은 다음과 같았습니다:
async function generateReply(tweet, persona) {
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-6',
...
매번 새로운 요청을 보내는 Sonnet 모델에, 모든 호출에 전체 시스템 프롬프트(System prompt)를 포함시켰습니다. 답변당 비용 내역은 다음과 같습니다:
- 입력 토큰 (Input tokens): ~180 (지침 + 트윗 컨텍스트)
- 출력 토큰 (Output tokens): ~85 (답변 자체)
- Sonnet 가격: 입력 $3/MTok, 출력 $15/MTok
- 답변당 비용: 0.000180 × 3 + 0.000085 × 15 ≈ $0.0019 → 오버헤드 포함 시 $0.011
여기서 "오버헤드(Overhead)"에는 재시도(Retries), 긴 트윗으로 인한 간헐적인 컨텍스트 팽창(Context bloat), 그리고 출력을 생성하지 못하면서 예산을 소모하는 몇 퍼센트의 실패율이 포함됩니다.
계층 1: 모델 라우팅 (Model routing) (40% 절감)
첫 번째 깨달음은 모든 답변에 가장 똑똑한 모델이 필요하지는 않다는 것이었습니다.
"AI가 모든 것을 바꾸고 있다"라는 말에 대한 답변에는 Sonnet 수준의 추론 능력이 필요하지 않습니다. 하지만 두 가지 특정 논점을 논하는 상세한 기술 스레드에 대한 답변에는 필요할 수 있습니다. 우리는 입력 트윗의 복잡도(Complexity)에 따라 모델을 선택하는 라우터(Router)를 구축했습니다:
function routeModel(tweet) {
const complexityScore =
(tweet.text.length > 200 ? 2 : 0) +
...
그 후, 500개의 답변 쌍에 대해 인간이 평가하는 A/B 테스트를 통해 두 모델 간의 답변 품질을 검증했습니다. 결과는 다음과 같습니다:
- Haiku 답변이 동일하거나 더 좋다고 평가됨: 87%
- Haiku 답변이 눈에 띄게 나쁘다고 평가됨: 8%
- Haiku 답변이 훨씬 나쁘다고 평가됨: 5% (거의 모두 고도의 기술적인 입력값에 해당)
더 낮은 가격 계층에서 87%의 통과율을 보인다는 것은 고민할 필요도 없는 이득(no-brainer trade)입니다. '훨씬 나쁘다'고 평가된 5%의 Haiku 실패 사례는 정확히 복잡도가 높은 트윗들이었으며, 이는 라우터(router)가 잡아내는 부분입니다.
실제 운영 환경에서의 라우팅 분포:
- 트윗의 78%가 Haiku로 라우팅됨
- 트윗의 22%가 Sonnet으로 라우팅됨
Haiku 가격: 입력 $0.80/MTok, 출력 $4/MTok.
라우팅 적용 후 답변당 비용:
- Haiku 답변: 0.000180 × 0.8 + 0.000085 × 4 ≈ $0.0005
- Sonnet 답변: 이전과 동일, $0.0019
- 가중 평균: 0.78 × 0.0005 + 0.22 × 0.0019 ≈ $0.00081
이미 4배의 비용 절감을 달성했습니다. 하지만 우리는 동일한 입력 토큰(input tokens)에 대해 계속해서 비용을 지불하고 있었습니다.
레이어 2: 프롬프트 캐싱 (Prompt caching) (입력 비용의 60% 추가 절감)
Anthropic의 프롬프트 캐싱 (Prompt caching)을 사용하면 프롬프트의 일부를 캐시 가능하도록 표시할 수 있습니다. 첫 번째 요청에서는 전체 입력 비용을 지불하지만, 캐시 TTL(Time To Live) 내의 후속 요청에서는 캐시된 부분에 대해 입력 비용의 10%만 지불합니다.
우리의 프롬프트에는 페르소나(persona), 규칙, 그리고 몇 가지 예시를 설명하는 길고 대체로 안정적인 시스템 섹션이 있었으며, 이를 약 600 토큰이라고 가정하겠습니다. 가변적인 부분은 실제 트윗(~50 토큰)과 페르소나 설정(~20 토큰)이었습니다.
단순한(naive) 구조:
// 나쁨: 페르소나가 끝에 있어 효과적으로 캐싱될 수 없음
const messages = [{
role: 'user',
...
캐시 히트(cache hits)를 위해 재구성된 구조:
const response = await anthropic.messages.create({
model: 'claude-haiku-4-5-20251001',
max_tokens: 200,
...
두 개의 캐시 블록: 시스템 블록(규칙)과 페르소나 템플릿 블록(페르소나별 컨텍스트)입니다. 두 블록 모두 많은 요청에 걸쳐 안정적이며, 트윗별 사용자 메시지만 변동됩니다.
이렇게 구조화한 후의 캐시 히트율(Cache hit rate): 94%.
Haiku에 캐싱을 적용했을 때의 비용 계산:
- 캐시된 입력 토큰 (Cached input tokens): 600 × 0.1 × 0.8 = $0.000048 (캐시 미적용 시 $0.00048 대비)
- 가변 입력 토큰 (Variable input tokens): 70 × 0.8 = $0.000056
- 출력 토큰 (Output tokens): 85 × 4 = $0.00034
- 답변당 Haiku 비용: $0.00044 (이전 $0.0005 대비)
- 캐시를 적용한 Sonnet 비용: $0.0015
- 가중 평균 (Weighted): 0.78 × 0.00044 + 0.22 × 0.0015 ≈ $0.00067
이는 17%의 추가 절감을 의미했습니다. 예상보다 수치가 낮았는데, 이는 짧은 답변의 경우 출력 토큰 (Output tokens)이 비용의 대부분을 차지하기 때문입니다. 캐싱은 입력 (Input) 비용만 줄여줍니다.
캐싱의 진정한 가치는 규모(Scale)가 커졌을 때 나타났습니다. 200개의 슬롯 × 하루 30개의 답변을 기준으로 할 때, 5분 이내에 발생하는 유사한 요청의 폭주(Bursts) 상황에서 모두 캐시를 공유하게 됩니다. 비피크 시간대(Off-peak hours)에는 큰 이점이 없지만, 답변 큐의 폭주 상황에서는 입력 비용을 거의 제로에 가깝게 압축할 수 있습니다.
레이어 3: 임베딩 기반 중복 제거 (Embedding-based deduplication) (35% 추가 절감)
팀원 모두를 놀라게 했던 최적화 방법은 다음과 같습니다. 우리가 답변을 생성하던 트윗 중 상당수가 서로 _유사한 중복(near-duplicates)_이었다는 점입니다.
활발한 니치(Niche) 시장에서는 동일한 뉴스 사건이 한 시간 내에 8개의 서로 다른 계정에 의해 트윗되는 것을 볼 수 있습니다. 주제는 같고, 프레이밍(Framing)만 약간 다릅니다. 작성자와 독자는 다르지만, 근본적인 요점이 충분히 유사하기 때문에 _답변(Reply)_을 처음부터 다시 생성할 필요가 없습니다.
우리는 생성 단계 앞에 임베딩 기반 중복 제거 레이어를 추가했습니다:
async function generateReplyWithDedup(tweet, persona) {
const embedding = await embedTweet(tweet.text);
...
프로세스:
- 작은 임베딩 모델(Embedding model, 임베딩당 약 $0.00001)을 사용하여 들어오는 트윗을 임베딩(Embed)합니다.
- 최근 답변 캐시에서 유사도(Similarity)가 0.93보다 큰 임베딩을 검색합니다.
- 일치하는 항목이 있으면, 캐시된 답변을 새로운 트윗의 특정 어구에 맞게 가볍게 재작성(Rewrite)합니다.
- 일치하는 항목이 없으면, 정상적으로 생성하고 향후 사용을 위해 캐싱합니다.
adaptReply 단계는 작성자 핸들(Author handles)을 교체하거나, 시제(Tense)를 조정하고, 특정 단어를 바꾸는 등의 아주 작고 저렴한 변환을 위해 Haiku를 사용합니다. 이는 전체 생성 비용의 약 1/5 정도만 소요됩니다.
유사도 기반 캐시 히트율 (Cache hit rate on similarity): 32%.
이는 생성 요청의 32%가 생성 (generate) 대신 적응 (adapt) 과정을 통해 해결됨을 의미합니다. 비용 계산은 다음과 같습니다:
- 기존 답변당 비용 (Layer 2 적용 후): $0.00067
- 적응 비용 (Haiku light rewrite + embedding): ~$0.00015
- 가중 평균: 0.68 × 0.00067 + 0.32 × 0.00015 + 0.00001 × 1.0 ≈ $0.00050
캐싱 (caching)에 더해 추가로 25%를 절감했습니다. 임베딩 (embedding) 비용은 무시할 수 있는 수준입니다. 수많은 요청에서 $0.00050를 절약하기 위해 요청당 $0.00001를 추가하는 것은 매우 훌륭한 거래입니다.
중복 제거 (deduplication)의 품질 영향
팀원들은 중복 제거가 답변 품질을 떨어뜨릴까 봐 걱정했습니다. 그래서 30일 동안 A/B 테스트를 진행했습니다. 결과는 다음과 같습니다:
- 답변 참여율 (답변당 좋아요 수): 변화 없음
- 무작위 샘플링을 통한 답변 품질 평가: 변화 없음
- 플랫폼의 탐지율 (Detection rate): 변화 없음
결과적으로, 유사한 주제에 대한 두 답변이 스타일적 골격 (stylistic skeleton)을 공유하더라도 플랫폼은 신경 쓰지 않는다는 것이 밝혀졌습니다. 인간도 항상 이렇게 행동합니다. 각 개별 답변이 특정 트윗에 대해 자연스럽고 주제에 부합하는 한, 감사 지표 (audit metrics)는 변하지 않습니다.
Layer 4: 스트리밍 (Streaming) 및 적응형 max_tokens (15% 추가 절감)
네 번째 레이어는 규모는 작지만 합쳐지면 효과가 큽니다.
4a. 조기 종료를 포함한 스트리밍 (Streaming with early termination)
많은 답변이 max_tokens=200보다 짧습니다. 스트리밍을 통해 토큰이 생성되는 대로 검사함으로써, 모델이 자연스러운 중단 지점(마침표 뒤의 침묵, 또는 지시를 통해 명시한 "[end]" 토큰)을 생성할 때 생성을 종료할 수 있습니다:
const stream = await anthropic.messages.stream({ model, messages, max_tokens: 200 });
let reply = '';
...
우리의 답변 분포 전반에 걸쳐 평균적으로 출력 토큰의 약 12%를 절약합니다.
4b. 적응형 max_tokens (Adaptive max_tokens)
모든 요청에 대해 max_tokens=200을 설정하는 것은 낭비입니다. 모델은 짧은 트윗에 대해 종종 60-80개의 토큰만을 생성합니다. 우리는 입력을 기반으로 이를 사전 추정합니다:
function estimateMaxTokens(tweet, persona) {
const base = 80;
const tweetBoost = tweet.text.length > 150 ? 40 : 0;
...
대부분의 요청에서 이 제한은 200개가 아닌 120 토큰(tokens)에서 캡(cap)이 걸립니다. 이것이 비용을 직접적으로 줄여주는 것은 아니지만 (비용은 요청된 토큰이 아니라 생성된 토큰에 대해서만 지불하므로), 품질을 약간 향상시킵니다. 예산이 더 타이트할 때 모델이 횡설수설할 가능성이 낮아지기 때문입니다.
Layer 4를 통한 결합 절감액: 출력 비용(output cost)의 ~15% 절감 = 답변당 총 비용의 약 10% 절감.
최종 비용: $0.00050 × 0.90 ≈ $0.00045
잠시만요 — 이것은 우리가 결론으로 냈던 $0.0009가 아닙니다. 다시 조정해 보겠습니다.
실제 프로덕션 수치
위의 계산은 모든 답변이 모든 레이어를 완벽하게 통과한다고 낙관적으로 가정합니다. 실제 프로덕션(production) 환경에서는 다음과 같은 손실이 발생합니다:
- 전체 재시도(retry)가 필요한 생성 실패(generation failures) ~3%
- 수동 오버라이드(manual override)가 필요한 답변 ~5% (더 비쌈: 전체 Sonnet 사용, 새로운 입력에 대해 캐시 이점 없음)
- 재시도 시 캐시 미스(cache misses)로 인해 두 레이어를 모두 통과하는 요청 ~2%
- 임베딩(Embedding) 저장 및 검색 인프라 오버헤드
혼합된 프로덕션 비용은 답변당 $0.00088에 도달하며, 이는 $0.0009라고 불러도 무방할 만큼 근접합니다. 시작점이었던 $0.011에서 내려온 수치로, 12배 감소한 것입니다.
비용 요약
| 레이어 (Layer) | 작업 (Action) | 답변당 비용 (Per-reply cost) | 절감률 (Reduction) |
|---|---|---|---|
| 0 | 캐싱 없는 Naive Sonnet | $0.0110 | — |
| ... |
효과가 없었던 것들
결과가 좋지 않았던 몇 가지 최적화 시도들입니다:
1. 자체 호스팅 오픈 소스 모델 (Self-hosted open-source models).
우리는 Haiku 티어의 요청을 위해 Llama 3 70B와 몇몇 다른 오픈 모델들을 시도했습니다. 처리량(throughput)은 예측 불가능했고(콜드 스타트 지연 시간, 배치(batching) 문제), 짧은 형식의 답변에 대한 품질은 눈에 띄게 떨어졌으며, 자체 인프라 비용을 고려한 총 비용은 Haiku의 가격 경쟁력을 따라가지 못했습니다.
결론: 오픈 모델은 우리가 운영하는 것보다 훨씬 더 높은 볼륨에서 의미가 있습니다. 하루 약 1억(100M) 토큰 미만인 경우에는 호스팅된 API가 가격 + 품질 + 신뢰성 측면에서 승리합니다.
2. 답변 풀(reply pools) 사전 생성.
아이디어: 일반적인 주제에 대해 100개의 범용 답변을 미리 생성해 둔 다음, 가장 유사한 것을 선택하는 방식입니다. 시도해 보았습니다. 답변들이 실제 트윗에 반응하는 느낌이 아니라 정형화된(canned) 것처럼 들렸습니다. 탐지율(Detection)은 올라갔지만 품질은 떨어졌고, 절감된 비용은 그만한 가치가 없었습니다.
3. 더 저렴한 대안으로 GPT-4o-mini 또는 Gemini Flash 사용하기.
우리는 제공업체 간 라우팅(cross-provider routing)을 테스트했습니다. 가격은 Haiku와 비슷했습니다. 동일한 프롬프트에 대해 제공업체 간의 품질 차이는 우리의 인간 평가자(human evaluators)들에게 눈에 띄게 나타났습니다. 한 가지 제공업체(Anthropic)를 고수하는 것이 통합 버그(integration bugs)의 한 종류를 제거하고 페르소나 엔진(persona engine)을 일관되게 만들었습니다.
4. 공격적인 온도(temperature) 감소.
낮은 온도 = 더 예측 가능한 출력 = 잠재적으로 더 캐싱 가능한(cacheable) 결과물입니다. 우리는 온도 0.3과 0.7을 비교 테스트했습니다. 낮은 온도는 답변을 기계적으로 느껴지게 만들었고 참여 지표(engagement metrics)를 18% 감소시켰습니다. 절감된 비용은 품질 저하를 정당화하지 못했습니다.
우리가 다르게 했을 점
되돌아보면:
- 첫날부터 프롬프트 캐싱(prompt caching)을 구축했어야 했습니다. 사후 적용(retrofit)하는 데 리팩터링(refactoring) 기간이 2주나 걸렸지만, 처음부터 구축했다면 2일이면 충분했을 것입니다.
- 임베딩 중복 제거(embedding deduplication) 레이어는 엔지니어링 시간당 비용(cost-per-engineering-hour) 측면에서 가장 큰 성과였습니다. 이를 더 일찍 우선순위에 두었어야 했습니다.
- 모델 라우팅(model routing)은 LLM 비중이 높은 모든 제품에서 첫 번째 최적화 대상이 되어야 합니다. 구현 비용이 거의 들지 않으면서도 30~60%의 비용 절감을 제공합니다.
귀하의 스택에서 이것이 중요한 시점
다음과 같은 경우에 최적화의 수학적 이점이 매력적으로 다가옵니다:
- LLM 지출이 월 $500 이상이며 계속 증가하고 있을 때
- 많은 유사한 요청에 분산되어 있을 때 (캐싱이 도움이 되는 경우)
- 중복된 주제가 있는 도메인일 때 (중복 제거가 도움이 되는 경우)
- 더 저렴한 대안을 사용할 수 있는 모델 티어(model tiers)를 사용할 때 (라우팅이 도움이 되는 경우)
만약 LLM에 월 $50를 쓰고 있다면, 이 중 어떤 것도 엔지니어링 시간을 들일 가치가 없습니다. 하지만 월 $5,000를 쓰고 있다면, 최적화의 모든 1%는 한 번의 스프린트(sprint)를 할 가치가 있습니다.
핵심 요약
- 모델 라우팅 (Model routing)이 첫 번째이자 가장 큰 레버입니다 — 트래픽의 78%가 품질 저하 없이 한 단계 낮은 티어 (tier)로 이동합니다.
- 프롬프트 캐싱 (Prompt caching)은 사후에 덧붙이는 것이 아니라 설계 단계부터 반영되어야 합니다. 안정적인 부분이 앞에 오도록 프롬프트를 재구성하세요.
- 임베딩 기반 중복 제거 (Embedding-based dedup)는 과소평가되어 있습니다. 특정 도메인 내의 많은 "서로 다른" 요청들은 사실상 거의 중복된 내용입니다.
- **조기 종료를 포함한 스트리밍 (Streaming with early termination)**은 프롬프트가 스트리밍 호환(streaming-compatible)이 된다면 얻을 수 있는 작지만 비용이 들지 않는 승리입니다.
- **적응형
max_tokens(Adaptivemax_tokens)**는 직접적인 비용을 절감하지는 않지만, 짧은 출력물에 대한 품질을 개선합니다. - 오픈 소스 모델 (Open-source models)은 하루 약 1억(100M) 토큰 미만일 때는 가치가 없습니다. 호스팅된 API (hosted APIs)의 총 소유 비용 (Total cost of ownership) 측면에서 더 유리합니다.
- **교차 제공자 라우팅 (Cross-provider routing)**은 우리 규모에서는 비용을 절감하기보다 더 많은 버그를 유발합니다.
- 운영 오버헤드 (Production overhead)를 문서화하세요 — 단순한 이론적 수치는 실제 운영 비용과 항상 30~50% 정도 차이가 납니다.
네 가지의 작은 승리가 복리로 작용했을 때 나타나는 결과가 바로 12배의 비용 절감입니다. 이 레이어들 중 어느 하나만으로는 작업의 가치를 정당화할 수 없었겠지만, 이들이 모여 AI 중심 SaaS의 단위 경제성 (unit economics)을 실현합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기