[WebTranslator 개발기 #11] 자동 모델 탐색의 과금 위험과 모델 헬퍼 도구 전환
지난 10편에서는 긴 기술 문서와 무한 스크롤 웹페이지에서 발생하던 분당 요청 수 제한(RPM, Requests Per Minute)을 큐 후방 재진입 지속 루프와 중앙 통합 프롬프트 빌더로 극복하여, 요청 병목을 해소한 바 있습니다.
그러나 RPM을 해결하자마자 또 다른 쿼터와 과금의 복병을 마주했습니다. 구형 모델 하드코딩으로 인한 404 장애를 해결하려던 중, AI의 추천대로 도입한 '실시간 자동 모델 선택' 기능이 모델별 일일 총 요청 수(RPD, Requests Per Day)와 토큰 단가 차이를 무시한 채 동작하면서, Gemini의 일일 쿼터를 조기 소진시키고 OpenAI 선결제 크레딧을 예상보다 빠르게 소진시키는 문제를 일으킨 것입니다.
이번 글에서는 10편의 RPM에 이어 직면한 Gemini의 RPD 한계와 유료 모델 단가 격차의 문제를 짚어보고, 런타임 자동 선택을 전면 폐기하여 초가성비 모델 직접 고정과 모델 탐색 헬퍼 도구로 아키텍처를 전면 전환한 과정을 정리해 보고자 합니다.
1. 하드코딩된 구형 모델의 한계와 404 장애
개발 초기에는 Gemini 번역을 위해 코드 내에 gemini-2.5-flash 모델명을 고정 문자열로 하드코딩해 두었습니다.
그러나 gemini-2.5-flash는 구형 모델로서 기본 제공되는 일일 무료 허용량(RPD)이 Flash-Lite 모델에 비해 무려 25배나 적었습니다 (Flash 20 RPD vs Flash-Lite 500 RPD). 결국 웹서핑 중 쿼터가 빠르게 소진되거나, 공급자 측에서 해당 구형 엔드포인트를 사전 예고 없이 종료(Deprecated)하면서 콘솔에 다음과 같은 404 에러가 발생했습니다:
[WebTranslator] 배치 1 오류 — 스킵하고 계속 진행 Error: 404 This model models/gemini-2.5-flash is no longer available to new users
이 404 에러를 해결하기 위해 AI 어시스턴트에게 문제 해결을 맡겼고, 여기서부터 예상치 못한 두 번째 문제가 발생했습니다.
2. AI의 '실시간 자동 모델 선택' 추천과 RPD 소진 및 과금 문제
AI 어시스턴트는 404 에러를 방지하겠다며 구글 API(GET /v1beta/models)를 호출하여 실시간으로 가용한 모델을 자동으로 탐색하여 번역 런타임에 즉시 꽂아 넣는 자동 모델 선택 모듈을 제안했습니다.
// translator.js: 모델별 RPD와 단가를 고려하지 않는 자동 선택 (결함 코드)
async function getRuntimeModel(apiKey) {
const models = await fetchAvailableModelsFromAPI(apiKey);
// 치명적 결함: RPD 한도나 토큰 단가를 전혀 고려하지 않고 임의의 모델을 선택!
return models.find(m => m.supportsGeneration).name;
}
발생한 RPD 소진 및 과금 메커니즘 분석
이 자동 탐색 모듈은 각 AI 모델의 쿼터 구조와 토큰 단가(Pricing) 격차를 전혀 구분하지 못했습니다:
| 엔진 분류 | AI 자동 선택의 동작 방식 | 실제 발생한 운영 문제 |
|---|---|---|
| Google Gemini | 모델별로 일일 허용량(RPD)이 다른데, RPD 한도가 극히 적은 상위/구형 모델을 임의 선택 | 전체 계정의 무료 번역 여유가 남아있음에도 해당 모델의 RPD가 조기 소진되어 번역 중단 |
| OpenAI GPT | 초저가 모델 대신 요청당 단가가 수십 배 비싼 상위 모델(gpt-4o)을 임의 호출 |
선결제 방식으로 충전해 둔 $10 크레딧이 단시간에 소진되는 비용 누수 발생 |
3. 실측 데이터로 살펴본 모델별 쿼터 및 단가 구조
트러블슈팅 과정에서 각 공급자의 API 정책을 면밀히 분석한 결과, 모델군에 따라 쿼터와 단가가 극명하게 나뉘어 있음을 확인했습니다.
(1) Google Gemini의 3대 쿼터 축 (RPM / TPM / RPD)
Gemini 무료 API는 RPM(분당 요청 수), TPM(분당 토큰 수), RPD(일일 요청 수)라는 세 가지 축으로 엄격히 관리되며, Pro → Flash → Flash-Lite 순으로 허용량에 큰 차이가 있습니다:
| 모델 라인업 | 대표 모델 | 무료 허용량 (RPM / TPM / RPD) | 특징 및 적합성 |
|---|---|---|---|
| Pro 계열 | Gemini 2.5 Pro, 3.1 Pro | 0 / 0 / 0 (무료 티어 미제공 또는 극소) | 웹페이지 대량 번역용으로 사용 불가 |
| Flash 계열 | Gemini 2.5/3.5/3.6/3.7 Flash | 5 RPM / 250K TPM / 20 RPD | 일일 요청 수(RPD 20)가 매우 적어 단시간 내 소진 |
| Flash-Lite 계열 | Gemini 3.1/3.5 Flash Lite | 15 RPM / 250K TPM / 500 RPD | Flash 대비 RPD가 25배(500회)에 달해 대량 번역에 최적 |
gemini-flash-lite-latest는 구글이 제공하는 최신 안정화 버전 중 무료 허용량(RPD/RPM)이 가장 넉넉한 Flash-Lite 모델을 자동으로 가리키는 공식 별칭(Alias)입니다.
(2) OpenAI GPT의 모델별 1M 토큰당 가격 구조
OpenAI는 무료 플랜이 없으며 선결제 크레딧 기반으로 과금됩니다. 모델별 1M(100만) 토큰당 입출력 단가는 다음과 같이 큰 차이를 보입니다:
| 모델명 | 입력 단가 (Input / 1M) | 출력 단가 (Output / 1M) | 비고 및 특성 |
|---|---|---|---|
| GPT-5.4 Pro | $30.00 | $180.00 | 최고성능 전문가 추론 모델 |
| GPT-5.4 mini | $0.75 | $4.50 | 성능과 비용 균형형 미드레인지 |
| GPT-5.4 nano | $0.20 | $1.25 | 초경량 초저가 번역 최적화 모델 |
| GPT-5.6 Sol | $5.00 | $30.00 | 최신 플래그십 모델 |
| GPT-5.6 Terra | $2.00 | $12.00 | 미드레인지 균형 모델 |
| GPT-5.6 Luna | $0.20 | $1.20 | 대량 처리용 초저가 모델 |
| GPT-4o | $2.50 | $10.00 | 구형 멀티모달 주력 모델 |
| GPT-4o mini | $0.15 | $0.60 | 경량형 가성비 모델 |
만약 초저가 모델인 GPT-5.4 nano($0.20)나 GPT-5.6 Luna($0.20) 대신 GPT-4o($2.50)나 GPT-5.4 Pro($30.00)가 호출될 경우, 입력 기준 12배에서 무려 150배 이상의 비용 차이가 발생하므로 런타임 자동 선택은 반드시 배제되어야 합니다.
4. 해결책 1: 자동 선택 전면 폐기 및 '사용자 직접 모델 고정' 전환
번역 런타임 내의 위험한 자동 모델 선택기를 전면 폐기하고, 사용자가 RPD와 단가가 검증된 초가성비 모델을 설정창에서 직접 명시하여 고정 사용하는 방식으로 전환했습니다:
- Google Gemini:
gemini-flash-lite-latest를 고정하여 500 RPD의 넉넉한 한도 내에서 100% 무료 완주 달성. - OpenAI GPT:
gpt-5.4-nano,gpt-5.6-luna, 또는gpt-4o-mini를 고정하여 토큰 단가를 최소화하고 크레딧 조기 소진 방어.
5. 해결책 2: 가용 모델 조회를 '옵션 헬퍼 도구'로 변형 분리
사용자가 직접 모델명을 입력하도록 변경하자, "현재 제공되는 최신 모델명이 정확히 무엇인지 알기 어렵다"는 사용성 문제가 생겼습니다.
이에 AI가 만들었던 실시간 가용 모델 조회 API(GET /v1beta/models)를 번역 실행 경로에서 완전히 떼어내어, 옵션 페이지에서 사용자가 현재 쓸 수 있는 최신 모델 목록을 손쉽게 검색하고 복사할 수 있는 '모델 탐색 헬퍼(Model Helper)' 도구로 변형 재활용했습니다.
// src/options/model_helper.js: 옵션 UI 전용 가용 모델 탐색 헬퍼
export async function fetchAvailableModelList(apiKey) {
try {
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`
);
const data = await response.json();
if (data.models && Array.isArray(data.models)) {
// 텍스트 생성을 지원하는 모델 목록을 필터링하여 사용자에게 안내
return data.models
.filter(m => m.supportedGenerationMethods?.includes("generateContent"))
.map(m => m.name.replace("models/", ""));
}
} catch (err) {
console.warn("[ModelHelper] 모델 목록 조회 실패:", err);
return ["gemini-flash-lite-latest", "gemini-1.5-flash"];
}
}
향후 모델 인스펙터로의 발전 방향
현재는 가용 모델의 이름 목록만 단순 조회하지만, 향후에는 이를 발전시켜 무료 모델은 RPM, TPM, RPD 쿼터 지표를 표 형태로 보여주고, 유료 모델은 입력·출력 단가를 함께 시각화하여 사용자가 비용과 쿼터를 한눈에 비교하고 최적의 모델을 선택할 수 있는 '지능형 모델 인스펙터'로 고도화할 계획입니다.
마무리하며
10편의 RPM 제어에 이어 RPD와 토큰 단가 문제까지 해결하여 비용과 404 장애를 모두 정복했으나, 스팀 커뮤니티나 레딧처럼 특수문자, 이모지, 복잡한 인라인 HTML 태그가 다수 섞인 긴 포럼 글을 번역할 때 간헐적으로 "번역 응답이 비어 있습니다"라는 빈 문자열 응답이 오며 번역이 멈춰버리는 무한 로딩 버그가 나타났습니다.
다음 글에서는 빈 응답(Empty Response) 수신 시 원인을 분석하고 안전하게 재시도하는 Empty Response Retry 메커니즘 구축 과정을 다루어 보겠습니다.
외부 유료 LLM API를 연동할 때 발생할 수 있는 예기치 않은 비용 누수를 방지하기 위해 어떤 모델 관리 방식을 적용하고 계신지 댓글로 여러분의 경험을 공유해 주시기 바랍니다.