웹 성능 최적화의 첫 단계는 "측정"입니다. Performance API는 페이지 로딩 시간, 리소스 로딩, 사용자 정의 마크/측정을 정밀하게 기록하는 브라우저 내장 API입니다.

performance.now() — 고정밀 시간 측정

JS
const start = performance.now();

// 측정할 작업
heavyCalculation();

const end = performance.now();
console.log(`실행 시간: ${(end - start).toFixed(2)}ms`);
// Date.now()보다 정밀 (마이크로초 단위)

Mark와 Measure — 코드 구간 측정

JS
// 마크 찍기
performance.mark("fetch-start");
const data = await fetch("/api/data").then((r) => r.json());
performance.mark("fetch-end");

// 구간 측정
performance.measure("fetch-duration", "fetch-start", "fetch-end");

// 결과 확인
const measures = performance.getEntriesByName("fetch-duration");
console.log(`API 호출: ${measures[0].duration.toFixed(2)}ms`);

// 정리
performance.clearMarks();
performance.clearMeasures();

Navigation Timing — 페이지 로드 성능

JS
const timing = performance.getEntriesByType("navigation")[0];

// 주요 지표
console.log(`DNS 조회: ${timing.domainLookupEnd - timing.domainLookupStart}ms`);
console.log(`TCP 연결: ${timing.connectEnd - timing.connectStart}ms`);
console.log(`TTFB: ${timing.responseStart - timing.requestStart}ms`);
console.log(`DOM 파싱: ${timing.domContentLoadedEventEnd - timing.responseEnd}ms`);
console.log(`전체 로드: ${timing.loadEventEnd - timing.startTime}ms`);
console.log(`DOM Interactive: ${timing.domInteractive}ms`);

Resource Timing — 리소스별 로딩 시간

JS
const resources = performance.getEntriesByType("resource");

resources.forEach((entry) => {
  console.log(`${entry.name}: ${entry.duration.toFixed(2)}ms (${entry.initiatorType})`);
});

// 타입별 분류
const scripts = resources.filter((r) => r.initiatorType === "script");
const images = resources.filter((r) => r.initiatorType === "img");
const styles = resources.filter((r) => r.initiatorType === "link");

// 가장 느린 리소스 찾기
const slowest = resources.sort((a, b) => b.duration - a.duration)[0];
console.log(`가장 느린 리소스: ${slowest.name} (${slowest.duration}ms)`);

PerformanceObserver — 성능 이벤트 실시간 감시

JS
// 새로운 성능 엔트리를 실시간으로 관찰
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(`[${entry.entryType}] ${entry.name}: ${entry.duration}ms`);
  }
});

// 관찰할 타입 지정
observer.observe({ entryTypes: ["measure", "resource", "longtask"] });

// Long Task 감지 (50ms 이상 걸리는 작업)
const longTaskObserver = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.warn(`Long Task 감지: ${entry.duration.toFixed(2)}ms`);
  }
});
longTaskObserver.observe({ entryTypes: ["longtask"] });

Core Web Vitals 측정

JS
// LCP (Largest Contentful Paint)
new PerformanceObserver((list) => {
  const entries = list.getEntries();
  const lcp = entries[entries.length - 1];
  console.log(`LCP: ${lcp.startTime.toFixed(2)}ms`);
}).observe({ type: "largest-contentful-paint", buffered: true });

// FID (First Input Delay) → INP로 대체됨
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(`FID: ${entry.processingStart - entry.startTime}ms`);
  }
}).observe({ type: "first-input", buffered: true });

// CLS (Cumulative Layout Shift)
let clsValue = 0;
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (!entry.hadRecentInput) {
      clsValue += entry.value;
    }
  }
  console.log(`CLS: ${clsValue.toFixed(4)}`);
}).observe({ type: "layout-shift", buffered: true });

성능 데이터 전송

JS
// 비콘으로 성능 데이터 전송 (페이지 언로드 시에도 안전)
function sendPerformanceData() {
  const data = {
    navigation: performance.getEntriesByType("navigation")[0]?.toJSON(),
    resources: performance.getEntriesByType("resource").map((r) => ({
      name: r.name,
      duration: r.duration,
      type: r.initiatorType,
    })),
    measures: performance.getEntriesByType("measure").map((m) => ({
      name: m.name,
      duration: m.duration,
    })),
  };

  navigator.sendBeacon("/api/analytics", JSON.stringify(data));
}

window.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "hidden") {
    sendPerformanceData();
  }
});

성능 지표 기준

지표좋음보통나쁨
LCP< 2.5s< 4s> 4s
INP< 200ms< 500ms> 500ms
CLS< 0.1< 0.25> 0.25
TTFB< 800ms< 1.8s> 1.8s

**기억하기 **: performance.mark()performance.measure()로 코드 구간의 실행 시간을 측정하고, PerformanceObserver로 Long Task와 Core Web Vitals를 실시간 감시합니다. 측정 없는 최적화는 추측일 뿐입니다.

댓글 로딩 중...