ClOr

ClOr

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

Claude Code 해부학 (완결)

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

전체 시리즈 보기 →

백엔드 트러블슈팅

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

전체 시리즈 보기 →

최신 글

article thumbnail

결론부터 말하면, 클로드 코드(Claude Code)의 터미널 UI는 React로 만들어져 있습니다. 소스코드를 분석해보면, 브라우저가 아니라 터미널에서 React 컴포넌트가 돌아가고 있죠. Ink라는 라이브러리의 커스텀 버전 위에 Yoga 레이아웃 엔진까지 붙여서, 터미널 안에서 Flexbox를 쓰고 있습니다.

목차

  • React가 터미널에서 어떻게 돌아가는 거죠?
  • Ink를 그대로 쓰는 건 아닌가요?
  • 화면은 어떤 구조로 나뉘어 있나요?
  • 가상 스크롤은 왜 필요한가요?
  • 상태 바에는 뭐가 표시되나요?
  • 스피너는 단순한 애니메이션이 아닙니다
  • 정리

React가 터미널에서 어떻게 돌아가는 거죠?

React는 원래 브라우저용이잖아요. 근데 react-reconciler라는 패키지를 쓰면 렌더링 대상을 바꿀 수 있습니다. React Native가 모바일 네이티브 뷰에 렌더링하듯이, Claude Code는 터미널에 렌더링하는 겁니다.

// src/ink/reconciler.ts
import createReconciler from 'react-reconciler'
import {
  appendChildNode,
  createNode,
  createTextNode,
  type DOMElement,
  insertBeforeNode,
  removeChildNode,
  setAttribute,
  setStyle,
  setTextNodeValue,
} from './dom.js'

reconciler.ts에서 react-reconciler를 가져와서 커스텀 DOM을 만듭니다. 브라우저의 document.createElement 대신 자체 createNode를 쓰는 거죠. 이 노드들이 Yoga 레이아웃 엔진을 통해 좌표를 계산받고, 최종적으로 ANSI 이스케이프 시퀀스로 변환되어 터미널에 그려집니다.

Ink를 그대로 쓰는 건 아닌가요?

아닙니다. Claude Code는 오픈소스 Ink를 포크해서 src/ink/ 디렉토리에 통째로 내장하고 있습니다. 컴포넌트 18개, 훅, 이벤트 시스템, 셀렉션, 마우스 트래킹까지 전부 커스텀입니다.

// src/ink/components/Box.tsx
/**
 * `<Box>` is an essential Ink component to build
 * your layout. It's like
 * `<div style="display: flex">` in the browser.
 */
export type Props = Except<Styles, 'textWrap'> & {
  tabIndex?: number;
  autoFocus?: boolean;
  onClick?: (event: ClickEvent) => void;
  onMouseEnter?: () => void;
  onMouseLeave?: () => void;
};

Box 컴포넌트가 브라우저의 <div>에 해당합니다. 클릭, 마우스 진입/이탈, 포커스까지 지원해요. 터미널에서 마우스 이벤트를 처리한다는 게 인상적입니다.

화면은 어떤 구조로 나뉘어 있나요?

 

메인 화면은 REPL.tsx라는 거대한 스크린 컴포넌트가 관리합니다. 이 파일 하나에 250개 이상의 import가 들어있을 정도로 복잡합니다.

// src/screens/REPL.tsx (주요 import 일부)
import { Messages } from '../components/Messages.js';
import PromptInput from
  '../components/PromptInput/PromptInput.js';
import { SpinnerWithVerb } from '../components/Spinner.js';
import { StatusLine } from '../components/StatusLine.js';
import { VirtualMessageList } from
  '../components/VirtualMessageList.js';
import { PermissionRequest } from
  '../components/permissions/PermissionRequest.js';

위에서 아래로 로고/상태 알림, 메시지 목록, 스피너, 입력창, 상태 바 순서로 배치됩니다. 웹 앱의 레이아웃과 구조적으로 똑같죠.

가상 스크롤은 왜 필요한가요?

대화가 길어지면 메시지가 수천 개가 됩니다. 전부 렌더링하면 터미널이 버벅이겠죠. VirtualMessageList가 화면에 보이는 메시지만 렌더링합니다.

// src/components/VirtualMessageList.tsx
import { useVirtualScroll } from
  '../hooks/useVirtualScroll.js';

export type JumpHandle = {
  jumpToIndex: (i: number) => void;
  setSearchQuery: (q: string) => void;
  nextMatch: () => void;
  prevMatch: () => void;
  warmSearchIndex: () => Promise<number>;
};

검색 기능(/ 키)도 이 컴포넌트에서 처리합니다. warmSearchIndex로 검색 인덱스를 미리 빌드하고, nextMatch/prevMatch로 결과를 탐색하는 구조입니다. 브라우저 개발할 때 쓰는 가상 스크롤 라이브러리랑 패턴이 동일해요.

상태 바에는 뭐가 표시되나요?

StatusLine.tsx는 터미널 하단에 모델명, 토큰 사용량, 비용, 권한 모드 같은 정보를 보여줍니다.

// src/components/StatusLine.tsx
return {
  model: {
    id: runtimeModel,
    display_name: renderModelName(runtimeModel)
  },
  workspace: {
    current_dir: getCwd(),
    project_dir: getOriginalCwd(),
    added_dirs: addedDirs
  },
  version: MACRO.VERSION,
};

현재 모델, 작업 디렉토리, 버전 정보가 실시간으로 표시됩니다. 컨텍스트 윈도우 사용률이나 Rate Limit 상태까지 보여주는 게 꽤 세심합니다.

스피너는 단순한 애니메이션이 아닙니다

Spinner.tsx는 단순 로딩 표시가 아니라, AI가 뭘 하고 있는지 실시간 상태를 보여주는 컴포넌트입니다. 현재 실행 중인 도구, 팀원 에이전트 상태, 토큰 예산까지 스피너 옆에 표시합니다.

// src/components/Spinner.tsx
const SPINNER_FRAMES = [
  ...DEFAULT_CHARACTERS,
  ...[...DEFAULT_CHARACTERS].reverse()
];

// Brief 모드 vs 풀 모드 분기
if (isBriefOnly && !viewingAgentTaskId) {
  return <BriefSpinner mode={mode} />;
}
return <SpinnerWithVerbInner {...props} />;

Brief 모드일 때는 간소한 스피너를, 일반 모드에서는 상세 정보가 포함된 스피너를 보여줍니다. 프레임 배열을 앞뒤로 이어붙여서 부드러운 왕복 애니메이션을 만드는 것도 재미있는 디테일이에요.

정리

  • Claude Code의 터미널 UI는 React + 커스텀 Ink + Yoga Flexbox로 구축
  • react-reconciler로 터미널을 렌더링 대상으로 지정하는 구조
  • Box, Text 등 웹과 동일한 컴포넌트 패턴으로 레이아웃 구성
  • VirtualMessageList로 가상 스크롤 + 검색 기능 구현
  • 상태 바, 스피너 등 모든 UI 요소가 독립 React 컴포넌트로 분리

관련글

profile

ClOr

@ClOr

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

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