1편의 잔상
Claude Code Switcher (CCS) — 프로바이더 전환·멀티 계정·로컬 모델까지 한 명령으로 가 표면의 글이었다. CCS 가 왜 등장했는지, 무엇을 풀고 있는지, 누구에게 언제 맞고 안 맞는지. 그 글의 한 단락을 그대로 옮기면 이렇다.
CCS 는 Claude Code 가 환경변수 두 개로 정의되는 도구라는 사실을 그대로 받아들인 도구다. 그 두 변수를 프로파일 이라는 단위로 정리하고, OAuth 우회와 다중 계정까지 한 명령 표면 위로 올려놓았다.
2편은 그 표면 아래 다. ~/.ccs/ 한 디렉토리에 무엇이 어떻게 사는지, config.yaml 의 어느 키가 무엇을 결정하는지, instance 와 shared 가 어떻게 갈리는지, 로컬 프록시는 정확히 어떤 변환을 거치는지. 1편에서 표 한 줄로만 끝난 부분을 시스템적 그림으로 풀어 본다.
이 글의 모든 디렉토리·키 이름은 사용자 머신의 실제 CCS 설치를 직접 들여다본 결과다. 토큰·세션 같은 민감한 값은 보지 않고 구조만 인용한다.
~/.ccs/ 한 장 지도
CCS 가 머신에 자리 잡으면 홈 디렉토리에 ~/.ccs/ 한 폴더가 생긴다. 그 안의 트리가 사실상 CCS 의 운영 모델 전부다 — 다만 진짜 그림은 심볼릭 링크 체인 이다.
~/.claude/ ← 사용자의 기존 Claude Code 글로벌 (canonical)
├── settings.json
├── commands/, skills/, agents/, plugins/
└── projects/
~/.ccs/ ← CCS 영역
├── config.yaml ← 메인 설정 (YAML, 11K 정도)
├── .session-secret ← 세션 비밀 (64B, 절대 공유 금지)
├── .claude/ ← CCS *번들* (ccs.md, ccs-delegation 스킬 등 자체 주입)
├── cliproxy/bin/ ← CLIProxyAPI 바이너리 (자동 다운로드)
├── cache/, completions/, logs/
│
├── shared/ ← 사용자 정의 공유 영역
│ ├── settings.json → ~/.claude/settings.json (symlink)
│ ├── commands → ~/.claude/commands (symlink)
│ ├── skills → ~/.claude/skills (symlink)
│ ├── agents → ~/.claude/agents (symlink)
│ ├── plugins → ~/.claude/plugins (symlink)
│ └── context-groups/default/projects/ (실 디렉토리)
│
└── instances/ ← per-account 격리 영역
├── team/ ← 회사 Team account
│ ├── settings.json → ~/.ccs/shared/settings.json (symlink)
│ ├── commands → ~/.ccs/shared/commands (symlink)
│ ├── skills → ~/.ccs/shared/skills (symlink)
│ ├── agents → ~/.ccs/shared/agents (symlink)
│ ├── projects → ~/.ccs/shared/context-groups/default/projects/ (symlink)
│ ├── plugins/, .anthropic/, sessions/, session-env/ ← 실 디렉토리
│ ├── todos/, file-history/, shell-snapshots/, logs/ ← 실
│ ├── backups/, cache/, debug/, image-cache/, paste-cache/, plans/ ← 실
│ └── .claude.json, history.jsonl, .session-stats.json,
│ mcp-needs-auth-cache.json, policy-limits.json, remote-settings.json
└── enterprise/ ← 회사 Enterprise account (구조 동일)
핵심 두 가지를 짚어둔다.
1. 2단계 symlink 체인 — instances/team/settings.json 을 따라가면 ~/.ccs/shared/settings.json 으로 가고, 그게 다시 ~/.claude/settings.json 으로 간다. 즉 사용자의 기존 Claude Code 글로벌 설정이 모든 인스턴스에 자동 반영된다. 한 번 만든 슬래시 커맨드·스킬·MCP 가 team / enterprise 양쪽에서 자동으로 살아 있는 이유.
2. projects/ 만 한 단계 — 대화 히스토리는 instances/team/projects → ~/.ccs/shared/context-groups/default/projects/ (실 디렉토리) 까지만 간다. ~/.claude/projects/ 까지 체인되지 않는다 — CCS 는 자기 context-group 단위로 대화를 모아 두고, 사용자의 평소 Claude Code 대화와는 자연히 분리시켜 둔다. 회사 대화가 평소 개인용 Claude Code 에 섞이지 않도록 의도된 끊음.
이 2단계 체인이 이중 구조 라고 표현했던 것의 실제 메커니즘이고, 1편의 “공유 vs 분리” 가 단일 축이 아니라 어느 단위까지 체인을 잇느냐 의 문제였다는 뜻이다. §5–6 에서 이 단위들을 풀어 본다.
config.yaml 의 9개 영역
설정의 거의 전부는 ~/.ccs/config.yaml 한 파일에 모인다. JSON 이 아니라 YAML 인 점이 의외인데, 사람이 손으로 읽고 고치기 좋은 쪽으로 의도된 선택으로 보인다.
키 구조만 추리면 9개 영역으로 나뉜다.
version: # 메타
default: # 기본 프로파일
accounts: # *instance 단위* 메타
team:
context_mode: # isolated | shared
context_group: # shared 일 때 그룹 이름
continuity_mode: # 추가 공유 정책
enterprise:
...
profiles: # 프로바이더 프로파일 (glm, kimi, ollama, ...)
cliproxy: # OAuth 프록시 백엔드 설정
backend:
oauth_accounts:
providers:
routing:
strategy: # round-robin / fill-first
session_affinity:
session_affinity_ttl:
proxy: # 로컬 OpenAI-호환 프록시
profile_ports: # 프로파일별 포트 매핑
routing:
longContextThreshold:
cliproxy_server: # 원격/로컬 CLIProxy 서버
remote: { enabled, host, protocol, auth_token }
fallback: { enabled, auto_start }
local: { port, auto_start }
logging: # 로그 회전·보관
preferences: # theme, telemetry, auto_update
websearch: # ★ 8개 fallback 프로바이더
providers:
exa, tavily, brave, searxng, duckduckgo, gemini, opencode, grok
# --- 통합 (Claude Code 외부 도구들) ---
copilot: # GitHub Copilot 라우팅
cursor: # Cursor 통합 (ghost_mode 포함)
channels: # Telegram/Discord/iMessage
thinking: # opus/sonnet/haiku 별 thinking 기본값
global_env: # DISABLE_TELEMETRY 등 전역 env 자동 주입
이 9개를 한 번 훑으면 CCS 가 단순 스위처가 아니라 Claude Code 주변의 운영 표면 전체 를 흡수해 가고 있다는 인상이 분명해진다. §12 에서 이 중 안 알려진 부분을 따로 추린다.
Instance 시스템 — symlink 와 실 상태의 갈림
instances/<name>/ 디렉토리는 언뜻 보면 Claude Code 의 모든 상태를 담은 통 같지만, ls -la 로 열어 보면 들어 있는 항목 절반은 사실 shared/ 로의 symlink, 나머지 절반만 인스턴스별 실 상태다. 둘을 갈라 두면 멘탈 모델이 명확하다.
Symlink 항목 (모든 인스턴스가 공유):
| 항목 | 가는 곳 |
|---|---|
settings.json | ~/.ccs/shared/settings.json → ~/.claude/settings.json |
commands/ | ~/.ccs/shared/commands → ~/.claude/commands/ |
skills/ | ~/.ccs/shared/skills → ~/.claude/skills/ |
agents/ | ~/.ccs/shared/agents → ~/.claude/agents/ |
projects/ | ~/.ccs/shared/context-groups/default/projects/ (한 단계만) |
settings.json 의 최상위 키 9개는 (체인을 따라가 본 결과) 다음과 같다 — 사용자의 기존 ~/.claude/settings.json 의 스키마 그대로다.
alwaysThinkingEnabled
enabledPlugins
env
extraKnownMarketplaces
hooks
permissions
skipAutoPermissionPrompt
skipDangerousModePermissionPrompt
statusLine
인스턴스별 실 상태 (격리된 자기 데이터):
| 분류 | 항목 |
|---|---|
| 인증 (절대 안 섞임) | .anthropic/ (OAuth), .claude.json (사용자 메타) |
| 세션·실행 | sessions/, session-env/, shell-snapshots/, history.jsonl |
| 작업 상태 | todos/, file-history/, plans/ (team 만), backups/ |
| 로깅·캐시 | logs/, cache/, debug/, image-cache/, paste-cache/ |
| 플러그인 | plugins/ — 인스턴스별 (claude-hud 같은 설치) |
| 메타·정책 | policy-limits.json, remote-settings.json, mcp-needs-auth-cache.json, .session-stats.json |
| 외부 통합 | .omc/ (oh-my-claudecode 상태) |
흥미로운 사실 — 이 글의 plan 파일은 instances/team/plans/ccs-hashed-llama.md 에 있다 (이 항목은 real, symlink 아님). 이 글을 쓰는 동안 사용자의 Claude Code 세션은 team 인스턴스에서 돌고 있었다는 뜻이다. CCS 는 보이지 않게 CLAUDE_CONFIG_DIR 같은 환경변수로 인스턴스 컨텍스트를 끼워 넣는다.
enterprise 인스턴스는 team 보다 가볍게 비어 있는 상태였는데, 활성 사용 중인지 아닌지 가 디렉토리 충실도에 그대로 드러난다. CCS 는 인스턴스를 만들 때 symlink 와 빈 디렉토리만 만들어 두고, 실제로 사용해야 실 데이터가 채워지는 lazy 한 모델이다.
shared/ 와 context-groups — 1편의 빈칸 메우기
1편의 “설정·대화는 공유하면서 계정만 분리하고 싶다면” 단락의 진짜 답 이 여기 있다. CCS 는 이 시나리오를 shared/ 라는 별도 영역과 context_mode/context_group 설정으로 정식 지원한다.
config.yaml 의 accounts: 섹션:
accounts:
team:
context_mode: isolated # 또는 shared
context_group: default # shared 일 때만 의미
continuity_mode: deeper # 또는 default
enterprise:
context_mode: shared
context_group: default
continuity_mode: deeper
세 키의 역할:
context_mode: isolated | shared— 인스턴스가 자기 디렉토리에만 사느냐, 같은 그룹의 다른 인스턴스와 공유하느냐context_group—shared일 때 어느 그룹 이름인지.~/.ccs/shared/context-groups/<group>/가 그 그룹의 공유 데이터 자리continuity_mode: deeper— 공유의 깊이.deeper는 더 많은 디렉토리를 공유 대상에 포함
이 셋의 조합으로 1편에서 “공유 vs 분리” 라고 단순화한 축이 사실 세 축의 조합 임이 드러난다.
무엇이 공유 가능하고 무엇이 항상 격리되는가
CCS 의 공식 docs 는 이 부분을 못 박아 둔다.
CCS only shares workspace context paths (project/session context files). It does not merge or copy authentication credentials between accounts.
—
docs/session-sharing-technical-analysis.md
요약 표:
| 분류 | 대상 | 동작 |
|---|---|---|
| shared (조건부) | session-env/ | shared + deeper 일 때 그룹 안에서 공유 |
| shared (조건부) | file-history/ | 동일 |
| shared (조건부) | shell-snapshots/ | 동일 |
| shared (조건부) | todos/ | 동일 |
| 항상 격리 | .anthropic/ (OAuth) | 인증은 어떤 모드에서도 인스턴스별 |
| 항상 격리 | 인증 토큰 / 자격증명 | 동일 |
| 수동 공유 | commands/, skills/, agents/, plugins/ | shared/<dir>/ 에 두면 모든 인스턴스가 사용 |
핵심 한 줄 — 인증은 절대 안 섞인다. 한 인스턴스의 OAuth 토큰이 실수로 다른 인스턴스의 요청에 흘러갈 일이 구조적으로 없다. 회사 환경에서 가장 큰 사고 가능성을 시스템 자체가 막아 둔 셈이다.
ccs -r (resume) 는 현재 활성 lane 만 이어가고, ccs <account> -r 은 그 인스턴스의 lane 만 이어간다. 두 인스턴스 모두 다른 continuity 인벤토리를 가질 수 있다는 점을 의식하고 운영해야 한다.
4가지 진입점 — Target Adapter System
ccs 가 Claude Code 만 호출하는 게 아니다. CCS 는 runtime 자체를 바꾸는 4가지 바이너리를 노출한다.
| 바이너리 | runtime |
|---|---|
ccs | Claude Code (기본) |
ccsd / ccs-droid | Factory Droid |
ccsx / ccs-codex | Codex CLI (네이티브) |
ccsxp | Codex CLI + CLIProxy 프로바이더 오버라이드 |
내부적으로는 각 바이너리가 CCS_INTERNAL_ENTRY_TARGET 환경변수를 세팅한 후 target resolver 에 위임한다. resolver 의 우선순위:
- CLI 플래그 (
--target) - 진입 바이너리 자체
argv[0]이름 검출- 프로파일별 config
- 기본값
이 추상화 덕분에 “Claude Code 로 GLM 돌리기” 와 “Droid 로 GLM 돌리기” 가 같은 명령 표면 위에서 가능하다 — ccs glm vs ccs --target droid glm. Codex 의 경우 자기 ~/.codex/ 상태를 별도 보존하기 위해 환경변수만 임시로 덮어쓰는 식의 신경을 더 쓴다.
로컬 프록시 (127.0.0.1:포트) 의 5단계 흐름
ccs glm 같은 OpenAI-호환 프로바이더 명령이 들어오면 다음 5단계를 거친다 (docs/openai-compatible-providers.md 정리).
127.0.0.1에서 그 프로파일의 로컬 포트 바인딩 (포트는proxy.profile_ports에서 할당)- Claude Code 가 보내는 Anthropic 형식
/v1/messages요청 수신 - OpenAI chat-completions 형식으로 변환
- 업스트림 프로바이더로 포워드
- 스트리밍 응답을 다시 Anthropic SSE 로 역변환해 Claude Code 에 돌려줌
요점 — Claude Code 는 자기가 Anthropic 과 이야기하는 줄 안다. 변환은 프록시 한 점에서만 일어나고, 한 자리에서 처리하니 디버깅 지점도 한 곳이다.
Anthropic-호환 엔드포인트는 프록시를 우회한다 — 즉 https://api.anthropic.com 이나 Z.AI 의 /api/anthropic 으로 가는 요청은 변환이 필요 없으니 그냥 직통. 이 분기 덕분에 GLM 사용은 추상화 비용이 거의 0 이다.
CLIProxyAPI 서버 자체는 기본적으로 port 8317 에서 동작하는데, cliproxy_server.local.port 로 바꿀 수 있고 remote.host 를 켜면 다른 머신의 CLIProxy 를 쓸 수도 있다.
시나리오 라우팅 4종
CCS 가 수동으로 프로바이더를 명시하지 않아도 자동으로 라우팅하는 4가지 시나리오가 있다.
| 시나리오 | 트리거 |
|---|---|
background | 요청이 Haiku 를 포함 (가벼운 백그라운드 작업) |
think | Anthropic extended thinking 활성화 |
longContext | 토큰 추정치가 임계값 초과 (proxy.routing.longContextThreshold) |
webSearch | web_search 툴 호출 |
config.yaml 에 proxy.routing.longContextThreshold 키가 실제로 있어 임계값을 손으로 조절할 수 있다. 자동화의 깊이는 워크로드에 따라 잘 맞기도, 헷갈리기도 한다 — 처음부터 라우팅 룰을 복잡하게 짜기보다 한 주 단순 운영 후 패턴을 보고 추가 하는 편이 낫다 (1편 팁 §7 참조).
CLIProxy 서브시스템 — OAuth 프로바이더의 자리
API 키가 없는 OAuth 기반 프로바이더 — Gemini, GitHub Copilot, AWS Kiro 등 — 는 별도 서브시스템이 책임진다. ~/.ccs/cliproxy/bin/ 에 자동 다운로드되는 CLIProxyAPI 바이너리가 그 핵심이다.
이 서브시스템이 다루는 것들:
- Account manager — OAuth 토큰 lifecycle (발급·갱신·만료)
- Quota manager — 프로바이더별 quota 추적, 자동 failover
- Quota fetcher — 사용량 실시간 동기화
- Auth handler — 각 프로바이더의 OAuth 흐름 (Anthropic, Gemini, Copilot, Kiro 등)
- Model catalogs — 프로바이더별 모델 목록 + compatibility 가드 (
codex-plan-compatibility.ts같은 파일이 plan 별로 안 되는 모델 조합 을 막아준다) - Hybrid quota strategy —
round-robin또는fill-first(cliproxy.routing.strategy) — 같은 프로바이더의 여러 OAuth 계정을 어떻게 분산시킬지
config.yaml 의 cliproxy.oauth_accounts 가 등록된 OAuth 계정 목록을 담는데, 이 부분은 토큰을 포함하므로 git 에 올리면 안 되는 영역이다. 다음 §11 에서 정리.
파일 관리 실전 — symlink 가 dotfiles 의 답이다
§2 의 symlink 체인을 본 다음에 던질 만한 자연스러운 질문 — 내 CCS 환경을 다른 머신에 어떻게 옮기나? ~/.ccs/ 통째로 git 에 넣으면 되나? 답은 "~/.ccs/ 를 versioning 하는 게 아니라, ~/.claude/ 를 versioning 하면 CCS 가 그쪽으로 끌어들인다" 다.
멘탈 모델: ~/.claude/ 가 source of truth
체인의 시작점은 ~/.claude/ 다. CCS 의 shared/ 와 instance 들은 그쪽으로 가는 포인터 일 뿐. 따라서 dotfiles 의 핵심은 다음 한 줄로 압축된다.
~/.claude/를 git 으로 관리하면, 그 머신에 CCS 를 새로 설치하더라도shared/만 동일한 symlink 로 다시 걸어 두면 모든 인스턴스가 자동으로 같은 환경.
git 에 올리기 좋은 자산 (~/.claude/ 쪽)
~/.claude/commands/— 슬래시 커맨드~/.claude/skills/— 스킬~/.claude/agents/— 에이전트 정의~/.claude/CLAUDE.md— 글로벌 메모리~/.claude/settings.json— 주의: 안에 토큰을 직접 적어 두지 않은 경우만.env필드에 비밀이 있으면 분리 (settings.local.json패턴)~/.ccs/shared/context-groups/default/— CCS 의 그룹 정의 자체 (실 디렉토리). 안에 들어 있는projects/는 빼고 그룹 메타만 versioning 하는 식.
~/.ccs/.claude/ (CCS 번들) 은 npm 패키지 일부라 install 시 자동으로 따라온다. 직접 versioning 할 필요 없음.
절대 git 금지 (자격증명·세션·트랜스크립트)
~/.ccs/config.yaml— OAuth 토큰·refresh_token 포함. 머신별로 보관.~/.ccs/.session-secret— 세션 비밀.~/.ccs/instances/*/.anthropic/— Anthropic OAuth.~/.ccs/instances/*/.claude.json— 사용자 메타 (계정 식별자 포함).~/.ccs/instances/*/sessions/,session-env/— 세션·세션별 env (토큰 환경변수 가능).~/.ccs/instances/*/history.jsonl,file-history/— 명령·파일 history (회사 코드 흔적).~/.ccs/shared/context-groups/*/projects/— 대화 히스토리 본체. PII·회사 코드 포함 가능.~/.ccs/cliproxy/,cache/,logs/— 바이너리·휘발성.~/.claude/projects/— 평소 Claude Code 대화 히스토리 (위와 동일 이유).
새 머신 부트스트랩 4단계
dotfiles 가 ~/.claude/ 만 들고 있다고 할 때 새 머신에서 같은 환경을 만들려면:
- dotfiles clone —
~/.claude/가 채워짐 npm install -g @kaitranntt/ccs— CCS 설치ccs config— 초기~/.ccs/골격 생성- symlink 재구성 —
~/.ccs/shared/{commands,skills,agents,plugins,settings.json}을~/.claude/의 동명 항목으로 link. 한 줄 스크립트로 자동화 권장.
ln -sf ~/.claude/commands ~/.ccs/shared/commands
ln -sf ~/.claude/skills ~/.ccs/shared/skills
ln -sf ~/.claude/agents ~/.ccs/shared/agents
ln -sf ~/.claude/plugins ~/.ccs/shared/plugins
ln -sf ~/.claude/settings.json ~/.ccs/shared/settings.json
이 다섯 줄로 새 머신에서도 1편의 instance 들이 평소 환경을 그대로 본다. OAuth 인증만 각 인스턴스에서 새로 하면 끝.
격리의 운영 이점
- 한 인스턴스가 망가져도 (
plugins/깨짐,policy-limits.json누락 등) 다른 인스턴스는 그대로. 단일 장애점이 줄어드는 부수 효과. - 인스턴스 단위 백업 —
tar czf team-backup.tgz -C ~/.ccs/instances team(symlink 는 그대로 보존하는 옵션-h추가 시 따라가서 archive). 보통은 symlink 그대로 두는 게 의미 있다 (복원 시 같은 dotfiles 환경을 가정).
놀라운 발견 모음 — config.yaml 의 비공식 영역들
CCS 의 README 가 강조하지 않지만 config.yaml 키만 봐도 드러나는, 단순 “프로바이더 스위처” 라기에는 풍부한 통합들.
- WebSearch 8 프로바이더 fallback —
websearch.providers아래에exa, tavily, brave, searxng, duckduckgo, gemini, opencode, grok8개. Claude Code 의 WebSearch 가 Anthropic 외 8곳으로 fallback 가능하다는 의미.docs/websearch.md가 별도 문서로 있을 정도로 일급 통합. - GitHub Copilot 라우팅 —
copilot.account_type,copilot.rate_limit,copilot.model. CCS 가 Copilot 도 프로바이더 한 슬롯 으로 다룬다. - Cursor 통합 —
cursor.ghost_mode,cursor.port등. Cursor 의 ghost-mode 까지 다루는 건 CCS 의 야심을 보여준다. - Channels (Telegram/Discord/iMessage) —
channels.selected,channels.unattended. 폰의 메신저로 Claude 에게 일을 시키는 통합 (1편의 free-claude-code 텔레그램 봇 사례 와 같은 결). - Thinking tier_defaults —
thinking.tier_defaults.opus / sonnet / haiku별로 thinking 기본 모드를 다르게 잡을 수 있다. Opus 는 항상 deep thinking, Haiku 는 끄기, Sonnet 은 상황에 따라 — 식의 정책이 가능. - Global env 자동 주입 —
global_env.env.DISABLE_BUG_COMMAND,DISABLE_ERROR_REPORTING,DISABLE_TELEMETRY. 보안·프라이버시에 민감한 회사 환경에서 자동으로 텔레메트리를 끄게 하는 헬퍼. - session_affinity —
cliproxy.routing.session_affinity와session_affinity_ttl. 같은 세션은 같은 OAuth 계정으로 라우팅되도록 sticky 하게 묶는 정책.
이런 영역들은 CCS 가 단순 스위처에서 운영 플랫폼 으로 진화 중이라는 신호다.
한 줄로
1편이 CCS 의 표면이었다면 2편은 안쪽이고, 안쪽을 보고 나면 1편의 단순 광고문 — “프로파일로 옮겨다닌다” — 이 사실은 instance 격리 + shared 공유 + OAuth 분리 + OpenAI ↔ Anthropic 변환 + 시나리오 라우팅 + 통합 점들 의 다섯 축으로 짜여 있다는 게 보인다. 추상화가 두껍다는 1편의 단점 평가는 그래서 정확하지만, 그 두께가 풀고 있는 문제도 같이 두껍다는 사실이 디렉토리 한 통에 적나라하다.
다음 편이 있다면 두 갈래 중 하나로 갈 가능성이 크다 — (a) proxy.routing 시나리오를 실제 워크로드로 맞춰 보는 실측 운영 글, (b) CCS + dotfiles + Anthropic Workspaces 의 멀티-머신 계정 운영 글. 어느 쪽이 먼저 나올지는 다음 글에서 정한다.
참고
- CCS 1편 — 프로바이더 전환·멀티 계정·로컬 모델까지 한 명령으로
- CCS 홈페이지
- kaitranntt/ccs — GitHub
- docs/codebase-summary.md — 아키텍처 4 레이어
- docs/session-sharing-technical-analysis.md — 공유·격리 모델 (이 글의 §5–6 출처)
- docs/openai-compatible-providers.md — 로컬 프록시 변환 흐름
- docs/project-overview-pdr.md — 5 서브시스템 개요
- docs/websearch.md — WebSearch 8 fallback 상세
- Z.AI Claude Code 가이드 — Anthropic-호환 엔드포인트의 공식 출처