나의 AI API 호출이 실패했던 이유와 해결 방법
요약
LLM API를 활용한 기사 요약 서비스 개발 중 겪은 비용 상승과 Rate Limit(429 에러) 문제를 다룹니다. 단순한 부하 분산이나 재시도 방식의 한계를 극복하기 위해 시맨틱 캐싱(Semantic Caching)을 도입하여 API 호출을 60% 절감한 사례를 소개합니다.
핵심 포인트
- API 호출 시 발생하는 Rate Limit과 비용 문제를 해결하는 전략 필요
- 단순 라운드 로빈 방식은 응답 시간 불일치와 에러 처리의 어려움이 있음
- 시맨틱 캐싱을 통해 유사한 콘텐츠에 대한 중복 API 호출 방지 가능
- 임베딩과 코사인 유사도를 활용한 효율적인 캐시 검색 구현
저는 긴 기사들을 요약해 주는 작은 독서 앱을 만들고 있었습니다. 대단한 것은 아니었습니다. 그저 밀린 읽을거리들을 따라잡기 위한 개인적인 프로젝트였죠. 저는 이렇게 생각했습니다. "이봐, LLM API를 사용하자, 그건 쉽잖아!" 스포일러를 하자면, 그렇지 않았습니다. 어떤 일이 일어났는지, 그리고 제가 결국 어떻게 해결했는지 말씀드리겠습니다.
내가 직면했던 진짜 문제
매일 아침 약 50개의 기사가 대기 상태였습니다. 각 기사에는 간결한 요약이 필요했습니다. 저는 제 노트북에서 실행되는 로컬 모델 (Mistral 7B)로 시작했습니다. 작동은 했지만, 기사당 5분이 걸렸습니다. 매일 4시간 이상이 소요되는 셈이었죠. 받아들일 수 없는 수준이었습니다.
그래서 클라우드로 옮겼습니다. 인기 있는 AI API를 선택했고, 엔드포인트 (endpoint)를 호출하여 10초 만에 요약을 얻었습니다. 마법 같았죠. 하지만 며칠 후, 두 가지 사실을 발견했습니다:
- 비용이 빠르게 상승하고 있었습니다 (요약당 약 $0.01 × 50 = 하루 $0.50였지만, 재시도 (retries)를 추가하면서 더 늘어났습니다...)
- 특히 피크 시간대에 무작위 HTTP 429 에러가 나타나기 시작했습니다.
저는 한 곳이 다운되면 다른 제공업체 (provider)를 호출하면 된다고 생각했습니다. 하지만 여러 개의 API 키, 엔드포인트, 그리고 가격 등급 (pricing tiers)을 관리하는 것은 금세 작은 악몽이 되었습니다.
시도했지만 실패했던 것들
라운드 로빈 (round-robin) 방식의 부하 분산 (Load balancing).
세 개의 제공업체를 순환하는 간단한 스크립트를 작성했습니다. 이는 속도 제한 (rate limits) 문제를 해결하는 데 도움이 되었지만, 응답 시간이 극심하게 차이 났습니다. 어떤 것은 2초가 걸렸고, 어떤 것은 30초가 걸렸습니다. 그리고 여전히 에러를 수동으로 처리해야 했습니다.
백오프 (backoff) 없는 큐 (queue) 구축.
asyncio.gather()를 사용하여 50개의 요청을 한꺼번에 던졌습니다. API가 저를 제한 (throttling)하기 시작하자, 모든 요청이 한꺼번에 실패했습니다. 설상가상으로, 일부 제공업체는 몇 분 동안 IP를 차단하기도 했습니다.
고정된 지연 시간을 가진 하드코딩된 재시도 (retries).
재시도하기 전에 1초의 대기 시간을 추가했습니다. 하지만 제공업체의 윈도우 (window)가 제 지연 시간보다 짧았기 때문에 여전히 속도 제한에 걸렸습니다.
저는 요약본을 실제로 사용하는 시간보다 API 계층을 관리하는 데 더 많은 시간을 허비하고 있었습니다.
결국 성공했던 방법
저는 한 걸음 물러나 깨달았습니다. 제가 읽는 대부분의 기사는 동일한 패턴의 변형일 뿐이라는 것을요. 기술 컨퍼런스에 관한 뉴스 기사는 다른 기사와 유사한 구조를 가집니다. 제가 매일 접하는 피드 중 상당수는 동일한 주제(AI, 스타트업, 과학)에 관한 것이었습니다. 매번 API를 호출하는 대신, 요약본을 캐싱(Caching)하여 유사한 콘텐츠에 재사용할 수 있었습니다.
1단계: 시맨틱 캐싱 (Semantic caching)
저는 각 기사의 제목과 처음 200단어에 대해 임베딩 (Embeddings, Sentence Transformers 사용)을 계산했습니다. 새로운 기사가 들어오면 기존 캐시에서 가장 유사한 항목을 검색했습니다. 코사인 유사도 (Cosine similarity)가 0.85 이상이면 캐싱된 요약본을 반환했습니다. 저는 벡터 확장 기능이 포함된 SQLite를 사용했지만, 여기서는 단순화된 로직을 보여드리겠습니다:
from sentence_transformers import SentenceTransformer
import numpy as np
...
이를 통해 일반적인 하루 기준으로 API 호출을 약 60% 줄일 수 있었습니다. 나머지 40%는 정말로 독특한 기사들이었습니다.
2단계: 지수 백오프 (Exponential backoff)와 지터 (Jitter)를 적용한 견고한 재시도
실제 API 호출을 위해, 저는 속도 제한 (Rate limits)을 준수하고 실패를 우아하게 처리하는 래퍼 (Wrapper)를 작성했습니다:
import asyncio
import random
...
저는 이를 일반적인 속도 제한을 넘지 않도록 한 번에 5개의 동시 요청을 실행하는 벌크 처리 (Bulk processing) 함수와 함께 사용했습니다.
3단계: 폴백 (Fallback)을 위한 다중 제공자 활용
저는 API 설정 리스트를 정의하고, 하나가 실패하면 순서대로 시도하도록 했습니다. 이런 방식을 통해 단일 서비스에 의존하지 않게 되었습니다:
providers = [
{"url": "https://api.openai.com/v1/completions", "key": "sk-..."},
{"url": "https://ai.interwestinfo.com/generate", "key": "my_key_here"}, # 이 제공자가 제 사용량 규모에서는 결국 가장 안정적이었습니다.
...
교훈 / 트레이드오프 (Lessons learned / trade-offs)
- 캐싱 (Caching)은 과소평가되어 있습니다. 입력 데이터에 중복이 있다면 비용과 시간을 모두 절약할 수 있습니다. 하지만 시맨틱 캐싱 (Semantic caching)이 완벽한 것은 아닙니다. 막연하게 유사한 두 기사가 서로 다른 요약(예: 하나는 리뷰, 다른 하나는 보도 자료)을 필요로 할 수도 있기 때문입니다. 저는 환각 (Hallucination)에 의한 재사용을 방지하기 위해 유사도 임계값 (Similarity threshold)을 높게 (0.85) 설정했습니다.
- **지터 (Jitter)를 포함한 지수 백오프 (Exponential backoff)**는 필수적입니다. 지터가 없다면 모든 재시도 (Retry)가 동시에 발생하여 계속 충돌하게 됩니다. 이 기술 덕분에 차단(Ban)을 면할 수 있었습니다.
- **동시성 제한 (Concurrency limits)**은 여러분의 친구입니다. 한 번은 20개의 동시 요청을 실행하려 했다가 IP가 일시적으로 차단된 적이 있습니다. 수치를 낮추세요.
- API 제공업체는 다운될 수 있습니다. 폴백 (Fallback) 목록을 갖추는 것은 저렴한 보험과 같습니다. 제가 사용한 interwestinfo 서비스는 가동 시간 (Uptime)이 좋고 관대한 무료 티어 (Free tier)를 제공했지만, 여전히 다른 옵션들을 유지했습니다.
다음에 다르게 할 점
제 앱을 위한 명확한 SLA (Service Level Agreement)를 먼저 설정하겠습니다. 제 독서 앱의 경우, 2분 이내의 요약이면 충분합니다. 1초 미만의 응답은 필요하지 않습니다. 이는 더 느리지만 저렴한 모델을 사용하거나, 심지어 밤사이에 요청을 배치 (Batch) 처리할 수 있음을 의미합니다.
또한, 확정하기 전에 제공업체들을 벤치마킹 (Benchmark)하겠습니다. 서로 다른 요청 스키마 (Request schemas)에 맞추느라 며칠을 허비했습니다. 처음부터 작은 어댑터 레이어 (Adapter layer)를 작성했을 것입니다.
마지막으로, 단조 캐싱 키 (Monotonic caching keys)를 추가하겠습니다. 텍스트 유사도만 사용하면 포맷팅이 약간 다른 완벽하게 동일한 기사를 놓칠 수 있습니다. 정규화된 텍스트 (Normalized text)를 해싱 (Hashing)하면 더 많은 중복을 잡아낼 수 있을 것입니다.
요점 (The takeaway)
API 속도 제한 (Rate limits)과 혼자 싸울 필요는 없습니다. 스마트한 캐싱, 재시도 위생 (Retry hygiene), 그리고 제공업체의 다양성을 결합함으로써 저의 불안정했던 요약기는 신뢰할 수 있는 일꾼으로 변했습니다. 제가 사용한 특정 URL들은 단지 예시일 뿐이며, 이 기술은 백엔드와 상관없이 작동합니다.
이제, 여러분의 AI API 관련 공포 이야기는 무엇인가요? 신뢰할 수 없는 엔드포인트 (Endpoints)나 예상치 못한 비용을 어떻게 처리했는지 듣고 싶습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기