ClOr

ClOr

백엔드 실무 트러블슈팅과 AI 에이전트 구조 분석을 기록합니다.

Claude Code 해부학 (완결)

51만 줄 소스코드를 19편에 걸쳐 분석한 완결 시리즈

전체 시리즈 보기 →

백엔드 트러블슈팅

실무에서 겪은 장애와 해결 과정 기록

전체 시리즈 보기 →

최신 글

article thumbnail

시리즈 이전 편: [Part 0] 소스코드 51만 줄이 유출됐다

목차

  • 1,900개 파일, 어떻게 정리했을까
  • 전체 디렉토리 구조
  • 핵심 디렉토리 5개 해부
    • src/tools/ — LLM이 사용하는 도구들
    • src/commands/ — 사용자가 치는 슬래시 명령어
    • src/components/ — 터미널 UI의 모든 것
    • src/services/ — 외부 세계와의 접점
    • src/entrypoints/ — 앱이 시작되는 곳
  • 진입점 흐름: cli.tsx -> init.ts -> main.tsx
  • 그래서 뭐가 달라지나 -- 구조 설계 교훈

1,900개 파일, 어떻게 정리했을까

Claude Code의 src/ 디렉토리 안에는 정확히 1,920개의 파일이 들어 있습니다. 이 정도 규모면 아무리 잘 만든 프로젝트라도 디렉토리 구조가 엉망이면 개발자가 파일 하나 찾는 데 5분씩 쓰게 됩니다.

Anthropic 엔지니어들은 이걸 어떻게 정리했을까요? 실제 소스를 열어보니, 예상보다 훨씬 체계적이었습니다. 오늘은 디렉토리 구조만 집중적으로 뜯어보겠습니다.

전체 디렉토리 구조

src/ 아래 최상위 디렉토리는 총 30개입니다. 핵심만 추리면 아래와 같습니다.

src/
  entrypoints/     # 진입점 (cli.tsx, init.ts, mcp.ts)
  main.tsx          # CLI 메인 로직 (Commander.js 기반)
  tools/            # 40개 도구 (BashTool, FileReadTool, GrepTool...)
  commands/         # 102개 슬래시 명령어 (/commit, /compact, /config...)
  components/       # React(Ink) UI 컴포넌트 389개
  services/         # API, MCP, OAuth, LSP 등 외부 연동
  constants/        # 시스템 프롬프트, 설정값
  hooks/            # React 훅
  state/            # 전역 상태 관리
  utils/            # 유틸리티 함수
  bootstrap/        # 초기화 단계 상태 셋업
  context/          # 프로젝트 컨텍스트 감지
  types/            # TypeScript 타입 정의
  ...

눈에 띄는 건 tools/commands/가 명확히 분리되어 있다는 점입니다. 도구(LLM이 호출하는 것)와 명령어(사용자가 /로 호출하는 것)는 완전히 다른 디렉토리에 삽니다.

핵심 디렉토리 5개 해부

src/tools/ — LLM이 사용하는 도구들

파일 수: 184개 | 도구 수: 40개

tools/
  BashTool/
  FileReadTool/
  FileEditTool/
  FileWriteTool/
  GrepTool/
  GlobTool/
  AgentTool/
  WebFetchTool/
  WebSearchTool/
  MCPTool/
  TaskCreateTool/
  ToolSearchTool/
  ...

각 도구는 독립된 폴더 안에 구현 파일, 테스트 파일, 유틸리티가 함께 들어갑니다. 이건 전형적인 "feature-based 구조"입니다. 도구 하나를 수정할 때 다른 폴더를 건드릴 일이 거의 없습니다.

src/commands/ — 사용자가 치는 슬래시 명령어

파일 수: 209개 | 명령어 수: 102개

/commit, /compact, /config, /clear 같은 슬래시 명령어가 여기 있습니다. agents, bridge, chrome 같은 하위 폴더도 보이는데, 복잡한 명령어는 자체 디렉토리를 가집니다.

구분 tools/ commands/
호출 주체 LLM (Claude) 사용자 (슬래시 입력)
예시 BashTool, GrepTool /commit, /compact
구조 도구당 폴더 1개 명령어당 파일 또는 폴더
파일 수 184개 209개

src/components/ — 터미널 UI의 모든 것

파일 수: 389개

Claude Code의 터미널 UI는 React + Ink로 만들어져 있습니다. App.tsx, AutoUpdater.tsx, BridgeDialog.tsx 같은 이름을 보면, 일반적인 웹 프론트엔드 프로젝트와 구조가 거의 같습니다. 터미널인데 React라니 -- 이 부분은 나중 편에서 더 파겠습니다.

src/services/ — 외부 세계와의 접점

파일 수: 136개

