- Published on
CRP(Critical rendering path)
- Authors

jangth
#Table Of Contents
CRP (Critical rendering path)
리액트의 특징 중 하나는 가상 DOM을 사용하는 것이다. 따라서 가상 DOM이 왜 만들어졌고, 실제 DOM과 어떤 차이가 있으며, 어떠한 이점이 있는지 알아보는 것이 중요하다. 이를 이해하기 위해 먼저 브라우저의 렌더링 과정과 DOM이 무엇인지 정확히 알고 있어야 한다.
DOM(Document Object Model)은 웹페이지의 콘텐츠와 구조를 브라우저가 어떻게 보여줄지를 결정하는 인터페이스다. 
브라우저 렌더링 과정
브라우저가 웹사이트에 접근한 후 화면을 그리는 과정을 살펴보자:
- 리소스 요청: 브라우저가 웹사이트에 접근하면 서버는 정적 리소스(HTML, CSS, JavaScript)를 응답한다.
- HTML 파싱 및 DOM 생성: 브라우저의 렌더링 엔진은 HTML을 파싱한다.
- 문자열을 파싱해 토큰화하고, 노드 객체를 생성하여 트리 자료구조인 DOM을 생성한다.
- CSSOM 생성: HTML 파싱 과정 중 CSS 파일을 만나면, CSS 파일을 병렬적으로 다운로드한다.
- CSS 파일이 다운로드 완료될 때까지 렌더 트리를 완성할 수 없기 때문에 렌더링이 블로킹된다.
- 렌더 트리 생성: 생성된 DOM과 CSSOM(CSS Object Model)을 결합하여 렌더 트리(Render Tree)를 생성한다. 이 과정에서 DOM 노드를 순회하며 각 노드에 CSS 스타일을 적용한다.
- 페인팅 준비: 생성된 렌더 트리를 기반으로 화면에 다음과 같은 순서로 페인팅한다:
- 레이아웃(reflow): 각 노드가 브라우저 화면의 어느 좌표에 정확히 나타나야 하는지를 계산하는 과정이다. 레이아웃 과정을 거치면 반드시 페인팅 과정도 거친다.
- 페인팅(painting): 레이아웃 단계에서 계산된 위치와 스타일 정보를 사용하여 실제 화면에 픽셀을 그리는 과정이다.
위와 같이 브라우저의 렌더링 과정은 여러 단계로 이루어지며, 각 단계에서 페이지의 최종 표시가 결정된다.
아래는 실제 성능탭에서 브라우저 렌더링 과정을 직접 살펴보았다.
HTML을 파싱하면서 병렬적으로 style.css를 다운로드받는 것을 확인할 수 있고, 이후 렌더링과 페인팅 작업이 이루어지는 것을 확인할 수 있다.
리플로우와 리페인팅의 발생 과정
그럼 리플로우와 리페인팅이 어떻게 발생하는지, 어떤 차이가 있는지 실제로 확인해보자
아래는 리플로우와 리페인팅을 유발하는 간단한 코드 예제다. 각 버튼을 클릭하면 DOM 요소의 스타일이 변경되어 리플로우 또는 리페인팅이 발생한다.
...
<style>
#container {
width: 100px;
height: 100px;
background-color: red;
margin-top: 20px;
}
</style>
</head>
<body>
<div id="container"></div>
<button id="triggerReflow">Reflow</button>
<button id="triggerRepaint">Repaint</button>
<script>
document.getElementById('triggerReflow').addEventListener('click', function () {
const container = document.getElementById('container');
container.style.width = '200px'; // 리플로우를 유발하는 스타일 변경
});
document.getElementById('triggerRepaint').addEventListener('click', function () {
const container = document.getElementById('container');
container.style.backgroundColor = 'blue'; // 리페인트를 유발하는 스타일 변경
});
</script>
</body>
리플로우
위 코드에서 리플로우를 유발하기 위해 Reflow 버튼을 클릭하면, #container의 width 속성을 200px로 변경한다. 이로 인해 레이아웃이 변경되며 브라우저는 이를 다시 계산해야 한다. 
위 호출 트리에서 아래와 같은 순서로 이벤트가 발생한다 :
- 스타일 다시 계산 (Recalculate Style): DOM 요소의 스타일을 다시 계산한다. 스타일이 변경되면 브라우저는 레이아웃을 다시 계산해야 한다.
- 레이아웃 (Layout): 브라우저는 요소의 위치와 크기를 다시 계산한다. 이는 리플로우의 핵심 과정이다.
- 페인팅 (Painting): 레이아웃이 변경된 후 브라우저는 요소를 화면에 다시 그린다.
이처럼 스타일 재계산 후 레이아웃을 다시 계산하고, 그 결과를 화면에 다시 그리는 과정을 통해 리플로우가 발생한다. 레이아웃이 다시 발생한다는 것은 모든 관련 요소의 위치와 크기를 다시 계산해야 한다는 것을 의미한다.
또한, 페인팅 작업도 리플로우 후에 반드시 수행된다. 레이아웃이 변경되면 브라우저는 화면에 요소를 다시 렌더링해야 하기 때문이다. 이는 레이아웃 재계산이 발생할 때마다 브라우저가 많은 리소스를 사용하게 된다는 것을 보여준다.
다음 리페인팅 과정을 보자
리페인팅
Repaint 버튼을 클릭하면, #container의 background-color 속성이 빨간색에서 파란색으로 변경된다. 이 경우 요소의 크기나 위치는 변하지 않으므로 레이아웃을 다시 계산할 필요가 없다. 
위 호출 트리에서 아래와 같은 순서로 발생한다:
- 사전 페인팅 (Pre-Paint): 페인팅 전에 수행되는 사전 준비 작업이다.
- 페인팅 (Painting): 요소의 시각적 변경을 화면에 다시 그린다. 레이아웃 변경 없이 시각적 속성만 업데이트된다.
리페인팅은 레이아웃 변경 없이 발생하기 때문에 스타일 다시 계산하는 단계가 없다. 대신, 시각적 변경 사항을 반영하기 위해 브라우저는 해당 요소를 다시 페인팅하는 작업만 수행한다.
리플로우와 리페인팅의 차이점
리플로우는 요소의 레이아웃이 변경되어 위치와 크기를 재계산해야 할 때 발생한다. 이는 스타일 재계산과 레이아웃 재계산을 포함하며, 브라우저가 페이지를 다시 레이아웃하고 그려야 하므로 많은 리소스를 소모한다.
반면, 리페인팅은 요소의 시각적 속성만 변경될 때 발생하며, 레이아웃을 재계산할 필요 없이 페인팅 작업만 수행된다. 따라서 리플로우는 리페인팅보다 성능에 더 큰 영향을 미칠 수 있다.
그렇다면 리페인팅만 발생해도 성능에 영향이 없을까?
리플로우와 리페인팅이 발생하는 경우
- 리플로우: 레이아웃에 영향을 미치는 CSS 속성 변경, DOM 요소의 추가/제거, 창 크기 조절 등.
- 리페인팅: 시각적 속성 변경 (
background-color,color,visibility등).
#마무리(가상 DOM?!!)
리플로우와 리페인팅을 줄이는 것은 웹 성능 최적화의 핵심이다. 요소의 위치나 크기가 변경될 때마다 리플로우와 페인팅 작업이 발생하여 브라우저가 많은 리소스를 소모하게 된다. 특히, DOM 구조가 복잡하거나 자식 요소가 많은 경우, 이러한 작업은 전체적인 성능 저하를 초래할 수 있다.
브라우저가 웹페이지를 렌더링하는 과정은 매우 복잡하며, 성능 최적화를 위해 많은 비용이 든다. 사용자의 인터랙션으로 인해 다양한 변화가 발생할 때, 이러한 브라우저 렌더링 과정을 깊이 이해하는 것이 중요하다. 이를 통해 웹페이지 성능을 개선하고, 사용자 경험을 더 향상시킬 수 있다.
CRP에 대해 이해하고 나면 자연스럽게 React의 가상 DOM에 대해 더 궁금해진다. React가 가상 DOM을 도입한 이유도 바로 이러한 브라우저 렌더링 과정에서 발생하는 성능 문제를 효과적으로 최적화하기 위해서가 아닐까? 다음 포스팅에서는 React의 가상 DOM이 이러한 문제를 어떻게 해결하는지에 대해 살펴보겠다.
