최적화
크롬 개발자도구 Performance 탭 읽는 법
한 줄 요약
웹 페이지의 로딩·렌더링·상호작용 과정에서 일어나는 모든 작업(스크립트 실행, 스타일 계산, 레이아웃, 페인팅, 네트워크 등)을 시간 축으로 시각화해서 성능 병목 구간을 찾아내고 최적화하는 도구.
왜 알아야 했나
- 기능 구현에만 집중하다 보면 성능 최적화는 뒤로 밀리기 쉬운데, 어느 순간 "왜 이렇게 느리지?"를 감이 아니라 데이터로 진단해야 하는 순간이 온다.
- Network 탭은 "리소스가 언제, 얼마나 걸려 받아졌나"만 보여주지만, Performance 탭은 받은 이후 브라우저가 그걸 처리하면서 어디서 시간을 쓰는지까지 보여준다.
- 구체적으로 이런 상황에서 필요했다:
- 버튼 클릭 후 반응이 느리거나 이벤트 처리 시간이 길 때
- 애니메이션·스크롤이 뚝뚝 끊길 때 (프레임 드롭)
- 특정 DOM 조작이나 JS 코드가 메인 스레드를 막을 때
- 페이지 초기 렌더링(FCP, LCP)이 느릴 때
핵심 개념
Performance 탭이란?
- 웹 애플리케이션의 성능을 분석·최적화하는 도구.
- 페이지 로딩 및 사용자 상호작용 중 발생하는 모든 작업을 시각적으로 확인 가능.
- 애니메이션, 스크롤 성능, JavaScript 실행 최적화 등 다양한 성능 문제 디버깅에 유용.
- 📌 덧붙임: 핵심은 "메인 스레드(Main Thread)는 하나뿐"이라는 사실이다. JS 실행, 스타일 계산, 레이아웃, 페인팅이 모두 이 한 스레드에서 순서대로 일어나기 때문에, 한 작업이 오래 걸리면 화면이 멈춘다(=병목). Performance 탭은 결국 이 메인 스레드를 들여다보는 도구라고 생각하면 이해가 쉽다.
상단 컨트롤 버튼
| 버튼 | 이름 | 역할 |
|---|---|---|
| ● | 녹화(Record) | 성능 기록 시작. 다시 누르면(■) 중지 |
| 🔄 | Record and Reload | 페이지를 새로고침하며 초기 로드 성능을 자동 기록 |
| ⊘ | Clear | 기록한 데이터 삭제·초기화 |
| ⬇ | Download | 녹화 데이터를 파일로 저장(공유·재분석용) |
| Screenshots | 스크린샷 | 기록 중 페이지 상태를 캡처해 시간별 변화 확인 |
| Memory | 메모리 | 메모리 사용량 기록 → 메모리 누수 분석 |
성능 기록하는 2가지 방법
1. 녹화 버튼 (●) — 상호작용 분석용
- 페이지 로드 이후 발생하는 문제(클릭, 스크롤, 애니메이션 등) 디버깅에 적합.
- 사용 순서: Performance 탭 열기 → ● 클릭 → 분석할 동작 수행 → ■ 클릭으로 중지 → 데이터 분석.
2. Record and Reload (🔄) — 초기 로드 분석용
- 새로고침과 함께 페이지 로딩 과정의 문제(FCP, LCP, 리소스 로드, 서버 응답 등)를 디버깅.
- 사용 순서: 🔄 클릭 → 자동 새로고침 + 데이터 수집 → 완료 시 자동 중지 → 초기 로드 데이터 분석.
성능 보고서 구조
① 상단 타임라인 (Overview)
페이지 로드·상호작용 동안의 주요 성능 데이터를 요약.
- CPU 타임라인: CPU 사용량 그래프. JavaScript(노란색), 레이아웃(보라색) 등 작업 유형별로 CPU가 얼마나 쓰였는지 표시. 과도하게 치솟는 구간 = 병목 후보.
- NET (Network): 네트워크 요청·응답 시간 표시.
② 메인 작업 타임라인 (Main Section)
- Network: 각 리소스(HTML/CSS/JS/이미지/API)가 언제 시작·완료·대기했는지 시각화. 순차 로드인지 병렬 로드인지 확인 가능.
- Frames: 초당 렌더링 프레임(FPS)을 시각화. 프레임 드롭(낮은 FPS) 위치와 프레임별 작업 시간을 확인 → 렌더링 최적화에 유용. (📌 덧붙임: 부드러운 화면은 보통 60FPS, 즉 프레임당 약 16.7ms 안에 작업이 끝나야 한다. 이걸 넘으면 끊김으로 느껴진다.)
- Timings: 페이지 로딩 중 주요 이벤트의 타이밍 표시. DCL·FCP·LCP 간 간격을 분석해 병목을 파악.
- Layout Shifts: CLS(누적 레이아웃 이동) 관련 정보. 레이아웃이 갑자기 밀린 위치·정도를 보여줌. CLS가 높으면 UX 저하.
- Main: 메인 스레드 작업을 타임라인으로 표시. 병목 구간을 찾는 가장 핵심 영역.
주요 로딩 지표 (Timings)
| 약자 | 풀네임 | 의미 |
|---|---|---|
| DCL | DOMContentLoaded | DOM이 완전히 파싱된 시점 |
| FP | First Paint | 화면에 첫 픽셀이 렌더링된 시점 |
| FCP | First Contentful Paint | 텍스트·이미지 등 콘텐츠가 처음 렌더링된 시점 |
| LCP | Largest Contentful Paint | 가장 큰 콘텐츠가 렌더링된 시점 |
| TTI | Time to Interactive | 페이지가 실제로 사용 가능한 상태가 되는 시간 |
Main의 작업 범주(렌더링 파이프라인 순서대로)
| 범주 | 설명 |
|---|---|
| Parse HTML | HTML을 파싱해 DOM 생성 |
| Evaluate Script | JavaScript 코드 실행 |
| Style | CSS 계산 |
| Layout | DOM 요소의 위치·크기 계산 |
| Painting / Compositing | 요소를 화면에 렌더링 |
③ 하단 탭들
- Summary: 전체 작업 시간 분포를 원형 차트로 요약. 각 작업이 전체 시간에서 차지하는 비율을 한눈에 파악.
- Bottom-up: 가장 낮은 레벨의 함수 호출부터 시작해 상위 작업까지 시간 소비를 분석. 어떤 함수가 시간을 많이 잡아먹는지 찾기 좋음.
- Call Tree: 호출 계층을 위 → 아래(상위 함수 → 하위 함수)로 정리. 호출 흐름·경로 추적에 유용.
- Event Log: 발생한 이벤트를 시간순으로 나열. 작업 발생 시점·순서로 문제 원인 추적.
Summary 원형 차트 색상 의미
| 색상 | 범주 | 의미 |
|---|---|---|
| 연한 파란색 | Loading | 리소스를 네트워크로 다운로드하는 시간 |
| 노란색 | Scripting | JS 실행, 이벤트 핸들러 처리, DOM 동적 조작 시간 |
| 보라색 | Rendering | CSS 스타일 계산 + 레이아웃 변환(화면 표시 준비) |
| 초록색 | Painting | 픽셀 데이터 생성 후 화면에 그리는 작업 |
| 진한 회색 | System | 브라우저 내부 처리 시간(직접 최적화 어려움) |
| 연한 회색 | Idle | CPU 대기 시간 (높으면 오히려 효율적이라는 신호일 수 있음) |
| 흰색 | Total | 모든 작업의 총합 시간 (예: 약 3123ms = 로딩 완료까지 걸린 시간) |
Summary로 병목 진단하는 법
- Scripting이 많다 → JavaScript 최적화 필요
- Rendering이 많다 → DOM 구조 간소화 / 스타일 최적화 필요
- Loading이 많다 → 네트워크 요청 병렬화 / 리소스 크기 최적화 필요
Self Time vs Total Time (Bottom-up·Call Tree·Event Log에서)
| 용어 | 의미 |
|---|---|
| Self Time | 함수 자체가 직접 소비한 시간 (하위 함수 호출 시간 제외). 함수 자체의 성능 문제 확인용 |
| Total Time | Self Time + 모든 하위 작업 실행 시간. 이 함수가 상위 프로세스에 끼치는 영향 확인용 |
| Activity | 작업의 유형 또는 실행된 특정 활동 |
예시 / 코드
성능 기록 자체는 코드가 아니라 UI 조작이지만, "메인 스레드를 막는 코드"가 어떤 것인지 감을 잡으면 보고서 해석이 쉬워진다.
// ❌ 나쁜 예: 동기적으로 무거운 계산 → 메인 스레드 블로킹
// Performance 탭 Main에서 노란색(Scripting) 긴 막대로 나타나고, FPS가 떨어진다.
button.addEventListener('click', () => {
let result = 0;
for (let i = 0; i < 1_000_000_000; i++) {
result += Math.sqrt(i);
}
console.log(result);
});
// ✅ 개선 예 1: 작업을 잘게 쪼개 메인 스레드 양보
function chunkedWork(total, chunk = 1_000_000) {
let i = 0, result = 0;
function next() {
const end = Math.min(i + chunk, total);
for (; i < end; i++) result += Math.sqrt(i);
if (i < total) {
// requestIdleCallback / setTimeout 으로 다음 프레임에 양보
requestIdleCallback(next);
} else {
console.log(result);
}
}
next();
}
// ✅ 개선 예 2: 무거운 계산은 Web Worker로 분리해 메인 스레드를 비움
const worker = new Worker('heavy.js');
button.addEventListener('click', () => worker.postMessage({ total: 1_000_000_000 }));
worker.onmessage = (e) => console.log(e.data);
// ❌ 레이아웃 스래싱(Layout Thrashing): 읽기/쓰기를 번갈아 → 강제 동기 리플로우
// Performance 탭 Main에서 보라색(Rendering/Layout) 막대가 반복적으로 길게 찍힌다.
items.forEach((el) => {
const h = el.offsetHeight; // 읽기(레이아웃 강제 계산)
el.style.height = h + 10 + 'px'; // 쓰기(레이아웃 무효화) → 다음 읽기에서 또 재계산
});
// ✅ 개선: 읽기를 모아서 먼저, 쓰기를 나중에
const heights = items.map((el) => el.offsetHeight); // 읽기 일괄
items.forEach((el, i) => { el.style.height = heights[i] + 10 + 'px'; }); // 쓰기 일괄
헷갈렸던 점
- Network 탭 vs Performance 탭: Network는 "리소스 다운로드 타이밍"만, Performance는 "다운로드 + 그 이후 브라우저 처리(파싱·실행·렌더링) 전부"를 본다. Performance 안에도 Network 항목이 있어서 더 헷갈리는데, 여기 Network는 전체 타임라인의 _맥락 속_에서 본다는 점이 다르다.
- Self Time vs Total Time: "이 함수 자체가 느린 건가(Self)?" vs "이 함수가 부르는 하위까지 합쳐서 느린 건가(Total)?"를 구분하는 용도. 최적화 대상을 잘못 잡지 않으려면 둘을 같이 봐야 한다.
- Bottom-up vs Call Tree: 둘 다 함수 시간 분석인데 방향이 반대. Bottom-up = "가장 많이 쓴 함수가 뭐야?"(원인부터), Call Tree = "이 함수가 어디서 호출됐어?"(흐름부터).
- Rendering vs Painting: Rendering(보라색)은 어디에 얼마나 크게 둘지 계산(Style + Layout), Painting(초록색)은 실제 픽셀을 칠하는 단계. 색이 다르고 최적화 방법도 다르다.
- Idle이 높으면 나쁜 거 아냐?: 오히려 좋을 수 있다. CPU가 놀고 있다 = 리소스를 효율적으로 쓰고 있다는 신호일 수 있다.
실무 적용
- 초기 로딩이 느린 페이지 진단: 🔄(Record and Reload)로 기록 → Timings에서 FCP/LCP 확인 → Summary에서 Loading 비중이 크면 리소스 최적화, Scripting이 크면 번들 크기·실행 최적화.
- 클릭/스크롤이 버벅일 때: ●(녹화)로 해당 동작만 기록 → Frames에서 FPS 드롭 위치 확인 → Main에서 그 시점의 긴 작업(Long Task) 찾기 → Bottom-up으로 범인 함수 특정.
- 레이아웃이 갑자기 튀는 문제: Layout Shifts 섹션에서 CLS 발생 지점 확인 → 이미지·광고·폰트에 크기 예약(width/height, aspect-ratio) 적용.
- 애니메이션 최적화: Main에서 Layout/Paint가 반복되면
transform·opacity기반 애니메이션으로 전환(레이아웃 안 건드림). - 관련 작업: [[개발자도구-Network]] · [[개발자도구-Lighthouse]] · [[Core-Web-Vitals]] · [[브라우저-렌더링-과정]]
더 공부할 것
- Core Web Vitals (LCP / INP / CLS) 의 구체적 기준 점수와 측정 방식
- 브라우저 렌더링 파이프라인 (Parse → Style → Layout → Paint → Composite) 전체 흐름
- Long Tasks API 와 메인 스레드 블로킹 50ms 기준
- requestIdleCallback / requestAnimationFrame / Web Worker 로 메인 스레드 부담 분산하기
- Lighthouse 와 Performance 탭의 역할 차이 (자동 점수 vs 수동 정밀 분석)
- Memory 탭 / Performance Memory 로 메모리 누수 추적하기
- Flame Chart 읽는 법 (스택의 깊이·폭이 의미하는 것)