services/
  api/              # Anthropic API 호출
  mcp/              # MCP 서버 연동
  oauth/            # OAuth 인증
  lsp/              # Language Server Protocol
  analytics/        # 텔레메트리, GrowthBook
  compact/          # 컨텍스트 압축
  policyLimits/     # 사용량 제한
  ...

API 호출, 인증, MCP 프로토콜, LSP 연동이 모두 여기 모여 있습니다. tools/가 "무엇을 할 수 있는가"라면 services/는 "외부와 어떻게 통신하는가"입니다.

src/entrypoints/ — 앱이 시작되는 곳

파일 수: 8개

entrypoints/
  cli.tsx           # 최초 진입점
  init.ts           # 초기화 로직
  mcp.ts            # MCP 서버 모드 진입점
  sdk/              # Agent SDK 진입점
  agentSdkTypes.ts
  sandboxTypes.ts

겨우 8개 파일이지만, 여기가 모든 것의 시작점입니다. 다음 섹션에서 흐름을 따라가 봅니다.

진입점 흐름: cli.tsx -> init.ts -> main.tsx

Claude Code를 실행하면 어떤 순서로 파일이 로드될까요?

1단계: cli.tsx -- 빠른 분기 처리

// src/entrypoints/cli.tsx
async function main(): Promise<void> {
  const args = process.argv.slice(2);

  // Fast-path for --version/-v: zero module loading needed
  if (args.length === 1 && (args[0] === '--version' || args[0] === '-v')) {
    console.log(`${MACRO.VERSION} (Claude Code)`);
    return;
  }

  const { profileCheckpoint } = await import('../utils/startupProfiler.js');
  profileCheckpoint('cli_entry');
  // ...

--version 같은 간단한 플래그는 모듈을 하나도 로드하지 않고 바로 응답합니다. 주석에 "zero module loading"이라고 적혀 있을 정도로, 시작 속도에 집착하는 모습이 보입니다.

2단계: init.ts -- 초기화

설정 로드, OAuth 인증 확인, 텔레메트리 셋업, LSP 서버 준비, 프록시 설정 등 "무거운 준비 작업"이 여기서 일어납니다. import 목록만 봐도 40줄이 넘습니다.

3단계: main.tsx -- 본격 실행

// src/main.tsx
profileCheckpoint('main_tsx_entry');
startMdmRawRead();       // MDM 설정 병렬 읽기
startKeychainPrefetch();  // macOS 키체인 병렬 프리페치

main.tsx는 시작하자마자 MDM 설정 읽기와 키체인 프리페치를 병렬로 실행합니다. 모듈 import가 ~135ms 걸리는 동안 이 작업들이 백그라운드에서 돌아가도록 설계한 겁니다. Commander.js로 CLI 옵션을 파싱하고, 최종적으로 React(Ink) REPL을 띄웁니다.

그래서 뭐가 달라지나 -- 구조 설계 교훈

1,920개 파일을 관리하는 이 프로젝트에서 배울 점을 정리하면 이렇습니다.

feature-based 폴더 구조를 쓴다. tools/BashTool/ 안에 구현, 테스트, 유틸리티가 다 들어갑니다. controllers/, models/, views/ 식의 레이어 분리가 아닙니다. 도구 40개가 서로 간섭 없이 독립적으로 존재할 수 있는 이유입니다.

호출 주체로 디렉토리를 나눈다. LLM이 호출하는 건 tools/, 사용자가 호출하는 건 commands/. 이 구분이 없으면 "이 코드를 누가 부르는 거지?"라는 질문에 매번 코드를 뒤져야 합니다.

진입점을 극도로 가볍게 만든다. cli.tsx는 fast-path 분기만 처리하고, 무거운 import는 전부 dynamic import로 미룹니다. 0.5초 안에 터미널에 반응을 보여주려는 설계입니다.

services와 tools를 분리한다. "무엇을 할 수 있는가(tools)"와 "외부와 어떻게 통신하는가(services)"를 섞지 않습니다. 덕분에 도구를 추가할 때 API 통신 코드를 건드릴 필요가 없습니다.

다음 편에서는 이 구조 위에서 실제로 부팅이 어떻게 일어나는지, 0.5초 안에 무슨 일이 벌어지는지를 코드 레벨로 추적합니다.


다음 편 예고: [Claude Code 해부학 Part 2] 클로드 코드(Claude Code) 부팅 과정 분석 — 실행하면 0.5초 안에 벌어지는 일들

[Claude Code 해부학] 시리즈

  • Part 0: 소스코드 51만 줄이 유출됐다
  • Part 2: 부팅 과정 분석
  • Part 3: 시스템 프롬프트 분석
profile

ClOr

@ClOr

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

ClOr · 백엔드 트러블슈팅과 AI 에이전트 구조 분석을 기록합니다.