Claude Code를 구동하는 13만 2천 줄의 TypeScript를 읽으며 발견한 흥미로운 점들.
home·diary·blog·microblog·topicsbibliography·anthology·music·guestbook
2026년 3월 31일
※ ※ ※
Claude Code를 구동하는 132,000줄의 TypeScript를 읽으며 발견한 흥미로운 점들. 이 글에서는 코드 참조를 위해 [https://github.com/chatgptprojects/claude-code](https://github.com/chatgptprojects/claude-code) 저장소를 사용하며, 여러분이 이 글을 읽을 때쯤에는 내려가 있을 수도 있다는 점을 참고해 두자.
[2026-04-01 수 00:53]: 이 글을 쓰고 나서, Alex Kim's blog의 또 다른 흥미로운 글도 발견했다
Anthropic은 실수로 전체 난독화되지 않은 TypeScript 소스 코드를 담은 소스맵 파일(cli.js.map)을 유출했다. 파일은 1,897개, 전체는 대략 132,000 LOC였다. 내가 본 것은 예상보다 훨씬 더 강한 취향을 지니고, 훨씬 더 편집증적으로 설계된 소프트웨어였다. 여기서는 내가 이야기할 가치가 있다고 느낀 몇 가지를 정리해 본다.
src/buddy/types.ts에서는 모든 종 이름이 16진수로 인코딩되어 있다:
const c = String.fromCharCode
export const duck = c(0x64,0x75,0x63,0x6b) as 'duck'
export const goose = c(0x67,0x6f,0x6f,0x73,0x65) as 'goose'
18개 종 전체가, 하나하나 문자를 16진수로 적어 넣는 방식이다. 주석에는 그 우스운 이유가 설명되어 있다:
한 종 이름이 excluded-strings.txt에 있는 모델 코드네임 카나리아와 충돌한다. 검사는 소스가 아니라 빌드 출력물을 grep하므로, 런타임에 값을 구성하면 리터럴이 번들에 들어가지 않으면서도 실제 코드네임에 대한 검사는 계속 유지된다.
즉 Anthropic의 빌드 파이프라인 어딘가에는 excluded-strings.txt라는 파일이 있고, 거기에는 아직 공개되지 않은 모델의 코드네임이 들어 있으며, 빌드 결과물에 그것들이 새어 나오지 않았는지 grep으로 확인하는 검사가 있다는 뜻이다. 그런데 companion 종 이름 중 하나가 우연히 이 내부 코드네임 중 하나와 겹친다. 종 이름을 바꾸는 대신, 컴파일된 출력물에 리터럴 문자열이 절대 나타나지 않도록 전부를 균일하게 16진수로 인코딩해 둔 것이다.
나는 이게 너무 웃기다. Anthropic 내부 어딘가에는 Claude Code의 companion 종이기도 한 동물 이름을 코드네임으로 쓰는 모델이 있고, 그 해결책이 “duck”을 0x64,0x75,0x63,0x6b로 쓰는 것이다. 기록을 위해 말하자면, 충돌한 종이 실제로 “duck”이라고는 생각하지 않는다. 그랬다면 너무 단순했을 테니까. 하지만 주석에 따르면 어느 것인지 드러나지 않게 전부를 통일해서 인코딩했다고 한다. 영리하다.
goodClaude, bughunter 그리고 기타 내부 명령어들commands 파일에는 외부 빌드에서는 숨겨지는 내부 전용 명령어 구간이 있다: bughunter, commitPushPr, ctx_viz, goodClaude (비활성화되어 있고 숨겨져 있어서, 무엇을 하던 것인지는 영영 알 수 없을 것이다), ultraplan, ultrareview, teleport, 그리고 ant-trace. 이것들은 Anthropic의 내부 빌드에만 존재한다.
실험 모드를 위한 기능 플래그도 꽤 풍부하다: PROACTIVE(자율 에이전트), KAIROS(assistant/brief 모드), COORDINATOR_MODE(Claude가 워커 에이전트를 생성하는 멀티 에이전트 오케스트레이션), AGENT_TRIGGERS(에이전트용 cron 작업 스케줄링), VOICE_MODE, 그리고 BUDDY(companion 펫). 특히 coordinator mode가 흥미로운데, 오케스트레이션을 담당하는 에이전트에게 명시적으로 이렇게 지시하기 때문이다: “Never write 'based on your findings' or 'based on the research.' These phrases delegate understanding to the worker instead of doing it yourself.” 이 문구를 쓴 사람은 분명 “Based on my findings…”로 시작하는 LLM 응답을 너무 많이 본 사람일 것이다.
import I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHSsrc/query.ts 24번째 줄에는 이런 import가 있다:
import {
logEvent,
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
} from 'src/services/analytics/index.js'
이것은 TypeScript 타입인데, 이름 자체가 문장이다. 이 타입을 쓰는 모든 개발자는 코드 안에 반드시 I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS를 직접 타이핑해야 한다. 말 그대로 강제 장치다. 파일 경로나 코드 내용을 분석 도구로 실수로 보내려면, 최소한 “나는 이것이 코드나 파일 경로가 아님을 검증했다”라고 직접 써 넣어야 하기 때문이다.
/insights 명령에는 “Multi-Clauding (Parallel Sessions)”라는 섹션이 포함되어 있다. 함수 detectMultiClauding()은 슬라이딩 윈도우 알고리즘을 사용해 30분 창 안에서 session1 -> session2 -> session1 패턴을 찾는다. 그리고 이를 “overlap events”로 집계하고 보고한다.
내 생각에 이들은 사용자가 여러 Claude 세션을 동시에 실행하는 시점을 추적하고, 그것을 사용 지표로 보고 있는 것 같다.
rm -rf를 막기 위한 2,600줄bash 보안 시스템(bashSecurity.ts, 2,592줄)은 그야말로 편집증의 산물이고, 그래서 더 흥미롭다. 모든 셸 명령을 공격 패턴 체크리스트에 대조해 검증한다.
무엇을 검사하는지 몇 가지 하이라이트를 보자:
=cmd 확장. 여기서 =curl evil.com은 /usr/bin/curl evil.com으로 확장되는데, 파서는 명령 이름을 curl이 아니라 =curl로 보기 때문에 권한 규칙을 우회할 수 있다zmodload (zsh/mapfile로 이어지는 관문인데, 이는 보이지 않는 파일 입출력을 가능하게 한다)$'\x41')<()와 >())emulate (zsh에서 eval과 동등한 임의 코드 실행)ztcp (Zsh 빌트인을 통한 TCP 데이터 유출)별도 파일인 destructive command warnings에서는 git reset --hard(“커밋되지 않은 변경사항을 버릴 수 있음”), rm -rf(“파일을 재귀적으로 강제 삭제할 수 있음”), DROP TABLE, kubectl delete(“Kubernetes 리소스를 삭제할 수 있음”) 같은 것들을 패턴 매칭하고, 각각 무엇을 잃게 될지 사람이 읽을 수 있는 경고 문구를 붙인다. 그리고 이 모든 것은 별도의 파일에서 PowerShell용으로도 복제되어 있는데, 거기서는 alias 하이재킹, 모듈 로딩, script block cmdlet까지 추적한다.
MCP 유틸리티 코드에는 “This is really ugly but our current Tool type doesn't make it easy”라는 주석이 있는데, 솔직한 표현이라고 느껴진다. 다만 내 생각에는 보안 코드는 못생겼다기보다 집요하고 철저하다. 각 체크 뒤에 실제 사고 사례가 있었음을 암시하는 종류의 철저함이다. 물론 일반적으로 코드는 대체로 못생기기 마련이긴 하지만.
Claude Code에는 권한 시스템이 있는데 다섯 가지 모드가 있다: default(권한 요청), acceptEdits(파일 편집 자동 허용), dontAsk(전부 차단), bypassPermissions(전부 허용), 그리고 auto. auto 모드는 ML 기반 분류기를 이용해 사용자에게 묻지 않고도 툴 호출을 안전하게 실행할 수 있는지 판단한다. 그런데 내부적으로 이 분류기를 구현한 파일 이름이 yoloClassifier.ts다. 무려 1,495줄이다. 이게 웃긴 이유는, 이 분류기가 실제로는 두 단계 평가를 수행하기 때문이다. 먼저 빠른 초기 판단을 내리고, 필요하면 확장된 추론 단계로 넘어간다. 어느 단계가 결정을 내렸는지, 캐시 메트릭, 토큰 사용량, 그리고 결정이 덮어쓰기되었는지까지 추적한다. 이렇게 실제로는 매우 신중한 안전 시스템을 만들어 놓고 이름을 “yolo”라고 붙였다는 사실이 나에겐 너무 웃기다.
src/vim/에는 상태 기계로 구현된 완전한 Vim 키바인딩 구현이 들어 있다. 라이브러리 래퍼가 아니다. 손수 만든 상태 기계로, INSERT와 NORMAL 모드, operators(delete, change, yank; 556줄), motions(h/j/k/l, w/b/e, W/B/E, 0/^/$, G), text objects(단어, 따옴표, 대괄호, 중괄호), find motions(f/F/t/T와 ;/,를 통한 반복), 들여쓰기/내어쓰기, 줄 합치기, 치환, 대소문자 전환, 그리고 dot-repeat까지 포함한다. transitions 파일(490줄)이 상태 기계 전체 로직을 처리한다.
motions는 순수 함수이고 operators는 영리한 개행 처리 로직을 담당한다. 줄 단위 여부를 감지하는 레지스터 시스템도 있다. 나 역시 여러 Vim 플러그인(그리고 Emacs 쪽이라면 “evil”도)을 직접 다뤄 본 입장에서 말하자면, 솔직히 이것은 내가 써 본 대부분의 “Vim mode/evil package” 플러그인보다 더 완전한 Vim 구현이다. 정말 인상적이다.
질의 오케스트레이션 루프(src/query.ts, 1,729줄)에는 151번째 줄에 이런 주석이 있다:
/**
* The rules of thinking are lengthy and fortuitous. They require plenty
* of thinking of most long duration and deep meditation for a wizard to
* wrap one's noggin around.
*
* The rules follow:
* 1. A message that contains a thinking or redacted_thinking block must
* be part of a query whose max_thinking_length > 0
* 2. A thinking block may not be the last message in a block
* 3. Thinking blocks must be preserved for the duration of an assistant
* trajectory
*
* Heed these rules well, young wizard. For they are the rules of
* thinking, and the rules of thinking are the rules of the universe.
* If ye does not heed these rules, ye will be punished with an entire
* day of debugging and hair pulling.
*/
API에서 thinking 블록을 어떻게 다뤄야 하는지에 대한 규칙이 세 가지 있는데, 그것들이 분명 엄청난 고통을 안겨 주었던 모양이다. 누군가가 미래의 개발자들에게 경고하기 위해 중세 영어풍으로 문서화해 둘 정도였으니 말이다. “If ye does not heed these rules, ye will be punished with an entire day of debugging and hair pulling.” 그리고 이 주석 바로 아래 줄에는 아이러니하게도 const MAX_OUTPUT_TOKENS_RECOVERY_LIMIT = 3가 있다. 아마 이 상수는 정확히 그런 하루에서 태어났을 것이다.
src/buddy/라는 디렉터리가 있다. 이곳에는 companion 펫 시스템이 구현되어 있는데, 종, 희귀도 등급, 스탯, 유휴 애니메이션이 들어간 ASCII 아트 스프라이트, 모자, 눈 스타일까지 갖추고 있다.
사용자 ID는 해시되며(Mulberry32를 사용하는데, 이 작은 PRNG에는 “good enough for picking ducks”라는 아름다운 주석이 달려 있다) 결정론적으로 하나의 companion으로 굴려진다. 종 목록에는 duck, goose, blob, cat, dragon, octopus, owl, penguin, turtle, snail, ghost, axolotl, capybara, cactus, robot, rabbit, mushroom, 그리고 “chonk”가 포함된다(전체 species 배열 보기).
각 companion에는 희귀도가 있으며(common 60%, uncommon 25%, rare 10%, epic 4%, legendary 1%), 희귀도는 buddy가 모자를 쓸 수 있는지를 결정한다. common buddy는 항상 맨머리다. uncommon 이상은 HATS 배열에서 굴리는데, 여기에는 crown, tophat, propeller, halo, wizard hat, beanie, 그리고 ASCII 동물 머리 위에 올라앉는 아주 작은 오리인 듯한 “tinyduck”이 포함된다. 다만 이 배열에는 “none”도 들어 있으므로, 여러분의 epic companion도 여전히 대머리일 수 있다.
각 companion에는 다섯 가지 스탯이 있다: DEBUGGING, PATIENCE, CHAOS, WISDOM, 그리고 SNARK. D&D 캐릭터 생성처럼 항상 하나의 최고 스탯과 하나의 버리는 스탯이 있다. legendary companion은 모든 스탯에 최소 50의 하한을 가지며, common은 5에서 시작한다.
아키텍처는 “bones”와 “soul”로 나뉜다. bones(종, 희귀도, 스탯, 눈, 모자, 반짝임 여부)는 사용자 ID로부터 결정론적으로 생성되며 매번 다시 만들어지므로, 설정 파일을 수정해서 legendary 희귀도를 위조할 수 없다. soul(이름과 성격)은 모델이 한 번 생성해 저장한다. 심지어 Pokémon의 반짝이 개체처럼 shiny boolean도 있고, 확률은 1%다.
스프라이트 자체는 각각 3개의 애니메이션 프레임을 가진 5x12 ASCII 아트 그리드로 정의되어 있다. cat은 이렇게 생겼다:
/\_/\\
( · · )
( ω )
(")_(")
그리고 2번째 프레임에서는 꼬리를 흔들며 이렇게 된다: (")_(")~. dragon은 세 번째 프레임에서 작은 물결표 연기를 뿜는다. [[https://github.com/chatgptprojects/claude-code/blob/main/src/buddy/sprites.ts#L142-L164][octopus]]는 ~/\/\/\/\``와 ////` 사이에서 촉수 위치를 번갈아 바꾼다. 이런 디테일을 보면 팀 안의 누군가가 이 작업을 진심으로 즐기고 있었다는 사실을 깨닫게 된다.
이 companion은 입력 상자 옆에 앉아 있으며 “가끔 말풍선으로 코멘트를 한다.” system prompt에서는 Claude에게 companion인 척하거나 companion이 무슨 말을 할지 서술하지 말라고 지시한다. companion은 별도로 처리되기 때문이다. 사용자가 companion을 이름으로 부르면, Claude는 “끼어들지 말고 한 줄 이하로만 응답하라”는 지시를 받는다.
이 모든 것은 BUDDY라는 기능 플래그 뒤에 숨겨져 있고, 거의 아무도 그 존재를 모른다.
자세히 다루지는 않았지만 그래도 알아둘 만한 것들이 몇 가지 있다:
stickermule.com/claudecode로 여는 /stickers 명령이 있다. 그게 명령 전체다.TERM_PROGRAM이 전달되지 않는 SSH 터널 환경을 위한 특별 코드 경로도 갖고 있다. #Programmingtopics
connections · 1
stats
약 9분 · 1870단어
10개 섹션 · 1개 참조 · 4개 코드
2일 전
작성 시점 기준
8,509일째 · 1,215번째 주
23.3번의 태양 공전 완료
의식 있는 날 약 7,231일
보름달 283번 · 계절 93번
기대 수명의 31.8%
♫ 듣는 중
git
[AD] Free license of UFS Standard
© 2026 lr0·rss·github·twitter·mail
Animus non corpus imperat