TrendShift 딥다이브 · 2026-07-13 · Daily #3

CLIProxyAPI 딥다이브
— AI 구독 계정을 OpenAI/Gemini/Claude 호환 API로 바꾸는 Go 게이트웨이

CLIProxyAPIClaude Code · ChatGPT/Codex · Gemini CLI 같은 "구독형 AI 계정"을 표준 API 엔드포인트로 바꿔주는 Go 프록시 서버다. 핵심 아이디어는 하나다 — 각 벤더의 공식 CLI 도구가 쓰는 OAuth 로그인 세션을 재사용해서, 월정액 구독을 마치 종량제 API처럼 /v1/chat/completions(OpenAI)·/v1/messages(Claude)·/v1beta(Gemini)로 호출하게 해준다. "API 키 없이(no API keys)" 구독을 코드에서 쓴다는 게 이 프로젝트의 한 줄 요약. 이 문서는 "CLIProxyAPI를 설치하는 법"이 아니라, 멀티 프로바이더 API 게이트웨이를 어떻게 설계하는가를 공부하기 위한 자료다 — API 포맷 변환 계층·OAuth 인증·계정 풀 로드밸런싱·SSE 스트리밍의 기술 밀도가 매우 높은 교재. 동시에 이 도구가 딛고 선 이용약관(ToS) 회색지대도 솔직하게 짚는다. (저장소: router-for-me/CLIProxyAPI · Go 1.26 · 766개 .go 파일 ~24.7만 줄 · v7.2.71 · MIT 라이선스 · 매우 활발 · TrendShift 오늘 Daily #3)
목차
  1. 프로젝트 한 줄 요약
  2. 왜 지금 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트
  7. 하드웨어 · 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한 줄 요약

이름부터 뜯어보기 — CLI(명령줄 도구) + Proxy(대리 중계) + API = "CLI 계정을 대신 중계해주는 API"

CLIProxyAPI"Claude Pro/Max·ChatGPT Plus·Gemini 같은 월정액 AI 구독 계정을, 표준 API처럼 프로그램에서 호출할 수 있게 중계해주는 프록시 서버"다. 이름을 풀면 CLI(각 벤더의 공식 명령줄 도구가 로그인한 세션) + Proxy(그 세션을 대신 붙잡고 요청을 중계) + API(OpenAI/Claude/Gemini 호환 HTTP 엔드포인트로 노출). 즉 "내가 이미 구독한 계정을, API 키를 따로 발급받지 않고도 API 클라이언트·SDK·에이전트에서 쓰게 해준다".

한 문장 비유

"놀이공원 자유이용권을, 놀이기구 하나하나 표로 바꿔주는 매표 대행소"

보통 AI API는 놀이기구를 탈 때마다 표를 사는 종량제다 — 토큰을 쓴 만큼 돈을 낸다(OpenAI/Anthropic API 키). 반면 Claude Code·Codex·Gemini CLI 구독은 월정액 자유이용권이다 — 정해진 한도 안에서 마음껏 탄다. 문제는 이 자유이용권이 "그 공식 CLI 앱 안에서만" 통한다는 것.

CLIProxyAPI는 그 자유이용권을 대신 들고 서서, 누가 표를 달라고 하면(=API 요청) 자기 이용권으로 기구를 태워주는 매표 대행소다. 내 코드는 평범한 OpenAI API를 부르는 것처럼 요청하고, 프록시가 뒤에서 구독 세션으로 실제 응답을 받아 형식을 맞춰 돌려준다. 여러 계정을 모아 돌려 쓰면(로드밸런싱) 한도도 늘어난다.

핵심 용어
API 게이트웨이 / 프록시 (Gateway / Proxy)
클라이언트와 실제 백엔드 사이에 서서 요청을 대신 받아 뒤로 넘기고, 응답을 다시 가공해 돌려주는 중계 서버. CLIProxyAPI는 앞쪽으로는 OpenAI/Claude/Gemini 호환 API를 열어두고, 뒤쪽으로는 각 벤더의 구독 세션에 연결한다. 여러 상류(upstream) 제공자를 하나의 엔드포인트로 묶는다는 점에서 LiteLLM·one-api·OmniRoute와 같은 계열이다.
핵심 용어
OAuth 로그인 세션 재사용
공식 CLI 도구(Claude Code 등)로 로그인하면 브라우저 OAuth 절차를 거쳐 액세스 토큰이 발급된다. CLIProxyAPI는 바로 그 공식 CLI와 동일한 로그인 흐름을 자기가 대신 밟아 토큰을 얻고, 그걸로 구독 계정에 접속한다. 이 방식 덕분에 "API 키"가 필요 없지만, 동시에 이 프로젝트가 서 있는 약관 회색지대의 뿌리이기도 하다(§6 주의).

2왜 지금 주목받는가

TrendShift Daily #3 · "구독을 API로" + "멀티 프로바이더 한 엔드포인트"라는 실용성

2026년 개발자들에게 AI 도구 비용은 현실적 고민이다. 에이전트·자동화를 굴리면 종량제 API 토큰 요금이 순식간에 불어난다. 반면 Claude Max·ChatGPT Pro 같은 월정액 구독은 한도가 넉넉하지만 "공식 앱 안에서만" 쓸 수 있다. CLIProxyAPI는 이 간극을 정확히 노린다 — 이미 낸 구독료로 API를 쓰게 해주는 것. 여기에 여러 벤더·여러 계정을 한 엔드포인트로 묶는 실용성이 더해져 X·해커뉴스에서 화제가 되며 Daily 최상위로 올라왔다. (TrendShift의 굵은 숫자는 별 수가 아니라 사회적 언급 모멘텀이다.)

주목 포인트 1 — "구독을 API로" (토큰 과금 회피)

핵심 가치 제안은 명확하다: 종량제 API 대신, 이미 결제한 정액 구독을 API처럼 쓴다. Cursor·Windsurf가 토큰 단위로 과금하는 것과 달리, 구독 한도 안에서 자율 에이전트·배치 작업을 돌릴 수 있다. DesktopCommanderMCP 같은 도구가 내세운 "API 토큰이 아닌 구독으로 굴린다"와 같은 철학이되, CLIProxyAPI는 이를 표준 API 엔드포인트 형태로 일반화했다.

주목 포인트 2 — 멀티 프로바이더를 하나의 엔드포인트로

Codex(ChatGPT)·Claude Code·Gemini CLI·Google Antigravity·Moonshot Kimi·xAI Grok·Vertex AI·AI Studio까지 10종의 상류 실행기(executor)를 내장하고, 이를 전부 OpenAI/Claude/Gemini 호환 포맷으로 노출한다. 클라이언트는 어느 모델을 쓰든 익숙한 OpenAI API 한 벌로 말을 걸면 된다 — 백엔드가 무엇이든 프록시가 형식을 번역해준다.

주목 포인트 3 — 계정 풀 로드밸런싱 · 자동 장애조치

여러 계정을 등록하면 round-robin(순번 분배)이나 fill-first(한 계정을 다 쓰고 다음으로) 전략으로 요청을 여러 구독에 분산한다. 한 계정이 한도 초과(429)되면 자동으로 다른 계정으로 넘기고(cooldown/failover), 세션 고정(session affinity)으로 대화 연속성도 유지한다. "여러 API 키를 묶어 한도를 늘리는" 게이트웨이의 전형적 기능을 구독 계정에 적용한 셈.

주목 포인트 4 — 매우 활발한 개발 + 임베더블 SDK

최신 커밋이 2026-07-13(PR #4190)일 만큼 개발이 빠르고, 릴리스가 v7.2.71까지 찍혔다. 게다가 sdk/ 아래에 Go 라이브러리로 임베드할 수 있는 미러 구조를 제공해, 다른 프로젝트가 CLIProxyAPI를 통째로 삼켜 쓰기도 한다(README의 "built on" 목록). 파일·Postgres·Git·S3 토큰 저장소, 클러스터(control-plane) 모드까지 갖춘, 완성도 높은 인프라 소프트웨어.

비교 축CLIProxyAPILiteLLM / one-api공식 벤더 API
뒤에 붙는 대상구독 계정(OAuth 세션)API 키(종량제)API 키(종량제)
과금 모델월정액 구독 재활용토큰 종량제 그대로토큰 종량제
노출 포맷OpenAI·Claude·Gemini·Codex 호환주로 OpenAI 호환각 벤더 고유
구현 언어GoPython / Go
약관 위치회색지대(§6)합법(정식 키)합법(정식 키)
덧붙임 — "그럼 이건 합법인가?"

기술적으로 CLIProxyAPI는 내 계정의 정당한 토큰을 쓴다. 하지만 각 벤더의 이용약관은 대개 "구독은 공식 클라이언트에서, 개인 용도로"를 전제하고, API 재판매·자동화 우회를 금지하기도 한다. 즉 코드 자체는 인프라 소프트웨어지만, 쓰는 방식에 따라 약관 위반 소지가 생긴다. 실제로 이 프로젝트는 공식 CLI인 척 트래픽을 위장(uTLS 지문 스푸핑)하는 코드까지 품고 있다(§6). 이 문서는 설계를 공부하려는 것이지, 약관 위반을 권하는 게 아니다 — 이 경계를 분명히 인지하고 읽어야 한다.

3기술 스택 전체 지도

Go 1.26 · Gin · gjson/sjson · uTLS · gorilla/websocket · Bubbletea · pgx

CLIProxyAPI는 단일 Go 모듈이다. 모듈 경로가 github.com/router-for-me/CLIProxyAPI/v7로, 메이저 버전 7이 경로에 박혀 있다(Go의 semantic import versioning). 언어는 Go 1.26.0(꽤 최신), 빌드에 CGO_ENABLED=1이 필요하다 — C-ABI 동적 라이브러리 플러그인을 지원하기 때문(§4-6). 규모는 937개 추적 파일 중 766개가 .go, 총 약 247,614줄로, 사실상 Go 100%다.

정확성 주의 — 모델 카탈로그가 "미래 시점"
models.json에 아직 없는 모델명이 들어 있다 (claude-opus-4-8 · gpt-5.5 …)

레포의 internal/registry/models/models.json(2,559줄)에는 claude-opus-4-8·claude-sonnet-5·gemini-3.5-flash·gpt-5.5 같은 근미래/추정 모델명이 실려 있고, 런타임에 원격 업데이터가 목록을 갱신한다(--local-model로 끄기 가능). 2026년이라는 저장소의 시간 설정과 일치하는 부분이니, 실제 사용 시 지금 내 계정이 접근 가능한 모델과 카탈로그가 다를 수 있음을 감안하라. 이 딥다이브의 수치·구조는 전부 클론한 소스 기준이다.

HTTP · JSON · 네트워크 계층

영역패키지 (버전)역할 / 왜 골랐나
HTTP 프레임워크gin-gonic/gin 1.10.1모든 API 라우트·미들웨어의 뼈대
JSON "수술"tidwall/gjson 1.18 + sjson 1.2.5구조체 대신 경로로 JSON 필드를 읽고 쓰는 방식 — 번역 계층 전체가 이걸로 굴러감
TLS 지문 위장refraction-networking/utls 1.8.2Chrome ClientHello를 흉내 내 Cloudflare TLS 지문 검사 우회(Anthropic 도메인)
WebSocketgorilla/websocket 1.5.3Codex/xAI ws 실행기 · /v1/responses ws · wsrelay
OAuthgolang.org/x/oauth2 0.30각 벤더 OAuth2 로그인 흐름
토큰 카운팅tiktoken-go/tokenizer 0.7요청 토큰 수 계산(과금·한도 추적)

저장소 · 운영 · TUI 계층

영역패키지 (버전)역할
로깅sirupsen/logrus 1.9.3 + lumberjack.v2 2.2.1구조화 로그 + 로그 회전
터미널 UIcharmbracelet/bubbletea 1.3.10 · bubbles · lipgloss--tui 대화형 터미널 대시보드
토큰 저장소jackc/pgx v5 · minio-go v7 · go-git v6인증 토큰을 Postgres · S3 · Git 저장소에 보관(파일이 기본)
사용량 큐redis/go-redis v9.19사용량 통계 보존 큐(선택)
설정 핫리로드fsnotify/fsnotify 1.9 + joho/godotenv 1.5config.yaml 변경 자동 감지 · .env 자동 로드
압축andybalholm/brotli · klauspost/compress응답 브로틀리/gzip 처리

배포 · 빌드

Dockerfile은 golang:1.26-bookworm(빌더) → debian:bookworm(런타임) 2단계로, CGO_ENABLED=1과 ldflags로 버전/커밋/빌드일자를 주입하고 EXPOSE 8317을 연다(공식 이미지 eceasy/cli-proxy-api). docker-compose.yml은 API 포트 8317과 함께 OAuth 콜백용 포트들(8085·1455·54545·51121·11451)을 매핑하고, config.yaml·auths/·logs/를 볼륨으로 뺀다. GitHub Actions의 release.yamldarwin·windows·linux(glibc/musl)·freebsd 멀티플랫폼 매트릭스 빌드를 돌린다(musl/freebsd는 플러그인 없는 no-plugin 변형).

핵심 용어
gjson / sjson — "구조체 없는 JSON 수술"
보통 Go는 JSON을 구조체(struct)에 맞춰 파싱한다. 하지만 gjsongjson.Get(json, "messages.0.content")처럼 경로 문자열로 값만 콕 집어 읽고, sjson은 같은 방식으로 값을 써넣는다. CLIProxyAPI의 번역 계층은 상류마다 스키마가 제각각이라, 고정 구조체 대신 이 "경로 기반 수술"로 필드를 이리저리 옮긴다. 유연하지만 타입 안전성은 포기하는 트레이드오프.

4아키텍처 심화 분석

부팅 → 번역 계층 · 상류 실행기 · OAuth 인증 · 라우팅 지휘자 · SSE 스트리밍 · 플러그인

이 섹션의 첫 문장만 기억해도 절반은 이해한 것이다: CLIProxyAPI = "들어온 요청을 표준 포맷으로 정규화 → 알맞은 계정을 고르고 → 그 상류의 포맷으로 번역해 보내고 → 응답을 다시 클라이언트 포맷으로 되번역"하는 파이프라인이다. 이 파이프라인이 세 개의 큰 근육 위에서 돈다 — ①번역 계층(translator) · ②상류 실행기(executor) · ③라우팅 지휘자(conductor). 각각이 이 레포에서 가장 큰 패키지들이다.

4-0. 전체 요청 흐름 한눈에

┌──────────────────────────────────────────────────────────────┐ │ cmd/server/main.go (773줄) │ │ · 저장소 백엔드 결정(file / Postgres / Git / S3 / home) │ │ · config.yaml 로드 · 토큰 저장소 등록 · 플러그인 호스트 │ │ · 로그인 명령( -claude-login 등) 또는 서버 기동 분기 │ └───────────────────────────┬──────────────────────────────────┘ ▼ Gin 서버 (internal/api/server.go, 2018줄) : EXPOSE 8317 POST /v1/chat/completions · /v1/messages · /v1beta/... · ws │ ┌───────────────────┼────────────────────────┐ ▼ ▼ ▼ ① translator ③ conductor(6157줄) ② executor(×10) 들어온 포맷 → 계정 선택·순번분배· Codex/Claude/ canonical(정규형) 쿨다운·장애조치· Gemini/Antigravity/ → 상류 포맷 번역 토큰 자동갱신 Kimi/xAI/Vertex... │ │ │ └───────────────────┴────────────────────────┘ ▼ 선택된 구독 계정으로 실제 호출 (uTLS 지문 위장) ▼ 응답 → translator 되번역 → SSE 스트림으로 클라이언트에

4-1. 번역 계층 — 스키마가 제각각인 API들을 하나로 잇기

internal/translator/는 이 레포에서 가장 큰 패키지다(약 45,554줄). 문제의 본질은 이렇다: OpenAI·Claude·Gemini·Codex는 요청/응답 JSON 스키마가 전부 다르다. 클라이언트가 OpenAI 포맷으로 보냈는데 뒤에 붙은 건 Claude라면, 누군가 형식을 바꿔줘야 한다. CLIProxyAPI는 이를 (출발 포맷, 도착 포맷) 쌍을 키로 하는 변환 레지스트리로 푼다.

// sdk/translator/registry.go — (from,to) 쌍으로 변환 함수를 등록
// 7개 표준 포맷: openai, openai-response, claude, gemini,
//                codex, antigravity, interactions
translator.Register(
    "claude", "openai",          // Claude 요청 → OpenAI 스키마
    ConvertClaudeRequestToOpenAI,   // 요청 변환 함수
    ConvertOpenAIResponseToClaude,  // 응답 되번역 함수
)
// 이런 init.go가 26개 — 각자 한 방향(쌍)을 배선한다

변환 함수는 전부 순수 함수 func(model string, rawJSON []byte, stream bool) []byte 꼴이다 — 앞서 본 gjson/sjson으로 도착 스키마의 JSON 틀을 만들고 필드를 하나씩 옮겨 담는다. 스트리밍 변환은 청크 슬라이스([][]byte)를 돌려준다. 만약 (출발,도착)에 맞는 변환기가 없으면 model 필드만 정규화한 채 그대로 통과(passthrough)시킨다.

배울 점 — "정규형(canonical form)" 설계 패턴
N×N 변환을 N개 변환으로 줄이는 허브 발상

포맷이 7개일 때, 모든 쌍을 직접 변환하면 최악 7×6=42가지 조합이 필요하다. 대신 "가운데 정규형(canonical)"을 두고 각 포맷 ↔ 정규형만 만들면 변환 수가 확 준다. CLIProxyAPI의 thinking 모듈이 좋은 예다 — 각 벤더의 "추론(reasoning) 설정"을 일단 canonical ThinkingConfig로 모은 뒤 상류별로 다시 내보낸다. 컴파일러의 중간표현(IR), 환율의 기축통화와 같은 발상.

4-2. 상류 실행기 — 벤더 10종을 같은 인터페이스로

internal/runtime/executor/(약 39,900줄)에는 상류 제공자마다 하나씩 Executor가 있다. 전부 같은 계약(Identifier() + Execute/ExecuteStream)을 구현해, 지휘자가 어느 상류든 똑같이 다룰 수 있다.

// sdk/cliproxy/executor/types.go — 모든 실행기가 지키는 계약
type Request struct {
    Model    string
    Payload  []byte
    Format   sdktranslator.Format
    Metadata map[string]any
}
type StreamResult struct {
    Headers http.Header
    Chunks  <-chan StreamChunk   // 스트리밍은 채널로 흘려보냄
}
type StatusError interface {   // 401/402/429를 인증상태로 올림
    error
    StatusCode() int
}
실행기상류(구독)비고
CodexAutoExecutorChatGPT / CodexHTTP + WebSocket 두 방식
ClaudeExecutorClaude CodeuTLS 지문 위장 · 서명(signing) 처리
GeminiExecutor 외 3종Gemini CLI · Vertex AI · AI StudioInteractions/Vertex/AIStudio 변형
AntigravityExecutorGoogle Antigravity서명 캐싱 · 크레딧 폴백
KimiExecutorMoonshot Kimi
XAIAutoExecutorxAI GrokHTTP + WebSocket
OpenAICompatExecutor범용 OpenAI 호환OpenRouter 등 일반 상류

4-3. OAuth 인증 — 공식 CLI의 로그인 흐름을 그대로 밟기

구독 계정에 붙으려면 각 벤더의 OAuth2 + PKCE 로그인을 통과해야 한다. CLIProxyAPI는 공식 CLI 도구가 쓰는 것과 동일한 client ID를 그대로 사용한다 — 이게 "API 키 없이" 가능한 이유이자, 동시에 §6에서 짚을 회색지대의 뿌리다.

// internal/auth/claude/anthropic_auth.go (개념 발췌)
AuthURL  = "https://claude.ai/oauth/authorize"
TokenURL = "https://api.anthropic.com/v1/oauth/token"
ClientID = "9d1c250a-...-5944d1962f5e"  // 실제 Claude Code 클라이언트 ID
Redirect = "http://localhost:54545/callback"  // 로컬 콜백 서버
Scopes   = "user:profile user:inference user:sessions:claude_code ..."

-claude-login·-codex-login·-antigravity-login·-kimi-login·-xai-login 같은 명령을 실행하면, 프록시가 로컬 포트에 콜백 서버를 잠깐 띄우고 브라우저 OAuth를 유도해 토큰을 받는다(Codex는 device-code 흐름도 지원). 받은 토큰은 기본적으로 ~/.cli-proxy-api의 JSON 인증 레코드로 저장되며(FileTokenStore), 백그라운드 루프가 만료 전 자동으로 갱신한다(auth-auto-refresh-workers). 저장소는 Postgres·Git·S3로도 바꿀 수 있다.

4-4. 라우팅 지휘자(conductor) — 계정 풀을 지휘하는 뇌

sdk/cliproxy/auth/conductor.go6,157줄로 이 레포에서 가장 큰 단일 파일이다. 등록된 여러 계정 중 어느 것으로 이 요청을 보낼지를 결정하고, 실패하면 다른 계정으로 넘기며, 토큰 갱신·쿨다운을 총괄한다.

기능동작
라우팅 전략round-robin(계정을 순번대로) 또는 fill-first(한 계정을 소진 후 다음 — 롤링 한도 분산용)
세션 고정X-Session-ID·conversation_id 등으로 대화를 한 계정에 묶음(기본 TTL 1시간, 불가 시 자동 전환)
쿨다운/한도계정·모델별 쿨다운 상태 추적, 429에 reset_seconds 반환, quota-exceeded 시 프로젝트 전환·프리뷰 모델·Antigravity 크레딧으로 폴백
재시도request-retry(기본 3) · max-retry-credentials · max-retry-interval
모델 풀·별칭같은 별칭을 여러 상류 모델명에 반복하면 내부 라운드로빈 풀 생성(요청 중 장애조치), oauth-model-alias로 채널별 모델명 개명
핵심 용어
round-robin vs fill-first · 세션 고정(affinity)
round-robin은 요청을 계정 A→B→C→A… 순번으로 고르게 분배한다. fill-first는 A를 한도까지 다 쓰고 나서야 B로 넘어간다 — 구독마다 "롤링 윈도우 한도"(예: 5시간당 N회)가 어긋나게 차오르도록 일부러 몰아 쓰는 전략이다. 세션 고정은 한 대화(conversation)를 계속 같은 계정에 보내, 상류가 대화 맥락을 잃지 않게 한다.

4-5. API 표면 · SSE 스트리밍

internal/api/server.go(2,018줄, Gin)가 라우트 표다. OpenAI(/v1/chat/completions·/v1/completions·/v1/responses+ws·/v1/images/*·/v1/videos/*), Claude(/v1/messages·count_tokens), Gemini(/v1beta/models 등), 그리고 Codex 원문 통과(/backend-api/codex/responses)를 연다. 스트리밍은 SSE(text/event-stream)로, 변환기가 청크 슬라이스를 흘려보내고, streaming.keepalive-seconds가 빈 줄을 주기적으로 보내 유휴 타임아웃을 방지한다.

운영 측면에서 특이한 규약: 상류 연결이 열린 뒤에는 타임아웃을 걸지 않는다(긴 에이전트 스트림을 지원하려고). 타임아웃은 "계정 획득 단계"에서만 허용된다. log.Fatal은 금지, 토큰은 로그에 절대 남기지 않는다는 규칙도 AGENTS.md에 명문화돼 있다.

4-6. 관리 API · 플러그인 호스트

/v0/management/* 그룹은 설정·API 키·프로바이더별 키 CRUD·라우팅 전략·플러그인 설치·로그·사용량·쿼터 리셋을 웹으로 제어한다. remote-management.secret-key가 비어 있으면 이 그룹 전체가 404로 잠긴다(로컬에서도 키 필요). 번들 웹 관리 패널이 /management.html로 제공된다.

더 나아가 internal/pluginhost/(약 17,800줄)는 C-ABI 동적 라이브러리 플러그인 호스트다 — Go/C/Rust로 짠 플러그인이 실행기·번역기·인증 제공자·스케줄러 등 ~24종의 확장점에 꽂힌다. 다만 플러그인은 "프로세스 안에서 신뢰되는 코드"라 기본 비활성이고, 설치형 플러그인 스토어가 따로 있다(§6 주의).

5디렉토리 구조 해부

internal/(비공개 구현) + sdk/(임베더블 미러) + cmd/(진입점)
CLIProxyAPI/ # 리포 루트: README · config.example.yaml · Dockerfile · AGENTS.md ├── cmd/ │ └── server/main.go # ★ 진입점(773줄): 저장소 결정 · 로그인 · 서버 기동 ├── internal/ # ── 비공개 구현(가장 큰 코드) ── │ ├── api/ # ★ Gin 서버·라우트·미들웨어 (server.go 2018줄, ~21.8k) │ ├── translator/ # ★ 포맷 변환 (~45.5k줄, 26개 init.go, 최대 패키지) │ ├── runtime/executor/ # ★ 상류 실행기 ×10 (~39.9k줄) │ ├── auth/ # 벤더별 OAuth (claude/codex/antigravity/kimi/xai/vertex) │ ├── pluginhost/ # C-ABI 동적 라이브러리 플러그인 호스트 (~17.8k) │ ├── thinking/ # 추론(reasoning) 설정을 canonical로 정규화 │ ├── registry/ # 모델 카탈로그(models.json) + 원격 갱신 │ ├── store/ # Postgres · Git · S3 토큰 저장소 │ ├── watcher/ # config.yaml 핫리로드(fsnotify) │ ├── wsrelay/ # WebSocket 세션 릴레이 │ ├── home/·homeplugins/ # control-plane 클러스터 모드(mTLS+JWT) │ ├── tui/ # Bubbletea 터미널 UI │ └── cache/·signature/·redisqueue/ # 서명·요청 캐시 · 사용량 큐 ├── sdk/ # ★ 임베더블 Go SDK (internal의 공개 미러) │ ├── cliproxy/ # service.go(2814): 실행기 10종 등록 · 매니저 빌드 │ │ └── auth/conductor.go # ★ 라우팅 지휘자 (6157줄, 최대 단일 파일) │ ├── translator/registry.go # (from,to) 변환 레지스트리 (278줄) │ ├── auth/ # FileTokenStore · device-code 흐름 │ └── api/handlers/ # 스트림 포워더 등 핸들러 └── config.example.yaml # 26KB, 주석 가득한 설정 템플릿

구조를 읽는 요령: "internal은 진짜 구현, sdk는 남이 임포트하는 공개 얼굴"이다. 두 트리가 상당 부분 짝을 이룬다 — internal/은 이 서버 자체의 비공개 구현이고, sdk/는 다른 Go 프로젝트가 CLIProxyAPI를 라이브러리로 삼킬 때 쓰는 안정적 표면이다. 처음 읽는다면 별표(★) 여섯 곳부터 — cmd/server/main.go(부팅) → internal/api/server.go(라우트) → internal/translator/(번역) → internal/runtime/executor/(상류) → sdk/cliproxy/auth/conductor.go(지휘) → sdk/translator/registry.go(변환 등록) 순.

정독 우선순위파일 / 폴더배우는 것
★★★cmd/server/main.go (773)저장소 백엔드 선택 · 로그인/서버 분기 · 부팅 배선
★★★internal/api/server.go (2018)Gin 라우트 표 · 미들웨어 · 관리 API 등록
★★★sdk/translator/registry.go (278)(from,to) 변환 레지스트리 — 번역 계층의 뼈대
★★sdk/cliproxy/auth/conductor.go (6157)계정 선택·쿨다운·장애조치·토큰 갱신의 지휘부
★★internal/translator/openai/claude/*.go (505·793)Claude↔OpenAI 구체 변환 — gjson/sjson 실전
internal/auth/claude/anthropic_auth.go + utls_transport.goOAuth2+PKCE 흐름 · uTLS 지문 위장 RoundTripper

6학습 포인트

이 레포에서 무엇을 훔쳐 배울 것인가 — 그리고 무엇을 조심할 것인가

API 게이트웨이 · 포맷 번역 계층 설계

이 레포의 가장 값진 교재는 "서로 다른 API 스키마를 어떻게 하나로 잇는가"다. (from,to) 레지스트리 + 정규형(canonical) 패턴 + 순수 함수 변환기는 컴파일러의 IR, 데이터 ETL, 마이크로서비스 어댑터 어디서나 재사용되는 보편 설계다. gjson/sjson으로 "구조체 없이 JSON을 수술"하는 실전 코드도 여기서 통째로 읽을 수 있다.

OAuth2 + PKCE 로그인 흐름의 실물

"브라우저를 열어 로그인하고 → 로컬 콜백 서버가 코드를 받고 → 토큰으로 교환하고 → 백그라운드로 자동 갱신"하는 OAuth2+PKCE 전 과정이 벤더별로 구현돼 있다. OAuth를 글로만 배운 사람에게, 콜백 포트·PKCE 챌린지·refresh 루프가 실제 코드로 어떻게 생겼는지 보여주는 살아있는 예제.

계정 풀 로드밸런싱 · 장애조치

conductor.go의 round-robin/fill-first·쿨다운·세션 고정·재시도는 "여러 백엔드를 묶어 한도를 늘리고 장애를 견디는" 분산 시스템의 축소판이다. 로드밸런서·API 게이트웨이·프록시를 설계할 때 필요한 개념(헬스체크·서킷브레이커 유사 쿨다운·스티키 세션)을 한 파일에서 배운다.

Go 서버 엔지니어링 관례

Gin 라우팅·SSE 스트리밍·typed 저장소 추상화(파일/Postgres/Git/S3를 같은 인터페이스로)·fsnotify 핫리로드·Bubbletea TUI·멀티플랫폼 릴리스 매트릭스까지 — 규모 있는 Go 인프라 서비스의 표준 관례가 한 레포에 모여 있다. "log.Fatal 금지, 토큰 로깅 금지, 연결 후 타임아웃 금지" 같은 규약을 AGENTS.md에 문서화한 것도 팀 엔지니어링 참고가 된다.

주의 1 — 이용약관(ToS) 회색지대가 이 프로젝트의 본질
구독 계정을 API로 재노출하는 것은 벤더 약관 위반 소지가 있다

CLIProxyAPI는 공식 CLI의 client ID를 그대로 써서 구독 세션을 API로 바꾼다. 기술적으로는 내 계정의 정당한 토큰이지만, 대부분의 벤더 약관은 구독을 "공식 클라이언트에서 개인 용도로" 쓰도록 제한하고 API 재판매·자동화 우회를 금지한다. README 생태계(스폰서 다수)가 AI 구독 계정 재판매·중계 서비스로 채워져 있다는 점도, 이 도구가 상업적 우회에 쓰인다는 방증이다. 계정 정지·법적 리스크는 사용자 몫이며, 이 문서는 설계 학습용이지 약관 위반을 권하지 않는다.

주의 2 — 트래픽을 "공식 CLI인 척" 위장하는 코드
uTLS Chrome 지문 스푸핑 · identity-confuse 옵션

internal/auth/claude/utls_transport.gorefraction-networking/utlsChrome ClientHello를 흉내 내 Cloudflare의 TLS 지문 검사를 우회한다("공식 CLI 트래픽처럼 보이게"). 더 노골적으로, codex.identity-confuse 설정은 요청 추적 식별자(prompt_cache_key 등)를 계정마다 바꿔치기한다 — 주석은 이를 "일부 사용자가 추적 ID가 정지 근거가 될까 걱정해서 넣은 옵션"이라 설명한다. 즉 개발자 스스로 정지 회피가 사용 동기임을 인지하고 있다. 이런 위장 기법이 존재한다는 사실 자체가 §주의1의 회색지대를 뒷받침한다.

주의 3 — 플러그인은 "프로세스 내 신뢰 코드"
C-ABI 동적 라이브러리 = 임의 코드 실행 위험

플러그인 호스트는 C/Rust/Go로 짠 .so/.dll을 프로세스 안에서 로드한다 — 샌드박스가 아니라 서버와 같은 권한으로 도는 신뢰 코드다. 그래서 기본 비활성이고, 설정 주석도 이 점을 경고한다. 플러그인 스토어에서 남의 플러그인을 설치할 때는 소스를 반드시 확인해야 한다. 관리 API도 로컬조차 secret-key를 요구하도록 설계된 이유가 이 위험성 때문이다.

7하드웨어 · 시스템 요구사항

가벼운 프록시 — 무거운 건 상류 계정 쪽 · Go 1.26 + CGO
항목요구사항
빌드 도구Go 1.26 SDK · CGO_ENABLED=1(플러그인 지원 시). 미리 빌드된 릴리스 바이너리도 제공
실행 자원프록시 자체는 매우 가볍다 — 실제 추론은 상류(구독 계정)가 수행. GPU·대용량 RAM 불필요
포트API 8317 + OAuth 콜백 포트(1455·54545·8085·51121·11451)
OS / 아키텍처darwin · windows · linux(glibc/musl) · freebsd, amd64/arm64 (musl·freebsd는 no-plugin 변형)
전제 조건유효한 구독 계정(Claude/ChatGPT/Gemini 등) — 이게 없으면 프록시는 빈 껍데기
설정config.yaml(예시 config.example.yaml 26KB) + auths/ 토큰 디렉토리. Docker·Compose 제공
선택 인프라토큰 저장소로 Postgres/S3/Git, 사용량 큐로 Redis, 클러스터 control-plane(mTLS+JWT)

현실 점검: CLIProxyAPI를 돌리는 데 필요한 건 성능 좋은 서버가 아니라 유효한 구독 계정과, 그 구독을 이렇게 쓰는 것이 약관상 괜찮은지에 대한 스스로의 판단이다. 기술적 진입장벽은 낮지만(가벼운 Go 바이너리), 실질적 진입장벽은 §6의 회색지대에 있다. 학습 목적이라면 구독 없이도 소스만으로 충분히 공부할 수 있다.

8직접 해볼 수 있는 실습 과제

CLIProxyAPI를 "쓰는" 것을 넘어, 그 설계 조각들을 "재현"해 보는 과제들 (구독·약관 무관하게 배울 수 있는 것 중심)
난이도 ★ — 읽기 실습

1. 요청 흐름 코드로 따라가기

레포를 git clone --depth 1로 받아, cmd/server/main.gointernal/api/server.gosdk/translator/registry.gosdk/cliproxy/auth/conductor.go 순으로 읽으며, §4-0의 ASCII 흐름이 어느 코드에 대응하는지 표시해보라. "부팅 → 라우트 → 번역 → 지휘"의 큰 그림을 몸에 새기는 연습.

난이도 ★★ — 패턴 재현

2. 미니 포맷 번역기 (OpenAI ↔ Claude)

파이썬이나 Go로, OpenAI chat/completions 요청 JSON을 Claude messages 스키마로 바꾸는 40줄짜리 변환 함수를 짜보라(messages 역할 매핑·system 분리·max_tokens 대응). 그런 다음 (from,to)를 키로 하는 작은 레지스트리에 등록해보면, §4-1의 정규형 패턴이 왜 유용한지 손에 잡힌다.

난이도 ★★ — 알고리즘 구현

3. 라운드로빈 vs fill-first 로드밸런서

계정 3개를 흉내 낸 리스트에, 각자 "5분당 10회 한도"를 두고 요청을 분배하는 미니 스케줄러를 round-robinfill-first 두 방식으로 구현해보라. 한 계정이 429가 되면 쿨다운 타이머를 걸고 건너뛰는 로직까지 넣으면, conductor.go가 왜 그렇게 커졌는지 체감한다.

난이도 ★★★ — 네트워크 실습

4. SSE 스트리밍 프록시 만들기

Gin(또는 아무 HTTP 서버)으로, 상류의 text/event-stream 응답을 받아 청크 단위로 클라이언트에 흘려보내는 프록시를 짜보라. 유휴 타임아웃 방지용 keepalive 빈 줄, 연결이 끊겼을 때의 정리(context 취소)까지 다루면 §4-5의 스트림 포워더를 축소 재현한 것이다.

난이도 ★★★ — OAuth 이해

5. OAuth2 + PKCE 로컬 콜백 흐름 구현

아무 합법적인 OAuth 제공자(GitHub·Google 등, 본인이 발급한 정식 client ID)로, "브라우저 열기 → 로컬 포트 콜백 수신 → 코드를 토큰으로 교환 → 만료 시 refresh"를 구현해보라. CLIProxyAPI의 internal/auth/*가 벤더마다 반복하는 바로 그 패턴을, 약관 문제 없는 대상으로 안전하게 익히는 과제.

9관련 기술 심화 학습 로드맵

CLIProxyAPI를 완전히 소화하기 위한 4주 코스
주차주제학습 내용
1주차Go 웹 서버 & GinGin 라우팅·미들웨어, 컨텍스트, 그레이스풀 종료, 설정 로딩(fsnotify 핫리로드). cmd/server/main.go·internal/api/server.go 읽기 + 과제 1.
2주차API 포맷 & 번역 계층OpenAI/Claude/Gemini 스키마 비교, gjson/sjson JSON 조작, (from,to) 레지스트리·정규형 패턴. sdk/translator/registry.go·internal/translator/ + 과제 2.
3주차OAuth2 & 토큰 수명주기OAuth2 인가 코드 흐름·PKCE, 로컬 콜백 서버, refresh 루프, 토큰 저장소 추상화(파일/DB/S3). internal/auth/*·sdk/auth/ + 과제 5.
4주차로드밸런싱 & 스트리밍round-robin/fill-first, 쿨다운·세션 고정·장애조치, SSE 스트리밍·keepalive, 상류 실행기 인터페이스. conductor.go·runtime/executor/ + 과제 3·4.
학습 순서 팁
"관문(서버·라우트) → 통역(번역) → 열쇠(인증) → 교통정리(라우팅)" 순으로

먼저 요청이 어디로 들어와 어떻게 갈라지는지(1주차)를 잡아라. 그다음 "서로 다른 API를 어떻게 통역하나"(2주차)가 이 레포의 핵심 지력이고, "구독에 어떻게 붙나"(3주차·OAuth)와 "여러 계정을 어떻게 지휘하나"(4주차)가 실전 운영 지혜다. 플러그인 호스트(§4-6)는 가장 무겁고 특수하니 나중에 곁가지로 봐도 된다. AI 인프라·게이트웨이 쪽 관심자라면 2·4주차만으로도 "멀티 프로바이더 게이트웨이의 뼈대"를 완주할 수 있다.

비교하며 배우기 — 이웃 프로젝트들

같은 "여러 LLM을 한 API로 묶는" 문제를 다르게 푼 도구들과 나란히 놓으면 시야가 넓어진다. LiteLLM(Python)·one-api(Go)는 정식 API 키를 묶는 게이트웨이라 CLIProxyAPI와 구조는 닮았지만 약관 위치가 다르다(합법). OmniRoute는 같은 트렌딩 목록의 유사 프로젝트다. 뒤집어, DesktopCommanderMCP는 "구독으로 굴린다"는 철학을 MCP 도구로 구현한 사례다. 이들을 비교하면 "구독을 API로"라는 CLIProxyAPI의 선택이 얼마나 실용적이면서도 약관상 미묘한지가 또렷해진다.

10핵심 키워드 사전

문서에 나온 용어를 한눈에
용어
API 게이트웨이 / 프록시
클라이언트와 실제 백엔드 사이에서 요청을 대신 받아 넘기고 응답을 가공해 돌려주는 중계 서버. CLIProxyAPI는 앞으로 OpenAI/Claude/Gemini 호환 API를 열고, 뒤로 여러 구독 계정에 붙는다.
용어
상류(upstream) · 실행기(executor)
프록시 뒤에 있는 실제 제공자가 상류. CLIProxyAPI는 상류마다 같은 인터페이스의 실행기(Executor)를 두어(Codex·Claude·Gemini 등 10종), 지휘자가 어느 상류든 동일하게 다루게 한다.
용어
포맷 번역 · 정규형(canonical)
API마다 다른 요청/응답 JSON 스키마를 서로 맞바꾸는 작업. 가운데 "정규형"을 두면 N×N 변환이 N개로 줄어든다. CLIProxyAPI는 (from,to) 쌍 레지스트리 + 순수 함수 변환기로 구현한다.
용어
gjson / sjson
Go에서 구조체 없이 "messages.0.content" 같은 경로 문자열로 JSON 값을 읽고(gjson) 쓰는(sjson) 라이브러리. 스키마가 제각각인 번역 계층을 유연하게 만들어준다(타입 안전성은 포기).
용어
OAuth2 · PKCE
제3자 앱이 비밀번호 없이 계정 접근 토큰을 받는 표준 흐름. PKCE는 그 흐름에 코드 챌린지를 더해 가로채기를 막는 확장. CLIProxyAPI는 공식 CLI와 같은 client ID로 이 흐름을 밟아 구독 토큰을 얻는다.
용어
round-robin · fill-first · 세션 고정
계정 풀에 요청을 분배하는 전략. round-robin=순번 분배, fill-first=한 계정 소진 후 다음(롤링 한도 분산). 세션 고정=한 대화를 같은 계정에 묶어 맥락 유지.
용어
쿨다운 · 장애조치(failover)
한 계정이 한도 초과(429)되면 일정 시간 쉬게 하고(쿨다운), 요청을 즉시 다른 계정으로 넘기는(failover) 회복 메커니즘. 로드밸런서의 서킷브레이커와 유사한 발상.
용어
SSE (Server-Sent Events)
text/event-stream으로 서버가 클라이언트에 토큰을 조각조각 밀어 보내는 단방향 스트리밍. LLM의 "타이핑되듯 나오는" 응답이 이것. keepalive 빈 줄로 유휴 타임아웃을 막는다.
용어
uTLS 지문 스푸핑
TLS 핸드셰이크의 ClientHello 지문을 Chrome 등 특정 클라이언트처럼 위장하는 기법. CLIProxyAPI는 이걸로 Cloudflare의 TLS 지문 검사를 우회한다 — "공식 CLI 트래픽처럼" 보이게 하려는 의도.
용어
C-ABI 플러그인 호스트
C 호출 규약(ABI)을 따르는 동적 라이브러리(.so/.dll)를 프로세스 안에서 로드해 기능을 확장하는 구조. 샌드박스가 아니라 서버와 같은 권한으로 돌므로, 신뢰할 수 있는 코드만 넣어야 한다(기본 비활성).
용어
control-plane (클러스터 모드)
여러 프록시 노드의 설정·플러그인·토큰을 중앙에서 배포·관리하는 운영 층. CLIProxyAPI의 "home" 모드가 mTLS+JWT로 이를 구현한다(대규모 배포용).

11참고 링크

공식 소스와 함께 볼 자료
구분링크 / 위치
GitHub 저장소github.com/router-for-me/CLIProxyAPI
TrendShifttrendshift.io/repositories/19331
핵심 파일cmd/server/main.go (부팅 · 저장소 선택 · 로그인 분기)
핵심 파일internal/api/server.go (Gin 라우트 표 · 관리 API)
핵심 파일sdk/translator/registry.go · internal/translator/ (포맷 번역)
핵심 파일sdk/cliproxy/auth/conductor.go (계정 라우팅 지휘자)
핵심 파일internal/auth/*/ · internal/auth/claude/utls_transport.go (OAuth · 지문 위장)
이웃 프로젝트LiteLLM · one-api(API 키 게이트웨이) · OmniRoute · DesktopCommanderMCP
연관 개념API 게이트웨이 · 포맷 번역/정규형 · OAuth2+PKCE · 로드밸런싱/failover · SSE 스트리밍