브라우저의 렌더링 과정(CRP)
브라우저 렌더링
브라우저 렌더링에서 렌더링(redering)이란 HTML, CSS, JavaScript로 작성된 문서를 파싱하여 브라우저에 시각적으로 출력하는 것을 말합니다. 현존하는 브라우저마다 다르지만, 브라우저는 기본적으로 렌더링을 수행하는 렌더링 엔진을 가지고 있습니다.
브라우저 렌더링 과정
- 사용자가 브라우저를 통해 웹 사이트에 접속합니다.
- 브라우저는 HTML, CSS, 자바스크립트, 이미지, 폰트 등 렌더링에 필요한 리소스를 요청하고 서버로부터 응답을 받습니다.
- 브라우저의 렌더링 엔진은 서버로부터 응답된 HTML과 CSS를 파싱하여 DOM과 CSSOM을 생성하고 이들을 결합하여 렌더 트리를 구축합니다.
- 레이아웃 과정을 통해 렌더 트리를 기반으로 각 요소의 레이아웃(위치, 크기)을 계산하고, 브라우저 화면에 HTML 요소를 페인팅합니다.
파싱(Parsing)
파싱
이란 프로그래밍 언어로 작성된 파일을 실행시키기 위해 구문 분석(syntax analysis)을 하는 단계입니다. HTML 파일의 코드를 문법적 의미를 갖는 최소 단위인 토큰(token)으로 분해하고, 토큰에 문법적 의미와 구조에 따라 노드라는 요소로 바꿉니다. 노드들은 상하 관계에 따라 하나의 트리를 형성하는데, 이를 파스 트리(parse tree/syntax tree)라고 부릅니다.
문서 파싱(document parsing)
은 브라우저가 코드를 이해하고 사용할 수 있는 구조로 변환하는 것을 의미합니다. 렌더링 과정에서 HTML 파일을 바탕으로 DOM 트리를 구축하고 CSS 파일로 CSSOM 트리를 만드는 것을 파싱한다고 표현합니다.
DOM 트리
DOM(Document Object Model)은 HTML 문서의 요소들의 중첩 관계를 기반으로 노드들을 트리 구조로 구성한 것을 의미합니다. 브라우저의 요청으로 서버가 응답한 HTML 문서는 문자열로 이루어진 순수한 텍스트입니다. 그런데 브라우저는 JavaScript 언어만 알아듣기 때문에 HTML의 태그나 속성들을 이해하지 못합니다. 순수한 텍스트인 HTML 문서를 브라우저에 시각적인 픽셀로 렌더링하기 위해 브라우저가 이해할 수 있는 형태인 객체로 만들어 준 것이 바로 DOM 트리입니다.
CSSOM 트리
HTML 파일을 DOM 트리로 파싱하던 렌더링 엔진은 CSS를 로드하는 link 태그나 style 태그를 만나면 DOM 생성을 일시 중단합니다. 그리고 link 태그의 href 어트리뷰트에 지정된 CSS 파일을 서버에 요청하여 로드한 CSS 파일이나 style 태그 내의 CSS를 HTML과 동일한 파싱 과정(바이트 → 문자 → 토큰 → 노드 → CSSOM)을 거치며 해석하여 CSSOM을 생성합니다. 이후 CSS 파싱을 완료하면 HTML 파싱이 중단된 지점부터 다시 DOM 트리를 구축합니다.
렌더 트리
렌더링 엔진은 서버로부터 응답된 HTML과 CSS를 파싱하여 각각 DOM과 CSSOM을 생성합니다. 그리고 DOM과 CSSOM은 렌더링을 위해 렌더 트리로 결합합니다.
렌더 트리는 렌더링을 목적으로 만들어지는 트리입니다. 따라서 meta
태그, script
태그처럼 브라우저 화면에 렌더링 되지 않는 노드와 display: none
처럼 CSS에 의해 표시되지 않는 노드들은 포함되지 않습니다. 다시 말해, 렌더 트리는 브라우저 화면에 렌더링 되는 노드만으로 구성됩니다.
레이아웃
브라우저 렌더링에서 레이아웃
과정은 렌더 트리를 기반으로 HTML 요소의 레이아웃(위치, 크기)을 계산하여 브라우저 화면 어디에 배치할지 결정하는 과정입니다. 브라우저는 각 요소가 전체 화면에서 어디에, 어떤 크기로, 어떻게 배치가 되어야 하는지 파악하기 위해 렌더트리를 위에서 아래로 읽어 내려갑니다. 그리고 모든 값은 절대적인 단위인 px 값으로 변환됩니다.
페인팅
위치에 대한 계산을 마치면 이제 화면을 보여주기 위해 브라우저는 화면 위에 레이아웃에서 결정된 대로 그림을 그리기 시작합니다. 브라우저 화면은 픽셀이라고 하는 작은 점들로 구성되어 있습니다. 페인팅은 픽셀에 대한 정보들을 바탕으로 픽셀을 채워나가는 과정입니다.
리플로우와 리페인트
만약 자바스크립트 코드에 DOM이나 CSSOM을 변경하는 DOM API가 사용된 경우 DOM이나 CSSOM이 변경됩니다. 이때 변경된 DOM과 CSSOM은 다시 렌더 트리로 결합하고 변경된 렌더 트리를 기반으로 레이아웃과 페인트 과정을 거쳐 브라우저의 화면에 다시 렌더링합니다.
리플로우
는 레이아웃 계산을 다시 하는 것을 말하며, 노드 추가/삭제, 요소의 크기/위치 변경, 윈도우 리사이징 등 레이아웃에 영향을 주는 변경이 발생한 경우에 한하여 실행됩니다. 리페인트
는 재결합된 렌더 트리를 기반으로 다시 페인트를 하는 것을 말합니다.
따라서 리플로우와 리페인트가 반드시 순차적으로 동시에 실행되는 것은 아닙니다. 레이아웃에 영향이 없는 변경은 리플로우 없이 리페인트만 실행됩니다.
DOM API
브라우저는 웹 문서를 로드한 후, 파싱하여 DOM을 생성합니다. 정적인 웹페이지를 변경하는 유일한 방법은 DOM을 변경하는 것이며, 이때 필요한 것이 DOM API입니다. DOM을 통해 웹페이지를 조작하려면 API를 사용하여 조작하고자 하는 요소를 선택 또는 탐색하고, 선택된 요소의 콘텐츠 또는 Attribute를 조작해야 합니다. 예를 들어 document.getElementById(id)는 주어진 id를 가진 요소를 선택하기 위한 DOM API 메서드입니다.
리플로우와 리페인트가 일어나는 상황
- 자바스크립트에 의한 노드 추가 또는 삭제
- 브라우저 창의 리사이징에의한 뷰포트(viewport) 크기 변경
- HTML 요소의 레이아웃에 변경을 발생시키는 width/height, margin, padding, border, display, position, top/right/bottom/left 등의 스타일 변경
리플로우와 리페인트의 최적화
-
불필요한 레이아웃을 줄입니다. CSSOM 트리의 CSS 속성 중에 레이아웃을 발생시키는 속성들이 있습니다. 이 속성을 사용하게 되면 그때마다 변경이 되어 렌더 트리를 만들고, 레이아웃을 발생시키고, 페인트를 하는 과정이 연속적으로 발생합니다. 따라서 이런 속성을 피하면 해당 과정이 발생하는 횟수를 줄일 수 있습니다.
-
리플로우 시 리페인트는 필연적으로 일어나므로 가능하다면 리플로우가 발생하는 속성보다 리페인트만 발생하는 속성을 사용하는 게 좋습니다.
리플로우가 일어나는 대표적인 속성 중 left가 있습니다. 이 left 속성을 사용하면 위치가 변경됩니다. left 속성은 레이아웃을 발생시키는 속성입니다. 따라서 이 속성을 가지고 애니메이션을 만들 때는 프레임 유지를 보장하기 어려워집니다. 그래서 이 속성을 피해
transform
이라는 속성을 사용합니다. transform에 있는translate
를 사용하면 좌푯값을 사용해 위치를 이동하지만, 레이아웃을 발생시키지 않고 페인트만 다시 발생시키는 쪽으로 렌더링 과정이 일어나기 때문에 유지하고자하는 프레임 수를 기대할 수 있습니다.리페인트가 일어나지 않게 해주는
opacity
라는 속성도 있습니다. visibility 또는 display보다 opacity를 사용하는 것이 성능 개선에 도움을 줍니다. -
영향을 주는 노드를 줄입니다. 자바스크립트 + CSS를 조합한 애니메이션이 많거나, 레이아웃 변화가 많은 요소의 경우
position
속성을absolute
또는fixed
로 값을 주면 지정된 노드는 전체 노드에서 분리됩니다. 이 경우 리플로우 과정이 필요 없기 때문에 리페인트 연산 비용만 들일 수 있습니다.
참고 자료
이웅모, 모던 자바스크립트 Deep Dive, 위키북스, 2020