모델 라우팅
하나의 API로 여러 AI 모델 라우팅하기: 키 하나로 모든 모델 호출
OpenAI 호환 Base URL과 API 키 하나로 Claude, Gemini, DeepSeek 등을 호출하는 방법. 설정, 모델 전환, 비용 확인, 장애 대응을 정리합니다.
하나의 키로 모든 모델 호출하기
하나의 API로 여러 AI 모델을 라우팅한다는 것은 애플리케이션이 Base URL, API 키, 요청 형식을 각각 하나만 유지하고 model 필드로 어느 업스트림 공급자가 처리할지 결정하는 구조입니다. 공급자마다 자격 증명을 따로 보관할 필요가 없고, 새 모델이 추가되어도 클라이언트를 고치지 않아도 됩니다.
실질적인 이점은 운영에 있습니다. 잔액 하나, 유출된 키 교체 지점 하나, 로그 한 곳, 모델 전환 지점 하나입니다. 대신 게이트웨이가 요청 경로에 들어가므로 무엇을 전달하고 토큰을 어떻게 계산하는지 검증해야 합니다. 이 글에서는 최소 구성, 같은 키로 서로 다른 모델 계열을 호출하는 방법, 청구서와 대조하는 절차를 다룹니다.
모델 이름을 보낸 뒤 벌어지는 일
라우팅을 결정하는 요소는 보통 네 가지입니다. 모델 식별자, 메시지 본문, 스트리밍 여부, 그리고 도구 정의나 캐시 제어 같은 선택 필드입니다. 게이트웨이는 모델 이름을 사용 가능한 채널과 가격 그룹으로 해석하고, 요청을 해당 공급자의 기본 프로토콜로 변환한 뒤 응답을 SDK가 기대하는 형식으로 되돌립니다.
운영 전에 두 가지를 확인해야 합니다. 첫째, 모델 이름에는 버전이 있습니다. 테스트 환경에서 되는 이름이 내 키의 그룹에 존재한다는 보장은 없습니다. 둘째, 추상화는 무료가 아닙니다. 프롬프트 캐싱, 도구 호출, 구조화 출력, 추론 토큰은 게이트웨이가 실제로 전달할 때만 동등합니다. 텍스트 응답이 한 번 성공했다고 호환성을 가정하지 말고 의존하는 기능을 각각 검증하세요.
| 필드 | 역할 |
|---|---|
| model | 어느 채널과 가격 그룹에서 처리할지 결정 |
| messages / input | 업스트림에 전달되는 문맥, 입력 토큰의 주된 원인 |
| stream | SSE로 반환할지, 하나의 JSON으로 반환할지 |
| tools, response_format | 공급자 고유 기능 전달이 필요한지 |
키 하나를 준비하고 첫 요청 보내기
API 연결에서 키를 만들고 요금 페이지에서 사용할 모델을 확인하세요. 첫 확인은 OpenAI 호환 엔드포인트로 충분합니다. 대상 모델이 OpenAI 모델이 아니어도 게이트웨이가 변환합니다. 첫 호출은 출력 한도를 낮추고 도구와 캐시를 쓰지 않는 작은 요청으로 시작하세요.
키는 코드에 쓰지 말고 환경 변수에 두세요. macOS나 Linux에서는 export API_ROUTE_KEY=..., Windows PowerShell에서는 $env:API_ROUTE_KEY="..."를 사용합니다. 아래 예제는 채팅 완료를 한 번 보내고 결과를 출력합니다.
export API_ROUTE_KEY="sk-your-key"
curl https://global.api-route.com/v1/chat/completions \
-H "Authorization: Bearer $API_ROUTE_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"messages": [{"role": "user", "content": "Name one trade-off of multi-model routing."}],
"max_tokens": 80
}'200과 choices 배열이 오면 요청이 공급자에 도달한 것입니다. 하지만 스트리밍, 도구, 캐시, 두 번째 모델 계열이 동작한다는 증거는 아닙니다. 기능을 하나씩 추가로 검증하고 응답 본문의 모델 이름과 usage를 확인하세요.
SDK를 바꾸지 않고 모델 계열 전환하기
같은 키로 Claude, Gemini, DeepSeek, Qwen 등 서로 다른 계열을 호출할 수 있는 이유는 게이트웨이가 프로토콜을 변환하기 때문입니다. 채팅 형태의 트래픽은 모두 /v1/chat/completions로 보내고 model 값만 바꾸면 됩니다. 요청과 응답 구조는 OpenAI 호환이 유지됩니다.
프롬프트 캐싱이나 특정 베타 헤더처럼 Anthropic 고유 동작이 필요하면 /v1/messages와 Anthropic 형식을 사용하세요. 두 엔드포인트는 같은 계정 잔액을 공유하기 때문에 단일 키 구성이 가능합니다. 참고 규격: OpenAI Chat Completions, Anthropic Messages.
from openai import OpenAI
client = OpenAI(
base_url="https://global.api-route.com/v1",
api_key="sk-your-key",
)
for model in ["claude-sonnet-4-6", "gemini-3.8-flash", "deepseek-v4.1-flash"]:
reply = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": "Reply in one sentence."}],
max_tokens=60,
)
print(model, "->", reply.choices[0].message.content)const models = ["claude-sonnet-4-6", "gemini-3.8-flash"];
for (const model of models) {
const res = await fetch("https://global.api-route.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.API_ROUTE_KEY}`,
},
body: JSON.stringify({
model,
messages: [{ role: "user", content: "Reply in one sentence." }],
max_tokens: 60,
}),
});
const data = await res.json();
console.log(model, "->", data.choices?.[0]?.message?.content ?? data.error?.message);
}루프에서 모델 두 개를 호출하면 라우팅 계층이 의도대로 동작하는지 확인할 수 있습니다. 호출마다 반환된 모델 이름과 usage를 기록하세요. 다른 계열로 조용히 폴백되어도 문장은 자연스러워 보이지만 품질과 요금은 달라집니다.
프로젝트가 아니라 요청 단위로 모델 선택하기
하나의 키로 모든 모델에 도달하면 결정은 "어느 공급자와 계약할까"에서 "이 요청은 어느 모델이 처리할까"로 옮겨갑니다. 먼저 트래픽을 분류하세요. 짧은 분류와 추출에는 최상위 모델이 거의 필요 없고, 긴 문맥 분석에는 필요합니다. 코드 생성과 채팅은 우선순위가 다릅니다. 작업별로 라우팅하고 벤치마크 제목이 아니라 자체 평가 세트로 품질을 측정하세요.
가격 그룹은 모델 이름만큼 중요합니다. 같은 모델이라도 기본 그룹과 할인·기업 그룹의 단가가 다를 수 있고, 실제 지불액은 키가 속한 그룹이 결정합니다. 홍보 숫자로 예산을 세우기 전에 요금 페이지와 사용 로그의 그룹을 확인하세요.
| 워크로드 | 최적화 대상 | 일반적인 선택 |
|---|---|---|
| 분류·추출·태깅 | 1000회당 비용 | 작고 빠른 모델 |
| 긴 문맥 분석·RAG 답변 | 문맥 길이와 캐시 비용 | 중간 모델과 프롬프트 캐싱 |
| 복잡한 코딩·에이전트 루프 | 정확성과 도구 사용 | 최상위 모델, 낮은 온도 |
| 대량 생성 | 출력 단가와 처리량 | 중간 모델과 배치 처리 |
라우트와 청구서 확인하기
트래픽을 늘리기 전에 세 가지를 확인하세요. 같은 키로 계획한 모델을 나열할 수 있는지, 응답이 요청한 모델을 반환하는지, 로그의 과금이 usage와 일치하는지입니다. 모델 나열이 가장 저렴한 점검이며 좁은 그룹에 묶인 키를 찾아냅니다.
curl -s https://global.api-route.com/v1/models \
-H "Authorization: Bearer $API_ROUTE_KEY" \
| jq -r '.data[].id' | sort다음으로 한 번의 응답을 호출 로그의 해당 행과 비교하세요. 모델, 입력·출력 구분, 캐시 필드 존재 여부를 봅니다. 캐시된 요청은 읽기량을 따로 보고해야 합니다. 전혀 보고하지 않는다면 할인이 아니라 미검증으로 취급하세요. 모델 계열마다 기록 표본을 하나씩 남기면 이후 가격이나 라우트 변경을 알아챌 수 있습니다.
자주 겪는 실패와 그 의미
첫 주의 문제는 대부분 모델 품질이 아니라 설정입니다. 먼저 확인할 오류를 표로 정리했습니다. 문의할 때는 시각, HTTP 상태, 모델 이름, 요청 ID를 포함하고 키 자체는 붙이지 마세요.
| 증상 | 가능한 원인 | 먼저 확인할 것 |
|---|---|---|
| 404 모델 없음 | 키 그룹에서 활성화되지 않음 | /v1/models로 목록을 확인하고 요금 페이지와 대조 |
| 401 또는 403 | 키 누락·폐기·잘못된 헤더 | Authorization: Bearer 사용, 노출되었다면 즉시 교체 |
| 400이 스키마 필드를 지목 | 변환되지 않은 키워드를 공급자가 거부 | 해당 필드를 빼고 재시도하고 파라미터를 보고 |
| 스트리밍이 멈추거나 한 번에 도착 | 프록시 버퍼링 또는 미지원 스트림 옵션 | stream을 끄고 재시도한 뒤 짧은 프롬프트로 확인 |
| 예상보다 비쌈 | 그룹 차이 또는 읽기가 아닌 쓰기 발생 | usage와 로그 행을 맞추고 그룹 확인 |
200인데 다른 모델처럼 동작한다면 고정 프롬프트로 몇 번 비교하고 반환된 모델 이름도 확인하세요. 일관성 문제는 대개 프롬프트가 아니라 라우팅이나 폴백 정책에서 비롯됩니다.
운영 체크리스트
- 키는 시크릿 관리나 환경 변수에 두고 교체용 두 번째 키를 준비합니다.
- 유동 별칭 대신 명시적 모델 ID를 고정하고 검증한 버전을 기록합니다.
- 설정을 바꿀 때마다 모델 계열별 고정 스모크 요청을 보냅니다.
- 총지출뿐 아니라 오류율과 요청당 비용에 알림을 설정합니다.
- 트래픽을 늘리기 전에 첫 청구서를 기록한 usage와 대조합니다.
이 다섯 가지가 갖춰지면 하나의 키와 Base URL로 여러 모델 계열을 라우팅할 수 있습니다. 소액 잔액으로 계정을 만들고, 키로 사용 가능한 모델 목록을 확인한 뒤 빠른 시작에 따라 첫 요청을 보내세요.