들어가며
웹페이지를 최적화하기 위해서는 브라우저가 HTML/CSS/JS를 어떻게 해석하고 화면에 출력하는지 정확히 이해해야 합니다.
렌더링 파이프라인의 각 단계를 알면, 불필요한 렌더링/리플로우/리페인트를 방지해 웹 성능을 높일 수 있습니다.
이 글에서는 브라우저 렌더링 과정과, 이를 기반으로 한 실전 성능 최적화 전략을 함께 정리합니다.
1. 브라우저 렌더링 파이프라인
HTML 파싱 → DOM 트리 생성
CSS 파싱 → CSSOM 생성
DOM + CSSOM → 렌더 트리 생성
Layout (Reflow) → Paint → Composite
① 파싱 단계
- HTML → DOM(Document Object Model)
- CSS → CSSOM(CSS Object Model)
② 렌더 트리 생성
- DOM과 CSSOM을 조합해 실제 화면에 표시할 노드를 구성
- display: none은 렌더 트리에 포함되지 않음
③ Layout (Reflow)
- 각 요소의 위치와 크기 계산
④ Paint
- 텍스트, 색상, 이미지 등 실제 픽셀 단위로 그리기
⑤ Composite
- 여러 레이어를 병합해 화면에 최종 렌더링
2. 성능에 영향을 주는 주요 요소들
구분 | 주요 트리거 |
Reflow (Layout) | 요소 위치, 크기 변경 (width, padding 등) |
Repaint | 색상, 그림자, visibility 등 시각적 스타일 변경 |
Composite | transform, opacity 등 레이어 분리 속성 |
- Reflow는 가장 비용이 크고, 트리 전체에 영향을 줄 수 있음
3. 실전 성능 최적화 전략
✅ 1. CSS와 JS는 head에서 async/defer로 나누기
<script src="main.js" defer></script>
- defer: HTML 파싱 후 JS 실행 (DOMContentLoaded 이후)
- async: 병렬 실행 → 순서 보장 안됨 (작은 모듈용)
✅ 2. Layout Thrashing 방지
// ❌ 잘못된 방식 – 읽기/쓰기 섞임 → 연속 Reflow 발생
el.style.width = el.offsetWidth + 10 + "px";
el.style.height = el.offsetHeight + 10 + "px";
// ✅ 개선 – 값을 캐싱한 뒤 한 번에 반영
const width = el.offsetWidth;
el.style.width = width + 10 + "px";
✅ 3. 애니메이션은 transform/opacity로 구현
/* ❌ top, left는 Layout 발생 */
/* ✅ GPU 가속이 가능한 속성 사용 */
.transform {
transform: translateX(100px);
opacity: 0.8;
}
✅ 4. CSS 선택자 최적화
/* ❌ 비효율적인 선택자 */
div ul li span {}
/* ✅ 태그 or 클래스 위주로 */
.card-title {}
✅ 5. 이미지 최적화
- WebP, AVIF 포맷 활용
- lazy loading (loading="lazy")
- 적절한 width/height 속성 지정
4. DevTools로 렌더링 분석하기
✅ Performance 탭
- 타임라인으로 Recalculate Style / Layout / Paint 등 확인 가능
- 긴 Layout/Reflow 시간이 보이면 코드 위치 추적
✅ Layers 탭
- 레이어가 분리된 영역 시각화 (composite 비용 확인)
마치며
브라우저는 우리가 작성한 HTML/CSS/JS를 단순히 출력하는 것이 아니라,
매우 복잡한 과정을 거쳐 한 프레임(16.6ms 이하) 안에 빠르게 처리해야 합니다.
렌더링 구조를 이해하고 불필요한 Reflow나 Repaint를 줄이는 코드를 작성하면, 눈에 띄게 부드러운 사용자 경험과 더 빠른 앱을 만들 수 있습니다.
📂 다음 글에서는:
- 프론트엔드 성능 측정 지표 (LCP, FID, CLS)
- 크리티컬 렌더링 경로 최적화
- TTI/TTFB 분석과 Lighthouse 실습
등을 이어서 다루겠습니다.
'architecture_spa' 카테고리의 다른 글
ESModules 심화1: 동적 import(), 번들러 처리 방식, tsconfig 설정 가이드 (0) | 2025.04.26 |
---|---|
Web Vitals(LCP, CLS, FID)과 Lighthouse 성능 측정 실전 (1) | 2025.04.26 |
SPA 구조 설계(모듈화): 라우터/스토어 모듈 분리와 파일 구조 구성법 (1) | 2025.04.26 |
프레임워크 없이 바닐라 JS로 SPA 만들기 – 라우팅, 렌더링, 상태관리 직접 구현해보기 (1) | 2025.04.20 |
JavaScript 모듈 시스템 완전 이해하기 – CommonJS vs ESModules (0) | 2025.04.20 |
댓글