“LLM 게이트웨이”라는 하나의 이름이 서로 다른 세 가지 역할을 포괄한다. 소스를 읽어보면 무엇을 라우팅하고 누구를 위한 것인지에 따라 깔끔하게 나뉜다. CLIProxyAPI는 개인 구독 로그인을 풀링한다. LiteLLM은 앱 내부에서 사용량 계측과 장애 조치를 갖춰 API 키를 라우팅한다. Portkey는 요청마다 전달하는 선언적 설정에 따라 라우팅하는 무상태 엣지 게이트웨이다. 나는 CLIProxyAPI v7.2.47, LiteLLM v1.92.0-dev.1, Portkey v1.15.2의 소스를 읽었고, 이들을 가장 선명하게 구별하는 방법은 결국 단 하나의 질문이었다. 게이트웨이는 어떤 상태를 유지하는가? 모든 주장은 근거가 되는 코드 줄을 가리킨다.
단 하나의 질문
게이트웨이는 코드와 모델 프로바이더 사이에 놓인다. 이 셋을 구분하는 것은 라우팅하는 단위와 이를 위해 보유하는 상태다.
- 구독 계정 — 이미 정액 요금을 내고 있는 로그인을 풀링한다. (CLIProxyAPI)
- API 키 / 배포 — 신뢰성을 위해 프로바이더 전반에 걸쳐 사용량 기반의 토큰당 과금 키를 분산한다. (LiteLLM)
- 어떤 요청이든 — 설정에 이름이 지정된 어떤 프로바이더로도 변환하고 라우팅하는 무상태 프런트 도어다. (Portkey)
진영 1 — 구독 풀링: CLIProxyAPI
CLIProxyAPI는 이미 정액 요금을 내고 있는 ChatGPT/Claude/Gemini 구독을 더 알차게 쓰기 위해 존재한다. OAuth로 로그인하면, 로그인한 계정들에 요청을 분산한다. CLIProxyAPI 티어다운에서 확인했듯, 구독 한도는 불투명하기 때문에 할당량을 볼 수 없다. 따라서 순전히 반응형으로 작동한다. 프로바이더가 429를 반환할 때까지 계정을 사용하고, 그 뒤 쿨다운 타이머가 만료될 때까지 벤치로 보낸다(selector.go:305). 이 도구가 유지하는 상태는 계정별 쿨다운이다. 개인 구독을 최대한 활용하려는 개인을 위한 진영이지, 팀에 비용을 청구하기 위한 진영은 아니다.
진영 2 — 키 라우팅: LiteLLM
LiteLLM은 토큰당 요금을 내는 API 키를 위한 라우팅 라이브러리이자 선택적으로 쓸 수 있는 프록시다. 기본 동작은 가중치가 적용된 동전 던지기다. simple-shuffle은 정상 상태인 배포 하나를 무작위로 고른다(simple_shuffle.py:59). 하지만 LiteLLM 티어다운에서 확인했듯, 진짜 강점은 모든 키를 계측한다는 데 있다. 스마트 전략은 배포별 tpm/rpm/지연 시간을 캐시에 기록하고 호출 전에 용량을 예약할 수도 있으며(lowest_tpm_rpm_v2.py:116), 그다음 재시도, 쿨다운, 폴백을 거쳐 장애 조치한다. 이 도구가 유지하는 상태는 사용량 미터와 쿨다운이다. 라우팅 로직을 자기 프로세스 안에 두고 유료 프로바이더 전반에서 신뢰성을 확보하려는 개발자나 앱을 위한 진영이다.
진영 3 — 프로덕션 게이트웨이 운영: Portkey
Portkey는 모든 것의 앞에 세우는 인프라다. 무상태 Hono 앱(index.ts:46)으로, Cloudflare Workers 또는 Node에 배포되고, 통합 OpenAI 호환 API를 노출하며(index.ts:147), 프로바이더별 트랜스포머를 통해 각 요청을 약 70개 프로바이더 중 하나에 맞게 변환한다(providers/index.ts:78). 이 설계를 규정하는 선택은 라우팅이 코드가 아니라 데이터라는 것이다. x-portkey-config 헤더에 선언적 config를 전달하면(globals.ts:18), tryTargetsRecursively가 이를 순회한다(handlerUtils.ts:476). 이 함수는 strategy.mode에 따라 fallback(대상을 순서대로 시도), loadbalance(가중 무작위), single로 분기하고, 중첩된 대상으로 재귀 진입한다. 따라서 이를테면 폴백 안에 로드 밸런싱을 넣는 구성을 순수하게 JSON으로 표현할 수 있다(handlerUtils.ts:662). 라우팅 경로에는 사용자별 미터를 전혀 유지하지 않는다. 결정은 설정과 실시간 응답 코드만으로 이뤄지는 순수 함수다. 바로 이 선택 덕분에 워커 수를 늘리는 만큼 수평 확장할 수 있다. 그 위에 가드레일과 캐시 플러그인을 더한다. 많은 앱과 사용자의 앞단을 맡는 플랫폼 팀을 위한 진영이다.
비교
| CLIProxyAPI | LiteLLM | Portkey | |
|---|---|---|---|
| 라우팅 대상 | 구독 OAuth 계정 | API 키 / 배포 | 어떤 요청이든, 약 70개 프로바이더로 |
| 대상 사용자 | 구독을 풀링하는 개인 | 개발자 / 앱 | 플랫폼 팀 |
| 라우팅 결정 방식 | 반응형 쿨다운(코드) | 계측 + 전략 | 요청별 선언적 설정 |
| 유지하는 상태 | 계정별 쿨다운 | 사용량 미터 + 쿨다운 | 라우팅 경로에는 없음 |
| 비용/사용량을 보는가? | 아니요(불투명한 할당량) | 예(키를 계측) | 응답 코드를 통해서만 |
| 제공 형태 | 셀프 호스팅하는 Go 바이너리 | Python 라이브러리 / 프록시 | Hono 엣지 워커 |
| 대표 부가 기능 | 세션 어피니티 | 예산 및 폴백 | 가드레일, 캐시, 중첩 가능한 설정 |
이 구분이 모든 것을 결정하는 이유
상태 열이 이야기의 전부다. CLIProxyAPI는 구독 할당량을 볼 수 없기 때문에 반드시 반응형으로 작동해야 한다. 그래서 쿨다운 상태를 유지하고 429를 기다린다. LiteLLM은 키를 계측하기 때문에 선제적으로 작동할 수 있다. 그래서 사용량 상태를 유지하고 이를 토대로 라우팅한다. Portkey는 의도적으로 라우팅 상태를 전혀 유지하지 않는다. 그래서 엣지에서 동일한 무상태 워커 무리로 실행할 수 있고, 라우팅 결정을 사용자가 제공하는 설정으로 밀어낸다. 이들은 같은 것의 세 가지 변형이 아니다. 지능과 상태가 어디에 있어야 하는지를 두고 서로 다른 세 가지 선택을 한 것이다. 반응형 스케줄러, 계측 라이브러리, 선언적 프런트 도어 가운데 하나다.
나는 어디에 해당할까?
- 개인적으로 ChatGPT/Claude/Gemini 구독 몇 개의 요금을 내고 있고 이들을 풀링하고 싶다 → CLIProxyAPI. 다만 초기화 윈도우를 읽는 것이 아니라 추측한다는 점은 알아야 한다. 이것이 해당 티어다운의 핵심 결론이다.
- 앱을 만들고 있으며 유료 API 키 전반에서 신뢰할 수 있는 사용량 기반 라우팅을 인프로세스로 원한다 → LiteLLM. 전략을 설정하고, 미터를 공유할 수 있도록 가능하면 Redis도 설정하자. 기본값은 가중 무작위일 뿐이다.
- 여러 앱 앞에 통제된 프런트 도어를 두는 플랫폼 팀이다 → Portkey. 선언적 설정, 가드레일, 캐싱, 무상태 엣지 배포는 정확히 이 용도를 위해 만들어졌다. 그 대가로 게이트웨이를 직접 운영해야 한다.
범위와 정직함
나는 CLIProxyAPI v7.2.47 (00114be), LiteLLM v1.92.0-dev.1 (88e03e5), Portkey v1.15.2 (ca77129)를 읽었다. CLIProxyAPI와 LiteLLM의 메커니즘은 링크한 각 티어다운에서 소스와 대조해 검증했다. Portkey의 무상태 설정 게이트웨이 구조는 이 커밋에서 반증을 시도하는 claim-verifier를 거쳤고 확인됐다. 철저히 살펴보고도 무상태라는 주장은 살아남았다. 회로 차단기와 예산 훅은 주입되지만 오픈 소스 레포에서는 연결되지 않는다.
정직하게 밝혀둘 점이 두 가지 있다. 이 진영들은 각 도구를 가두는 울타리가 아니라 무게중심을 나타낸다. LiteLLM도 독립 실행형 프록시로 실행되고 Portkey도 셀프 호스팅할 수 있어 중간 영역에서는 겹친다. 그리고 혼동하지 말아야 할 숫자가 하나 있다. Portkey는 “250개 이상의 LLM”을 내세우지만 이는 모델 수다. 레포에 있는 프로바이더 통합은 약 70개다. 이로써 내가 분석하려 했던 세 가지 시리즈인 코딩 에이전트, 로컬 추론, 게이트웨이는 각각 허브 글을 갖추게 됐다.


