Advanced (업데이트: 2026. 6. 2.)

Claude Code 성능 최적화: 측정부터 Core Web Vitals 개선까지

Claude Code로 LCP, INP, API 지연, 번들, 캐시를 측정하고 개선하는 실전 가이드.

Claude Code 성능 최적화: 측정부터 Core Web Vitals 개선까지

웹 앱 성능 최적화는 막연히 “빠르게 만드는” 작업이 아닙니다. 사용자가 첫 화면을 기다리는 시간, 클릭 후 반응이 늦는 시간, 이미지나 광고 때문에 레이아웃이 흔들리는 문제, 서버와 데이터베이스가 낭비하는 비용을 숫자로 보고 줄이는 작업입니다.

Claude Code는 코드 조사, 병목 추적, 작은 패치 제안, 검증 절차 작성에 강합니다. 하지만 순서가 중요합니다. 먼저 측정하고, 가설을 세우고, 한 가지를 고친 뒤, 같은 방법으로 다시 측정해야 합니다. “성능을 최적화해줘”라고만 하면 그럴듯하지만 증명되지 않은 수정이 나오기 쉽습니다.

공식 기준은 web.dev Core Web Vitals, Lighthouse docs, MDN Performance API를 확인하세요.

빠르다는 뜻부터 나누기

성능은 하나의 점수가 아닙니다. LCP(Largest Contentful Paint)는 주요 콘텐츠가 보이는 시간입니다. INP(Interaction to Next Paint)는 클릭, 탭, 입력 뒤 화면이 반응하기까지의 무게입니다. CLS(Cumulative Layout Shift)는 로딩 중 예상치 못한 레이아웃 이동입니다.

앱에서는 API p75 지연, DB 쿼리 수, JavaScript 번들 크기, 이미지 전송량, 브라우저 메인 스레드의 동기 작업도 봐야 합니다. 평균값만 보면 느린 사용자가 숨습니다. p75는 정상 트래픽 중 느린 쪽 경험을 보기 좋은 지표입니다.

flowchart LR
  Measure["측정"] --> Hypothesis["가설 수립"]
  Hypothesis --> Patch["작게 수정"]
  Patch --> Verify["다시 측정"]
  Verify --> Keep["효과 있는 변경만 유지"]
지표의미첫 목표
LCP주요 콘텐츠 표시 시간2.5초 이하를 목표
INP상호작용 반응성200ms 이하를 목표
CLS예기치 않은 레이아웃 이동0.1 이하를 목표
API p75많은 사용자가 체감하는 API 지연화면별로 목표 설정
JS 전송량초기 로드 JavaScript라우트별 차이 비교

Claude Code에 줄 입력

좋은 요청에는 증상, 재현 방법, 현재 수치, 제한 조건이 들어갑니다.

이 저장소의 /products 페이지가 느립니다.
목표는 모바일 LCP와 /api/products p75를 낮추는 것입니다.

현재 측정:
- Lighthouse mobile Performance: 58
- LCP: 4.2s
- INP: 180ms
- CLS: 0.04
- /api/products p75: 920ms

요청:
1. 이미지, 번들, API, 데이터베이스 접근 순서로 조사하세요.
2. 추측만으로 코드를 바꾸지 말고 근거 파일을 적어주세요.
3. 영향 범위가 작고 측정 가능한 수정을 우선하세요.
4. 변경 후 실행할 확인 명령도 제안하세요.

관련 글로는 Claude Code 디버깅 가이드, Claude Code로 React 개발 가속하기, Claude Code 이미지 최적화 가이드를 함께 보면 좋습니다.

실행 가능한 측정 스크립트

Node.js 18 이상에서 페이지나 API의 평균 지연과 p75를 측정할 수 있습니다.

// measure-url.mjs
import { performance } from "node:perf_hooks";

const url = process.argv[2] ?? "https://example.com/";
const runs = Number(process.argv[3] ?? 5);

async function measureOnce() {
  const start = performance.now();
  const res = await fetch(url, { cache: "no-store" });
  await res.arrayBuffer();
  return {
    status: res.status,
    ms: performance.now() - start,
  };
}

const samples = [];
for (let i = 0; i < runs; i += 1) {
  samples.push(await measureOnce());
}

samples.sort((a, b) => a.ms - b.ms);
const avg = samples.reduce((sum, s) => sum + s.ms, 0) / samples.length;
const p75Index = Math.floor((samples.length - 1) * 0.75);
const p75 = samples[p75Index].ms;

console.table(
  samples.map((sample, index) => ({
    run: index + 1,
    status: sample.status,
    ms: sample.ms.toFixed(1),
  }))
);
console.log(`avg=${avg.toFixed(1)}ms p75=${p75.toFixed(1)}ms`);
node measure-url.mjs http://localhost:3000/api/products 7

이 결과를 수정 전후에 Claude Code와 공유하면 “빨라진 것 같다”가 아니라 반복 가능한 비교가 됩니다.

현실적인 사용 사례

사례흔한 원인Claude Code에 맡길 일
SaaS 대시보드 첫 화면이 느림모든 위젯이 처음에 요청되고 차트 번들이 큼중요하지 않은 위젯 지연 로드, 차트 코드 분리
쇼핑몰 상품 목록이 느림이미지가 크고 재고/리뷰가 N+1 쿼리이미지 크기와 include/select 점검
미디어 글의 LCP가 나쁨대표 이미지가 늦고 광고 스크립트가 먼저 실행대표 이미지 우선, 서드파티 스크립트 지연
관리자 검색이 멈춤큰 배열을 메인 스레드에서 동기 필터링복잡도 감소, 페이지네이션, 백그라운드 처리

