측정 → 가설 → 최소 변경 → 재측정의 반복 루프로 Claude Code를 사용해 성능 조사를 진행하세요. 측정 가능한 기준과 작은 실험으로 회귀를 피하고 신뢰할 수 있는 개선을 만들어냅니다.

성능 버그는 추측을 초대합니다. 누군가 페이지가 느려 보인다고 하거나 API가 타임아웃 난다고 보고하면, 가장 빠른 대응은 코드를 "정리"하거나 캐시를 추가하거나 루프를 다시 쓰는 것입니다. 문제는 "느껴진다"는 수치가 아니고, "더 깔끔하다"가 곧 빠르다는 뜻이 아니라는 점입니다.
측정이 없으면 팀은 잘못된 것을 바꾸며 시간을 낭비합니다. 병목은 데이터베이스, 네트워크, 혹은 예상치 못한 단일 할당일 수 있는데 팀은 거의 실행되지 않는 부분을 닦고 있을지도 모릅니다. 더 나쁜 경우에는 멋져 보이는 변경이 성능을 악화시킬 수 있습니다: 촘촘한 루프에 로깅을 추가하거나 캐시가 메모리 압력을 높이고, 병렬 작업이 락 경쟁을 일으키는 등입니다.
추측은 동작을 망가뜨릴 위험도 있습니다. 속도를 내기 위해 코드를 바꾸면 결과, 오류 처리, 순서, 재시도 로직이 바뀔 수 있습니다. 속도와 정합성을 함께 다시 확인하지 않으면 벤치마크에서는 "이긴" 것처럼 보여도 버그를 몰래 배포할 수 있습니다.
성능을 논쟁이 아니라 실험으로 다루세요. 루프는 단순하고 반복 가능합니다:
많은 개선은 겸손합니다: p95 지연 8% 단축, 피크 메모리 50MB 감소, 데이터베이스 쿼리 하나 제거 등. 이런 개선들은 측정되고 검증되며 재현 가능할 때 의미가 있습니다.
이 방법은 일회성 "더 빠르게 해" 요청이 아니라 루프로 돌릴 때 가장 효과적입니다. 루프는 모든 행동이 증거와 관찰 가능한 숫자에 연결되기 때문에 정직하게 만듭니다.
명확한 순서:
각 단계는 다른 종류의 자기기만을 막아줍니다. 먼저 측정하면 "실제 문제"가 아니었던 것을 고치지 않게 합니다. 문서화된 가설은 한 번에 다섯 가지를 바꾸고 어느 것이 효과였는지 추측하는 일을 막습니다. 최소 변경은 동작을 깨뜨리거나 새로운 병목을 만드는 위험을 줄입니다. 재측정은(예: 캐시가 따뜻해져서 빨라진 것 같은) 플라시보 개선을 걸러내고 회귀를 드러냅니다.
"완료"는 느낌이 아닙니다. 결과입니다: 목표 메트릭이 올바른 방향으로 움직였고, 변경이 명백한 회귀(오류, 더 높은 메모리, 나쁜 p95 지연, 인근 엔드포인트의 속도 저하)를 유발하지 않았을 때입니다.
언제 멈출지 아는 것도 워크플로의 일부입니다. 개선이 평평해질 때, 메트릭이 사용자에게 충분히 좋을 때, 혹은 다음 아이디어가 작은 이득을 위해 대규모 리팩토를 요구할 때 멈추세요. 성능 작업에는 항상 기회비용이 있으므로 루프는 시간을 가치 있는 곳에 쓰게 도와줍니다.
한 번에 다섯 가지를 측정하면 무엇이 개선되었는지 알 수 없습니다. 이번 조사에서 하나의 주요 메트릭을 선택하고 나머지는 보조 신호로 취급하세요. 사용자 대상 문제에서는 보통 지연(latency)이 주요 메트릭입니다. 배치 작업이라면 처리량, CPU 시간, 메모리 사용, 또는 런당 클라우드 비용이 될 수 있습니다.
시나리오를 구체적으로 적으세요. "API가 느리다"는 너무 모호합니다. "일반적인 3개 상품을 담은 POST /checkout"은 측정 가능합니다. 입력을 안정적으로 유지해 숫자가 의미 있게 하세요.
코드를 건드리기 전에 기준값과 환경 세부사항(데이터셋 크기, 머신 타입, 빌드 모드, 기능 플래그, 동시성, 워밍업)을 적어두세요. 이 기준값이 닻(anchor)입니다. 없으면 모든 변경이 진전처럼 보일 수 있습니다.
지연 측정은 평균만 보지 말고 퍼센타일을 보세요. p50은 일반적인 경험을, p95/p99는 사용자가 고통을 느끼는 꼬리를 드러냅니다. p50이 개선되었지만 p99가 악화되면 여전히 느리게 느껴질 수 있습니다.
사전에 "의미 있는" 개선 기준을 정하세요:
이 규칙을 정하면 실험 중 목표를 바꾸지 않고 아이디어를 검증할 수 있습니다.
신뢰할 수 있는 가장 쉬운 신호부터 시작하세요. 요청 하나의 시간 측정만으로도 실제 문제가 있는지, 얼마나 큰지 알 수 있습니다. 왜 느린지 설명해야 할 때 깊은 프로파일링을 하세요.
좋은 증거는 보통 여러 소스의 조합에서 나옵니다:
"더 느린가, 얼마나 느린가"를 묻는다면 간단한 메트릭을 쓰고, "시간이 어디로 가나"를 묻는다면 프로파일링을 사용하세요. 배포 후 p95가 두 배가 됐다면 타이밍과 로그로 회귀를 확인하고 범위를 좁히세요. 애플리케이션 코드 내부에 문제가 많다면 CPU 프로파일러나 플레임 그래프가 정확한 함수를 가리킬 수 있습니다.
측정은 안전하게 유지하세요. 디버깅에 필요한 것만 수집하고 사용자 콘텐츠는 수집하지 마세요. 원시 페이로드보다 집계(지속시간, 카운트, 크기)를 선호하고 식별자는 기본적으로 마스킹하세요.
노이즈는 현실입니다. 여러 샘플을 취하고 이상치를 기록하세요. 같은 요청을 10~30번 실행하고 중앙값과 p95를 기록하세요. 한 번의 최고 기록으로 판단하지 마세요.
정확히 같은 테스트 레시피(환경, 데이터셋, 엔드포인트, 요청 바디 크기, 동시성 수준, 결과 캡처 방법)를 적어 두어 변경 후 재현 가능하게 하세요.
다음과 같이 명명 가능한 증상에서 시작하세요: "피크 트래픽 동안 p95 지연이 220ms에서 900ms로 뛴다", "CPU가 두 코어에서 95%에 머문다", "메모리가 시간당 200MB씩 증가한다". "느낌상 느리다" 같은 모호한 증상은 무작위 변경으로 이어집니다.
다음으로, 측정한 것을 의심 지점으로 번역하세요. 플레임 그래프가 대부분 시간을 JSON 인코딩에서 소비한다고 나오거나, 트레이스가 느린 호출 경로를 가리키거나, DB 통계가 한 쿼리가 전체 시간의 대부분을 차지하고 있을 수 있습니다. 비용의 대부분을 설명하는 가장 작은 영역(함수, 단일 SQL 쿼리, 외부 호출 하나)을 선택하세요.
좋은 가설은 한 문장이고 시험 가능하며 예측과 연결됩니다. 도구가 마법처럼 모든 걸 빠르게 해주길 바라기보다, 아이디어를 시험하는 것입니다.
다음 형식을 사용하세요:
예: "프로파일에서 SerializeResponse에 CPU의 38%가 집중되어 있어서 각 요청마다 새 버퍼를 할당하는 것이 CPU 스파이크를 유발한다. 버퍼를 재사용하면 같은 부하에서 p95 지연이 약 10–20% 감소하고 CPU가 15% 정도 낮아질 것이다."
변경하기 전에 대안 설명을 2~3개 써두세요. 느린 부분이 실제로는 업스트림 의존성, 락 경쟁, 캐시 미스율 변화, 또는 페이로드 크기 증가 때문일 수도 있습니다. 변경이 메트릭을 움직이지 않으면 다음 가설로 바로 전환할 준비가 되어 있게 됩니다.
Claude는 신중한 분석가처럼 다룰 때 성능 작업에서 가장 유용합니다. 모든 제안이 측정값에 묶여 있고 각 단계가 틀릴 수 있음이 증명 가능해야 합니다.
모호한 설명이 아니라 실제 입력을 주세요. 작은, 집중된 증거를 붙여넣으세요: 프로파일 요약, 느린 요청 주변 로그 몇 줄, 쿼리 플랜, 특정 코드 경로. 기준값(예: p95 지연, CPU 시간, DB 시간)을 포함해 현재 상태를 알려주세요.
데이터가 무엇을 시사하고 무엇을 시사하지 않는지 설명해 달라고 요청하세요. 경쟁 가설을 강제로 요구하세요. 유용한 프롬프트 끝에는: "2–3개의 가설을 주고, 각 가설을 반증할 방법을 알려줘."를 넣으세요. 이렇게 하면 첫 번째 그럴듯한 설명에 집착하는 일을 막을 수 있습니다.
코드를 건드리기 전에 상위 가설을 검증할 수 있는 가장 작은 실험을 요청하세요. 빠르고 되돌릴 수 있게: 함수 앞뒤에 타이머 추가, 프로파일러 플래그 하나 켜기, EXPLAIN으로 쿼리 실행 등.
출력에 구조를 원하면 다음을 요구하세요:
특정 메트릭, 위치, 기대 결과를 말하지 못하면 다시 추측하는 셈입니다.
증거와 가설이 있으면 모든 것을 "정리"하고 싶은 유혹을 참으세요. 성능 작업은 변경이 작고 쉽게 되돌릴 수 있을 때 가장 신뢰할 수 있습니다.
한 번에 한 가지를 바꾸세요. 쿼리 튜닝, 캐싱 추가, 루프 리팩터를 같은 커밋에 섞으면 어떤 것이 효과였는지 알 수 없습니다. 단일 변수 변경이 다음 측정을 의미 있게 만듭니다.
코드를 건드리기 전에 숫자로 기대치를 적어두세요. 예: "p95가 420ms에서 300ms 미만으로 떨어지고 DB 시간이 약 100ms 줄 것". 목표에 못 미치면 가설이 약했거나 불완전하다는 것을 빨리 배웁니다.
변경을 되돌리기 쉽게 만드세요:
"최소"가 "사소하다"는 뜻은 아닙니다. 초점을 좁혀 한 번에 하나의 문제만 해결하세요: 비싼 함수 결과 하나 캐시하기, 촘촘한 루프에서 반복 할당 제거하기, 필요 없는 요청 작업을 중단하기 등.
의심되는 병목 주위에 가벼운 타이밍을 추가해 무엇이 움직였는지 확인하세요. 호출 전후의 타임스탬프 하나(로그나 메트릭으로 캡처)면 변경이 느린 부분을 건드렸는지 아니면 시간을 다른 곳으로 이동시켰는지 확인할 수 있습니다.
변경 후 기준값에 사용한 정확한 시나리오(같은 입력, 환경, 부하 형태)를 다시 실행하세요. 테스트가 캐시나 워밍업에 의존하면 그것을 명시하세요(예: "첫 실행은 콜드, 다음 5회는 웜"). 그렇지 않으면 우연한 개선을 발견하게 됩니다.
같은 메트릭과 동일한 퍼센타일로 결과를 비교하세요. 평균은 고통을 숨길 수 있으니 p95와 p99, 처리량과 CPU 시간을 주의 깊게 보세요. 숫자가 안정될 때까지 충분한 반복을 실행하세요.
축하하기 전에 한 번 더 회귀를 체크하세요:
그 다음 증거 기준으로 결정하세요. 개선이 실제이고 회귀가 없다면 배포하세요. 결과가 섞여 있거나 노이즈가 심하면 되돌리고 새 가설을 세우거나 변경을 더 좁히세요.
플랫폼(예: Koder.ai)에서 작업 중이라면 실험 전에 스냅샷을 찍어 롤백을 한 단계로 만들면 대담한 아이디어를 안전하게 테스트하기 쉽습니다.
마지막으로 배운 내용을 적어두세요: 기준값, 변경 내용, 새 숫자, 결론. 이 짧은 기록이 다음 라운드에서 같은 골목을 반복하지 않게 해줍니다.
성능 작업이 빗나가는 이유는 측정한 것과 변경한 것 사이의 연속성을 잃을 때입니다. 무엇이 더 나아지거나 나빠졌는지 자신 있게 말할 수 있도록 증거의 흐름을 깔끔하게 유지하세요.
자주 저지르는 실수들:
작은 예: 엔드포인트가 느려 보여 직렬화기를 튜닝했더니 테스트 데이터셋이 작아져 빨라졌다고 착각한다. 프로덕션 p99는 여전히 악화되는데 DB가 병목이고 변경이 페이로드 크기를 늘려 꼬리를 더 악화시켰을 수 있습니다.
Claude Code가 수정안을 제안한다면 짧게 통제하세요. 수집한 증거와 일치하는 1~2개의 최소 변경을 요구하고, 패치를 수용하기 전에 재측정 계획을 반드시 요구하세요.
테스트가 모호하면 속도 주장은 무너집니다. 축하하기 전에 무엇을 측정했고 어떻게 측정했는지, 무엇을 변경했는지 설명할 수 있어야 합니다.
하나의 메트릭을 정하고 기준값을 기록하는 것부터 시작하세요. 숫자를 바꿀 수 있는 세부사항(머신 타입, CPU 부하, 데이터 크기, 빌드 모드, 기능 플래그, 캐시 상태, 동시성)을 포함하세요. 내일 같은 설정을 재현할 수 없다면 기준값이 없는 것입니다.
체크리스트:
숫자가 좋아 보이면 빠른 회귀 점검을 하세요. 정합성, 오류율, 타임아웃, 메모리나 CPU 스파이크, 스타트업 지연, DB 부하 증가 같은 부수 효과를 확인하세요. p95를 개선했지만 메모리를 두 배로 늘렸다면 잘못된 거래일 수 있습니다.
팀이 GET /orders가 개발 환경에서는 괜찮지만 스테이징에서 중간 부하가 걸리면 느려진다고 보고했습니다. 사용자는 타임아웃을 보고하지만 평균 지연은 여전히 "괜찮아 보이는" 전형적인 함정입니다.
먼저 기준값을 정하세요. 동일한 데이터셋과 동시성, 같은 지속 시간의 부하 테스트에서 기록한 값:
증거를 수집하세요. 빠른 트레이스는 엔드포인트가 주문을 가져오는 메인 쿼리를 실행한 뒤 각 주문별로 관련 아이템을 루프 돌며 가져온다는 것을 보여주었습니다. JSON 응답이 크지만 DB 시간이 더 지배적입니다.
이를 시험할 가설 목록으로 바꾸세요:
가장 강한 증거에 맞는 최소 변경을 요청하세요: 주문 ID별로 아이템을 한 번에 가져오게 하여 명백한 N+1 호출을 제거(또는 느린 쿼리 플랜이 전체 스캔이면 누락된 인덱스 추가)합니다. 되돌리기 쉬운 집중된 커밋으로 유지하세요.
같은 부하 테스트로 재측정한 결과:
결정: 수정사항을 배포(명백한 개선). 그다음 DB가 더 이상 주요 제약이 아니므로 남은 격차와 CPU 스파이크에 초점을 맞춘 두 번째 사이클을 시작합니다.
성능 조사를 더 빠르게 잘하려면 각 실행을 반복 가능한 작은 실험처럼 다루세요. 프로세스가 일관되면 결과를 신뢰하고 비교하고 공유하기 쉬워집니다.
간단한 한 페이지 템플릿이 도움이 됩니다:
이 노트들이 사라지지 않을 장소를 정하세요. 완벽한 도구보다 공유 가능한 장소가 중요합니다: 서비스 옆의 리포지토리 폴더, 팀 문서, 티켓 노트 등. 누군가는 수개월 뒤에 "캐시 변경 후 p95 스파이크"를 찾아볼 수 있어야 합니다.
안전한 실험을 습관으로 만드세요. 스냅샷과 쉬운 롤백을 사용하면 아이디어를 두려움 없이 시도할 수 있습니다. Koder.ai로 개발 중이라면 Planning Mode는 측정 계획을 정리하고 가설을 정의하며 변경 범위를 제한하기 좋은 장소입니다.
주기를 정하세요. 사고가 날 때까지 기다리지 말고 쿼리 추가, 엔드포인트 추가, 페이로드 증가, 의존성 업그레이드 후 작은 성능 체크를 추가하세요. 지금 10분짜리 기준값 확인이 나중에 하루의 추측을 줄여줄 수 있습니다.
하나의 불만에 맞는 숫자부터 시작하세요. 보통 특정 엔드포인트와 입력에 대한 p95 지연 시간입니다. 동일한 조건(데이터 크기, 동시성, 캐시 상태)에서 기준값을 기록한 뒤 한 가지를 변경하고 다시 측정하세요.
기준값을 재현할 수 없다면 아직 측정하는 단계가 아니라 추측하는 단계입니다.
유용한 기준값에는 다음이 포함됩니다:
코드를 건드리기 전에 적어두면 목표를 바꾸지 않게 됩니다.
퍼센타일은 평균보다 사용자 경험을 더 잘 보여줍니다. p50은 "전형적"인 값을 보여주지만, 사용자는 느린 꼬리(= p95/p99)에 불만을 제기합니다.
p50이 좋아졌는데 p99가 나빠지면 평균은 좋아 보여도 체감 성능은 느려질 수 있습니다.
간단한 타이밍/로그는 **"느려졌나, 어느 정도인가"**를 답할 때 사용하세요. 프로파일러는 **"시간이 어디로 가나"**를 물을 때 사용합니다.
실용적인 흐름은: 요청 타이밍으로 회귀를 확인한 뒤, 지연이 실제이고 범위가 정해졌을 때 프로파일링을 합니다.
주요 메트릭 하나를 정하고 나머지는 가드레일로 취급하세요. 일반적인 세트는:
이렇게 하면 한 차트에서 이기면서도 타임아웃이나 메모리 증가 같은 문제를 몰래 만들어내는 일을 피할 수 있습니다.
증거와 예측에 묶인 한 문장형 가설을 쓰세요:
증거와 기대하는 메트릭 변화가 없다면 가설은 시험 가능하지 않습니다.
작고 되돌릴 수 있게 만드세요:
작은 diff는 다음 측정을 의미 있게 만들고 동작을 깨뜨릴 위험을 줄입니다.
동일한 테스트 레시피(같은 입력, 환경, 부하)를 다시 실행하세요. 캐시나 워밍업이 결과에 영향을 준다면 그 규칙을 명시하세요(예: "첫 실행은 콜드, 이후 5번은 웜"). 그렇지 않으면 우연한 개선을 발견하게 됩니다.
다음 항목들을 체크하세요:
결과가 섞여 있거나 노이즈가 많으면 되돌리고 새 가설을 세우거나 변경을 더 좁히세요.
구체적인 증거를 주고 테스트 중심으로 유지하세요:
출력이 특정 메트릭과 재시험 계획을 포함하지 않으면 다시 추측 단계로 돌아가는 것입니다.
다음 경우에 멈추세요:
성능 작업은 기회비용이 있으므로 숫자가 의미할 때만 시간을 쓰는 것이 목표입니다.