본문으로 건너뛰기

"CLI" 태그로 연결된 12개 게시물개의 게시물이 있습니다.

모든 태그 보기

mmdc 입문

· 약 6분

mermaid는 렌더링해 주는 곳에서만 그림이 돼요. GitHub README와 이 블로그에서는 도식으로 보이지만, PDF로 내보내면 코드 블록 그대로 남고, 사내 위키에 붙이면 텍스트 뭉치가 돼요. 슬라이드에 넣으려면 결국 캡처 도구를 꺼내야 해요.

mmdc는 이 지점을 없애는 도구입니다. mermaid 문법을 입력받아 SVG, PNG, PDF 파일로 저장하는 공식 커맨드라인 도구이고, mermaid 개발팀이 직접 관리합니다. 2026년 7월 기준 버전은 11.16.0입니다.

무엇을 하는 도구인가

내부는 단순합니다. Puppeteer로 headless Chromium을 띄우고, 그 안에서 mermaid 라이브러리로 다이어그램을 그린 다음, 결과 SVG를 꺼내거나 스크린샷을 찍습니다.

브라우저를 통째로 띄운다는 사실이 장점이자 단점입니다. 웹에서 보이는 그림과 결과물이 정확히 같다는 게 장점이고, 실행 한 번에 Chromium이 뜨니 가볍지 않다는 게 단점입니다.

설치

Node 18.19 이상 또는 20 이상이 필요합니다. 전역 설치는 이렇게 합니다.

npm install -g @mermaid-js/mermaid-cli
mmdc --version

한두 번 쓸 목적이라면 설치 없이 실행해 봅니다.

npx -p @mermaid-js/mermaid-cli mmdc -i diagram.mmd -o diagram.svg

CI나 로컬 환경에 의존성을 남기고 싶지 않으면 Docker 이미지를 씁니다.

docker run --rm -u $(id -u):$(id -g) -v "$PWD:/data" \
ghcr.io/mermaid-js/mermaid-cli/mermaid-cli \
-i /data/diagram.mmd -o /data/diagram.svg

-u $(id -u):$(id -g)를 빼면 결과 파일이 root 소유로 생깁니다. 컨테이너에서 파일을 만드는 도구의 흔한 함정입니다.

Homebrew 설치는 지원이 끊겼습니다. brew install mermaid-cli로 기억하고 있다면 npm 또는 Docker로 옮겨야 합니다.

첫 렌더

.mmd 파일 하나를 만듭니다.

flowchart TD
A[요청] --> B{캐시 있나}
B -- 있음 --> C[캐시 응답]
B -- 없음 --> D[DB 조회]
D --> E[캐시 저장]
E --> C

변환합니다.

mmdc -i flow.mmd -o flow.svg

출력 확장자가 형식을 결정합니다. flow.png로 주면 PNG, flow.pdf로 주면 PDF가 나옵니다. 확장자와 무관하게 형식을 정하려면 -e svg 같은 식으로 명시합니다.

파일을 만들지 않고 파이프로 넘기는 방식도 됩니다.

cat << 'EOF' | mmdc -i - -o out.png
sequenceDiagram
Client->>Server: 요청
Server-->>Client: 응답
EOF

주요 옵션

옵션기본값용도
-i, --input필수입력 파일. .md는 마크다운으로 처리, -는 stdin
-o, --output입력 + .svg출력 파일. -는 stdout
-e, --outputFormat확장자에서 추론svg, png, pdf 중 선택
-t, --themedefaultdefault, forest, dark, neutral
-b, --backgroundColorwhitetransparent, #F0F0F0
-w, --width800페이지 폭 (px)
-H, --height600페이지 높이 (px)
-s, --scale1Puppeteer 배율. PNG 해상도를 올릴 때
-c, --configFile없음mermaid 설정 JSON
-C, --cssFile없음페이지에 주입할 CSS
-p, --puppeteerConfigFile없음Puppeteer 설정 JSON
-f, --pdfFit꺼짐PDF를 도식 크기에 맞춤
-j, --jobsCPU 절반다이어그램 여러 개를 병렬 렌더링
-q, --quiet꺼짐로그 억제

발표 자료에 넣을 PNG라면 배율과 배경을 함께 조정하는 조합이 무난합니다.

mmdc -i arch.mmd -o arch.png -t dark -b transparent -s 3

-s 3은 같은 도식을 3배 해상도로 그립니다. 슬라이드에서 확대해도 글자가 깨지지 않습니다.

마크다운 파일 일괄 변환

.md 파일을 입력으로 주면 동작이 달라집니다. 파일 안의 mermaid 코드 블록을 모두 찾아 각각 이미지로 렌더링하고, 원본 블록을 이미지 참조로 바꾼 새 마크다운을 내놓습니다.

mmdc -i README.md -o README-rendered.md

이미지는 README-rendered-1.svg, README-rendered-2.svg 같은 이름으로 순번이 붙고, 마크다운 안에는 ![diagram](README-rendered-1.svg) 형태로 들어갑니다. 이미지를 별도 디렉토리에 모으려면 --artefacts를 지정합니다.

mmdc -i README.md -o dist/README.md -a dist/images

mermaid를 렌더링하지 못하는 목적지에 문서를 올려야 할 때 쓰는 기능입니다. 사내 위키, 정적 PDF 매뉴얼, 오래된 문서 시스템이 대표적입니다. 원본은 mermaid 코드로 관리하고 배포할 때만 이미지로 바꿔 내보내는 흐름이 만들어집니다.

설정 파일 세 가지

옵션 플래그로 부족할 때 JSON 또는 CSS 파일을 붙입니다.

-c로 넘기는 mermaid 설정은 live editor의 Config 탭과 같은 내용입니다. 폰트, 곡선 스타일, ELK 레이아웃 사용 여부를 여기서 정합니다.

{
"theme": "neutral",
"themeVariables": { "fontFamily": "Noto Sans KR" },
"flowchart": { "curve": "basis" }
}

-C로 넘기는 CSS는 렌더링 페이지에 그대로 주입됩니다. mermaid가 테마에서 만들어 넣는 스타일을 덮어써야 하니 !important가 필요한 경우가 잦습니다.

-p로 넘기는 Puppeteer 설정은 실행 환경 문제를 푸는 열쇠입니다. 컨테이너나 CI에서 Chromium이 sandbox 때문에 죽으면 이렇게 우회합니다.

{ "args": ["--no-sandbox", "--disable-dev-shm-usage"] }

--disable-dev-shm-usage/dev/shm이 작게 잡힌 컨테이너에서 렌더링이 중간에 끊기는 문제를 막아 줍니다. Docker 기본값 64MB로는 큰 도식을 그리다 공유 메모리가 부족해집니다.

CI에서 문법 검증기로 쓰기

이미지 생성이 아니라 검증 목적으로도 씁니다. mermaid 문법이 깨졌으면 mmdc는 0이 아닌 종료 코드를 남깁니다. 문서에 도식이 많은 저장소라면 PR 단계에서 걸러냅니다.

#!/usr/bin/env bash
set -euo pipefail

fail=0
while IFS= read -r f; do
if ! mmdc -q -i "$f" -o /tmp/check.svg 2>/tmp/err; then
echo "FAIL: $f"
cat /tmp/err
fail=1
fi
done < <(git ls-files '*.md' | xargs grep -l '```mermaid')

exit "$fail"

이 블로그에는 활용처가 하나 더 있습니다. mermaid 도식이 본문 폭 720px 안에 들어가는지는 브라우저를 띄워 눈으로 확인해 왔는데, -w 720으로 렌더링한 SVG의 viewBox 값을 읽으면 폭 초과를 숫자로 판정할 길이 열립니다. 노드 라벨 길이 규칙을 지켰는지 사람이 세지 않아도 되는 셈입니다.

걸리는 지점들

한글이 사라지는 문제가 첫 관문입니다. 공식 Docker 이미지에는 font-noto-cjk가 들어 있어서 한글이 그려지지만, 직접 만든 슬림 이미지나 폰트 없는 CI 러너에서는 한글 라벨이 빈칸이나 두부(□)로 나옵니다. 렌더링 환경에 CJK 폰트를 깔았는지부터 확인합니다.

RUN apk add --no-cache font-noto-cjk font-noto-emoji

실행 비용도 무시하기 어렵습니다. 다이어그램마다 Chromium을 띄우는 구조라 수십 개를 순차 처리하면 시간이 꽤 듭니다. 마크다운 입력에서 도식이 여러 개면 -j로 병렬 처리하니, CPU 여유가 있는 CI에서는 이 값을 올려 잡습니다.

CSP를 켜 둔 환경에서 인라인 CSS가 막히는 사례도 보고돼 있습니다. -C로 준 스타일이 적용되지 않으면 이쪽을 의심합니다.

Node와 Chromium을 동시에 요구하니 의존성이 가볍지 않다는 점은 그대로 남습니다. Python만 있는 파이프라인이라면 브라우저 없이 도는 mermaidx 같은 대안을 검토하는 편이 낫습니다. 다만 mermaid 최신 문법 지원 범위는 공식 CLI가 앞섭니다.

언제 쓰고 언제 안 쓰나

도식 한두 개를 급하게 이미지로 받고 싶으면 mermaid live editor에서 다운로드 버튼을 누르는 편이 빠릅니다. 여러 서비스가 섞인 팀 문서 파이프라인이라면 Kroki 같은 렌더 서버를 세우는 선택지도 있습니다.

mmdc가 제 몫을 하는 자리는 반복과 자동화예요. 문서 저장소에 mermaid를 원본으로 두고 배포 때 이미지로 바꿔 내보내는 흐름, PR마다 도식 문법을 검사하는 파이프라인, 슬라이드용 고해상도 PNG를 스크립트 한 줄로 뽑는 작업이 여기 해당해요. 매번 마우스로 저장 버튼을 누르던 일이 커맨드가 되는 순간부터 제값을 해요.

참고

Gemini CLI 종료와 판도

· 약 4분

Google이 2026년 6월 18일부터 무료, Pro, Ultra 등 비엔터프라이즈 사용자에게 Gemini CLI의 요청 처리를 중단한다고 공지했어요. 도구 자체를 삭제하는 게 아니라 이들 등급에서 백엔드가 더 이상 요청을 받지 않는 식이며, 5월 19일 공지가 나온 뒤 후속 안내가 GeekNews를 통해 국내에도 돌면서 다시 화제가 됐어요.

CLI 코딩 도구 글을 자주 쓰는 블로그 입장에서 이건 그냥 지나칠 소식이 아니에요. 무엇이 끝나는지부터 정확히 짚고, 그다음에 판도 이야기를 해보겠습니다.

정확히 무엇이 종료되나

부풀리지 않고 공지 그대로 정리하면 이렇습니다.

종료 시점은 2026년 6월 18일이며, Gemini CLI와 Gemini Code Assist IDE 확장의 요청 처리가 끊기는 등급은 Google AI Pro/Ultra 구독자, 개인용 무료 Gemini Code Assist 사용자, 무료/표준 등급의 GitHub 조직 사용자입니다. Gemini Code Assist Standard/Enterprise 라이선스 보유자와 유료 Gemini Agent Platform API 키 사용자는 그대로 쓸 수 있습니다. GitHub 확장은 6월 18일 이후 신규 설치가 막히고, 기존 설치도 이어지는 몇 주 안에 요청 처리가 끊깁니다.

정리하면 이번 변경의 범위는 비엔터프라이즈 등급의 백엔드 요청 처리 중단입니다. 도구가 통째로 사라지는 것은 아니고, 엔터프라이즈와 유료 API 경로는 그대로 남습니다.

Google이 밝힌 이유는 통합입니다. 사용자 워크플로가 초창기를 넘어섰고 이제 "여러 agent가 서로 통신하는" 멀티 agent 환경이 됐기 때문에, 하나의 플랫폼으로 묶는 편이 낫다는 설명입니다. 그 하나의 플랫폼이 Antigravity CLI입니다.

