Rate Limit(속도 제한) 문제를 해결하기 위해 나만의 개인용 AI Gateway를 구축한 방법
요약
멀티 모델 채팅 어시스턴트 개발 중 발생하는 Rate Limit, API 키 관리, 응답 시간 불일치 문제를 해결하기 위해 직접 구축한 개인용 AI 게이트웨이를 소개합니다. Node.js 기반의 Express 서버를 활용하여 큐잉, 캐싱, 로깅, 폴백 기능을 구현하는 과정을 다룹니다.
핵심 포인트
- API 키 로테이션의 한계와 계정 수준 제한 문제 분석
- 단일 엔드포인트를 통한 요청 큐잉 및 캐싱 구현
- 모델별 사용량 로깅 및 장애 대비 폴백 전략 수립
- 비용 효율적이고 데이터 보안이 강화된 셀프 호스팅 방식 제안
저는 OpenAI, Anthropic, 그리고 몇몇 오픈 소스 모델들 사이를 전환할 수 있는 멀티 모델 채팅 어시스턴트라는 사이드 프로젝트에 몰두하고 있었습니다. 아이디어는 간단했습니다. 사용자가 선호하는 백엔드(Backend)를 직접 선택하게 하는 것이었죠. 하지만 현실은 Rate Limit(속도 제한), API 키 관리, 그리고 일관되지 않은 응답 시간이라는 악몽의 연속이었습니다.
처음에는 당연한 방법인 API 키 로테이션(Rotate)을 시도했습니다. 여러 계정에서 얻은 수십 개의 키를 코드에서 교체하며 최선의 결과를 기대했습니다. 그 방법은 약 하루 정도만 효과가 있었습니다. 그러다 다시 429 에러가 발생하기 시작했고, 심지어 키들이 남용(Abuse)으로 플래그(Flag) 처리되었습니다. 상황이 좋지 않았습니다.
다음으로 서드파티(Third-party) API 게이트웨이(Gateway)를 살펴보았습니다. 유망한 것들도 있었지만, 개인 프로젝트를 위해 추가적인 단계(Hop)와 원치 않는 월간 비용을 감수해야 했습니다. 저에게는 가볍고, 셀프 호스팅(Self-hosted)이 가능하며, 직접 제어할 수 있는 무언가가 필요했습니다.
그래서 저만의 AI 게이트웨이를 직접 구축했습니다. 어떻게 작동하는지, 왜 각 구성 요소를 선택했는지, 그리고 그 과정에서 발견한 트레이드오프(Trade-offs)에 대해 설명하겠습니다.
문제에 대한 상세 분석
프론트엔드(Frontend)나 백엔드(Backend)에서 AI API를 직접 호출하면, 해당 서비스의 Rate Limit(속도 제한)에 종속될 수밖에 없습니다. 예를 들어, OpenAI는 계정 이력에 따라 계층화된 제한을 두고 있습니다. 제한에 걸리면 기다려야 합니다. 여러 모델을 통합하면 서로 다른 제한 헤더(Limit headers), 재시도 로직(Retry logic), 그리고 비용을 동시에 관리해야 합니다.
저는 다음과 같은 기능을 갖춘 단일 엔드포인트(Endpoint)를 원했습니다:
- 제한을 초과하지 않도록 요청을 큐(Queue)에 대기시킴
- 동일한 프롬프트(Prompt)에 대한 응답을 캐싱(Caching) (합리적인 범위 내에서)
- 모델별, 사용자별 사용량 로깅(Logging)
- 한 제공업체가 다운되었을 경우를 대비한 폴백(Fallback) 처리
시도했지만 실패했던 방법들
1. 지수 백오프(Exponential Backoff)를 적용한 단순 재시도
지연 시간을 포함한 재시도 루프(Retry loop)를 코드로 구현했습니다. 가끔 발생하는 429 에러에는 괜찮았지만, 요청 빈도가 높을 때는 여전히 실패했습니다. 더 나쁜 점은, 클라이언트가 재시도를 기다리는 동안 앱이 응답하지 않게 되었다는 것입니다.
2. 여러 API 키를 순환하며 사용하기
키들을 풀(Pool)에 저장하고 순환시켰습니다. 에러는 줄어들었지만, 모든 키가 동일한 플랜(Plan)에 속해 있었기 때문에 계정 수준의 Rate Limit(속도 제한)에 걸렸습니다. 또한, 키의 취소(Revocation)를 관리하는 것도 매우 번거로웠습니다.
3. 외부 프록시(Proxy) 서비스 사용
몇 가지 관리형 API 게이트웨이 (API gateway) 서비스를 시도해 보았습니다. 작동은 했지만, 취미 프로젝트로 하기에는 비용(월 $20 이상)을 정당화하기 어려웠습니다. 게다가, 내 프롬프트 (prompt) 데이터를 또 다른 알 수 없는 서버로 전송하는 것도 마음에 들지 않았습니다.
해결책: Node.js 게이트웨이 구축
저는 제 앱과 AI 제공업체 (AI providers) 사이에 위치하는 간단한 Express 서버를 구축하기로 결정했습니다. 이 서버는 큐잉 (queuing), 캐싱 (caching), 그리고 로깅 (logging)을 처리합니다. 핵심 구조는 다음과 같습니다.
코드 스켈레톤 (Code Skeleton)
const express = require('express');
const axios = require('axios');
const Bottleneck = require('bottleneck'); // 속도 제한 (rate limiting)을 위해 사용
...
이것은 최소한의 버전입니다. 실제 운영 환경 (production)에서는 적절한 에러 핸들링 (error handling), 인증 (authentication), 그리고 Redis와 같은 지속성 캐시 (persistent cache)가 필요할 것입니다. 하지만 패턴을 파악하기에는 충분합니다.
배운 점 및 트레이드오프 (Trade-offs)
캐싱 (Caching): 양날의 검
동일한 프롬프트를 캐싱함으로써 수많은 API 호출과 지연 시간 (latency)을 절약할 수 있었습니다. 하지만 대화형 AI (conversational AI)의 경우, 테스트 중이 아니라면 프롬프트가 동일한 경우는 거의 없습니다. 결국 저는 정확히 일치하는 경우에만 캐싱하도록 설정하고, 오래된 응답을 피하기 위해 짧은 TTL (Time To Live, 5분)을 추가했습니다.
속도 제한 (Rate Limiting): Bottleneck 라이브러리
제공업체별로 요청을 큐에 넣기 위해 bottleneck을 사용했습니다. 잘 작동하지만, 적절한 minTime을 설정하는 것이 까다롭습니다. 너무 빠르면 여전히 429 에러가 발생하고, 너무 느리면 사용자가 기다려야 합니다. 저는 보수적인 값으로 시작하여 로그를 모니터링한 후 조정해 나갔습니다.
로깅 (Logging): 단순하게 유지하기
각 요청을 메모리 내의 배열에 로깅했습니다. 실제 앱이라면 데이터베이스 (database)를 사용해야 할 것입니다. 저는 이 데이터를 사용하여 어떤 모델이 가장 인기 있는지 확인하고 비용을 계산했습니다.
폴백 (Fallback) 전략
위의 코드에서는 한 제공업체가 실패하면 단순히 502 에러를 반환합니다. 더 나은 접근 방식은 동일한 프롬프트로 다른 제공업체를 시도하는 것입니다. 나중에 그 기능을 추가했지만, 주의해야 할 점이 있습니다. 어떤 프롬프트는 다른 모델에서 더 잘 작동할 수 있기 때문입니다. 완벽한 폴백은 아닙니다.
다음에 다시 한다면 다르게 할 점
- 캐싱 및 속도 제한(Rate Limiting)을 위해 Redis 사용 – 인메모리 캐시(In-memory cache)는 재시작 시 손실되며,
node-cache는 데이터를 유지(Persist)하지 못합니다. - 인증(Authentication) 추가 – 현재는 누구나 이 게이트웨이에 접근할 수 있습니다. 간단한 API 키 확인 절차를 추가하겠습니다.
- 제공자(Provider)별로 게이트웨이 분리 – 모든 제공자를 하나의 Express 앱으로 처리하는 것도 괜찮지만, 제공자가 늘어날수록 설정(Config)이 복잡해집니다.
- 메시지 큐(Message Queue) 고려 – 부하가 매우 높은 경우에는 RabbitMQ나 Kafka를 사용하여 요청 수신과 처리를 분리(Decouple)하십시오.
이 방식을 사용하지 말아야 할 때
- 단 하나의 AI 제공자만 사용하고 해당 제공자의 속도 제한(Rate limits)을 감수할 수 있다면, 복잡성을 추가하지 마십시오.
- 팀에 예산이 있다면, ai.interwestinfo.com(영감을 얻기 위해 확인해 보세요)과 같은 관리형 게이트웨이(Managed gateway)가 개발 시간을 절약해 줄 수 있습니다.
- 엄격한 지연 시간(Latency) 요구 사항이 있는 프로덕션 앱의 경우, 커스텀 게이트웨이는 오버헤드(Overhead)를 추가합니다. API를 직접 호출하고 클라이언트 측에서 재시도(Retry)를 처리하는 것이 좋을 수 있습니다.
마무리하며
나만의 AI 게이트웨이를 구축하는 것은 속도 제한(Rate limiting), 캐싱(Caching), 그리고 회복 탄력성이 있는 시스템(Resilient systems) 설계에 대해 많은 것을 배울 수 있었던 즐거운 연습이었습니다. 완벽하지는 않지만, 당면한 문제를 해결해 주었고 비용과 데이터 흐름에 대한 완전한 제어권을 부여했습니다.
궁금합니다. 여러분은 프로젝트에서 여러 AI API 통합을 어떻게 처리하시나요? 게이트웨이를 사용하시나요, 키를 교체(Rotate keys)하시나요, 아니면 다른 방법을 사용하시나요? 댓글로 알려주세요.
_Built with the help of AI Gateway
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기