N+1은 목록을 한 번 가져온 뒤 각 행마다 추가 쿼리가 실행되는 상태입니다. 캐시는 같은 결과를 잠시 재사용하는 방식이지만, 권한이나 개인정보가 있는 데이터는 공유 캐시에 넣으면 안 됩니다.

실행 가능한 캐시 예제

아래 Express 예제는 일부러 느린 API와 캐시된 API를 비교합니다. 운영에서는 Redis나 CDN을 쓰는 경우가 많지만, 원리를 보기에는 메모리 캐시가 간단합니다.

npm init -y
npm install express
node cached-api.mjs
// cached-api.mjs
import express from "express";
import { performance } from "node:perf_hooks";

const app = express();
const cache = new Map();
const ttlMs = 30_000;

async function loadProducts() {
  await new Promise((resolve) => setTimeout(resolve, 800));
  return [
    { id: 1, name: "Starter Plan", price: 1200 },
    { id: 2, name: "Pro Plan", price: 4800 },
  ];
}

async function cached(key, loader) {
  const now = Date.now();
  const hit = cache.get(key);
  if (hit && hit.expiresAt > now) return { data: hit.data, cache: "hit" };

  const data = await loader();
  cache.set(key, { data, expiresAt: now + ttlMs });
  return { data, cache: "miss" };
}

app.get("/api/products/raw", async (_req, res) => {
  const start = performance.now();
  const data = await loadProducts();
  res.json({ cache: "none", ms: performance.now() - start, data });
});

app.get("/api/products/cached", async (_req, res) => {
  const start = performance.now();
  const result = await cached("products", loadProducts);
  res.json({ ...result, ms: performance.now() - start });
});

app.listen(3000, () => {
  console.log("Open http://localhost:3000/api/products/raw");
});
node measure-url.mjs http://localhost:3000/api/products/raw 3
node measure-url.mjs http://localhost:3000/api/products/cached 3

캐시를 의뢰할 때는 “인기 상품만 30초 캐시”, “재고는 캐시하지 않음”, “사용자별 데이터는 userId를 키에 포함”처럼 범위를 명확히 적습니다.

실행 가능한 알고리즘 예제

네트워크만 병목은 아닙니다. 큰 배열을 매번 무겁게 처리하면 INP가 나빠집니다. 아래는 반복 includesSet으로 바꾼 예입니다.

// compare-lookup.mjs
import { performance } from "node:perf_hooks";

const a = Array.from({ length: 40_000 }, (_, i) => i);
const b = Array.from({ length: 40_000 }, (_, i) => i * 2);

function slowIntersection(left, right) {
  return left.filter((item) => right.includes(item));
}

function fastIntersection(left, right) {
  const rightSet = new Set(right);
  return left.filter((item) => rightSet.has(item));
}

function time(label, fn) {
  const start = performance.now();
  const result = fn();
  const ms = performance.now() - start;
  console.log(`${label}: ${ms.toFixed(1)}ms (${result.length} hits)`);
}

time("slow", () => slowIntersection(a, b));
time("fast", () => fastIntersection(a, b));
node compare-lookup.mjs

흔한 실패

Lighthouse 점수만 따라가면 안 됩니다. Lighthouse는 고정된 조건의 실험실 데이터이고, 실제 사용자는 다른 기기와 네트워크를 씁니다. Search Console, 실제 사용자 모니터링, 서버 로그와 함께 봐야 합니다.

캐시 규칙 없이 속도만 올리면 정확성이 깨집니다. 가격, 재고, 권한, 개인정보는 cache key, TTL, 무효화 정책이 필요합니다.

useMemo를 모든 곳에 넣는 것도 실패입니다. React 메모이제이션은 측정된 병목에만 써야 합니다.

모든 이미지를 우선 로드하면 LCP가 오히려 나빠질 수 있습니다. 첫 화면 이미지는 안정적인 크기와 우선순위가 필요하지만, 아래쪽 이미지는 보통 lazy가 맞습니다.

번들 분리도 측정 없이 하면 위험합니다. Claude Code 번들 분석Claude Code 코드 분할을 참고해 라우트별 효과를 확인하세요.

CLAUDE.md에 추가하기

## Performance rules
- Before optimizing, record the current metric and target metric.
- Prefer small, measurable changes over broad rewrites.
- Do not introduce shared cache for user-specific data.
- Avoid N+1 queries; use select/include or batching.
- Keep above-the-fold images sized and stable.
- After changes, report commands used for verification.

이 규칙은 Claude Code가 겉보기 수정이 아니라 증명 가능한 개선에 집중하게 합니다.

상담 CTA

ClaudeCodeLab은 Claude Code를 활용한 기존 웹 앱 성능 점검, Core Web Vitals 개선, API/DB 지연 분리, 실행 가능한 개선 문서 작성을 도울 수 있습니다. 첫 상담에는 대상 URL, 주요 화면, Lighthouse 결과, 느린 API 로그, 개선 희망 기한을 준비하면 좋습니다.

직접 확인한 결과

로컬에서 예제를 실행하면 의도적으로 800ms 지연한 /api/products/raw는 800ms대에 머물고, /api/products/cached는 두 번째 요청부터 몇 ms 수준으로 내려갑니다. compare-lookup.mjsSet 버전이 확실히 빠릅니다. 실제 프로젝트는 더 복잡하지만, 수정 전 p75를 재고 한 가지만 바꾼 뒤 같은 방식으로 다시 측정하는 흐름은 그대로 유효합니다.

#Claude Code #성능 최적화 #Core Web Vitals #프론트엔드 #캐시
무료

무료 PDF: Claude Code 치트시트

이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.

개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.

Masa

작성자 소개

Masa

Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.