대체재와 이전 경로

대체재는 Antigravity CLI입니다. 5월 19일 공개됐고, Go로 작성돼 속도가 빠르고 비동기 멀티 agent 오케스트레이션을 지원한다고 합니다. Agent Skills, Hooks, Subagents, Extensions 같은 핵심 기능은 이어집니다.

마이그레이션 문서는 Google이 안내한 가이드 페이지에 올라와 있고, 영상 튜토리얼은 "몇 주 안에" 제공한다고 했습니다. 비엔터프라이즈 사용자에게 이전은 사실상 선택이 아니라 강제에 가깝습니다. 6월 18일 이후 기존 경로가 막히기 때문입니다.

한 가지 짚을 점이 있습니다. Gemini CLI 저장소 자체는 Apache 2.0 라이선스 그대로 GitHub에 남습니다. 라이선스가 바뀌거나 코드가 내려가는 건 아닙니다. 다만 비엔터프라이즈 백엔드가 요청을 받지 않으면, 일반 사용자에게는 코드만 남고 실제로는 돌릴 수 없는 박제에 가까워집니다. 반면 후속작인 Antigravity CLI는 클로즈드 소스입니다. 공개된 GitHub 페이지에는 changelog, readme, GIF 정도만 있고 실제 코드는 없습니다.

이 지점이 커뮤니티 반발의 핵심입니다. Gemini CLI는 오픈소스로 출발해 10만 개 넘는 star와 6,000건 이상의 외부 기여 PR을 모았는데, 그 기여가 결국 클로즈드 소스 후속작을 살찌우는 데 쓰였다는 인식입니다. 오픈소스에서 출발해 클로즈드로 넘어가는 흐름, 그리고 무료 등급의 빠듯한 사용량 한도가 함께 도마에 올랐습니다.

CLI 코딩 도구 판도 관점

여기서 한 발 물러나 판을 보겠습니다. CLI에서 도는 AI 코딩 도구는 지난 1~2년 사이에 빠르게 늘었습니다. Anthropic의 Claude Code, OpenAI 계열 도구, 그리고 Google의 Gemini CLI가 비슷한 시기에 비슷한 형태로 등장했습니다. 터미널에 상주하면서 코드베이스를 읽고 고치고 명령을 실행하는 형태입니다.

지금 벌어지는 건 그 도구들이 각자 다른 길을 고르는 장면입니다.

Google의 선택은 "접고 통합"입니다. 개별 CLI 제품을 유지하기보다 멀티 agent 플랫폼 하나로 묶고, 무료 등급은 정리하고 유료/엔터프라이즈 경로를 남깁니다. 멀티 agent라는 다음 단계에 자원을 몰겠다는 판단입니다. 한편 Gemini CLI를 굴리던 무료 사용자층은 한도와 강제 이전을 동시에 마주하게 됐습니다.

누구는 접고 누구는 키우는 이유를 한 줄로 단정하긴 어렵습니다. 다만 드러나는 차이는 있습니다. 도구를 단품으로 계속 끌고 갈지, 아니면 더 큰 플랫폼의 입구로 재배치할지의 갈림길입니다. Google은 후자를 골랐고, 그 과정에서 오픈소스로 모은 무료 사용자층과 기여자 커뮤니티가 정리 대상에 올랐습니다.

특정 도구가 더 낫다는 이야기를 하려는 게 아니며, 무료로 쓰던 CLI 도구가 어느 날 등급별로 갈리고 후속작으로 강제 이전될 수 있다는 점과 오픈소스 라이선스가 그대로여도 백엔드가 끊기면 일반 사용자에게는 의미가 달라진다는 점이 이번 사례에서 챙겨둘 두 가지예요. CLI 코딩 도구를 업무에 깊이 엮어뒀다면 백엔드 의존성과 등급 정책을 한 번쯤 점검해 둘 만해요.

참고

Claude Code Switcher

· 약 15분

왜 "스위처"가 필요해졌나

Claude Code가 2024년 말 등장한 직후만 해도 사용량 캡은 거의 의식되지 않았어요. 그게 바뀐 건 2025-08-28이에요. Anthropic이 Pro / Max 플랜에 5시간 롤링 윈도우 + 7일 weekly cap 이중 제한을 도입한 날이에요. Max 5x ($100/mo)와 Max 20x ($200/mo)도 weekly 캡에서 자유롭지 않습니다.

cap에 걸리면 두 가지 길이 있습니다.

  1. 그 주가 끝날 때까지 기다립니다
  2. 다른 프로바이더로 옮깁니다. Z.AI의 GLM Coding Plan, Moonshot의 Kimi, OpenRouter의 300개 모델 중 하나, 또는 로컬 Ollama를 선택합니다

옮기는 일 자체는 어렵지 않습니다. Claude Code가 환경변수 두 개(ANTHROPIC_BASE_URLANTHROPIC_AUTH_TOKEN)만 보고 동작하기 때문입니다. 예를 들어 GLM으로 옮기는 방법은 Z.AI 공식 가이드 기준으로 두 줄이면 됩니다.

export ANTHROPIC_BASE_URL="https://api.z.ai/api/anthropic"
export ANTHROPIC_AUTH_TOKEN="..."

다른 프로바이더(Kimi, DeepSeek 등)도 같은 패턴이지만 정확한 Anthropic-호환 엔드포인트 URL은 각 프로바이더 공식 문서에서 직접 확인해야 합니다. 같은 회사라도 OpenAI-호환 엔드포인트(/v1)와 Anthropic-호환 엔드포인트가 다르고, 후자가 잘못 표기된 글이 인터넷에 흔합니다.

문제는 하루에 셋넷씩 갈아타게 되는 순간입니다. 회사 계정, 개인 계정, GLM, 로컬 모델, 임시 OpenRouter를 오가며 매번 셸 변수를 바꾸다 보면, 헷갈려서 회사 계정으로 사이드 프로젝트를 돌리기도 하고 캡이 어디서 깎이는지도 까먹습니다. 이 지점에서 등장한 도구가 **CCS (Claude Code Switcher)**입니다.

CCS 한 줄 요약

@kaitranntt/ccs는 Kai Tran(카이 트란)이 만든 Claude Code용 멀티 프로바이더/멀티 계정 프로필 매니저입니다. MIT 라이선스의 npm 패키지이며, GitHub ★2.2k, 2026-04-29 기준 v7.75.0입니다. 721개 릴리즈와 3,687개 커밋이 쌓일 만큼 개발이 매우 활발합니다.

홈페이지: https://ccs.kaitran.ca/ 저장소: https://github.com/kaitranntt/ccs

자기 소개는 한 문장입니다.

The multi-provider profile and runtime manager for Claude Code and compatible CLIs. — config 파일 갈아엎지 말고, 활성 세션 깨지 않으면서, 몇 초 안에 프로바이더를 옮겨라.

무엇을 지원하나

설치는 한 줄이면 됩니다.

npm install -g @kaitranntt/ccs
# 또는
bun add -g @kaitranntt/ccs

ccs config 한 번 돌려 초기 설정 후, 프로필 단위로 사용합니다.

명령라우팅
ccs기본값: Claude Sonnet 4.6
ccs glmZ.AI GLM-5.1 (API 또는 Coding Plan)
ccs kimiMoonshot Kimi (1M context)
ccs geminiGemini 3 Pro/Flash (OAuth)
ccs codexGPT-5.4 / Codex (OAuth)
ccs ollama로컬 Ollama 모델
ccs work / ccs personal같은 Claude 라도 계정 격리
ccs --target droid glmruntime 자체를 Factory Droid로 바꾸고 모델은 GLM

지원 프로바이더는 점점 늘어 현재 다음을 포함합니다.

  • Claude (Sonnet 4.6, Opus 4.6): 공식
  • GLM-5.1, GLM-5-Turbo, GLM-4.7: Z.AI
  • Kimi K2.5: Moonshot
  • Gemini 3 Pro/Flash: Google (OAuth)
  • GPT-5.4 / Codex: OpenAI (OAuth)
  • Antigravity Pro/Turbo: ccs agy (OAuth)
  • OpenRouter: 300+ 모델 한 번에
  • Ollama, llama.cpp, Novita, Alibaba Coding Plan 등 로컬/OpenAI-호환 모두

어떻게 동작하나

CCS의 핵심은 두 가지 컴포넌트입니다.

첫 번째는 로컬 Anthropic-호환 프록시입니다. GLM/Kimi처럼 이미 Anthropic-호환 엔드포인트를 제공하는 프로바이더는 단순 패스스루로 지나가고, 그렇지 않은 OpenAI-호환 프로바이더(OpenRouter, DeepSeek 등)는 로컬 프록시가 요청과 응답을 변환해 Claude Code가 자기 형식대로 받게 만듭니다.

두 번째는 CLIProxyAPI라는 OAuth 프록시 백엔드입니다. Gemini, Codex, Antigravity처럼 공식 API 키가 없는 OAuth 기반 프로바이더는 이 별도 컴포넌트가 처리하고, 라운드로빈이나 fill-first 같은 토큰 관리 정책도 여기서 돕습니다. 커뮤니티 fork(CLIProxyAPIPlus)도 옵트인으로 쓸 수 있습니다.

활성화는 eval "$(ccs proxy activate)" 같이 셸에 평가시켜 ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN을 그 세션 한정으로 주입하는 방식입니다. 다음 셸이나 다음 디렉토리에서는 다시 깨끗해집니다.

# CCS가 한 번에 처리하는 것
$ ccs glm "이 PR 리뷰해줘"
# 내부적으로:
# 1. ~/.ccs/profiles/glm.settings.json 읽기
# 2. ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic 주입
# 3. ANTHROPIC_AUTH_TOKEN=$GLM_KEY 주입
# 4. claude code 호출

겉으로는 한 줄이지만 안쪽에서 하는 일은 수동 env 셋과 다르지 않고, 그걸 프로필이라는 단위로 캡슐화했을 뿐입니다.

장점은 어디서 나오나

가장 눈에 띄는 건 명령 하나가 프로파일 하나에 대응한다는 점입니다. 회사 Claude, 개인 Claude, GLM, Kimi, 로컬까지 다섯 개를 셸 변수 갈아끼우지 않고 명령 하나로 분리합니다. 여기서 한 발 더 들어가면 멀티 계정 격리가 있습니다. ccs workccs personal이 같은 머신에서 동시에 다른 Claude 세션을 돌릴 수 있고 weekly cap도 따로 깎이므로, 회사 계정으로 사이드 프로젝트를 돌리다 cap을 축내는 사고가 구조적으로 사라집니다.

비용 이야기도 빼놓기 어렵습니다. 공식 페이지는 "Save $500–1000/month with strategic delegation"이라 적는데, 검증은 본인 워크로드에 따라 다릅니다. 다만 무거운 보일러플레이트는 GLM($10–30/mo)으로 돌리고 설계만 Claude Max로 가는 패턴은 실제로 cap 부담을 크게 줄여줍니다.

나머지 장점은 결이 비슷합니다. Z.AI처럼 API 키를 발급해 쓰는 곳, Gemini처럼 OAuth로 가는 곳, 로컬 Ollama까지 같은 명령 표면 위로 올라오고, proxy.routing 설정으로 "긴 컨텍스트는 Kimi, 추론은 Claude, 백그라운드는 GLM" 식의 자동 라우팅도 짤 수 있습니다. config 파일을 손으로 만지지 않고 비주얼 대시보드로 프로필을 추가/수정할 수 있어, 한국에서 GLM Coding Plan처럼 결제/키 발급이 번거로운 프로바이더를 쓸 때 편합니다. 721 릴리즈에 거의 매일 업데이트될 만큼 개발이 활발해 새 프로바이더가 나오면 빠르게 흡수되는 것도 덤입니다.

단점과 주의사항

