“그냥 TUI”가 아니다: Claude Code 렌더링이 게임 엔진인 이유
Claude Code를 처음 보면 “터미널에서 돌아가는 UI(=TUI)겠지” 하고 넘기기 쉽습니다. 그런데 내부를 들여다보면, 이건 단순히 글자를 찍는 프로그램이라기보다 매 프레임을 계산해 화면을 그려내는 “작은 게임 엔진”에 가깝습니다.
이번 글에서는 Chris Lloyd의 설명을 실마리로, Claude Code가 왜 게임 엔진 같은 렌더링 파이프라인을 택했는지, 그리고 그 선택이 깜빡임(flicker)·지연(latency) 같은 체감 문제와 어떻게 연결되는지 쉽게 풀어보겠습니다.
Claude Code는 왜 “작은 게임 엔진”처럼 움직일까
우리가 터미널에서 무언가를 볼 때, 뇌는 이렇게 상상합니다. “프로그램이 텍스트를 출력하면 터미널이 예쁘게 그려준다.”
하지만 Claude Code가 제공하려는 경험은 단순 출력이 아니라, 창 크기 변화에 적응하고, 여러 패널을 배치하고, 스크롤·입력·상태 표시가 동시에 부드럽게 돌아가는 ‘인터랙티브 화면’입니다. 즉, 문서(텍스트)를 내보내는 게 아니라 장면(scene)을 매 순간 갱신해야 하죠.
그래서 Chris Lloyd는 사람들이 생각하는 “그냥 TUI”라는 mental model이 실제와 다르다고 말합니다. Claude Code는 매 프레임 장면을 만들고, 그 장면을 화면으로 변환한 다음, 필요한 최소 변경분만 터미널에 전달하는 방식으로 움직입니다.1
React 장면 그래프부터 ANSI 시퀀스까지: 한 프레임의 여정
게임이 매 프레임 “월드 상태 → 화면 픽셀”로 바꾸듯, Claude Code도 비슷한 변환 체인을 가집니다. 흐름을 이야기처럼 따라가 볼게요.
첫 장면은 React입니다. 매 프레임마다 React로 “장면 그래프(scene graph)”를 구성합니다. 여기서 장면 그래프란 “어디에 어떤 박스/텍스트/강조가 놓일지”를 트리 구조로 표현한 설계도 같은 겁니다.1
그다음은 레이아웃입니다. 각 요소의 위치와 크기를 계산해 “이 박스는 몇 칸, 저 텍스트는 어느 줄” 같은 결정을 내립니다.
이후 래스터라이징 단계에서, 그 설계도를 실제 터미널의 2D 격자(행/열)로 바꿉니다. 터미널은 본질적으로 “문자 셀들의 바둑판”이니까요.
그 다음이 핵심인데, 바로 diff(차이 비교)입니다. 새로 만든 화면과 이전 화면을 비교해 “바뀐 셀만” 추립니다. 마지막으로 그 변경분을 ANSI 시퀀스로 만들어 터미널에 전송해 화면을 갱신합니다.1
결국 Claude Code는 “전체를 다시 그리는” 대신 “바뀐 부분만 그리는” 방향으로 최적화된 렌더러를 갖고 있는 셈입니다.
16ms 프레임 예산과 5ms 변환: 숫자가 말해주는 긴박함
여기서 갑자기 숫자 얘기가 나오면 어렵게 느껴지지만, 사실 직관적입니다.
사람이 “부드럽다”고 느끼는 상호작용은 대개 60fps 근처에서 만들어집니다. 60fps는 1초를 60장으로 쪼개는 거라, 한 장(한 프레임)에 약 16ms밖에 시간이 없습니다.
Chris Lloyd의 설명에 따르면 Claude Code도 대략 이 16ms 프레임 예산을 전제로 움직이고, 그중 React 장면 그래프에서 ANSI로 바꾸는 구간을 약 5ms 안에 끝내야 합니다.1 “터미널인데 5ms?”라고 느껴질 수 있지만, 매 키 입력마다 화면이 갱신되는 제품이라면 이 정도 타이트함이 체감 품질을 좌우합니다.
깜빡임(flicker)과 GC 멈춤: 사용자가 느끼는 진짜 문제
사용자는 “렌더링 파이프라인”을 보지 않고, 딱 두 가지만 느낍니다. 화면이 깜빡이거나, 타이핑이 버벅이는지.
Hacker News에서 Chris Lloyd는 Claude Code가 렌더링 시스템을 새로 쓰고(diff 기반 렌더러를 배포), “동기화 출력(synchronized output)”을 지원하는 환경에서는 깜빡임을 크게 줄일 수 있다고 설명했습니다.2 이는 단순히 ‘더 빨라져서’라기보다, “한 프레임을 깔끔하게 마무리하고 보여주는 방식”에 가까운 개선입니다.
또 하나의 복병은 GC(가비지 컬렉션)입니다. 화면을 매 프레임 만들다 보면 객체 할당이 늘고, 어느 순간 VM이 잠깐 멈추며 메모리를 정리합니다. 이 “잠깐”이 개발자 눈에는 짧아 보여도, 사용자는 입력 한 글자가 늦게 찍히는 걸로 바로 체감합니다. Chris Lloyd는 느린 머신에서는 이런 멈춤이 ms가 아니라 초 단위로도 보일 수 있었다고 말합니다.2
그래서 JSX 할당을 줄이기 위한 메모이제이션, 버퍼를 TypedArray로 바꾸는 식의 “예측 가능한 성능” 확보가 중요해졌고요.2
“왜 이렇게까지?”의 답: 터미널도 결국 병목이 있다
터미널은 단순한 듯하지만, 사실 “출력한 바이트를 반대편 에뮬레이터가 해석해 그리는 과정” 자체가 병목이 될 수 있습니다. 그래서 화면 전체를 매번 다시 출력하는 방식은 특정 상황에서 대역폭과 처리량 문제를 곧장 맞습니다.
이 지점에서 diff 기반 렌더링이 설득력을 얻습니다. 전체를 다시 보내지 않고, 바뀐 것만 보내면 터미널이 처리해야 할 양이 줄어들고, 그만큼 깜빡임과 지연 가능성도 내려가니까요. “게임 엔진스럽게” 들리는 구조가 사실은 터미널이라는 제약 속에서 사용자 경험을 지키기 위한 선택이었던 셈입니다.13
시사점 내용 (핵심 포인트 정리 + 개인적인 생각 또는 실용적 조언)...
Claude Code 렌더링 논쟁은 “React를 썼네/안 썼네” 같은 취향 싸움으로 끝내기 아까운 주제입니다. 핵심은 이거예요. Claude Code는 터미널 위에서 ‘앱 같은 경험’을 만들려 했고, 그 순간부터 문제는 텍스트 출력이 아니라 프레임·지연·병목·예측 가능성의 싸움이 됩니다.
만약 여러분이 TUI나 CLI 도구를 만들고 있다면, 이 사례가 주는 실용적 힌트는 분명합니다. 화면 전체를 다시 그리는 접근은 생각보다 빨리 한계에 부딪힐 수 있고, “바뀐 것만 그리기(diff)”와 “멈춤이 없는 성능(예측 가능성)”이 사용자 만족도를 가르는 진짜 기준이 될 수 있습니다.
참고
2Claude Chill: Fix Claude Code's flickering in terminal | Hacker News
3Is drawing a monospace terminal display straightforward? | Clifford Ressel
이 노트는 요약·비평·학습 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.
키워드만 입력하면 나만의 학습 노트가 완성돼요.
책이나 강의 없이, AI로 위키 노트를 바로 만들어서 읽으세요.
콘텐츠를 만들 때도 사용해 보세요. AI가 리서치, 정리, 이미지까지 초안을 바로 만들어 드려요.