URL: https://www.youtube.com/watch?v=FsDUOUV9Vs8 날짜: 2026-10-05 채널: latentspacepod
📌 핵심 질문 및 논점
==에이전트에게 “성능을 개선하라”고 지시하는 것만으로는 부족하다. 에이전트가 측정하고, 벤치마크를 만들고, 실제 사용자 지표와 대조하고, 안전장치를 갱신할 수 있게 만들면 성능 개선을 반복 가능한 운영 루프로 바꿀 수 있다.==
- Anthropic은 Claude.ai와 Claude Desktop의 사용자 활동 95%를 차지하는 네 가지 여정을 계측하고 약 2주 동안 개선했다.
- 새 측정값이 사용자 체감 속도와 상관있는지 검증한 뒤, 결정론적 벤치마크를 CI(Continuous Integration) 가드레일로 고정했다.
- Claude는 Slack의 Claude 봇(Claude for Slack, 대화 중에는 “Claude Tag”로 지칭)을 통해 Datadog MCP(Model Context Protocol)에 접근하고, 병목 탐색·벤치마크 생성·PR 작성·배포 관찰을 병렬로 수행했다.
- 사람이 사용자 경험의 취향, 변경 범위, 위험도, 우선순위를 결정하고 에이전트가 반복 측정과 구현을 맡는 분업이 핵심이었다.
- 측정 가능한 수치만 최적화하면 잘못된 언덕을 오를 수 있으므로, 숫자를 줄이는 것보다 먼저 그 숫자가 실제 사용자 경험을 대표하는지 입증해야 한다.
1. 출발점: Claude.ai와 Claude Desktop의 성능 문제
1.1. 발표자의 배경과 문제의식
-
웹 성능에 대한 관심
- 발표자는 AI 개발·바이브 코딩 채널로 알려졌지만, 과거에는 풀스택 애플리케이션 개발, 웹사이트, 시스템의 종단 간(end-to-end) 성능에 집중했다.
- 데이터 로딩 패턴, 브라우저 동작, 성능 병목, 좋은 엔지니어링의 세부 사항을 직접 파고드는 작업을 선호한다.
- Anthropic의 성능과 전반적인 엔지니어링 품질을 오랫동안 비판해 왔기 때문에, Claude가 이제 성능 문제를 일으키는 도구가 아니라 성능 개선을 돕는 도구가 됐는지 확인하려 했다.
-
성능 개선을 기대한 이유
- Claude Desktop을 열 때 메모리를 18GB 가까이 사용하는 경험 때문에 제품 성능에 회의적이었다.
- 깊은 성능 문제를 디버깅하려면 프로파일링 도구를 잘 구성해야 하지만, 기존 도구는 사용하기 어렵거나 실제 문제를 충분히 설명하지 못하는 경우가 많다.
- Claude를 이용해 웹과 데스크톱 앱을 동시에 의미 있게 빠르게 만들려면 어떤 계측, 프로파일링, 실행 권한을 제공해야 하는지가 핵심 관심사였다.
1.2. 부가 사례: 에이전트가 가입할 수 있는 인증 흐름
-
WorkOS가 제기한 문제
- 에이전트를 주된 작업 인터페이스로 사용하면 사람이 직접 버튼을 클릭하는 일반적인 회원가입·로그인 흐름은 제품 채택을 막는다.
- WorkOS는 사람과 기업이 앱에 로그인하도록 돕는 엔터프라이즈 인증 플랫폼이며, OpenAI, Anthropic, Figma, Cursor, Bolt, Vanta, Carta 등이 사용한다고 소개된다.
- WorkOS가 Firecrawl, Cloudflare 등과 함께 설계한 OMD(자막상 표기)는 에이전트가 사용자를 대신해 앱에 가입하기 쉽게 만드는 표준으로 소개된다.
-
에이전트 중심 제품의 조건
- 사용자가 에이전트에게 가입을 맡길 수 있어야 에이전트가 선택한 플랫폼이 실제로 사용될 수 있다.
- Neon, Monday, Parallel 등도 이 표준을 채택하기 시작했다고 소개된다.
- 관련 링크는
https://soy.link/workos로 제시된다.
2. 2주 성능 스프린트의 결과
2.1. 목표 사용자 여정과 체감 성과
-
95%의 사용 활동을 차지한 네 가지 여정
- 앱 실행(launching the app)
- 새 대화 시작(starting conversations)
- 기존 대화 불러오기(loading existing conversations)
- 웹·데스크톱과 제품 간 메시지 전송(sending messages across web, desktop, Claude Code, Claude Cowork)
-
핵심 수치
- 새로고침 기준 Claude의 시작 시간 75번째 백분위수(p75)가 3.1초에서 0.55초로 줄었다.
- 새 Claude Code 세션 시작은 0.8초에서 0.3초로 줄었다.
- Claude Cowork 세션 로딩은 거의 3초에서 1초 미만으로 줄었다.
- Claude.ai 웹사이트 실행은 약 3초에서 550ms로 줄었다.
- Claude Desktop 앱 실행은 6초 이상에서 3초를 조금 넘는 수준으로 줄었다.
- Claude Cowork의 메시지 전송은 약 1초에서 48ms로 줄었다.
- Claude Code 데스크톱 앱의 메시지 전송은 250ms에서 52ms로 줄었다.
- 집계 결과 매일 수만 시간의 사용자 대기 시간을 절약한다고 추정했다.
-
시간순 성과 확인
- 발표자는 6월부터 Claude.ai가 기가비트 네트워크보다 모바일 핫스팟에서 더 빠르게 느껴질 정도로 개선됐다고 체감했다.
- Claude.ai가 TanStack을 사용하기 시작했고, 특히 TanStack Router가 내비게이션 사례를 다루기 쉽게 만든 점을 확인했다.
- 이번 2주 스프린트는 프레임워크 교체 이후에도 남은 병목을 추가로 압축한 작업이었다.
- 13개 목표 중 12개를 3일째에 달성했으며, 계획보다 훨씬 빨리 진행됐다.
2.2. 발표자의 실제 브라우저 테스트와 남은 결함
-
빠른 입력과 느린 영속화의 공존
- 프롬프트를 보내 새 스레드를 만드는 동작은 빨라졌지만, 새 스레드가 사이드바에 영속화되기 전에 새로고침하면 사라질 수 있었다.
- 발표자가 프롬프트를 보내고 즉시 새로고침하자 현재 스레드가 최근 스레드 목록에 나타나지 않았고, 사실상 존재하지 않는 상태가 됐다.
- 어떤 경우에는 약 700ms에서 1초, 어떤 경우에는 2~3초가 걸린 뒤에야 스레드가 나타났다.
- 전반적인 내비게이션은 훨씬 좋아졌지만 데이터 영속화 계층에는 여전히 문제가 남았다고 평가했다.
-
IndexedDB 캐시와 무효화 실패
- 최근 사이드바 스레드는 로컬 저장소 또는 IndexedDB(Key-Value Store)에 캐시돼 새로고침 직후 빠르게 표시됐다.
- 다른 브라우저에서 스레드 두 개를 삭제한 뒤 새로고침해도 기존 브라우저에는 삭제된 스레드가 계속 남았다.
- 삭제된 스레드를 클릭하면 오류가 발생했지만 사이드바가 서버와 다시 검증(revalidation)하지 않았다.
- 새 프롬프트를 보내야 일부 상태가 갱신됐고, 문제의 스레드들이 한참 뒤에야 사라졌다.
- 발표자는 성능을 좋아 보이게 만든 대가로 캐시를 과도하게 오래 유지하고 무효화를 충분히 하지 않은 것 아니냐고 비판했다.
-
캐시와 신뢰성의 트레이드오프
- T3 Chat은 실제 서버 데이터를 가져오기 전에는 오래된 데이터를 흐리고(fade·blur) 보여 사용자가 정확한 데이터라고 오해하지 않게 한다.
- T3 Code는 사용자가 직접 운영하는 서버를 통해 데이터를 다루므로 Claude.ai와 구조가 다르다.
- Convex 같은 백엔드 엔진은 데이터를 최신 상태로 유지하는 데 유리하지만, 페이지가 뜨자마자 데이터를 보여 주지는 않는다.
- 그래서 T3 Chat은 사이드바를 로컬 캐시로 먼저 표시하고 백엔드 데이터를 뒤에서 갱신하는 방식을 사용한다.
- 발표자는 모델 선택기와 컴포저 하단의 다른 선택기도 사이드바처럼 캐시해 첫 화면에서 즉시 열리게 하는 PR을 에이전트에게 요청했다.
3. Claude를 성능 엔지니어로 만든 운영 구조
3.1. Slack 채널의 상시 지시문
-
채널의 역할
- Anthropic은 Claude.ai 웹사이트와 Claude Desktop의 성능 관련 작업을 전담하는 새 Slack 채널을 만들었다.
- Claude에게 앱 성능에 관한 모든 일을 촉진하는 역할을 부여했다.
-
상시 책임
- 배포를 감시해 성능 회귀(regression)를 찾는다.
- 기존 텔레메트리(telemetry)의 정확성과 포괄성을 평가한다.
- 관측 가능성(observability) 대시보드를 관리하고 정리한다.
- 발견한 문제와 손쉬운 개선 과제를 능동적으로 구현한다.
- 새로운 성능 프로젝트 기회를 제안한다.
- 인간 팀원과 소통한다.
- 궁극적으로는 최대한 자율적으로 일하는 것을 목표로 한다.
-
지시문에 대한 비판
- 발표자는 “실수하지 말라”는 식의 명시적인 안전 지시가 없었다는 점을 농담 섞어 비판했다.
- 안전장치가 충분하다면 에이전트에게 더 과감하게 행동하라고 명시해야 하며, 단순히 조심하라고 하면 안전한 범위의 작은 변경만 반복하게 된다.
- 실제 안전성은 지시문 한 줄이 아니라 측정·CI·사람의 승인·점진적 배포가 함께 만들어야 한다.
3.2. 계측과 기준선 만들기
-
Datadog MCP 활용
- Claude는 Datadog MCP 서버를 통해 사용량과 성능 데이터를 분석했다.
- 네 가지 여정을 13개의 서로 다른 측정값으로 분해했다.
- 각 측정은 사용자의 실제 상호작용에서 시작해 결과가 렌더링되는 시점에 끝나도록 정의했다.
- 클라이언트 작업과 서버 작업을 구분해 어느 쪽에서 시간이 사용되는지 확인했다.
-
실제 사용자 시간을 계측해야 하는 이유
- “느리다”는 사용자 체감과 대시보드의 200ms 측정값이 충돌하는 경우가 흔하다.
- 컴포넌트가 UI에 렌더링된 시점부터 로딩 완료까지 재면, 사용자가 페이지를 연 뒤 실제 완료될 때까지 8초가 걸려도 짧은 수치가 나올 수 있다.
- 시작점과 종료점을 잘못 잡은 트레이스는 실제 세계의 성능을 반영하지 못한다.
- 측정할 수 없는 문제는 고치기 어렵고, 잘못 측정한 문제는 에이전트가 자신 있게 잘못된 방향으로 최적화하게 만든다.
-
초기 프로젝트 목록
- Claude는 각 사용자 여정을 겨냥한 약 20개의 프로젝트를 손으로 선정했다.
- 각 프로젝트가 줄일 수 있는 시간을 밀리초 단위로 추정하고 합산해 2주 스프린트의 목표를 세웠다.
- 일부 프로젝트는 컸지만 2주 안에 대부분 달성할 수 있다고 판단했다.
- 실제로는 3일째에 13개 목표 중 12개를 달성했고, 이후 새 측정값과 프로젝트가 계속 추가됐다.
3.3. 성능 개선 루프
-
한 스레드의 기본 흐름
- 사람이 느린 여정을 발견하고 스크린샷이나 녹화와 함께 Slack 스레드를 연다.
- Claude가 흐름을 추적하고 문제를 재현하는 벤치마크를 찾거나 새로 만든다.
- 실험실에서 유망한 결과가 나오면 위험도별로 나눈 하나 이상의 PR을 제시한다.
- 사용자에게 보이는 변경은 기능 플래그(feature flag) 뒤에 둔다.
- 배포 뒤 Claude가 실제 현장 데이터(field data)를 읽는다.
- 성능이 좋아지면 벤치마크의 기준선을 더 낮은 값으로 고정하고, 좋아지지 않으면 플래그를 끄고 다시 반복한다.
- 같은 여정에서 다음 느린 지점을 찾아 새 스레드를 연다.
-
비동기·병렬 확장
- Claude는 요청 하나를 처리한 뒤 스레드를 닫지 않고 50개에서 100개의 최적화 PR을 계속 생성하는 경우도 있었다.
- 원래 조사에서 발견한 기회나 야간 작업을 바탕으로 새 스레드를 Claude 스스로 열었다.
- 한 스레드에서 검증된 루프를 여러 머신·여러 에이전트·여러 가설로 확장하면 5분짜리 작업을 3초에 실행하고 수백 개를 병렬 처리할 수 있다.
- T3 Code의 자체 성능 개선 작업에서도 자동 병합된 약 40개의 PR 가운데 38개가 실제 성능 향상이었고, 주요 회귀는 데스크톱 애니메이션 변경과 마케팅 홈페이지의 트윗 슬라이딩 티커 파손 두 건이었다.
4. 결정론적 벤치마크와 “측정 가능한 언덕”
4.1. 벽시계 시간의 한계
-
Wall-clock time의 문제
- 벽시계 시간(wall-clock time)은 사용자가 느끼는 값이지만 실행 환경과 네트워크에 따라 흔들린다.
- 밀리초 단위의 작은 변동 때문에 CI의 엄격한 게이트로 쓰기 어렵다.
- Anthropic은 Claude가 배포를 기다리지 않고 밤새 프로토타입을 검증하도록 실험실 안에서 측정할 방법을 찾았다.
-
명령어 수를 후보로 삼기
- 순수 JavaScript 핫 패스에서는 Node의
--predictable플래그와 Valgrind를 이용해 CPU 명령어 수를 셀 수 있다. - 체크인된 기준선과 한 번의 실행을 비교하면 통계 없이도 결정론적인 비교가 가능하다.
- Chromium에는 동일한 명령어 수 측정이 없어 React 커밋 수, V8의 정밀 커버리지 기반 함수 호출 수, 레이아웃·스타일 재계산 수, DOM 변경 수를 대체 지표로 사용했다.
- 순수 JavaScript 핫 패스에서는 Node의
4.2. 실제로 검증한 핫 패스
-
두 가지 핵심 경로
- 대화 메시지 트리를 조립하는 루틴을 측정했다.
- Claude Code 출력에서 상태 줄을 검색하는 스캐너를 측정했다.
- Claude는 Valgrind 프로파일링으로 동일한 메시지 ID를 세 번 해석하는 다형성 사전 조회(megamorphic dictionary lookup)가 명령어의 큰 비중을 차지한다는 사실을 찾았다.
-
성과와 CI ratchet
- 약 한 시간 뒤 메시지 트리 경로의 명령어 수를 48%, 상태 줄 스캐너를 31% 줄였다.
- 해당 경로의 벽시계 시간은 각각 78%, 44% 감소했다.
- 두 개의 새 ratchet을 체크인해 이후 PR이 명령어 수를 늘리면 CI가 실패하게 했다.
- 일일 작업은 수치가 낮아질 때마다 허용 상한선을 다시 낮췄다.
- 단, 절대 시간값이 공개되지 않았으므로 78% 개선이 4초에서 0.5초로 줄인 것인지 200ms에서 60ms로 줄인 것인지는 알 수 없다.
-
기존 코드 품질 지표에 대한 경고
- 발표자는 코드 커버리지처럼 숫자 자체를 목표로 삼는 지표가 실제 품질과 어긋날 수 있다고 비판했다.
- Twitch에서 50만 줄짜리 기존 기능을 5만 줄의 새 구현으로 교체하고 이전 구현을 삭제하려 했지만, 커버리지가 95%에서 94%로 떨어져 PR을 병합하지 못한 사례를 들었다.
- 새 5만 줄은 100% 커버리지였고 삭제 대상인 50만 줄도 96%였지만, 전체 비율 임계값을 통과하지 못했다.
- 성능 벤치마크도 실제 사용자 지연과 상관하지 않으면 같은 방식으로 잘못된 최적화를 유도한다.
4.3. 잘못된 지표를 버리는 규칙
-
각 벤치마크의 두 가지 역할
- 실험실에서 Claude가 움직일 수 있는 최적화 목표가 돼야 한다.
- CI에서 수치가 더 나빠지지 않도록 막는 가드레일이 돼야 한다.
-
상관관계 검증
- Claude에게 각 지표를 줄이는 일이 실제 벽시계 성능 향상으로 이어지는지 먼저 증명하게 했다.
- 불안정하거나 사용자 지연과 상관하지 않는 벤치마크는 폐기했다.
- 수치가 쉽게 낮아진다는 이유만으로 잘못된 언덕을 오르게 두지 않았다.
-
네트워크 요청 수만 줄이면 생기는 문제
- React 커밋 수나 V8 호출 수를 맹목적으로 줄이면 사용자에게 유용한 작업까지 끌 수 있다.
- 모든 네트워크 요청을 없애기보다, 사용자가 먼저 상호작용할 수 있도록 요청 순서를 바꾸고 이후 데이터를 안정적으로 최신화해야 한다.
- 사이드바 데이터가 이미 로컬 저장소에 있다는 이유로 요청을 끄면 한 트레이스에서 약 800개 요청 중 6개를 줄일 수 있지만, 삭제·변경된 스레드가 갱신되지 않는 문제가 생길 수 있다.
- 에이전트가 요청 수라는 숫자만 최적화하면 앞서 발견된 오래된 사이드바 캐시 회귀를 다시 만들 수 있다.
5. 구체적인 최적화와 발표자의 병렬 실험
5.1. Claude.ai·Claude Desktop의 변경
-
초기 렌더링과 실행 최적화
- React가 초기화되기 전에 사용자가 입력할 수 있도록 정적 컴포저(static composer)를 HTML에 미리 넣었다.
- 데스크톱 셸의 메인 프로세스가 V8 코드를 매번 처음부터 컴파일하지 않도록 V8 코드 캐시를 미리 컴파일했다.
- 대화 사이에서 컴포저를 마운트된 상태로 유지해 내비게이션 때 다시 만들지 않았다.
- 사용자가 세션 위에 마우스를 올렸을 때 세션을 미리 가져와(prefetch) 실제 클릭 시 즉시 열리게 했다.
- 이 변경으로 재렌더링을 90% 줄였다.
-
새로운 작업을 계속 발견하는 프롬프트
- Claude가 초기 프로젝트를 끝내도 스레드를 닫지 않고 새로운 개선 기회를 찾도록 했다.
- “가장 큰 기회가 어디인가?”, “측정하지 않은 것은 무엇인가?”, “엉뚱하고 과감한 아이디어도 제안하라”는 방향을 줬다.
- 발표자는 목표를 달성한 뒤에도 “더 깊이 파고들고, 극단적인 조치까지 포함한 세 가지 제안을 내라. 바다를 끓이는 것을 두려워하지 말라”고 요청한 경험을 들었다.
5.2. 발표자의 Lakebed 실험
-
지속형 V8 실행 서비스
- 발표자는 Lakebed라는 클라우드 실험 프로젝트에서 여러 차례 성능 개선을 했지만 첫 번째 개선만으로는 만족하지 못했다.
- 과감한 제안을 요구하자 에이전트는 지속형 V8 실행 서비스(persistent V8 execution service)를 제안했다.
- 다른 제안으로는 쿼리 컴파일러와 실시간 답변 유지, 앱마다 로컬 데이터를 가진 영속적 소유자를 두는 구조가 나왔다.
- 발표자는 지속형 V8 실행 서비스에 집중해 Rusty V8을 포크하고 자체 런타임을 만들었다.
- 결과는 약 2~4배의 성능 향상이었다.
-
검증 체계의 중요성
- 계획에 20분이 걸렸고 첫 빌드에는 약 2시간이 걸렸다.
- 작업 중 문제가 생긴 뒤 재개를 지시했고, 5시간 30분 후 PR이 완성돼 실제로 동작했다.
- 단순히 “빠르게 해”라고 지시한 것이 아니라 성능 향상이 실제로 유지되는지, 기능 동작이 회귀하지 않는지 검증하는 테스트를 함께 만들었다.
- 이 실험은 과감한 프롬프트가 잘못된 변경을 자동 병합하라는 뜻이 아니라, 충분한 검증 구조가 있을 때 탐색 범위를 넓힐 수 있다는 사례다.
5.3. T3 Code의 회귀 방지 실험
- 테스트를 빠르게 만드는 “슬롭”
- 발표자는 실제 제품에 직접 배포할 목적이 아니더라도 에이전트에게 테스트 도구를 빠르게 만들게 하는 것이 큰 이득이라고 강조했다.
- T3 Code의 다양한 요청 형식과 실제 저장 데이터를 가진 가짜 대화 스레드를 대상으로 로딩 데이터를 비교하는 GitHub Action을 만들었다.
- 해당 테스트는 수천 줄의 생성 코드로 구성됐지만, 성능 회귀가 나타날 때 PR 코멘트로 즉시 알려 주는 역할을 했다.
- 구현 에이전트와 PR 리뷰 에이전트가 회귀를 발견하고 스스로 수정할 수 있게 됐다.
- 성능에 중요한 코드라면 테스트 세부 사항을 더 엄밀히 다듬어야 하지만, 막연한 아이디어에서 시작한 생성 코드도 실제 회귀 방지 장치가 될 수 있다.
6. 레이아웃 안정성과 캐시의 충돌
6.1. 기존 모니터가 놓친 사이드바 점프
-
문제의 증상
- 페이지가 먼저 표시된 뒤 사이드바 행이 하나씩 나타났다.
- Claude Chat과 Claude Cowork 행이 서로 다른 시점에 완성돼 화면이 끊겨 보였다.
- 기존 모니터는 문제를 감지하지 못했고, 가장 가까운 지표인 Cumulative Layout Shift(CLS)도 각 이동이 약 0.008에 불과해 “좋음” 기준인 0.1 아래에 있었다.
-
새 계측과 테스트
- Isaac은 레이아웃 불안정성 API(Layout Instability API)를 직접 참조하자고 제안했다.
- Anthropic은 사이드바가 채워진 페이지를 열고, 첫 페인트 이후까지 사이드바 데이터를 지연시키는 통합 테스트를 추가했다.
- 이름이 붙은 각 영역에서 이동이 한 번이라도 발생하면 테스트가 실패하게 했다.
- 배포 후 Claude는 사용자가 아무것도 하지 않았는데 페이지가 사용 가능한 상태가 된 뒤 무언가 이동한 웹 로드가 31%라는 사실을 발견했다.
-
원인별 수정
- 늦게 도착한 헤더 행을 수정했다.
- 사용자 이름이 로드된 뒤 옆으로 움직이는 캐럿을 수정했다.
- 스크롤바가 나타날 때 목록이 움직이는 문제를 수정했다.
- 상위 원인을 일괄 수정한 다음, 다음 원인 묶음을 다시 찾아가는 방식으로 진행했다.
6.2. 브라우저 캐시와 레이아웃 테스트의 역설
-
캐시 불일치가 만드는 이동
- 로컬 캐시에 스레드 네 개가 있고 네트워크에서 다섯 번째 스레드가 도착하면, 새 행이 위에 끼어들어 기존 네 행이 아래로 밀릴 수 있다.
- 캐시를 즉시 표시하는 방법은 빠르지만 캐시가 서버 데이터와 다르면 레이아웃 이동을 만든다.
- 발표자는 이런 작은 이동을 데이터 로딩 완료를 나타내는 흐린 처리와 함께 보여 주면 큰 문제는 아닐 수 있다고 봤다.
-
테스트가 회귀를 만들 가능성
- 첫 페인트 뒤 네트워크 데이터를 늦게 도착시키는 테스트는 실제 캐시와 네트워크 데이터의 차이를 의도적으로 만든다.
- 따라서 테스트가 통과하도록 수정하면 오히려 캐시 우선 전략을 훼손할 수 있다는 가능성을 발표자가 제기했다.
- 측정값을 추가할 때는 지표를 만족시키는 것이 실제 제품 경험을 개선하는지 반드시 확인해야 한다.
6.3. Chrome 사전 렌더링으로 생긴 56px 이동
-
재현 조건
- 새 탭에서 Claude.ai를 열 때 컴포저가 아래로 떨어지는 레이아웃 이동이 발견됐다.
- Chrome은 주소창에 URL을 입력하는 동안 페이지를 백그라운드에서 speculative loading·pre-render한다.
- 관리형 Chrome 프로필의 새 탭에는 Claude가 관리하는 56px 높이의 하단 푸터가 있었고, Claude.ai로 이동하면 푸터가 사라져 페이지가 56px 더 높아졌다.
- 첫 페인트 뒤 약 100ms가 지나서 푸터가 사라지면서 이동이 발생했다.
-
컴포저 배치와 렌더링 타이밍
- 컴포저를 화면 아래 고정 위치가 아니라 페이지 높이의 비율과 상단으로부터의 거리로 계산한 것이 문제를 키웠다.
- JavaScript가 페이지 크기 변경을 감지하고 재계산해야 하므로 Chrome의 리사이즈 동작과 타이밍에 의존하게 됐다.
- 사전 렌더링 때의 높이와 실제 탭의 높이가 달라 JavaScript가 로드되기 전 또는 100ms 재페인트 뒤에 컴포저가 움직일 수 있었다.
- 이 사례는 브라우저 자체의 사전 렌더링과 관리형 프로필까지 포함해 사용자의 실제 환경을 테스트해야 함을 보여 준다.
7. 규모를 키울수록 발견된 새로운 병목
7.1. 훅·구독·CSS의 숨은 비용
-
컴포저의 과도한 React 작업
- Claude는 React hook census를 실행해 컴포저의 입력 경로에 약 6,900개의 훅과 900개의 스토어 구독이 있다는 사실을 찾았다.
- 이 구독들이 키 입력마다 재렌더링을 일으키고 있었다.
- 발표자는 약 7,000개의 훅이 있는 것은 코드 구조를 직접 읽어 봐야 할 수준이라고 농담했다.
-
스타일 재계산
- 단일 루트
has셀렉터가 DOM이 바뀔 때마다 스타일 재계산에 24ms를 추가했다. - 단순한 CSS 선택자 하나가 브라우저와 Electron에서 모든 변경을 느리게 만들 수 있다.
- Chrome DevTools는 JavaScript CPU·이벤트 루프 문제는 비교적 잘 보여 주지만, GPU 가속 CSS 계층의 비용은 찾기 어렵다.
- 단일 루트
-
숨겨진 리로드와 IndexedDB 작업
- 남은 한 코드 경로가 브라우저 새로고침과 비슷한 동작을 일으켜 로드 지표에 잡히지 않는 숨은 리로드를 하루 약 50만 번 만들고 있었다.
- Claude는 유휴 탭의 프로파일러 샘플을 읽어 동일한 캐시 스냅샷이 2분마다 메인 스레드에서 IndexedDB로 두 번 복제되는 것을 찾았다.
- 발표자는 이 작업을 줄인 변경이 사이드바 캐시 갱신 문제와 연결됐을 가능성도 제기했다.
7.2. 문자열 인코딩과 구문 강조
-
1초짜리 CPU 히치
- 완료된 코드 블록을 강조 표시할 때 페이지가 약 1초 멈추는 현상이 발견됐다.
- 원인은 Markdown 안의 em dash(—), curly quote 같은 비라틴 문자였다.
- 비라틴 문자가 포함되면 V8이 전체 문자열을 UTF-16으로 저장해 구문 강조의 모든 반응이 2바이트 경로로 이동했다.
- 코드 블록마다 강조 전에 1바이트 문자열로 복사하는 약 20줄의 변경으로 이 비용을 줄일 수 있었다.
-
메인 스레드 차단 회피
- 발표자는 메인 스레드를 0.35초 차단하는 구문 강조는 받아들일 수 없다고 평가했다.
- T3 Code는 구문 강조를 WASM(WebAssembly)으로 옮기고 워커에서 실행해 메인 스레드를 막지 않게 했다.
- 강조 결과를 지연 로드하면 초기 번들 크기 증가를 감수하면서도 이후 한 번만 로드할 수 있다.
- 메인 스레드가 비어 있는 동안 결과를 보여 주기 때문에 T3 Code는 여러 언어의 코드를 연속으로 강조해도 다른 탐색이 끊기지 않는다.
7.3. Anthropic 코드베이스에 대한 발표자의 해석
-
에이전트로 만든 슬롭을 에이전트로 정리하기
- 발표자는 Anthropic이 Sonnet 4 시기부터 AI가 많은 코드를 작성하게 하는 실험을 가장 먼저 크게 진행했다고 해석했다.
- 그 결과 Claude.ai, Claude Desktop, Claude Code에는 슬롭(slop)과 불필요한 코드가 쌓였고, 모델이 충분히 좋아진 뒤 같은 모델로 이를 정리하는 단계에 들어섰다고 주장했다.
- 이 제품들은 AI가 얼마나 많은 복잡성을 견딜 수 있는지, 모델이 만든 엉킨 구조를 언제 모델 스스로 정리할 수 있는지를 Anthropic이 직접 실험하는 도그푸딩(dogfooding) 장으로 기능한다.
-
조직과 엔지니어링에 대한 추론
- Convex의 Jamie는 “모델을 잘 만드는 회사가 소프트웨어 엔지니어링도 잘하는 것은 아니다”라고 지적했다.
- 발표자는 OpenAI와 Anthropic의 연구·엔지니어링 문화 차이를 근거로 Anthropic이 엔지니어를 충분히 존중하지 않았을 가능성을 추론했다.
- 그 추론에 따라 “싫어하는 것을 자동화하기는 어렵고, 존중하지 않는 것은 이해하기 어렵다”고 정리했다.
- 이 부분은 Anthropic의 공식 설명이 아니라 발표자의 조직·채용에 관한 해석이며, 성능 수치와는 구분해 읽어야 한다.
8. 안전장치와 배포 운영
8.1. 빠른 변경을 허용한 조건
-
검토와 승인
- 모든 PR은 자동 리뷰를 통과하고 최소 한 명의 사람이 승인했다.
- 최적화보다 먼저 단위 테스트를 작성했다.
- 사용자에게 보이는 위험한 변경은 수명이 짧은 기능 플래그 뒤에 배포했다.
-
기능 플래그 관리
- 2주 동안 거의 200개의 플래그를 추가했다.
- 플래그의 절반 이상은 스프린트가 끝나기 전에 제거했다.
- Claude는 각 플래그를 즉시 끌 수 있는 kill switch인지 점진적으로 확대하는 ramp인지 분류했다.
- 안전해진 플래그를 바로 정리해 빠른 개발 과정에서 오래된 복잡성이 쌓이지 않게 했다.
-
점진적 롤아웃
- 실험실과 테스트만으로 잡히지 않는 문제를 위해 고위험 변경은 먼저 임직원에게 배포했다.
- 그다음 1% 사용자에게 배포하고, 문제가 없을 때 전체 사용자로 확대했다.
- 실제 현장 데이터가 벤치마크와 다르게 반응하면 플래그를 되돌렸다.
8.2. 정적 컴포저의 가드레일
-
픽셀 단위 일치
- 정적 HTML은 실제 React 컴포넌트를 JSDOM에서 렌더링해 생성했다.
- 정적 마크업이 React 렌더와 어긋나지 않는지 테스트했다.
- 14개 뷰포트 크기에서 정적 페이지와 React 렌더를 비교하고 1px 이내로 정렬되는지 확인했다.
-
상호작용과 현장 측정
- 키 입력 테스트는 정적 컴포저에서 React로 넘어가는 순간까지 직접 타이핑해 키가 유실되거나 순서가 바뀌지 않는지 검사했다.
- 실제 사용자 환경에서는 핸드오프 중 발생한 이동을 0.1px 단위로 기록했다.
- 이동량이 0이 아닌 이벤트가 발생하면 Claude가 조사할 Slack 스레드를 열었다.
- 이런 구조가 있으면 사용자가 문제를 신고하기 전에 작은 레이아웃 결함을 발견할 수 있다.
9. 스티어링: 야망, 취향, 방향
9.1. 야망을 명시적으로 주기
-
신중함을 넘어서는 지시
- 기본 Claude는 범위를 조심스럽게 잡으므로 안전장치가 충분한 환경에서는 더 멀리 가도 된다는 허가를 명시해야 한다.
- Anthropic의 Raymond는 Claude가 “이번 주에 PR을 올리겠다. 며칠 뒤 병합·배포되고 기준선 기간을 기다리겠다”고 하자 “지금 올리면 바로 병합·배포하겠다. 더 용감해져라”고 답했다.
- Claude는 한 시간 안에 PR을 올리겠다고 답했지만, 에이전트의 시간 추정은 여전히 부정확했다.
-
목표를 달성해도 멈추지 않기
- 설정한 목표에 도달하자 스레드가 느려졌고, Sam은 모든 스레드에 “계속 낮추자. 목표는 종착점이 아니다. 다음은 무엇인가? 야심차게 하자”고 보냈다.
- “극단적인 조치”, “바다를 끓여라”, “요청한 범위를 넘어선 제안”, “엉뚱하고 과감한 아이디어” 같은 표현은 모델이 안전한 행복 경로(happy path)를 벗어나 더 큰 탐색을 하도록 유도한다.
- 단, 이러한 지시는 측정·테스트·플래그가 있을 때만 안전하다. 도구와 가드레일 없이 과감함만 요구하면 파괴적인 변경으로 이어질 수 있다.
9.2. 사람의 취향을 맡기기
-
이름 있는 인간 소유자
- 모든 스레드에는 인간 책임자가 있었다.
- 사용자에게 보이는 변화는 Claude가 전후 스크린샷이나 녹화로 제시하고 인간이 판단했다.
- 표를 셀 단위로 채울지 행이 완성될 때 보여 줄지, 로딩 스켈레톤을 즉시 보여 줄지 0.5초 후 보여 줄지, 스트리밍 텍스트의 단어별 페이드에 프레임 예산을 쓸지를 사람이 결정했다.
-
발표자의 렌더링 선호
- 표는 셀 단위보다 전체 표 또는 최소한 완성된 행 단위로 보여 주는 편을 선호한다.
- 스켈레톤 표시 지연은 실제 사용자 로딩 분포에 맞춰야 한다. 데이터가 500ms에 오면 0.5초에 스켈레톤을 표시해 두 프레임만 번쩍일 수 있다.
- 사용자별 요청 완료 시간과 로컬 캐시를 이용하면 스켈레톤을 표시할지 개인별로 판단할 수 있다.
- 단어별 페이드는 거의 가치가 없다고 보고, T3 Code에서는 문단·줄·코드 블록이 완성될 때 표시하는 방식을 택했다.
- Claude는 표를 셀 단위로 스트리밍했지만, 발표자는 완성된 표를 한 번에 보는 편을 선호한다고 밝혔다.
- 제목이 먼저 나타나고 아래 문단이 늦게 오는 현상은 제목 렌더를 아래 콘텐츠가 준비될 때까지 늦추는 편이 나을 수 있다고 봤다.
9.3. 방향과 범위 제한
-
150개의 망치와 하나의 못
- 각 스레드는 하나의 벤치마크나 사용자 여정에만 집중하게 했다.
- Claude 스레드를 “못을 찾는 150개의 망치”처럼 사용해 여러 가설을 병렬로 시험했다.
- 사람은 어떤 표면을 먼저 처리할지, 서로 충돌하는 스레드를 어떻게 합칠지, 수익 체감이 줄어드는 시점에 언제 닫을지를 결정했다.
-
작은 이득에 큰 복잡성을 추가하지 않기
- 900줄짜리 PR에서 메시지 전송을 2ms 줄이기 위해 빌드 플러그인을 추가하려 하자, 그 복잡성은 이득보다 크다는 한 줄 평가가 나왔다.
- 작은 수치를 줄이는 일에 큰 유지보수 비용을 추가하면 성능 최적화가 새로운 부채가 된다.
- 발표자가 Lakebed에서 자체 V8 런타임을 만든 것은 큰 이득을 얻기 위한 의도적인 선택이었지만, 에이전트가 모든 2ms 개선에 같은 수준의 구조 변경을 제안하도록 두면 안 된다.
10. 120Hz 프레임 예산과 스트리밍 최적화
10.1. 결정론적 8.33ms 테스트
-
120Hz의 기준
- 120Hz 디스플레이는 한 프레임에 약 8.33ms를 쓸 수 있다.
- iPhone, MacBook, 게임용 모니터, 일부 최신 TV 사용자는 120Hz를 기대할 수 있으므로 60fps만 맞추는 것은 충분하지 않을 수 있다.
- 최초 headless Chromium은 60Hz로 동작했지만 DevTools의 Begin Frame Control로 120Hz를 구동할 수 있는지 확인했다.
-
프레임 단위 측정
- Claude는 긴 답변을 스트리밍하면서 각 프레임의 애니메이션 타임스탬프로 프레임률을 계산했다.
- 8.33ms 프레임 예산으로 정확히 240개의 프레임과 240개의 begin frame을 실행하는 결정론적 평가를 구성했다.
- 발표자는 이 벤치마크가 실제로는 Opus 4.5 수준의 세련된 결과라기보다 Claude가 만든 “하드코어한 슬롭”이라고 농담했지만, 반복 가능한 기준선으로는 높이 평가했다.
10.2. 결과
-
스트리밍 렌더링 변경
- 메시지 길이에 비례해 매 청크마다 반복하던 작업을 제거했다.
- 이미 완료된 블록을 메모이제이션(memoization)했다.
- 커지는 코드 펜스를 매번 다시 처리하지 않고 토큰화 로직을 워커로 옮겼다.
- 완료된 표를 한 번에 기다리지 않고 셀 단위로 공개하는 경로도 구현했다.
-
측정된 개선
- 한 스레드에서 약 60개의 PR이 병합됐다.
- 긴 답변이 메인 스레드를 막는 총 시간이 750ms 이상에서 약 200ms로 줄었다.
- CPU 사용량은 약 3분의 1로 줄었다.
- 120Hz MacBook에서 처음부터 끝까지 120fps를 유지했다.
- 120Hz 테스트 환경은 Claude가 회귀를 감시하는 야간 작업이 됐다.
-
스트리밍에 대한 발표자의 입장
- 토큰 단위의 불완전한 텍스트보다 문단·코드 블록·도구 호출 단위로 스트리밍하는 편이 낫다고 주장했다.
- T3 Code에서는 전체 응답이 끝날 때까지 기다리던 방식을 문단·줄·코드 블록 완료 시 표시하는 방식으로 완화했다.
- 발표자는 스레드를 계속 바라보기보다 작업을 시작해 두고 다른 일을 하다가 필요할 때 돌아오는 사용 패턴이 많아, 셀 단위·토큰 단위 표시가 항상 이득은 아니라고 봤다.
11. Claude for Slack을 선택한 이유와 인터페이스 관찰
11.1. 채널 중심의 협업
-
공유 컨텍스트
- 모든 결과가 같은 Slack 채널과 스레드에 쌓여 여러 엔지니어가 서로의 논쟁과 성과를 볼 수 있었다.
- 다른 팀도 성능 리뷰를 받기 위해 변경을 채널에 가져왔다.
- 새 가드레일과 스킬이 공유되면서 이후 프로젝트가 처음부터 더 성능 친화적인 방식으로 작성됐다.
- 발표자는 여러 팀이 공통 컨텍스트와 능력을 공유하는 “갱 프롬프팅(gang prompting)” 구조가 매력적이라고 평가했다.
-
긴 작업을 읽는 방식
- Slack의 Claude 응답에는 코드, 도구 호출 세부 내역, 실행한 모든 명령, 긴 사고 과정이 전면에 나오지 않았다.
- 발표자는 실제로 중요한 것은 도구를 몇 번 호출했는지가 아니라 작업이 끝났는지이므로, T3 Code에도 세부 과정을 숨기고 진행 작업 수만 작은 표시로 보여 주는 기능을 고려한다고 했다.
- 역설적으로 Anthropic은 Claude.ai와 Claude Desktop의 성능을 개선하면서 그 작업 자체는 성능 문제가 덜한 Slack 인터페이스에서 수행했다.
- 발표자는 Slack에서 Claude를 쓰는 경험이 Claude Code보다 낫다는 점도 농담처럼 지적했다.
11.2. 성과의 한계
-
아직 남은 범위
- 95번째 백분위수(p95)의 다른 사용자 여정에는 개선 여지가 남았다.
- 매우 긴 대화도 여전히 더 빨라질 수 있다.
- Electron, Chromium, Node 등 상류 프로젝트에 기여한 사이드 퀘스트는 별도 글에서 다룰 예정이라고 했다.
-
전면 자율화가 아닌 관리된 자율화
- 루프는 생산적이지만 자율적이지 않았다.
- 인간은 속도·안전·방향을 조정했고, 현장 데이터와 사용자 취향이 맞지 않는 변경을 중단했다.
- “3,000개가 넘는 변경을 고객 대상 사고나 롤백 없이 병합했다”는 Anthropic의 설명도 사람이 승인하고 가드레일을 운영한 시스템의 결과로 읽어야 한다.
주요 발언 모음
“Claude가 무언가를 측정할 수 있게 되면, Claude는 그것을 더 빠르게 만들 수 있다.”
“측정 가능한 숫자를 갖게 되는 순간, 성능 개선은 다룰 수 있는 문제가 된다.”
“목표는 종착점이 아니다. 계속 낮추자. 다음은 무엇인가? 더 야심차게 하자.”
“느슨하거나 사용자 지연과 상관하지 않는 벤치마크라면, Claude가 잘못된 언덕을 오르게 두지 말고 버려라.”
“싫어하는 것을 자동화하기는 어렵고, 존중하지 않는 것을 이해하기도 어렵다.”
“코드만 읽는 것이 모든 문제의 해법이라고 생각하면, 별도의 문제와 훨씬 적은 해법을 얻게 된다.”
“슬롭을 다루는 방법과 슬롭을 중심으로 일하는 방법을 설계하는 일이 점점 새로운 엔지니어링의 핵심이 되고 있다.”
핵심 데이터 및 수치
- 활동 범위: 전체 사용자 활동의 95%를 차지하는 네 가지 여정.
- Claude 시작 p75: 3.1초 → 0.55초.
- Claude Code 새 세션: 0.8초 → 0.3초.
- Claude Cowork 세션 로딩: 약 3초 → 1초 미만.
- Claude.ai 실행: 약 3초 → 550ms.
- Claude Desktop 실행: 6초 이상 → 3초 조금 초과.
- Cowork 메시지 전송: 약 1초 → 48ms.
- Claude Code Desktop 메시지 전송: 250ms → 52ms.
- 스프린트 달성: 13개 목표 중 12개를 3일째 달성.
- 변경 규모: 3,000개 이상의 변경을 고객 대상 사고나 롤백 없이 병합했다고 Anthropic이 설명.
- 메시지 트리·상태 스캐너: 명령어 수 48%·31% 감소, 벽시계 시간 78%·44% 감소.
- 사이드바 레이아웃: 사용 가능 상태 이후 이동한 웹 로드 31%.
- CLS 사례: 개별 이동 약 0.008로 일반 기준 0.1보다 낮아 기존 모니터가 놓침.
- React 컴포저: 약 6,900개 훅, 900개 스토어 구독.
- CSS 비용: 루트 셀렉터 하나가 DOM 변경마다 24ms 추가.
- 숨은 리로드: 하루 약 50만 회.
- IndexedDB: 동일한 캐시 스냅샷을 2분마다 메인 스레드에서 두 번 복제.
- 플래그: 약 200개 도입, 절반 이상 스프린트 종료 전 제거.
- 정적 컴포저 검증: 14개 뷰포트, 1px 이내 정렬, 키 유실·재정렬 검사.
- 120Hz 최적화: 프레임 예산 8.33ms, 240프레임 결정론적 평가, 긴 답변 메인 스레드 차단 750ms 이상 → 약 200ms, CPU 약 3분의 1, 120fps 유지.
- T3 Code 병렬 실험: 약 40개 PR 자동 병합, 38개가 실질적인 성능 향상.
- Lakebed 실험: Rusty V8 기반 자체 런타임으로 약 2~4배 성능 향상.
결론 및 시사점
- 성능 최적화의 첫 단계는 코드 수정이 아니라 측정 설계다. 사용자의 상호작용부터 실제 결과 렌더링까지를 재고 클라이언트·서버 시간을 분리해야 한다.
- 에이전트에게 수치 하나만 주면 잘못된 목표를 최적화할 수 있다. 요청 수, React 커밋, 함수 호출 같은 지표는 사용자 체감과 상관관계를 검증한 뒤 사용해야 한다.
- 좋은 벤치마크는 최적화 목표이자 CI 가드레일이다. 개선된 기준선을 자동으로 낮추는 ratchet이 있으면 이후 변경이 성능을 되돌리기 어렵다.
- 벽시계 시간과 결정론적 내부 지표를 함께 봐야 한다. CPU 명령어 수·React 커밋·DOM 변경 수만으로는 부족하고, 실제 사용자 지연과 현장 데이터로 최종 검증해야 한다.
- 캐시는 초기 체감을 크게 높이지만 무효화가 틀리면 신뢰성을 망친다. 캐시 데이터를 즉시 보여 주되 오래된 항목을 어떻게 표시하고 언제 서버와 재검증할지 설계해야 한다.
- 레이아웃 안정성은 평균 지표만으로 놓칠 수 있다. 작은 CLS가 여러 번 누적되거나 특정 환경에서만 발생하는 이동을 직접 감지하는 테스트가 필요하다.
- 과감한 에이전트 운영은 안전장치와 함께해야 한다. 사람 승인, 단위 테스트, 기능 플래그, 1% 롤아웃, kill switch, 회귀 벤치마크가 있어야 “더 야심차게”라는 지시가 생산적인 탐색이 된다.
- 사람은 취향·위험·우선순위를 판단하고 에이전트는 반복 작업을 확장하는 편이 효율적이다. 표를 언제 보여 줄지, 스켈레톤을 어떻게 보여 줄지, 2ms가 복잡성의 대가를 치를 가치가 있는지는 숫자만으로 결정되지 않는다.
- 한 번 검증한 루프는 병렬화할 수 있다. 하나의 성능 문제를 추적하는 자동화가 완성되면 여러 스레드와 머신에서 수십 개의 가설을 동시에 검증할 수 있다.
- “슬롭”은 목적지가 아니라 탐색 재료다. 에이전트가 만든 테스트와 도구가 완벽하지 않아도 회귀를 발견하는 신호가 될 수 있지만, 제품 코드와 지표를 무비판적으로 자동 병합해서는 안 된다.
- 새로운 엔지니어링 역량은 코드를 전부 읽는 데만 있지 않다. 에이전트가 무엇을 측정하고 어떤 가정을 했는지 읽고, 실제 사용자 흐름과 연결되지 않는 최적화를 반박하는 판단력이 중요하다.
- Anthropic의 핵심 교훈은 “측정할 수 있게 만들면 에이전트가 개선할 수 있다”는 것이다. 측정 설계, 검증, 안전한 배포, 인간의 취향 판단을 하나의 반복 루프로 묶을 때 Claude는 단순 코드 생성기를 넘어 지속적인 성능 엔지니어로 작동한다.