가장 실질적인 부담은 추상화가 두껍다는 점입니다. 프록시가 두 단계(로컬 + CLIProxyAPI)로 끼어들고 프로바이더마다 라우팅 룰이 따로 사는 탓에, "내 요청이 어디로 갔지?"가 헷갈리는 순간이 생깁니다. 로그(~/.ccs/logs/)를 켜둘 가치가 여기 있습니다. 핵심 기능 일부가 CLIProxyAPI라는 별도 프로젝트에 묶여 있다는 점도 같은 맥락인데, 그쪽 이슈가 CCS 동작에 그대로 옮겨붙을 수 있습니다. 버전 업이 매우 빠른 것(v7.75.0, 721 릴리즈)도 새 프로바이더 흡수에는 좋지만 "내 환경에서 안정적인 v7.x"를 고수하려는 보수적 운영에는 부담이라, lockfile로 고정하는 편이 낫습니다.

쓸모의 경계도 분명합니다. 다른 프로바이더를 안 쓰고 회사/개인 두 계정만 격리하는 경우라면 direnv.envrc 같은 더 가벼운 도구로 충분하고, CCS는 옮겨 다닐 곳이 셋 이상일 때부터 의미가 생깁니다. 그리고 Codex, Antigravity 같은 비공개 OAuth 프로바이더를 우회로 쓰는 것은 각 서비스 이용약관 위반 소지가 있으니, 회사 환경에서는 한 번 확인하는 편이 안전합니다.

한국에서 쓸 때 걸리는 현실적 지점도 둘 있습니다. Z.AI Coding Plan은 분기 결제(분기당 $30–80), Moonshot Kimi는 위안화 결제라 CCS가 결제까지 해결해주지는 않습니다. 또 이 도구가 weekly cap 회피 수단으로 광고되지만, Anthropic 입장에서 "한 사람이 멀티 계정으로 cap을 돌파"하는 패턴이 쌓이면 정책이 다시 조여질 수 있습니다. 도구의 책임은 아니어도 시장 동학은 의식해 둘 만합니다.

한국 커뮤니티 반응

CCS 자체에 대한 긱뉴스(news.hada.io) 단일 글은 (작성 시점 기준) 아직 없지만, CCS 같은 도구가 왜 등장했는지를 보여주는 신호는 긱뉴스에 풍부합니다.

스레드(Threads) 쪽에서는 한국어 사용자들이 비슷한 도구를 다양하게 시도 중이라는 점이 눈에 띕니다. vibe proxy로 Claude Code에 AntiGravity와 GLM을 라이드 매핑하거나, oh-my-opencode(★9K)가 Kimi K2.5와 GLM 5를 메인 오케스트레이터로 튜닝하거나, free-claude-code가 NVIDIA NIM 무료 API로 Claude Code를 무료 운영하는 식입니다. 한국 개발자들이 프록시와 멀티 프로바이더라는 패턴 자체에 적극적으로 베팅하고 있다는 의미이고, CCS는 그 흐름에서 가장 잘 정리된 도구 중 하나입니다.

대안 비교

CCS만 있는 게 아닙니다. 같은 영역에 여러 도구가 경쟁 중입니다.

도구강점약점
CCS (kaitranntt)★2.2kOAuth 프록시 + GUI 대시보드 + 활발한 업데이트추상화 두꺼움, 의존성 복잡
claude-code-router (musistudio)★33.2k시나리오 라우팅(background/think/longContext), 토큰 임계값 자동 전환, JS 플러그인OAuth 미지원
cc-switch (farion1231)(데스크톱)데스크톱 GUI, Claude Code/Codex/OpenCode/Gemini 통합CLI 친화도 낮음
claude-code-switch (foreveryh)(소규모)미니멀, 한 명령으로 Anthropic 모델만 전환Anthropic 외 미지원
OpenCode (opencode.ai)(별도 트리)75+ 프로바이더, 완전 오픈, Claude Code 호환 인터페이스자체 CLI 라 Claude Code 그대로는 아님
DIY (.zshrc 두 줄)의존성 없음, 가장 투명함프로필 3개 넘어가면 관리 불가

API 키 기반 프로바이더가 2개 이내이고 라우팅이 필요하다면 claude-code-router가 어울립니다. 별 수도 가장 많고 시나리오 라우팅이 가장 강합니다. OAuth 프로바이더를 포함한 멀티 계정이라면 이 영역에서 거의 유일한 CCS가 맞습니다. 데스크톱 친화성과 멀티 툴 통합 GUI를 원한다면 cc-switch, Anthropic만 쓰면서 단순함을 우선한다면 DIY 또는 claude-code-switch를 고르면 됩니다.

시나리오: 회사 Team + Enterprise 두 계정만 쓴다면

CCS가 풀려는 문제가 프로바이더 다양화와 OAuth 우회라면, Anthropic만 쓰면서 회사 계정 두 개(Team + Enterprise)만 격리하는 시나리오에는 정확히 그 두 가치가 빠져 있습니다. 이때는 도구 선택이 달라집니다.

이 시나리오의 본질은 두 ANTHROPIC 계정 사이를 빠르고 정확하게 전환하는 한 가지로 줄어듭니다. 다른 프로바이더도, 시나리오 라우팅도, 로컬 모델도 무관합니다.

이 경우라면 direnv가 첫 손에 꼽힙니다. .envrcexport ANTHROPIC_API_KEY=... 한 줄을 두 회사 디렉토리에 각각 두면 cd만으로 키가 자동 로드됩니다. 이 시나리오에서 가장 큰 리스크는 한 회사 코드에 다른 회사 키가 실수로 흘러가는 사고인데, 디렉토리 경계로 그걸 막아주는 게 direnv의 장점입니다.

더 단순하게 가려면 ~/.zshrc에 alias 두 줄이면 됩니다.

alias cct='ANTHROPIC_AUTH_TOKEN=$TEAM_TOKEN claude'
alias cce='ANTHROPIC_AUTH_TOKEN=$ENT_TOKEN claude'

디렉토리에 묶이지 않아 빠른 대신, 지금 어느 회사 컨텍스트인지는 사람이 기억해야 합니다. Anthropic 전용 미니멀 스위처인 claude-code-switch도 CCS보다 가볍고 이 시나리오에 정확히 맞습니다. CCS를 굳이 쓴다면 멀티 계정 모드만(ccs auth create team / ccs auth create ent) 떼어 쓰는 것도 동작은 하지만, 프로바이더 다양화, 프록시, OAuth 같은 95% 기능을 놀리면서 의존성과 복잡도는 그대로 짊어지게 됩니다. 나중에 GLM 같은 외부 프로바이더로 확장할 가능성이 있을 때만 미리 깔아두는 의미가 있습니다.

Enterprise가 SSO를 강제하면 단순 API 키 토글로는 안 되고 OAuth 로그인 사이클이 필요합니다. 이때는 direnv만으로 부족해 CCS의 OAuth 프로필 분리나 Anthropic Console의 organization 스위처를 써야 하는데, SSO 강제에 잦은 전환까지 겹친 경우가 사실상 CCS를 정당화하는 거의 유일한 회사 시나리오입니다. 정리하면 API 키 기반 두 계정에는 direnv로 충분하고, SSO 강제에 잦은 전환이 겹칠 때만 CCS가 값을 하며, 그 밖에는 대체로 과한 도구입니다.

설정/대화는 공유하면서 계정만 분리하고 싶다면

같은 시나리오의 변종입니다. 두 회사 계정을 전환은 하되 슬래시 커맨드, MCP, hooks, CLAUDE.md/대화 히스토리는 한 군데에서 공유하고 싶은 경우인데, 이때는 정답이 다릅니다.

먼저 Claude Code가 무엇을 어디 두는지 알면 답이 나옵니다.

