AI API 비용을 95% 절감했습니다 — 더 빨리 알았더라면 좋았을 모든 것들
요약
OpenAI API 비용을 95% 절감하기 위해 추론 계층을 재구축한 엔지니어링 사례를 공유합니다. 비용 대시보드 구축, 프롬프트 압축, 캐싱 전략을 통해 비용 절감과 성능 최적화를 동시에 달성하는 방법을 다룹니다.
핵심 포인트
- 비용 가시성 확보를 위해 모델, 토큰, 지연 시간을 기록하는 미들웨어 구축 필수
- 프롬프트 압축을 통해 컨텍스트 윈도우 크기를 줄여 토큰 비용 절감
- 에지 레이어에서의 프롬프트 압축 및 멀티 리전 캐싱 활용
- 지연 시간(p99)과 비용 사이의 균형을 맞춘 라우팅 계층 설계
저는 아직도 그 Slack 메시지를 기억합니다. 목요일 밤 11시, 우리 엔지니어링 부사장(VP of Engineering)이 저에게 메시지를 보냈습니다: "왜 이번 달 OpenAI 비용이 갑자기 세 배나 늘었죠?" 대시보드를 열자마자 가슴이 철렁 내려앉았습니다. 세 개의 서로 다른 제품 팀이 모든 요청마다 GPT-4o를 호출하는 기능을 배포하기 시작한 것이었습니다. 아무도 예산을 설정하지 않았습니다. 아무도 알림(Alerts)을 설정하지 않았습니다. 아무도 업스트림 제공자(Upstream provider)의 p99 지연 시간(Latency)에 대해 생각조차 하지 않았습니다. 우리는 그저... 돈을 쓰고 있었습니다.
그날 밤을 시작으로 저는 전체 추론 계층(Inference layer)을 처음부터 다시 구축하는 6주간의 스프린트를 시작했습니다. 결과적으로 우리는 이전 지출의 약 5% 수준으로 운영할 수 있게 되었습니다. 동시에 더 나은 p99 지연 시간, 멀티 리전 페일오버(Multi-region failover)를 통한 99.9%의 가용성, 그리고 솔직히 조금은 자랑스러운 라우팅 계층(Routing layer)을 갖추게 되었습니다.
제가 배운 것들을 공유합니다. 아래의 모든 내용은 해당 프로젝트의 실제 수치를 바탕으로 합니다.
다른 무엇보다 먼저 비용 대시보드부터 시작하세요
무엇인가를 최적화하기 전에, 돈이 실제로 어디로 흘러가고 있는지 알아야 했습니다. 저는 모든 모델 호출을 감싸고 다음 항목들을 기록하는 작은 미들웨어(Middleware)를 구축했습니다: 모델 이름, 입력 토큰(Input tokens), 출력 토큰(Output tokens), 지연 시간(Latency), 리전(Region), 그리고 어떤 기능이 이를 트리거했는지 나타내는 태그(Tag).
이 단계를 건너뛴다면, 여러분은 엉뚱한 것을 최적화하게 될 것입니다. 제가 장담합니다.
대시보드가 가동된 후 수치를 뽑아보았습니다. 지출의 약 85%가 출력 토큰 100만 개당 10달러인 GPT-4o로 나가고 있었습니다. 나머지 15%는 100만 개당 0.60달러인 GPT-4o-mini와 몇 가지 작은 실험들에 분산되어 있었습니다. 그중 어느 것도 p99 지연 시간으로 측정되지 않았습니다. 그중 어느 것도 페일오버(Failover) 계획이 없었습니다.
그러한 가시성(Visibility)이 있었기에 나머지 작업들이 가능했습니다.
1단계: 엣지(Edge)에서 프롬프트 압축하기
제가 가장 먼저 다룬 문제는 프롬프트 팽창(Prompt bloat)이었습니다. 우리의 RAG 파이프라인은 모든 요청에 2,000 토큰의 컨텍스트 윈도우(Context windows)를 밀어 넣고 있었습니다. 그 컨텍스트의 대부분은 변하지 않는 반복적인 시스템 지침(System instructions), 예시, 그리고 도구 설명들이었습니다.
제가 선택한 접근 방식은 다음과 같습니다 — 실제 호출을 하기 전에 저렴한 요약(Summarization) 단계를 거치는 것입니다:
import openai
client = openai.OpenAI(
...
이 계산 결과는 저를 깜짝 놀라게 했습니다. 2,000토큰(token)의 시스템 프롬프트(system prompt)를 400토큰으로 압축하면 DeepSeek V4 Flash 기준으로 요청당 0.024달러를 절약할 수 있습니다. 하루 10,000건의 요청을 처리한다면 하루에 240달러, 연간으로는 약 87,600달러에 달하는 금액이 복리로 쌓입니다. 단 하나의 함수만으로 말이죠.
저는 압축 단계의 p99 예산을 50ms로 설정하여 이를 에지 레이어(edge layer)에 배포했습니다. 이보다 오래 걸리는 작업은 압축되지 않은 상태로 그대로 전달되었습니다. 압축된 프롬프트의 멀티 리전 캐싱(Multi-region caching)은 공짜로 얻은 승리였습니다. 프롬프트를 한 번 압축하고 나면 다시 압축할 필요가 없기 때문입니다.
2단계: 공격적인 캐싱, 신중한 무효화
다음 단계는 캐싱(caching)이었습니다. 전형적인 방식은 정확한 프롬프트를 해싱(hash)하고, 일치할 때 메모리에서 제공하는 것입니다. 하지만 아키텍트(architect)의 관점에서는 TTL(Time To Live), 캐시 스탬피드(cache stampede), 그리고 캐시 레이어가 다운되었을 때 어떤 일이 발생하는지를 반드시 고려해야 합니다.
제가 실제로 배포한 버전은 다음과 같습니다:
import hashlib, json, time
cache = {}
...
저희의 FAQ 및 문서 조회 기능의 경우, 이 방식은 50-80%의 캐시 적중률(cache rates)을 기록했습니다. 이는 다른 모든 절감 사항에 더해 추가로 20-50%의 비용을 더 아껴주었습니다.
신뢰성을 높이는 비결: 저는 캐시를 두 개의 리전(region)에서 최종 일관성(eventual consistency)을 갖는 별도의 서비스로 운영했습니다. 두 리전이 모두 다운되더라도 시스템은 직접 API 호출 방식으로 우아하게 성능을 저하시키며(gracefully degraded) 작동했습니다. 강력한 의존성(hard dependency)이 없었던 것입니다. 캐시 적중 시 p99 지연 시간(latency)은 5ms 미만이었습니다. 사용자는 전혀 눈치채지 못할 것입니다.
3단계: 모든 것에 GPT-4o를 사용하는 것을 중단하라
이것이 가장 핵심적인 부분이었습니다. 프로젝트 전체에서 가장 큰 영향력을 가진 레버(lever)였습니다.
저는 각 제품 팀과 마주 앉아 질문했습니다: "이 기능에 실제로 무엇이 필요한가요?" 답변은 겸허해질 정도였습니다. 저희의 내부 챗봇(chatbot)에는 프런티어 모델(frontier model)이 필요하지 않았습니다. 저희의 분류 파이프라인(classification pipeline)에도 프런티어 모델이 필요하지 않았습니다. 저희의 번역 기능은 확실히 프런티어 모델이 필요하지 않았습니다.
제가 작성하여 경영진과 공유한 매트릭스(matrix)는 다음과 같습니다:
| 작업 (Task) | 비용이 많이 드는 선택 (Expensive Choice) | 현명한 선택 (Smart Choice) | 절감액 (Savings) |
|---|---|---|---|
| 단순 채팅 (Simple chat) | GPT-4o ($10/M) | DeepSeek V4 Flash ($0.25/M) | 97.5% |
| ... |
팀들이 이 수치들을 확인하자마자, 논의는 사실상 종결되었습니다. 분류 작업 (classification task)에서 동일한 결과물을 얻기 위해 40배나 더 많은 비용을 지불해야 한다는 주장을 하는 사람은 아무도 없었습니다.
모델을 작업에 맞추는 이 단 한 번의 변화만으로, 영향을 받은 워크로드 (workloads)에서 약 90%를 절감했습니다. 그리고 솔직히 말해서, 더 작은 모델들은 더 가볍고 업스트림 제공업체 (upstream providers)의 여유 용량 (capacity headroom)이 더 많기 때문에 종종 더 나은 p99 지연 시간 (p99 latency)을 보여주었습니다.
4단계: 계층형 라우팅 레이어 (Tiered Routing Layer) 구축
이것은 설계자 (architect)의 수입니다. 모델 하나만 선택하지 마세요. 캐스케이드 (cascade)를 구축하세요.
아이디어는 이렇습니다: 가장 저렴한 모델을 먼저 시도합니다. 응답이 충분히 괜찮다면 그대로 반환합니다. 그렇지 않다면, 단계를 높입니다 (escalate). 그리고 정말로 필요할 때만 비용이 많이 드는 추론 모델 (reasoning model)로 단계를 높이세요.
def smart_generate(prompt, max_budget=0.50):
"""품질에 따라 저렴한 모델에서 비싼 모델로 캐스케이드(Cascade)합니다."""
...
quality_check 함수가 핵심 비결 (secret sauce)입니다. 저희의 사용 사례에서는 응답이 일관성(coherent) 있고, 완전하며(complete), 주제에 맞는지(on-topic) 점수를 매기는 간단한 분류기(classifier) — 또 다른 Qwen3-8B 호출 — 를 사용했습니다. 저희는 기존의 단일 모델 설정과 비교하여 전체 캐스케이드 구조를 2주 동안 A/B 테스트했습니다. 품질 지표 (quality metrics)는 통계적으로 차이가 없었습니다. 비용은 95% 감소했습니다.
신뢰성 관점에서도 이 설계는 매우 훌륭합니다. 저렴한 모델에 장애가 발생하면 트래픽은 자연스럽게 2단계 (Tier 2)로 흐릅니다. 2단계에 장애가 발생하면 3단계 (Tier 3)가 이어받습니다. 수동 장애 조치 (manual failover)가 필요 없습니다. 요청의 80%가 가장 저렴하고 빠른 모델에서 종료되었기 때문에 p99 지연 시간 (p99 latency)은 실제로 개선되었습니다.
앞서 언급했던 저희의 고객 지원 챗봇은 월 420달러에서 월 28달러로 줄었습니다. 품질 점수와 SLA는 동일하며, 99.9%의 가용성 (availability)을 유지했습니다.
5단계: 가능한 것은 배치 (Batch) 처리하기
마지막 레버는 배치 (Batch) 처리입니다. 만약 동일한 모델에 세 개의 별도 요청을 보내고 있다면, 세 개의 프롬프트(prompt)를 포함한 하나의 요청을 보내세요. 어차피 결합된 입력 토큰 (input tokens)에 대해 비용이 청구되지만, 세 번의 별도 왕복 (round trips)과 세 번의 별도 출력 토큰 (output tokens) 최소 비용을 피할 수 있습니다.
# 이전: 3번의 호출, 3배의 출력 토큰 최소 비용
for question in questions:
response = client.chat.completions.create(
...
이 방법이 모든 워크로드 (workload)에 적합한 것은 아닙니다. 지연 시간 (latency)에 민감한 사용자 대상 요청은 배치 처리를 해서는 안 됩니다. 하지만 저희의 야간 보고서 생성 및 대량 분류 작업 (bulk classification jobs)의 경우, 이를 통해 추가로 10-20%를 절감했습니다.
아무도 말하지 않는 신뢰성 계층 (Reliability Layer)
대부분의 비용 최적화 가이드가 생략하는 부분이 여기 있습니다. 여러 모델과 제공업체 (providers)를 가로질러 운영하기 시작하면, 가동 시간 (uptime)을 고려해야 합니다. 단일 제공업체 아키텍처 (Single-provider architectures)는 단일 장애점 (single points of failure)을 가집니다. 저희는 주 제공업체에 지역적 장애 (regional incident)가 발생하여 제품 전체가 중단되었을 때 이를 뼈아프게 배웠습니다.
그 이후 제가 구현한 것들은 다음과 같습니다:
- 두 개의 클라우드 리전 (cloud regions) 간의 액티브-액티브 (active-active) 라우팅을 포함한 멀티 리전 배포 (Multi-region deployment)
- 서킷 브레이커 (circuit breakers)를 포함한 모델별 상태 확인 (Per-model health checks) — 만약 특정 모델의 p99 지연 시간 (latency)이 기준치의 3배 이상으로 급증하면, 트래픽이 다음 옵션으로 자동 라우팅됩니다.
- 중복성 (redundancy)을 바탕으로 내부적으로 약속한 99.9% 가동 시간 SLA
- 라우팅 계층 자체의 오토스케일링 (Auto-scaling) — 모델이 다운되면 트래픽 패턴이 변하는 연쇄 반응 (cascade)이 일어나기 때문입니다.
이것은 비용 최적화가 실제로 안전하게 배포될 수 있도록 만드는, 화려하지는 않지만 필수적인 작업입니다. 이것이 없다면, 상위 단계의 장애 (upstream incident) 하나만으로도 서비스 중단(outage)을 겪게 될 것입니다.
종합 정리
6주 후, 저희가 도달한 결과는 다음과 같습니다:
- 스마트 모델 선택: 일치하는 워크로드에서 약 90% 절감
- 그 위에 적용된 계층형 라우팅 (Tiered routing): 특히 챗봇의 경우 약 95% 절감으로 이어짐
- 응답 캐싱 (Response caching): 기능에 따라 20-50% 추가 절감
- 프롬프트 압축 (Prompt compression): 긴 컨텍스트 (long-context) 워크로드에서 요청당 15-30% 절감
- 배치 처리 (Batching): 대량 작업에서 10-20% 절감
청구 금액이 "경악스러운" 수준에서 "거의 없는" 수준으로 바뀌었습니다. 더 중요한 점은, 우리가 99.9%의 SLA (Service Level Agreement)를 달성했고, p99 지연 시간 (latency)이 실제로 감소했으며 (저렴한 모델들은 더 빠른 경향이 있기 때문), 엔지니어링 팀이 마침내 매 달러가 무엇을 사고 있는지에 대한 가시성 (visibility)을 확보했다는 것입니다.
만약 당신이 지금 자신의 AI 청구서를 바라보며 밤 11시에 Slack 메시지를 받고 느끼는 것과 같은 공포를 느끼고 있다면, 그 마음을 충분히 이해합니다. 좋은 소식은 이 모든 것이 엔지니어 한 명과 단 한 번의 스프린트 (sprint)만으로도 달성 가능하다는 것입니다. 모델 생태계가 충분히 성숙했기 때문에, 품질 저하 없이 공격적으로 라우팅 (routing)을 수행할 수 있습니다.
저에게 큰 도움이 되었던 한 가지는 모든 것을 Global API (https://global-apis.com/v1)를 통해 라우팅하는 것이었습니다. DeepSeek, Qwen 및 기타 모델들에 대해 단일한 OpenAI 호환 엔드포인트 (endpoint)를 가짐으로써, 캐스케이드 레이어 (cascade layer)를 훨씬 깔끔하게 구축하고 훨씬 쉽게 모니터링할 수 있었습니다. 통합을 위한 복잡한 작업 (plumbing)을 건너뛰고 싶다면 확인해 볼 가치가 있습니다.
행운을 빕니다. 그리고 오늘 밤 잠자리에 들기 전에 예산 알림 (budget alerts)을 설정해 두세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기