Claude Code로 느린 Node.js 고치기: 추측 전에 병목부터 측정
Claude Code, 타이머, 사용 사례, 함정, 검증으로 Node.js 병목을 측정하고 안전하게 고칩니다.
Node.js 보고서 API가 느리고 다음 배포 시간이 가까워졌을 때, 회의에서 가장 먼저 나오는 말은 보통 “인덱스 추가하자”입니다. 맞을 수도 있지만, 어디에서 시간이 쓰였는지 측정하지 않았다면 아직 추측입니다. 느린 코드는 복잡한 곳보다 의외로 평범한 한 줄에서 시간을 잃는 경우가 많습니다.
이 글은 Claude Code를 예언자가 아니라 코딩 보조자로 사용합니다. 병목은 전체 요청 속도를 제한하는 가장 느린 구간입니다. 프로파일링은 실행 시간을 측정해 그 병목을 보이게 만드는 작업입니다. 안전한 순서는 작은 타이머 추가, 합성 데이터로 재현 가능한 명령 실행, 가장 느린 한 구간 수정, 같은 명령으로 재확인입니다.
범위는 서버 쪽 Node.js입니다. API 핸들러, 배치 작업, CSV 내보내기, 데이터 처리 스크립트가 대상입니다. 브라우저 렌더링이나 이미지 로딩 문제는 Core Web Vitals 성능 가이드를 보세요. 시간이 SQL 안에서 사라진다면 SQL 최적화 가이드로 넘어가는 편이 낫습니다.
먼저 할 일
가장 작은 작업 단위에서 시작하세요. 하나의 라우트, 하나의 스크립트, 하나의 fixture, 하나의 CSV 내보내기 명령이면 충분합니다. 느림을 보여주는 명령이 없는데 Claude Code에게 “프로젝트를 빠르게 해줘”라고 맡기면 읽는 파일이 늘고 검토할 증거는 흐려집니다.
용어도 먼저 맞춥니다. N+1 문제는 목록을 한 번 가져온 뒤 각 항목마다 관련 데이터를 다시 가져오는 패턴입니다. 직렬 await는 서로 독립인 비동기 작업을 순서대로 기다리게 만드는 코드입니다. 메모이제이션은 같은 입력의 결과를 다시 계산하지 않고 재사용하는 방식입니다.
측정 경계도 적어야 합니다. 로컬 API 한 번이 느린지, CI 배치가 느린지, 100행 CSV가 느린지, 10만 행 파일 쓰기가 느린지에 따라 볼 구간이 달라집니다. 경계가 없으면 Claude Code가 라우트, 미들웨어, 모델, 로그, 프런트 호출을 모두 뒤져서 서로 비교할 수 없는 제안을 내놓기 쉽습니다.
첫 프롬프트는 측정과 권한을 분리해야 합니다.
claude -p "src/api/report.ts is slow. Do not rewrite it yet.
Add timing around database fetch, API fetch, transform, and response formatting.
Use synthetic fixture data only. Return the changed files, command to run, and the slowest measured section.
Do not change production settings, billing limits, credentials, or customer-data handling without human approval."
복사해서 쓰는 측정 코드
첫 변경은 단순해야 합니다. 서비스를 다시 설계하지 말고 시간을 출력하세요. Node.js는 공식 node:perf_hooks 문서에 있는 performance.now()를 제공하므로 패키지를 추가할 필요가 없습니다.
테스트 브랜치나 임시 폴더에 measured-report.mjs를 만들고 Node로 실행합니다. 예시는 합성 데이터만 사용합니다. 비밀값, 토큰, 운영 URL, 고객 행은 들어가지 않습니다.
import { performance } from "node:perf_hooks";
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
function createTimer() {
const records = [];
return {
async measure(label, fn) {
const start = performance.now();
const value = await fn();
records.push({ label, ms: Number((performance.now() - start).toFixed(1)) });
return value;
},
report() {
return records.sort((a, b) => b.ms - a.ms);
},
};
}
async function fetchUsers() {
await wait(60);
return Array.from({ length: 6 }, (_, index) => ({ id: index + 1 }));
}
async function fetchOrdersOneByOne(users) {
const orders = [];
for (const user of users) {
await wait(45);
orders.push({ userId: user.id, total: user.id * 10 });
}
return orders;
}
async function fetchOrdersInBatch(users) {
await wait(55);
return users.map((user) => ({ userId: user.id, total: user.id * 10 }));
}
function buildReport(users, orders) {
const ordersByUser = new Map(orders.map((order) => [order.userId, order]));
return users.map((user) => ({ ...user, orderTotal: ordersByUser.get(user.id)?.total ?? 0 }));
}
async function run(fetchOrders) {
const timer = createTimer();
const users = await timer.measure("users", fetchUsers);
const orders = await timer.measure("orders", () => fetchOrders(users));
await timer.measure("report", () => buildReport(users, orders));
console.table(timer.report());
}
console.log("slow version");
await run(fetchOrdersOneByOne);
console.log("batch version");
await run(fetchOrdersInBatch);
실행 명령은 다음과 같습니다.
node measured-report.mjs
node --prof measured-report.mjs
node --prof-process isolate-*.log > profile.txt
node --prof는 공식 Node.js CLI 참고 문서에 있는 내장 프로파일러입니다. 단순 타이머로 CPU 병목이 보이지 않을 때 사용하세요. 많은 API와 배치 작업은 작은 시간표만으로도 첫 수정 대상을 찾을 수 있습니다.
세 가지 사용 사례
사례 1: 느린 API 응답
입력: API 핸들러, 라우트 테스트, 합성 요청 payload입니다. Claude Code는 데이터베이스 읽기, 외부 호출, 변환, 직렬화 주변에 타이머를 추가할 수 있습니다. 운영 자격 증명, 인증, 트래픽 제한, 고객 데이터 보존을 바꾸는 일은 사람이 승인해야 합니다.
출력: 짧은 시간표와 가장 느린 구간만 고치는 패치입니다. 느린 구간이 쿼리라면 임의로 인덱스를 추가하기 전에 SQL 체크리스트로 이동합니다. JSON 포맷이 느리다면 애플리케이션 코드 안에서 고칩니다.
사례 2: 배치 CSV 내보내기
입력: 가짜 행이 들어 있는 로컬 fixture와 CSV를 만드는 명령입니다. Claude Code는 행 로딩, 조인, 포맷, 파일 쓰기를 측정할 수 있습니다. fixture에서 운영 내보내기로 넘어가는 순간에는 고객 데이터와 계산 비용이 얽힐 수 있으므로 사람이 승인해야 합니다.
출력: 문제가 N+1 읽기인지, 직렬 호출인지, 반복 포맷인지 보여주는 증거입니다. 일반적인 수정은 배치 처리, 동시 실행 제한, 포맷터 메모이제이션입니다. 전체 작업을 새로 쓰는 것이 첫 단계는 아닙니다.
사례 3: 배포 전 풀 리퀘스트 리뷰
입력: 풀 리퀘스트 diff, 기존 성능 테스트, 기대 응답 예산입니다. Claude Code는 diff를 읽고 추가 측정을 제안할 수 있습니다. 배포 결정, 롤백 계획, 고객에게 말할 SLA 주장은 사람이 승인해야 합니다.
출력: 측정된 위험, 실행한 명령, 아직 측정하지 못한 영역을 적은 리뷰 메모입니다. “빨라 보인다”는 말보다 훨씬 검토하기 쉽습니다.
측정 뒤의 수정 패턴
| 측정된 증상 | 가능한 원인 | 안전한 첫 수정 |
|---|---|---|
| 비슷한 쿼리나 API 호출이 많음 | N+1 | ID를 묶어서 읽거나 관계를 한 번에 로드 |
| 독립 호출 시간이 서로 더해짐 | 직렬 await | 독립 작업에만 Promise.all 사용 |
| 같은 변환이 반복됨 | 불필요한 재계산 | 입력별 캐시 또는 루프 밖 초기화 |
| 행 수가 늘 때 시간이 급증 | 중첩 탐색 | Map을 한 번 만들거나 쿼리 계획 확인 후 인덱스 검토 |
이 표는 측정을 대체하지 않습니다. 시간표를 가장 작은 패치로 바꾸기 위한 안내입니다. 수정이 보안, 운영 트래픽, 과금, 고객 데이터에 닿으면 fixture 밖에서 실행하기 전에 멈추고 승인을 받아야 합니다.
함정과 수정
함정: 명령 없이 Claude Code에게 “이 endpoint를 최적화해줘”라고 맡기는 일입니다. 수정: 재현 명령, fixture, 시간표를 먼저 요구하고 그다음에만 rewrite를 허용합니다.
함정: 합성 데이터 결과를 운영 benchmark로 취급하는 일입니다. 수정: 로컬 데이터는 합성이라고 표시하고 가능한 병목 찾기에만 씁니다. 운영 결론은 자신의 시스템에서 승인된 관측 데이터가 필요합니다.
함정: 직렬 루프를 제한 없는 Promise.all로 바꾸는 일입니다. 수정: 작업이 독립인지 확인하고, 데이터베이스나 유료 API, rate limit 서비스에는 동시 실행 상한을 둡니다.
함정: 느린 경로에 “데이터베이스”가 있다는 이유만으로 인덱스를 추가하는 일입니다. 수정: 쿼리 계획을 보고 읽기 이득, 쓰기 비용, 저장 비용을 함께 적습니다.
Claude Code 범위와 사람 승인
Claude Code에는 반복 가능한 작업을 맡기세요. 임시 타이머 추가, 합성 fixture 작성, 루프 안 쿼리 검색, 배치 처리 제안, 테스트 수정, 수정 전후 증거 요약은 되돌리기 쉽고 리뷰하기 쉽습니다.
사람 승인이 필요한 영역은 운영과 비즈니스 위험입니다. 인증, 고객 기록, 외부 서비스로 데이터 전송, 유료 API 동시성 증가, 과금 제한 변경, 운영 배포, SLA 주장은 사람이 결정해야 합니다. 에이전트는 diff와 체크리스트를 준비하고, 위험 수용은 사람이 합니다.
명확한 인수 문장은 이렇게 쓸 수 있습니다. “Claude는 로컬 측정과 테스트를 수정할 수 있다. 운영 데이터 사용, secret 변경, 과금 제어 변경, 배포 전에는 사람 승인이 필요하다.”
기본 CTA
이 흐름을 팀의 공통 습관으로 만들고 싶다면 Claude Code 교육 및 상담 페이지를 사용할 수 있습니다. 목표는 마법 같은 속도 약속이 아니라 명령, fixture, 측정된 병목, 패치, 남은 위험을 남기는 반복 가능한 리뷰입니다.
직접 확인한 내용
이번 업데이트에서는 내부 링크가 /ko/를 쓰는지, 외부 링크가 Node.js 공식 문서인지, 코드 블록이 실행 가능한 JavaScript와 명령 형태인지, 예제가 합성 데이터만 쓰는지, CTA가 한국어 교육 페이지로 향하는지 확인했습니다.
운영 benchmark를 실행하지 않았고, 실제 고객 사고를 보지 않았으며, 살아 있는 서비스가 빨라졌다고 주장하지 않았습니다. 직접 확인한 범위는 글 내용, 코드 블록의 문법 형태, 링크의 로케일, Claude Code가 할 일과 사람이 승인할 일을 나눈 부분입니다.
자기 프로젝트에서 다시 확인할 때는 fixture에 고객 정보가 없는지, 같은 명령을 반복 실행할 수 있는지, 수정 전후 입력이 같은지 먼저 보세요. 셋 중 하나라도 빠지면 release note나 고객 보고서에 속도 결론을 쓰지 말고, 입력과 명령부터 고정하는 편이 안전합니다.
관련 글
Claude Code 프롬프트 라이브러리 운영: 팀 지시를 자산으로 만드는 법
Claude Code 팀을 위해 프롬프트 버전, 소유자, 리뷰 게이트, 폐기, 지표, 교육 CTA를 정리합니다.
Claude Code 위험한 프롬프트 피하기: 자동 push와 테스트 생략 막기
위험한 Claude Code 지시를 권한 경계, 리뷰 절차, 체크리스트가 있는 안전한 프롬프트로 바꿉니다.
Claude Code/API 비용 관리 가이드: 토큰 예산, 알림, 상한 설정
Claude Code/API 비용을 예측하고 토큰 사용량을 기록하며 알림과 상한을 설정하는 실전 예제.
무료 PDF: Claude Code 치트시트
이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.
개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.
작성자 소개
Masa
Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.