위치무엇계정 종속?
~/.claude/settings.json사용자 설정 (theme, env, hooks 트리거 등)무관
~/.claude/commands/, agents/, skills/커스텀 슬래시 커맨드, 에이전트, 스킬무관
~/.claude/CLAUDE.md글로벌 메모리무관
~/.claude/projects/<path-hash>/*.jsonl대화 히스토리 (프로젝트 경로 기준)무관
OS 키체인 / ANTHROPIC_AUTH_TOKEN인증 토큰종속

핵심은 계정 종속이 토큰 한 가지뿐이라는 점입니다. 토큰만 갈아끼우면 나머지는 자동으로 공유되므로, 답은 direnv로 토큰만 스위칭하는 것입니다. .envrcexport ANTHROPIC_AUTH_TOKEN=... 한 줄만 두고 ~/.claude/는 건드리지 않으면, 슬래시 커맨드, hooks, MCP, 대화가 전부 자동으로 공유됩니다. 반대로 ccs auth create work 같은 CCS의 isolated profile 모드는 정확히 이 공유를 분리하는 기능이라 피해야 합니다. CCS 자체는 써도 되지만 isolation 옵션은 끄고 토큰만 바꾸는 모드로만 씁니다. 여러 머신 사이에서도 공유하고 싶으면 ~/.claude/를 git으로 관리하는 dotfiles가 답인데, settings.json에 토큰이 평문으로 들어가지 않도록 .gitignoresettings.local.json을 등록해 두는 정도만 주의하면 됩니다.

다만 Enterprise 데이터 격리는 짚어둘 필요가 있습니다. Enterprise 플랜 약관에 대화 데이터를 다른 organization 환경과 섞지 못하게 하는 조항이 있을 수 있어서, PII, 고객 데이터, 비공개 코드를 다루는 프로젝트라면 ~/.claude/projects/를 공유하는 것이 회사 보안 정책 위반이 될 수 있습니다. 사내 보안팀에 한 번 확인하는 게 안전하고, 우려가 있으면 프로젝트 디렉토리 자체를 회사별로 분리해 자연히 다른 <path-hash>로 떨어뜨리는 패턴(예: ~/work/team/, ~/work/ent/ 두 루트)이 가장 깔끔합니다.

SSO 강제 환경, 한국 회사 현실에 더 가까운 케이스

위 두 서브섹션은 API 키를 사용자가 직접 발급받을 수 있다는 전제 위에 있습니다. 그런데 국내 대기업, 금융권, 보안 민감 조직 대부분은 그 전제 자체가 깨져 있습니다. Team/Enterprise 플랜에서 SSO를 강제하면 사용자 API 키 발급이 비활성화되고, 인증은 OAuth + IdP MFA 사이클로만 가능합니다. 이 환경에서는 도구 선택이 다시 한 번 달라집니다.

먼저 구조를 짚으면 다음과 같습니다.

  1. claude login → 브라우저 → 회사 IdP 로그인 (SSO + MFA) → Anthropic Console → 토큰 발급
  2. 토큰은 OS 키체인 또는 ~/.claude/.credentials.json에 저장
  3. 토큰은 보통 시간 제한이 있고 (몇 시간~며칠), 만료되면 다시 SSO 사이클
  4. 한 머신의 한 OS 유저는 동시에 한 organization의 세션만 보유 가능

즉 SSO 강제 환경에서 두 회사 organization 사이를 전환하려면 결국 logout, 다른 org로 login, 다시 작업 사이클을 돌아야 합니다. IdP가 매번 MFA를 요구하니 도구가 우회할 수 있는 영역이 아닙니다.

실제로 동작하는 패턴은 현실적 운영 순으로 정리할 수 있습니다. 가장 흔한 사례이자 가장 깔끔한 결론은 organization 하나만 쓰는 것입니다. 회사가 Enterprise 하나를 표준화했고 Team이 레거시/POC였다면 둘 다 유지할 이유가 없으니 회사 IT에 정리를 요청하는 편이 낫습니다. 두 organization이 같은 Anthropic Workspace 안의 서로 다른 workspace라면, admin이 workspace를 분리해 두는 것만으로 organization 전환 없이 claude /switch-workspace 식의 in-session 전환이 가능합니다. 이게 되는지부터 회사 IT에 확인하면 가장 매끄럽습니다.

두 organization이 진짜 별개라면 방법이 무거워집니다. 같은 노트북에 OS 계정 두 개를 만들어 각각 SSO 로그인 상태를 독립으로 두면 키체인과 ~/.claude/가 자연히 분리되지만, 설정/대화 공유가 깨져서 dotfiles를 두 OS 유저 사이에 공유하는 추가 작업이 필요합니다. CCS의 OAuth 프로필 분리(ccs auth create work-team / ccs auth create work-ent)는 이론상 이 시나리오에 정확히 맞지만, 회사 IdP가 OAuth 흐름에 거는 추가 검증(IP 화이트리스트, 디바이스 트러스트, MFA 빈도)에 따라 CCS의 토큰 캐시 방식이 IdP 정책과 충돌할 수 있어 시도 전에는 알기 어렵습니다. 사내 보안팀과 사전 협의를 권합니다. 가장 보수적인 선택은 두 회사용 Dev Container나 VM을 따로 띄워 각각 한 organization으로 로그인하는 것인데, 무겁고 호스트 키체인을 못 쓴다는 대가가 따릅니다.

반대로 SSO 환경에서 동작하지 않는 패턴은 다음과 같습니다.

  • direnv + ANTHROPIC_AUTH_TOKEN 토글: 사용자 API 키 자체가 없으니 토글할 대상이 없습니다.
  • 셸 alias로 토큰 swap: 같은 이유로 동작하지 않습니다.
  • "session 토큰을 추출해서 재사용": 만료가 짧고 IdP가 디바이스 핑거프린팅으로 거부할 가능성이 있습니다. ToS의 회색지대이기도 합니다.
  • 두 organization의 ~/.claude/.credentials.json을 수동으로 백업/복구: 동작은 할 수 있지만 MFA 빈도 정책을 우회하는 모양새가 되어 정책 위반 우려가 있습니다.

한국 회사 현실에 맞는 결론은, SSO 강제면 organization 전환 자체가 비용이 크다는 사실을 인정하고 두 organization을 동시에 자주 오가지 않는 워크플로우를 설계하는 쪽입니다. 오전엔 Team org에서 한 덩어리, 오후엔 Enterprise org에서 다른 덩어리를 처리하는 식의 시간 분할이, 전환을 도구로 매끄럽게 만드는 것보다 운영 부담이 훨씬 적습니다. 도구는 그 다음 문제입니다.

실전 팁

블로그, GitHub 이슈, 도구 README의 패턴을 종합해 한국 환경에서 바로 쓸 만한 것만 모았습니다.

가장 큰 가치는 라우팅 자동화보다 자기 워크플로우에 맞는 모델 분리에서 나옵니다. 설계/아키텍처는 ccs(Claude Sonnet), 보일러플레이트/테스트 정리는 ccs glm, 1M 컨텍스트가 필요한 대규모 리뷰는 ccs kimi, 민감 코드는 로컬 ccs ollama 정도가 무난한 시작점입니다. Claude Sonnet 4.6도 1M 컨텍스트를 지원하지만 토큰 값이 비싸서, 큰 monorepo 전체 리뷰는 ccs kimi 한 번이 훨씬 싸게 먹힙니다. 새 prompt가 안정적인지 확인하는 단계라면 첫 5~10번은 무료인 ccs ollama로 돌려보다 만족스러우면 Claude로 옮기면 됩니다.

회사/개인 계정은 무조건 격리하는 편이 좋습니다(ccs auth create work / ccs auth create personal). weekly cap이 섞이지 않아 둘 다 마음 편히 쓸 수 있습니다. 여기에 더해 Claude Max의 weekly cap이 60% 이상 깎였으면 남은 작업의 70% 정도는 GLM으로 돌리는 룰을 정해두면, cap이 갑자기 끊기는 사고가 사라집니다.

라우팅은 단순하게 시작하는 편이 낫습니다. proxy.routing 시나리오 라우팅을 처음부터 복잡하게 짜면 요청이 어디로 갔는지 못 따라가니, 모델을 직접 지정해 일주일 운용하고 패턴이 보이면 그때 룰을 추가합니다. 기본 CLIProxyAPI가 새 프로바이더를 못 잡으면 옵트인 fork(CLIProxyAPIPlus) 활성화부터 시도해 볼 만합니다.

매번 eval 치기 싫으면 ~/.zshrc에 한 줄을 넣어 영구화합니다.

[[ -s "$HOME/.ccs/init.zsh" ]] && source "$HOME/.ccs/init.zsh"

멀티 계정 사고를 디버깅할 때는 CCS_LOG=debug ccs glm "..." 식으로 한 번 돌려보면 어느 base URL로 갔는지, 어느 토큰을 썼는지 정확히 보이니 로그를 켜두는 게 필수입니다.

한 줄로

CCS는 Claude Code가 환경변수 두 개로 정의되는 도구라는 사실을 그대로 받아들인 도구입니다. 그 두 변수를 프로파일이라는 단위로 정리하고, OAuth 우회와 다중 계정까지 한 명령 표면 위로 올려놓았습니다. weekly cap 시대에 한 프로바이더에만 의지하는 게 부담스러워진 사용자에게 기본기에 가까운 도구가 됐고, 같은 영역에 claude-code-router 같은 더 큰 별 수의 경쟁자도 있습니다. 자기 워크플로우가 옮겨 다닐 곳이 셋 이상인지부터 보고, 그다음에 도구를 고르는 순서가 맞아요.


→ 다음 편: Claude Code Switcher (CCS) 2편: 디렉토리, 설정, 라우팅의 실제 — 사용자 머신의 ~/.ccs/를 직접 들여다보며 instance / shared 격리 모델, config.yaml의 9개 영역, 로컬 프록시의 5단계 변환을 풀어 봅니다.

참고

CCS 설정과 라우팅

· 약 14분

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 (구조 동일)

여기서 눈여겨볼 것은 두 가지입니다. 하나는 2단계 symlink 체인입니다. instances/team/settings.json을 따라가면 ~/.ccs/shared/settings.json으로, 다시 ~/.claude/settings.json으로 이어집니다. 사용자의 기존 Claude Code 글로벌 설정이 모든 인스턴스에 자동 반영된다는 뜻이며, 한 번 만든 슬래시 커맨드, 스킬, MCP가 team과 enterprise 양쪽에서 살아 있는 이유도 여기에 있습니다.

다른 하나는 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.yamlaccounts: 섹션:

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_groupshared일 때 쓸 그룹 이름이며, ~/.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
ccsClaude Code (기본)
ccsd / ccs-droidFactory Droid
ccsx / ccs-codexCodex CLI (네이티브)
ccsxpCodex CLI + CLIProxy 프로바이더 오버라이드

내부적으로는 각 바이너리가 CCS_INTERNAL_ENTRY_TARGET 환경변수를 세팅한 후 target resolver에 위임합니다. resolver의 우선순위:

  1. CLI 플래그 (--target)
  2. 진입 바이너리 자체
  3. argv[0] 이름 검출
  4. 프로파일별 config
  5. 기본값

이 추상화 덕분에 "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 정리).

  1. 127.0.0.1에서 그 프로파일의 로컬 포트 바인딩 (포트는 proxy.profile_ports에서 할당)
  2. Claude Code가 보내는 Anthropic 형식 /v1/messages 요청 수신
  3. OpenAI chat-completions 형식으로 변환
  4. 업스트림 프로바이더로 포워드
  5. 스트리밍 응답을 다시 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를 포함 (가벼운 백그라운드 작업)
thinkAnthropic extended thinking 활성화
longContext토큰 추정치가 임계값 초과 (proxy.routing.longContextThreshold)
webSearchweb_search 툴 호출

config.yamlproxy.routing.longContextThreshold 키가 실제로 있어 임계값을 손으로 조절할 수 있습니다. 자동화의 깊이는 워크로드에 따라 잘 맞기도 하고 헷갈리기도 하므로, 처음부터 라우팅 룰을 복잡하게 짜기보다 한 주 동안 단순하게 운영한 뒤 패턴을 보고 추가하는 편이 낫습니다(1편 팁 7절 참조).

CLIProxy 서브시스템, OAuth 프로바이더의 자리

API 키가 없는 OAuth 기반 프로바이더(Gemini, GitHub Copilot, AWS Kiro 등)는 별도 서브시스템이 책임집니다. ~/.ccs/cliproxy/bin/에 자동 다운로드되는 CLIProxyAPI 바이너리가 그 핵심입니다.

이 서브시스템의 Account manager는 OAuth 토큰의 lifecycle(발급, 갱신, 만료)을 관리하고, Quota manager와 Quota fetcher는 각각 프로바이더별 quota 추적과 자동 failover, 사용량 실시간 동기화를 맡습니다. Auth handler는 Anthropic, Gemini, Copilot, Kiro 등 각 프로바이더의 OAuth 흐름을 처리합니다. Model catalogs에는 프로바이더별 모델 목록과 compatibility 가드가 있으며, codex-plan-compatibility.ts 같은 파일이 plan별로 맞지 않는 모델 조합을 막아 줍니다. Hybrid quota strategy는 round-robin 또는 fill-first(cliproxy.routing.strategy) 방식으로 같은 프로바이더의 여러 OAuth 계정을 어떻게 분산할지 정합니다.

config.yamlcliproxy.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/에는 각각 슬래시 커맨드, 스킬, 에이전트 정의가 들어가므로 git에 올리기 좋습니다. 글로벌 메모리인 ~/.claude/CLAUDE.md도 관리 대상입니다. ~/.claude/settings.json은 토큰을 직접 적어 두지 않은 경우에만 포함해야 하며, env 필드에 비밀이 있다면 settings.local.json 패턴으로 분리합니다. 실 디렉토리인 ~/.ccs/shared/context-groups/default/에서는 projects/를 빼고 CCS의 그룹 메타만 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 Code 대화 히스토리인 ~/.claude/projects/도 git에 올리지 않습니다.

새 머신 부트스트랩 4단계

dotfiles가 ~/.claude/만 들고 있다고 할 때 새 머신에서 같은 환경을 만들려면:

  1. dotfiles를 clone해 ~/.claude/를 채웁니다.
  2. npm install -g @kaitranntt/ccs로 CCS를 설치합니다.
  3. ccs config로 초기 ~/.ccs/ 골격을 생성합니다.
  4. 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합니다. 복원할 때 같은 dotfiles 환경을 가정하므로 보통은 symlink를 그대로 두는 편이 의미 있습니다.

config.yaml의 비공식 영역들

CCS의 README가 강조하지 않지만 config.yaml 키만 봐도 드러나는, 단순 스위처라기엔 풍부한 통합이 여럿입니다.

가장 눈에 띄는 것은 WebSearch fallback입니다. websearch.providers 아래에 exa, tavily, brave, searxng, duckduckgo, gemini, opencode, grok 여덟 곳이 등록돼 있어, Claude Code의 WebSearch가 Anthropic 밖 8개 프로바이더로 넘어갈 수 있습니다. docs/websearch.md가 별도 문서로 있을 만큼 일급 통합입니다. 외부 에디터/툴 통합도 넓어서, copilot.account_type, rate_limit, model로 GitHub Copilot을 프로바이더 한 슬롯처럼 다루고 cursor.ghost_mode/cursor.port로 Cursor의 ghost-mode까지 건드립니다. channels.selected, channels.unattended를 통해 Telegram, Discord, iMessage로 Claude에게 일을 시키는 통합도 있습니다(1편의 free-claude-code 텔레그램 봇 사례와 같은 결입니다).

세밀한 정책 키도 있습니다. thinking.tier_defaults는 opus, sonnet, haiku별로 thinking 기본 모드를 다르게 잡게 해주고(Opus는 항상 deep thinking, Haiku는 끄는 식), global_env.envDISABLE_BUG_COMMAND, DISABLE_ERROR_REPORTING, DISABLE_TELEMETRY는 보안, 프라이버시에 민감한 회사 환경에서 텔레메트리를 자동으로 꺼줍니다. cliproxy.routing.session_affinitysession_affinity_ttl은 같은 세션을 같은 OAuth 계정으로 sticky하게 묶는 정책입니다. 이런 영역들은 CCS가 단순 스위처에서 운영 플랫폼으로 진화 중이라는 신호입니다.

한 줄로

1편이 CCS의 표면이었다면 2편은 안쪽입니다. 안쪽을 보고 나면 "프로파일로 옮겨 다닌다"는 1편의 단순한 설명이 사실은 instance 격리, shared 공유, OAuth 분리, OpenAI와 Anthropic 사이의 변환, 시나리오 라우팅, 여러 통합 지점이라는 축들로 짜여 있다는 게 보입니다. 추상화가 두껍다는 1편의 단점 평가는 그래서 정확하지만, 그 두께가 풀고 있는 문제도 같이 두껍다는 사실이 디렉토리 한 통에 적나라하게 드러납니다.

다음 편이 있다면 두 갈래 중 하나일 가능성이 커요. proxy.routing 시나리오를 실제 워크로드로 맞춰 보는 실측 운영 글이거나, CCS와 dotfiles, Anthropic Workspaces를 엮는 멀티-머신 계정 운영 글일 거예요. 어느 쪽이 먼저 나올지는 다음 글에서 정할게요.

참고

Claude Code 외부 에디터

· 약 5분

터미널에서 긴 프롬프트는 힘들다

Claude Code를 쓰다 보면 몇 줄 넘는 프롬프트를 치고 싶을 때가 종종 있어요. 들여쓰기가 있거나, 코드를 붙이거나, 한글 IME 상태에서 복잡한 편집이 필요한 순간이에요. 터미널의 한 줄 편집기는 이런 상황에 약해요.

이럴 때 Claude Code 프롬프트를 외부 에디터로 열어서 쓸 수 있습니다. 방법 자체는 간단한데, 운용하다 보면 사소한 함정이 하나 있어 같이 정리합니다.

$EDITORCtrl+X Ctrl+E를 쓰는 기본 방법

Claude Code는 프롬프트 입력 중 Ctrl+X 뒤이어 Ctrl+E 를 누르면 $EDITOR 환경변수에 지정된 에디터를 띄웁니다. 내부적으로는 claude-prompt-<uuid>.md 같은 임시 파일을 만들고 그걸 에디터에 넘깁니다. 편집 후 저장하고 닫으면 그 내용이 그대로 프롬프트에 입력됩니다.

가장 단순한 세팅:

# ~/.zshrc
export EDITOR='nvim'

이러면 Ctrl+X Ctrl+E로 nvim이 뜨고, :wq로 닫으면 내용이 Claude Code 프롬프트로 들어옵니다.

참고로 최근 Claude Code 버전은 Ctrl+G도 같은 동작으로 바인딩돼 있습니다. 둘 다 테스트해 보고 손에 맞는 것을 쓰면 됩니다.

전역 EDITOR는 nvim, Claude만 Cursor로

그런데 EDITOR 환경변수는 git, crontab, less 등 여러 곳에서 참조합니다. 저는 이걸 nvim으로 둔 상태가 편해서 바꾸고 싶지 않아요. Claude Code 프롬프트만 GUI 에디터(Cursor, VSCode)로 띄우고 싶을 때는 함수로 감싸서 EDITOR를 이 프로세스 범위에서만 오버라이드하면 됩니다.

~/.zshrc에 들어 있는 것은 이것입니다.

cc() {
# VSCode 쓸 때
# EDITOR="code -w" command claude "$@"

# Cursor 쓸 때
EDITOR="/Applications/Cursor.app/Contents/Resources/app/bin/cursor -w" command claude "$@"
}

중요한 점은 두 가지입니다.

  • command claude: alias나 동명 함수가 걸려 있어도 실제 claude 바이너리를 실행하도록 우회합니다.
  • -w 플래그: Cursor/VSCode는 기본적으로 연 뒤 바로 셸로 제어를 반환합니다. -w(또는 --wait)를 붙여야 에디터 창이 닫힐 때까지 대기합니다. 없으면 Claude Code가 빈 파일을 바로 읽어 갑니다.

이제 터미널에서 cc만 실행하면 Claude Code가 뜨고, 프롬프트 편집 시 Cursor로 열립니다. 다른 툴들은 EDITOR=nvim을 그대로 봅니다.

실제 동작

1. 프롬프트에서 Ctrl+X Ctrl+E

Claude Code 프롬프트에서 Ctrl+X Ctrl+E 입력

2. Cursor에 임시 파일이 열립니다

Cursor에 claude-prompt 임시 파일이 열린 모습

파일명은 claude-prompt-<uuid>.md 형식입니다. 마크다운이라 코드 블록이나 리스트도 편하게 쓸 수 있습니다.

3. 저장하고 닫으면 프롬프트로 들어옵니다

여기서 한 가지 이상한 게 생겼습니다.

![복귀 후 프롬프트에 ^[[O^[I 이상 문자가 끼어든 상태

프롬프트 앞부분의 기존 텍스트와 Cursor에서 쓴 내용 사이에 ^[[O^[[I^[[O^[[I^[[O가 끼어 있습니다. 정체가 무엇일까요?

^[[O, ^[[I의 정체

이 문자들은 Focus Reporting에서 나옵니다.

터미널에는 포커스 리포팅(Focus Reporting) 이라는 기능이 있습니다. ANSI/xterm 스펙의 DEC Private Mode 1004. 이게 켜져 있으면 터미널 창의 포커스가 들어오고 나갈 때 아래 두 이스케이프 시퀀스가 터미널에 주입됩니다.

이벤트시퀀스
포커스 들어옴 (focus-in)ESC [ I (표기상 ^[[I)
포커스 나감 (focus-out)ESC [ O (표기상 ^[[O)

이걸 왜 쓰느냐면, vim/tmux 같은 TUI 앱이 유휴 상태일 때 렌더링을 멈추거나 커서 스타일을 바꾸기 위해 필요합니다. 터미널이 보내주는 이 이벤트로 현재 창이 활성인지 아닌지를 알 수 있습니다.

문제는 이 지점입니다.

  1. Claude Code가 Cursor를 띄웁니다. 이 순간 터미널 포커스가 제 터미널에서 Cursor 윈도우로 이동하고, 터미널이 ^[[O (focus-out)을 내보냅니다.
  2. Cursor에서 저장/종료하면 포커스가 다시 터미널로 돌아옵니다. ^[[I (focus-in)이 내보내집니다.
  3. 창 전환을 두세 번 하다 보면 여러 번의 in/out이 생깁니다.
  4. 이 이스케이프 시퀀스들은 stdin 버퍼에 쌓입니다. Claude Code가 에디터 종료 후 다시 입력 읽기를 시작할 때, 앞서 쌓인 것들을 프롬프트 텍스트로 흡수합니다.

정리하면 Cursor/VSCode 같은 별도 윈도우 GUI 에디터를 띄울 때만 이 현상이 생깁니다. 터미널에서 포커스가 실제로 왔다갔다 하기 때문입니다.

해결 방법

1순위: nvim / vim / helix / micro

같은 터미널 세션 안에서 도는 TUI 에디터는 포커스가 창 단위로 움직이지 않습니다. 창은 하나, 안에서 그냥 화면이 바뀔 뿐입니다. focus-in/out 이벤트 자체가 생기지 않습니다.

export EDITOR='nvim'

제 경험상 이게 제일 안정적이에요. nvim이 충분히 좋은 마크다운 편집기이기도 하고요.

2순위: 그래도 Cursor/VSCode를 쓰고 싶다면

몇 가지 부분 해결책이 있지만 완벽한 건 없습니다.

  • 복귀 후 섞인 ^[[ 시퀀스를 backspace로 수동 제거한 뒤 Enter를 누릅니다.
  • 터미널 앱 설정에서 focus reporting을 비활성화합니다(Ghostty, iTerm2 등에 옵션이 있습니다). 단 vim/tmux 쪽 기능도 같이 잃습니다.
  • Cursor/VSCode를 쓰되 cc 함수 내에서 임시로 focus reporting을 꺼 주는 래퍼를 끼웁니다. 번거로운 방법입니다.

깔끔한 길은 첫 번째예요. 긴 프롬프트 편집에 IDE급 기능이 꼭 필요한 게 아니라면 TUI 에디터 쪽이 나아요.

요약

  • Claude Code 프롬프트는 Ctrl+X Ctrl+E (또는 Ctrl+G)로 $EDITOR를 호출합니다.
  • 전역 EDITOR를 건드리지 않고 싶다면 cc() 함수 패턴으로 Claude에 한정해 오버라이드합니다. -w 플래그를 꼭 붙여야 합니다.
  • Cursor/VSCode를 쓰면 포커스 리포팅 시퀀스가 프롬프트에 섞이는 부작용이 있습니다.
  • 제일 안정적인 조합은 TUI 에디터(nvim 등) + EDITOR=nvim입니다.

참고 링크

Claude Code Skills

· 약 4분

commands가 사라졌다?

Claude Code를 쓰면서 .claude/commands/ 디렉토리에 커스텀 명령어를 만들어 쓰던 분들이 있을 거예요. /deploy, /review, /commit 같은 것들이에요.

그런데 어느 날 문서를 보니 "Skills"라는 게 나옵니다. commands는 어디로 간 것인지 의문이 듭니다.

결론부터 말하면, commands가 skills로 통합됐습니다. 정확히는 v2.1.3 (2026년 1월)부터입니다.

"Merged slash commands and skills, simplifying the mental model with no change in behavior" — Claude Code v2.1.3 Changelog

타임라인

시점변경사항
2025.10 (v2.0.22)Skills 시스템 첫 도입
2026.01 (v2.1.3)Slash commands와 skills 공식 병합
2026.04 현재.claude/commands/ 여전히 동작. 새로 만들 땐 skills 권장

공식 deprecation 일정은 아직 없습니다. 하지만 방향은 명확합니다.

commands와 skills의 차이

기존: commands

.claude/commands/deploy.md

마크다운 파일 하나. 프롬프트를 적어두면 /deploy로 호출할 수 있었습니다. 단순하고 직관적이었습니다.

현재: skills

.claude/skills/deploy/
├── SKILL.md # 주요 지침 (필수)
├── template.md # Claude가 채울 템플릿
├── examples/
│ └── sample.md # 예제 출력
└── scripts/
└── validate.sh # Claude가 실행할 스크립트

디렉토리 구조입니다. SKILL.md가 진입점이고, 지원 파일을 함께 둘 수 있습니다.

핵심 차이: frontmatter

Skills의 가장 큰 차이는 YAML frontmatter입니다.

---
name: deploy
description: 프로덕션 배포
disable-model-invocation: true
allowed-tools: Bash(ssh *) Bash(rsync *)
model: claude-opus-4-7
context: fork
---

배포 순서:
1. 테스트 실행
2. 빌드
3. 서버에 전송

frontmatter로 할 수 있는 것들:

필드기능
descriptionClaude가 자동으로 skill을 선택하는 기준
disable-model-invocationClaude가 임의로 호출하는 것 방지
allowed-tools이 skill에서 쓸 수 있는 도구 제한
model특정 모델 강제 지정
context: forksubagent에서 격리 실행
paths특정 파일 패턴에서만 활성화

commands에서는 이런 제어가 전부 불가능했습니다.

자동 호출이 핵심

commands는 /명령어로 직접 호출해야만 동작했습니다. skills는 다릅니다.

description에 적절한 키워드를 넣어두면, Claude가 대화 맥락에서 관련이 있다고 판단할 때 자동으로 로드합니다. 예를 들면 다음과 같습니다.

---
name: api-conventions
description: API design patterns for this codebase
---

API 엔드포인트 작성 시:
- RESTful 명명 규칙 사용
- 일관된 에러 포맷 반환
- 요청 유효성 검사 포함

이 skill은 /api-conventions으로 호출할 수도 있고, Claude가 API 코드를 작성할 때 알아서 참조하기도 합니다.

반대로 배포처럼 사람이 타이밍을 통제해야 하는 작업은 disable-model-invocation: true를 설정해서 자동 호출을 막습니다.

마이그레이션은 어떻게

안 해도 된다

기존 .claude/commands/ 파일은 그대로 동작합니다. 이름이 같으면 skill이 우선할 뿐입니다.

하고 싶다면

  1. .claude/skills/<이름>/ 디렉토리 생성
  2. 기존 .md 파일 내용을 SKILL.md로 이동
  3. 필요하면 frontmatter 추가
  4. 기존 commands 파일 삭제

예시:

Before.claude/commands/deploy.md

프로덕션에 배포해줘.
1. 테스트 실행
2. 빌드
3. rsync로 서버에 전송

After.claude/skills/deploy/SKILL.md

---
name: deploy
description: 프로덕션 빌드 및 배포
disable-model-invocation: true
---

프로덕션에 배포해줘.
1. 테스트 실행
2. 빌드
3. rsync로 서버에 전송

내용은 같고, frontmatter만 추가됐습니다.

번들 Skills

Claude Code가 기본으로 제공하는 skills도 있습니다.

Skill기능
/batch <instruction>대규모 변경을 병렬 에이전트로 처리
/claude-apiClaude API 레퍼런스 로드
/debug [description]디버그 로깅 활성화 및 문제 분석
/loop [interval] <prompt>프롬프트를 반복 실행
/simplify [focus]최근 변경 코드의 품질 리뷰

이것들도 전부 skill입니다. 프롬프트 기반으로 Claude가 도구를 조율해서 실행합니다.

Skills 저장 위치

skill을 어디에 저장하느냐에 따라 적용 범위가 달라집니다.

위치경로적용 대상
Enterprise관리 설정조직 전체
Personal~/.claude/skills/<name>/SKILL.md모든 프로젝트
Project.claude/skills/<name>/SKILL.md해당 프로젝트만
Plugin<plugin>/skills/<name>/SKILL.md플러그인 활성화 시

제가 자주 쓰는 건 Personal에, 프로젝트 팀원과 공유할 건 Project에 넣으면 돼요.

커뮤니티 반응

병합이 발표됐을 때 커뮤니티에서 혼란이 있었습니다. GitHub에 "Merged slash commands and skills"가 뭔 말이냐는 이슈가 올라왔고, "no change in behavior"면 왜 changelog에 넣느냐는 비판도 있었습니다.

실제로 사용자 입장에서는 기존 commands가 그대로 동작하기 때문에 체감할 수 있는 변화가 거의 없었습니다. 하지만 앞으로 새로 만들 때는 skills로 만드는 편이 맞습니다. frontmatter의 자동 호출 제어, 도구 제한, subagent 실행 같은 기능은 commands에서는 쓸 수 없습니다.

정리

Commands (기존)Skills (현재)
구조마크다운 파일 1개디렉토리 (SKILL.md + 지원 파일)
자동 호출불가Claude가 맥락에 따라 자동 로드
frontmatter미지원도구 제한, 모델 지정, 자동 호출 제어 등
subagent 실행불가context: fork 지원
동작 여부여전히 동작권장
같은 이름 충돌 시-skill이 우선

기존 commands를 당장 마이그레이션할 필요는 없어요. 하지만 새로 만들 건 skills로, 기존 것도 기능이 필요하면 skills로 전환하는 게 좋아요.

참고 자료

Claude Code 필수 단축키

· 약 5분

Claude Code 단축키 도움말 화면

Claude Code에서 ?를 누르면 볼 수 있는 단축키 요약 화면이에요.

Claude Code 단축키 도움말

이 화면에 나오는 것들을 하나씩 풀어서 정리해보겠습니다.

단축키를 왜 알아야 하나

Claude Code를 터미널에서 쓰다 보면, 마우스 없이 모든 걸 해결해야 하는 순간이 옵니다. 긴 프롬프트를 수정하고, 실행을 취소하고, 모델을 바꾸고, 히스토리를 뒤지는 일이 전부 키보드로 가능합니다.

문제는 단축키가 꽤 많다는 점입니다. 전부 외울 필요는 없습니다. 이 글에서는 매일 쓰게 되는 것만 추렸습니다.

이 시리즈는 3편으로 구성됩니다.

  • 1편: 필수 단축키 (이 글)
  • 2편: Vim 모드
  • 3편: 커스터마이징 & 세부 컨텍스트

macOS 터미널 설정부터

시작하기 전에 하나 짚고 가겠습니다. macOS에서 Option(Alt) 키 조합이 안 먹히는 경우가 많습니다. 터미널 설정을 먼저 바꿔야 합니다.

터미널설정 방법
iTerm2Settings → Profiles → Keys → Left/Right Option key → "Esc+"
Terminal.appSettings → Profiles → Keyboard → "Use Option as Meta Key" 체크
VS Code설정에서 "terminal.integrated.macOptionIsMeta": true 추가

이걸 안 하면 Option+P, Option+T 같은 단축키가 특수문자만 입력됩니다. 꼭 먼저 설정해야 합니다.

일반 제어

가장 자주 쓰는 기본 조작입니다.

단축키기능비고
Ctrl+C현재 입력 또는 실행 취소가장 많이 누르게 됨
Ctrl+DClaude Code 종료EOF 시그널
Ctrl+L입력창 내용 지우기대화 기록은 유지
Ctrl+R히스토리 역방향 검색이전 프롬프트를 찾을 때
Ctrl+O트랜스크립트 뷰어 토글도구 실행 상세 내용 확인
Ctrl+B실행 중인 작업 백그라운드로tmux 사용자는 두 번 눌러야 함
Ctrl+T태스크 목록 토글진행 상황 확인
Esc × 2되감기(Rewind) / 요약이전 상태로 복원

히스토리 검색 모드 (Ctrl+R 이후)

히스토리 검색에 진입하면 쓸 수 있는 키가 따로 있습니다.

단축키기능
Ctrl+R다음(이전) 매칭 결과로 이동
Tab 또는 Esc현재 결과 선택 후 편집 계속
Enter현재 결과 선택 후 바로 실행
Ctrl+C검색 취소, 원래 입력 복원

텍스트 편집

프롬프트가 길어질수록 이 단축키들이 빛을 발합니다.

단축키기능비고
Ctrl+K커서부터 줄 끝까지 삭제삭제된 텍스트 저장됨
Ctrl+U커서부터 줄 시작까지 삭제줄 전체 지울 때 유용
Ctrl+Y삭제한 텍스트 붙여넣기Ctrl+K/Ctrl+U로 지운 것
Alt+B한 단어 뒤로 이동macOS: Option 설정 필요
Alt+F한 단어 앞으로 이동macOS: Option 설정 필요
Ctrl+_실행 취소(Undo)Ctrl+Shift+-도 동일
Ctrl+S현재 프롬프트 임시 저장(Stash)나중에 다시 불러올 수 있음

Ctrl+K로 지우고 Ctrl+Y로 붙여넣는 조합은 터미널의 킬 링(kill ring)과 같은 원리입니다. 익숙해지면 마우스 드래그보다 빠릅니다.

멀티라인 입력

Claude Code에서 한 줄짜리 프롬프트만 쓰지는 않습니다. 여러 줄 입력하는 방법을 알아두어야 합니다.

방법단축키비고
이스케이프\ + Enter모든 터미널에서 동작
macOS 기본Option+EntermacOS 기본값
Shift+EnterShift+EnteriTerm2, WezTerm, Ghostty, Kitty
줄바꿈 문자Ctrl+J라인 피드
붙여넣기코드 블록 직접 붙여넣기코드, 로그 등

VS Code, Alacritty, Zed, Warp 터미널에서 Shift+Enter를 쓰려면 /terminal-setup을 실행해서 바인딩을 설치해야 합니다.

또 하나 유용한 단축키가 있습니다.

단축키기능
Ctrl+G 또는 Ctrl+X Ctrl+E외부 에디터에서 프롬프트 편집

프롬프트가 정말 길어지면 터미널에서 편집하는 게 고역입니다. 이 단축키를 누르면 기본 에디터(보통 vim이나 nano)가 열리고, 거기서 편하게 작성할 수 있습니다. 저장하고 나오면 바로 입력됩니다.

모드와 모델 전환

Claude Code를 쓰다가 모델을 바꾸거나 모드를 전환하고 싶을 때는 명령어를 칠 필요가 없습니다.

단축키기능비고
Shift+Tab권한 모드 순환default → acceptEdits → plan → auto → bypassPermissions
Option+P모델 전환프롬프트 유지한 채로 변경
Option+T확장 사고(Extended Thinking) 토글
Option+OFast 모드 토글

Windows/Linux에서는 Option 대신 Alt를 사용합니다.

Shift+Tab은 특히 자주 씁니다. 코드 수정이 맘에 안 들면 plan 모드로 바꿔서 계획을 먼저 세우게 하고, 빠르게 진행하고 싶으면 auto로 전환하는 식입니다.

입력 프리픽스

프롬프트 첫 글자에 따라 동작이 달라집니다.

프리픽스기능예시
/명령/스킬 메뉴 열기/help, /config, /doctor
!셸 명령 직접 실행!git status, !npm test
@파일 경로 자동완성@src/index.ts

!은 Claude에게 시키지 않고 직접 셸 명령을 실행할 때 씁니다. git status 같은 단순 확인을 AI한테 시키면 아깝습니다.

프롬프트 제안

Claude Code가 회색 텍스트로 다음 입력을 제안할 때가 있습니다.

기능
Tab 또는 제안 수락
Enter제안 수락 + 바로 전송
아무 키나 타이핑제안 무시

이미지 붙여넣기

스크린샷이나 이미지를 Claude에게 보여줘야 할 때도 있습니다.

단축키기능
Ctrl+V클립보드 이미지 붙여넣기
Cmd+ViTerm2에서 이미지 붙여넣기

붙여넣으면 [Image #N] 형태로 입력창에 표시됩니다.

확인/권한 다이얼로그

Claude가 파일을 수정하거나 명령을 실행하려 할 때 나오는 확인 창에서 쓸 수 있는 키입니다.

단축키기능
Y 또는 Enter승인
N 또는 Escape거부
/ 옵션 이동
Ctrl+E권한 설명 토글

한눈에 보기

마지막으로 매일 쓰는 것만 골라서 한 장에 정리했습니다.

[일반]
Ctrl+C .......... 취소
Ctrl+D .......... 종료
Ctrl+L .......... 입력 지우기
Ctrl+R .......... 히스토리 검색
Esc × 2 ........ 되감기

[편집]
Ctrl+K .......... 줄 끝까지 삭제
Ctrl+U .......... 줄 시작까지 삭제
Ctrl+Y .......... 붙여넣기 (킬 링)
Alt+B / Alt+F ... 단어 단위 이동
Ctrl+J .......... 줄바꿈

[전환]
Shift+Tab ....... 권한 모드 순환
Option+P ........ 모델 전환
Option+T ........ 사고 모드 토글
Option+O ........ Fast 모드 토글

[입력]
/ ............... 명령 메뉴
! ............... 셸 실행
@ ............... 파일 자동완성

다음 편 예고

2편에서는 Claude Code의 Vim 모드를 다뤄요. /config에서 활성화하면 Normal/Insert 모드 전환, h/j/k/l 내비게이션, 텍스트 오브젝트까지 전부 쓸 수 있어요. Vim 사용자라면 기대해도 좋아요.

이 글은 2026년 4월 기준 Claude Code v2.1.x 기준으로 작성되었습니다. 버전에 따라 단축키가 다를 수 있습니다. ?를 눌러 현재 환경의 단축키를 확인할 수 있습니다.

참고 자료

Claude Code 고급 단축키

· 약 4분

여기까지 왔다면

1편에서 필수 단축키를, 2편에서 Vim 모드를 다뤘어요. 3편에서는 키 바인딩 커스터마이징과 특정 화면에서만 쓰는 세부 컨텍스트 단축키를 정리해요.

이 시리즈는 3편으로 구성됩니다.

키 바인딩 커스터마이징

설정 파일 열기

Claude Code 안에서 /keybindings를 입력하면 ~/.claude/keybindings.json 파일이 열립니다. 없으면 자동 생성됩니다. 변경 사항은 재시작 없이 즉시 적용됩니다.

설정 형식

[
{
"key": "ctrl+enter",
"command": "chat:submit",
"when": "Chat"
},
{
"key": "ctrl+shift+k",
"command": "chat:clearInput",
"when": "Global"
}
]
  • key: 키 조합 (ctrl, alt, shift, meta + 키)
  • command: namespace:action 형식의 액션 이름
  • when: 적용 컨텍스트

바인딩 해제

특정 단축키를 비활성화하려면 commandnull로 설정합니다.

[
{
"key": "ctrl+s",
"command": null,
"when": "Chat"
}
]

사용 가능한 컨텍스트

컨텍스트설명
Global어디서나 동작
Chat채팅 입력 중
Autocomplete자동완성 메뉴
Confirmation확인/권한 다이얼로그
Settings설정 화면
Help도움말 화면
Transcript트랜스크립트 뷰어
HistorySearch히스토리 검색 모드
Tabs탭 내비게이션
Attachments이미지 첨부
Footer하단 바
MessageSelector메시지 선택 (되감기)
DiffDialog디프 뷰어
ModelPicker모델 선택
Scroll스크롤 모드
Plugin플러그인 관리자
Doctor/doctor 화면
ThemePicker테마 선택

바인딩 불가 키

다음 키는 터미널 수준에서 예약되어 있어 변경할 수 없습니다.

이유
Ctrl+C인터럽트 시그널 (하드코딩)
Ctrl+DEOF 시그널 (하드코딩)
Ctrl+MEnter와 동일 (터미널에서 둘 다 CR 전송)

설정 검증

/doctor

키 바인딩 설정에 문제가 있으면 /doctor에서 확인할 수 있습니다.

트랜스크립트 뷰어

Ctrl+O로 트랜스크립트 뷰어를 열면, Claude가 어떤 도구를 실행했는지 상세하게 볼 수 있습니다. MCP 호출 내용도 여기서 확인 가능합니다.

단축키기능
Ctrl+E전체 내용 펼치기/접기
/검색 모드 진입
n다음 검색 결과
N이전 검색 결과
q, Ctrl+C, Esc뷰어 닫기

풀스크린 모드에서 Ctrl+O를 반복하면 normal → transcript → focus view 순서로 전환됩니다.

디프 뷰어

Claude가 파일을 수정했을 때 나오는 디프 화면에서 쓸 수 있는 키입니다.

단축키기능
Esc디프 뷰어 닫기
/ 이전/다음 디프 소스
/ 이전/다음 파일
Enter디프 상세 보기

되감기 / 요약 (메시지 선택)

Esc × 2로 진입하는 메시지 선택 화면의 단축키입니다.

단축키기능
, K, Ctrl+P위로 이동
, J, Ctrl+N아래로 이동
Shift+K, Ctrl+↑맨 위로 점프
Shift+J, Ctrl+↓맨 아래로 점프
Enter메시지 선택

Vim 스타일의 J/K 이동도 지원하는 점이 눈에 띕니다.

스크롤 / 풀스크린 모드

풀스크린 렌더링이 활성화된 상태에서 사용 가능합니다.

단축키기능
PageUp반 화면 위로 스크롤
PageDown반 화면 아래로 스크롤
Ctrl+Home대화 맨 처음으로
Ctrl+End최신 메시지로 (자동 따라가기 재활성화)
Ctrl+Shift+C / Cmd+C선택한 텍스트 클립보드에 복사
마우스 휠한 줄씩 스크롤

이미지 첨부 내비게이션

이미지를 여러 장 붙여넣은 후 관리할 때 씁니다.

단축키기능
다음 첨부
이전 첨부
Backspace / Delete선택한 첨부 삭제
또는 Esc첨부 내비게이션 종료

PR 상태, 태스크 등이 표시되는 하단 바에서 쓸 수 있는 키입니다.

단축키기능
/ 항목 이동
/ 위/아래 탐색
Enter선택한 항목 열기
Esc선택 해제
Cmd+클릭 (Mac)PR 링크를 브라우저에서 열기

설정 화면

/config로 진입하는 설정 화면의 단축키입니다.

단축키기능
/검색 모드
R사용량 데이터 다시 로드
Enter저장 후 닫기
Esc변경사항 버리고 닫기

모델 선택

Option+P로 모델 선택 화면에 진입하면 다음 단축키를 쓸 수 있습니다.

단축키기능
노력 수준(effort) 낮추기
노력 수준 높이기

플러그인 관리자

단축키기능
Space플러그인 선택/해제
I선택한 플러그인 설치

터미널 멀티플렉서 충돌

tmux나 screen을 쓰고 있다면 일부 단축키가 충돌할 수 있습니다.

단축키충돌 대상해결법
Ctrl+Btmux 프리픽스두 번 눌러서 Claude Code로 전달
Ctrl+AGNU screen 프리픽스두 번 눌러서 전달
Ctrl+ZUnix 프로세스 일시정지 (SIGTSTP)의도치 않게 누르지 않도록 주의

tmux 사용자는 Ctrl+B를 백그라운드 전환에 쓸 때 항상 두 번 눌러야 한다는 것만 기억하면 됩니다.

한눈에 보기: 커스터마이징 요약

[키 바인딩]
/keybindings .... 설정 파일 열기
/doctor ......... 설정 검증
재시작 불필요 .... 변경 즉시 적용

[트랜스크립트 뷰어] (Ctrl+O)
/ ............... 검색
n / N ........... 다음/이전 결과
Ctrl+E .......... 전체 펼치기
q ............... 닫기

[디프 뷰어]
← / → .......... 디프 소스 전환
↑ / ↓ .......... 파일 전환
Esc ............. 닫기

[되감기] (Esc × 2)
J / K ........... 위/아래 이동
Enter ........... 선택

[주의: 멀티플렉서]
Ctrl+B .......... tmux와 충돌 (두 번 누르기)
Ctrl+A .......... screen과 충돌 (두 번 누르기)

시리즈를 마치며

3편에 걸쳐 Claude Code의 단축키를 정리했습니다. 솔직히 전부 외울 필요는 없습니다. 1편의 필수 단축키만 익혀도 충분하고, 나머지는 필요할 때 이 글을 찾아보면 됩니다.

한 가지 팁을 남기자면, Claude Code 안에서 ?를 누르면 현재 환경에서 쓸 수 있는 단축키가 바로 나옵니다. 이 글보다 그게 더 빠를 수 있습니다.

이 글은 2026년 4월 기준 Claude Code v2.1.x 기준으로 작성했어요. /keybindings 커스터마이징은 v2.1.18 이상이 필요해요.

참고 자료

Claude Code Vim 단축키

· 약 5분

Claude Code에 Vim이 있다고?

있어요. /config에서 에디터 모드를 Vim으로 바꾸면 돼요. 그 순간부터 Claude Code의 입력창이 Vim처럼 동작해요.

Normal 모드와 Insert 모드가 분리되고, h/j/k/l로 이동하고, dd로 줄을 지우고, ciw로 단어를 바꿀 수 있습니다. Vim 사용자라면 손에 익은 그대로입니다.

이 시리즈는 3편으로 구성됩니다.

  • 1편: 필수 단축키
  • 2편: Vim 모드 (이 글)
  • 3편: 커스터마이징 & 세부 컨텍스트

활성화 방법

Claude Code 실행 후 /config를 입력하고, Editor mode를 Vim으로 변경합니다. 별도 설치나 플러그인은 필요 없습니다.

참고로 Vim 모드와 일반 키 바인딩은 독립적으로 동작합니다. Vim 모드를 켜도 Ctrl+C, Ctrl+R 같은 일반 단축키는 그대로 쓸 수 있습니다.

모드 전환

Vim의 핵심은 모드입니다. Claude Code에서도 동일하게 적용됩니다.

Insert → Normal

동작
EscNormal 모드 진입

Normal → Insert

동작커서 위치
i커서 앞에서 입력 시작현재 위치
I줄 시작에서 입력 시작줄 맨 앞
a커서 뒤에서 입력 시작한 칸 뒤
A줄 끝에서 입력 시작줄 맨 뒤
o아래에 새 줄 열기새 줄
O위에 새 줄 열기새 줄

Vim을 써본 적이 있다면 완전히 같은 동작입니다. 처음이라면 i로 입력하고 Esc로 나온다는 것만 기억하면 됩니다.

내비게이션 (Normal 모드)

기본 이동

동작
h왼쪽
j아래
k
l오른쪽

화살표 키도 동작하지만, Vim을 쓰는 이유는 홈 로우에서 손을 떼지 않는 데 있지 않습니까.

단어 단위 이동

동작
w다음 단어 시작으로
e현재 단어 끝으로
b이전 단어 시작으로

줄 단위 이동

동작
0줄 맨 앞
$줄 맨 뒤
^첫 번째 비공백 문자

전체 이동

동작
gg입력 맨 처음
G입력 맨 끝

문자 점프

동작
f{문자}다음 해당 문자로 이동
F{문자}이전 해당 문자로 이동
t{문자}다음 해당 문자 바로 앞으로
T{문자}이전 해당 문자 바로 뒤로
;마지막 f/F/t/T 반복
,마지막 f/F/t/T 반대 방향

예를 들어 f=을 누르면 커서가 다음 = 문자로 점프합니다. ;을 누르면 그다음 =으로 계속 이동합니다. 긴 프롬프트에서 특정 위치를 빠르게 찾을 때 유용합니다.

히스토리 탐색

동작
j (맨 아래에서)이전 명령어로
k (맨 위에서)다음 명령어로

커서가 더 이상 이동할 수 없는 경계에 도달하면 명령어 히스토리를 탐색합니다.

편집 (Normal 모드)

삭제

동작
x커서 위 문자 삭제
dd줄 전체 삭제
D커서부터 줄 끝까지 삭제
dw다음 단어까지 삭제
de단어 끝까지 삭제
db이전 단어까지 삭제

변경 (삭제 후 Insert 모드 진입)

동작
cc줄 전체 변경
C커서부터 줄 끝까지 변경
cw다음 단어까지 변경
ce단어 끝까지 변경
cb이전 단어까지 변경

d는 삭제만 하고 Normal 모드에 남습니다. c는 삭제 후 바로 Insert 모드로 전환됩니다. 뭔가를 지우고 바로 다시 타이핑하려면 c 계열이 키 입력을 한 번 줄여줍니다.

복사 & 붙여넣기

동작
yy 또는 Y줄 전체 복사(yank)
yw다음 단어까지 복사
ye단어 끝까지 복사
yb이전 단어까지 복사
p커서 뒤에 붙여넣기
P커서 앞에 붙여넣기

기타 편집

동작
>>들여쓰기
<<내어쓰기
J아래 줄과 합치기
.마지막 편집 반복

.은 꽤 강력합니다. cw로 단어를 바꾼 뒤 .을 누르면 같은 동작을 반복하므로, 여러 곳에서 같은 수정을 할 때 시간을 아낄 수 있습니다.

텍스트 오브젝트

Vim의 진짜 힘은 텍스트 오브젝트에 있습니다. d, c, y 같은 오퍼레이터와 조합해 사용합니다.

단어

범위
iw단어 안쪽 (inner word)
aw단어 + 주변 공백 (a word)
iWWORD 안쪽 (공백 기준 단어)
aWWORD + 주변 공백

괄호 / 따옴표

범위
i" / a"큰따옴표 안쪽 / 포함
i' / a'작은따옴표 안쪽 / 포함
i( / a(소괄호 안쪽 / 포함
i[ / a[대괄호 안쪽 / 포함
i{ / a{중괄호 안쪽 / 포함

실전 조합 예시

입력동작
ciw커서 위 단어를 삭제하고 입력 모드
di"큰따옴표 안의 내용 삭제
ya(소괄호 포함해서 복사
ci{중괄호 안의 내용을 바꾸기

예를 들어 프롬프트에 "이전 파일을 수정해줘"라고 적었는데 따옴표 안의 내용만 바꾸고 싶다면, 따옴표 안에 커서를 두고 ci"를 누르면 됩니다. 따옴표 안의 텍스트가 지워지면서 바로 새 내용을 입력할 수 있습니다.

Normal 모드에서 도움말

동작
?도움말 메뉴 표시

Vim 모드에서 Normal 모드일 때 ?를 누르면 현재 환경에서 사용 가능한 단축키 목록을 볼 수 있습니다.

한눈에 보기

[모드 전환]
Esc ............. Normal 모드
i / a ........... Insert 모드 (앞/뒤)
I / A ........... Insert 모드 (줄 시작/끝)
o / O ........... 새 줄 열기 (아래/위)

[이동]
h/j/k/l ........ 좌/하/상/우
w / e / b ...... 단어 (시작/끝/이전)
0 / $ / ^ ...... 줄 (맨앞/맨뒤/첫글자)
gg / G ......... 전체 (처음/끝)
f{c} / t{c} .... 문자 점프

[편집]
x .............. 문자 삭제
dd / D ......... 줄 삭제 / 줄끝까지
cc / C ......... 줄 변경 / 줄끝까지
yy / p / P ..... 복사 / 붙여넣기
. .............. 마지막 편집 반복

[텍스트 오브젝트]
ciw ............ 단어 변경
di" ............ 따옴표 안 삭제
ci( ............ 괄호 안 변경
ya{ ............ 중괄호 포함 복사

다음 편 예고

3편에서는 커스터마이징과 세부 컨텍스트 단축키를 다뤄요. keybindings.json으로 키를 재설정하는 방법, Transcript Viewer, Diff Viewer, 스크롤 모드 등 특정 상황에서만 쓰는 단축키를 정리할게요.

이 글은 2026년 4월 기준 Claude Code v2.1.x 기준으로 작성되었습니다. Vim 모드는 /config → Editor mode에서 활성화할 수 있습니다.

참고 자료

Obsidian CLI 자동화

· 약 6분

이 글은 Obsidian CLI 시리즈의 세 번째 글이에요.

  1. Obsidian CLI 입문: 터미널에서 노트를 다루는 새로운 방법
  2. Obsidian CLI 비교: 공식 CLI vs URI vs 직접 파일 조작
  3. Obsidian CLI 자동화: 스크립트와 워크플로우 실전 가이드 ← 현재 글

왜 자동화하는가

노트를 꾸준히 쓰기는 어렵습니다. 데일리 노트를 열고, 양식을 채우고, 태스크를 옮기는 작업은 반복적이고 귀찮습니다. 귀찮으면 안 하게 되고, 안 하면 쌓이고, 쌓이면 포기합니다.

자동화의 핵심은 반복적인 부분을 제거해서 본질(기록)에만 집중하게 만드는 것입니다.

Obsidian CLI 덕분에 이 반복 작업을 셸 스크립트로 만들 수 있습니다.

셸 별칭과 함수

~/.zshrc에 추가해두면 매일 쓰게 되는 것들:

기본 별칭

# 데일리 노트 단축키
alias dn="obsidian daily"
alias dna="obsidian daily:append"

# 빠른 검색
alias qs="obsidian search"

# 태그 현황
alias tags="obsidian tags counts"

memo() 빠른 메모

타임스탬프와 함께 데일리 노트에 한 줄 메모를 추가합니다.

memo() {
local timestamp=$(date +%H:%M)
obsidian daily:append content="- **${timestamp}** $*"
}
# 사용
memo "내일 블로그 배포 일정 확인할 것"
memo "obsidian CLI 포스트 초안 완성"

# 데일리 노트에 이렇게 추가된다:
# - **14:23** 내일 블로그 배포 일정 확인할 것
# - **15:10** obsidian CLI 포스트 초안 완성

til() TIL 노트 자동 생성

오늘 배운 것(Today I Learned)을 빠르게 기록합니다.

til() {
local today=$(date +%Y-%m-%d)
local slug=$(echo "$1" | tr ' ' '-' | tr '[:upper:]' '[:lower:]')
obsidian create name="TIL/${today}-${slug}" content="---
tags: [til]
date: ${today}
---
# $1

$2"
}
# 사용
til "Obsidian CLI" "터미널에서 데일리 노트를 관리할 수 있다"

cron으로 정기 작업

반복 스케줄이 정해진 작업은 cron에 맡깁니다.

매일 아침 데일리 노트 자동 생성

# crontab -e
0 9 * * * obsidian daily

매일 오전 9시에 데일리 노트가 자동 생성됩니다. Obsidian을 열면 이미 오늘 노트가 준비되어 있습니다.

주간 리뷰 노트

# 매주 금요일 오후 5시에 주간 리뷰 노트 생성
0 17 * * 5 obsidian create name="Review/$(date +\%Y-W\%V)" template="Templates/weekly-review"

전일 태스크 알림

# 매일 오전 9시 30분에 어제 미완료 태스크 확인
30 9 * * * obsidian tasks daily --offset=-1

주의: cron에서 Obsidian CLI가 동작하려면, Obsidian 데스크톱 앱이 이미 실행 중이어야 합니다. macOS라면 로그인 시 Obsidian이 자동 실행되도록 설정해두면 됩니다.

macOS sleep 함정: 노트북이 sleep 상태일 때는 cron 자체가 발사되지 않습니다. 데스크톱이라면 무관하지만 노트북에서 매일 9시 데일리 노트가 안 만들어지면 이 때문입니다. caffeinate -s로 일시 차단하거나, 아예 launchd (launchctl)의 StartCalendarInterval로 옮기면 sleep 후 깨어났을 때 한 번 따라잡습니다.

vault 경로: Obsidian CLI는 OBSIDIAN_VAULT 환경변수 또는 ~/.obsidian-cli/config.jsonvault 항목으로 vault 위치를 찾습니다. cron 환경은 셸 환경변수를 자동 상속받지 않으므로, crontab 상단에 OBSIDIAN_VAULT=/Users/<you>/Documents/MyVault 한 줄을 명시해야 안전합니다.

tmux 연동

tmux를 쓰고 있다면, Obsidian CLI와의 조합이 강력합니다.

작업용 레이아웃

tmux 패널 하나를 Obsidian 전용으로 쓰면, 코드를 쓰면서 노트를 바로 확인할 수 있습니다.

┌──────────────────┬──────────────────┐
│ Neovim │ obsidian read │
│ │ │
├──────────────────┼──────────────────┤
│ Shell │ obsidian search │
│ │ │
└──────────────────┴──────────────────┘

좌상: 코드 편집, 우상: 노트 확인, 좌하: 빌드/테스트, 우하: 검색

자동화 스크립트

프로젝트 시작 시 tmux 워크스페이스를 한 번에 구성하는 스크립트입니다.

#!/bin/bash
# obsidian-workspace.sh

SESSION="obsidian-work"

tmux new-session -d -s $SESSION

# 왼쪽: 에디터
tmux send-keys -t $SESSION "nvim" C-m

# 오른쪽: Obsidian 노트
tmux split-window -h -t $SESSION
tmux send-keys -t $SESSION "obsidian read path='Projects/current.md'" C-m

# 하단: 터미널
tmux split-window -v -t $SESSION:0.0
tmux send-keys -t $SESSION "obsidian daily" C-m

tmux attach -t $SESSION

tmux에서 빠른 메모

tmux의 아무 패널에서 memo 함수를 실행하면 작업 흐름을 끊지 않고 메모를 남길 수 있습니다. 에디터를 떠나지 않아도 됩니다.

Git 연동

커밋 로그를 데일리 노트에 자동 기록

git의 post-commit hook을 활용합니다.

#!/bin/bash
# .git/hooks/post-commit

COMMIT_MSG=$(git log -1 --pretty=format:"%s")
REPO_NAME=$(basename $(git rev-parse --show-toplevel))

obsidian daily:append content="- **commit** \`${REPO_NAME}\`: ${COMMIT_MSG}"

hook에 실행 권한을 부여합니다.

chmod +x .git/hooks/post-commit

이제 커밋할 때마다 데일리 노트에 자동으로 기록됩니다.

- **commit** `blog-repo`: Obsidian CLI 시리즈 포스트 추가
- **commit** `blog-repo`: 오타 수정

하루가 끝나면 데일리 노트에 오늘의 작업 이력이 쌓여 있습니다.

프로젝트 노트 자동 생성

새 프로젝트를 시작할 때 git repo와 Obsidian 노트를 동시에 만드는 함수입니다.

new-project() {
local name=$1
mkdir -p ~/Projects/$name && cd ~/Projects/$name
git init
obsidian create name="Projects/$name" template="Templates/project"
echo "프로젝트 '$name' 생성 완료 — git repo + Obsidian 노트"
}

실전 워크플로우 예제

아침 루틴 스크립트

매일 아침 실행하는 스크립트 하나로 하루를 시작합니다.

#!/bin/bash
# morning.sh — 아침 루틴

echo "☀️ 아침 루틴 시작"

# 1. 오늘 데일리 노트 생성
obsidian daily
echo "✓ 데일리 노트 생성"

# 2. 어제 미완료 태스크 확인
echo ""
echo "📋 어제 미완료 태스크:"
obsidian tasks daily --offset=-1

# 3. 태그 현황
echo ""
echo "🏷️ 태그 현황:"
obsidian tags counts
# 실행
chmod +x morning.sh
./morning.sh

프로젝트 대시보드 생성

볼트의 현재 상태를 요약하는 대시보드 노트를 자동 생성합니다.

#!/bin/bash
# dashboard.sh — 프로젝트 대시보드 갱신

VAULT=~/Documents/MyVault
TODAY=$(date +%Y-%m-%d)

# 통계 수집
TOTAL_NOTES=$(find $VAULT -name "*.md" | wc -l | tr -d ' ')
TODO_COUNT=$(grep -rc "\- \[ \]" $VAULT --include="*.md" | awk -F: '{sum+=$2} END{print sum}')
DONE_COUNT=$(grep -rc "\- \[x\]" $VAULT --include="*.md" | awk -F: '{sum+=$2} END{print sum}')

# 최근 수정된 노트 (상위 5개)
RECENT=$(find $VAULT -name "*.md" -mtime -1 -exec basename {} .md \; | head -5 | sed 's/^/- /')

# 대시보드 노트 갱신
obsidian create name="Dashboard" content="---
tags: [dashboard]
---
# 프로젝트 대시보드
> 마지막 갱신: ${TODAY}

## 통계
| 항목 | 수 |
|------|-----|
| 전체 노트 | ${TOTAL_NOTES} |
| 미완료 태스크 | ${TODO_COUNT} |
| 완료 태스크 | ${DONE_COUNT} |

## 최근 수정된 노트
${RECENT}
"

cron으로 매일 실행하면 항상 최신 상태의 대시보드를 유지할 수 있습니다.

마치며

Obsidian CLI의 진짜 가치는 명령어 자체보다 자동화 가능성에 있습니다.

셸 별칭으로 매일 쓰는 명령을 줄이고, cron으로 반복 작업을 없애고, tmux와 결합해 워크플로우에 녹여넣고, git hook으로 기록을 자동화합니다. 이 모든 것이 CLI가 있기 때문에 가능해졌습니다.

터미널에서 Neovim으로 코드를 쓰고, tmux로 세션을 관리하고, Obsidian CLI로 노트까지 다뤄요. GUI로 전환할 이유가 하나 줄었어요.

참고 자료