Deep Dive 38. 브라우저의 렌더링 과정
렌더링 과정
- 브라우저는 HTML, CSS, 자바스크립트, 이미지, 폰트 파일 등 렌더링에 필요한 리소스를 요청하고 서버로부터 응답을 받는다.
- 브라우저의 렌더링 엔진은 서버로부터 응답된 HTML과 CSS를 파싱하여 DOM과 CSSOM을 생성하고 이들을 결합하여 렌더 트리를 생성한다.
- 브라우저의 자바스크립트 엔진은 서버로부터 응답된 자바스크립트를 파싱하여 AST(Abstruct Syntax Tree)를 생성하고 바이트코드로 변환하여 실행한다. 이때 자바스크립트는 DOM API를 통해 DOM이나 CSSOM을 변경할 수 있다. 변경된 DOM과 CSSOM은 다시 렌더 트리로 결합된다.
- 렌더 트리를 기반으로 HTML 요소의 레이아웃(위치와 크기)을 계산하고 브라우저 화면에 HTML 요소를 페인팅한다.
요청과 응답
서버에 요청을 전송하기 위해서는 브라우저 주소창에 URL을 입력해야한다.
1
https://poiemaweb.com
브라우저의 주소창에 URL을 입력하고 엔터키를 누르면 URL의 호스트 이름이 DNS를 통해 IP 주소로 변환되고 이 IP 주소를 갖는 서버에게 요청을 전송한다.
루트 요청에 대해 암묵적으로 index.html을 응답하도록 기본 설정되어 있다. 만약 https://poiemaweb.com/assets/data/data.json
과 같이 요청할 정적 파일의 경로와 파일 이름을 UIRE의 호스트 뒤에 패스에 기술하여 요청하면 data.json
을 응답받게 된다.
HTML 파싱과 DOM 생성
브라우저의 요청에 의해 서버가 응답한 HTML 문서는 문자열로 이루어진 순수한 텍스트다. 브라우저의 렌더링 엔진은 위 그림과 같은 과정을 통해 응답받은 HTML 문서를 파싱하여 브라우저가 이해할 수 있는 자료구조인 DOM(Document Object Model)을 생성한다.
- 문자열로 변환된 HTML 문서를 읽어 들여 문법적 의미를 갖는 코드의 최소 단위인 토큰들로 분해한다.
- 각 토큰들을 객체로 변환하여 노드들을 생성한다. 노드는 DOM을 구성하는 기본 요소가 된다.
CSS 파싱과 CSSOM 생성
렌더링 엔진은 HTML을 한 줄씩 순차적으로 파싱하다가 CSS를 로드하는 link
태그나 style
태그를 만나면 DOM 생성을 일시 중단한다.
그리고 link
태그의 href
어트리뷰트에 지정된 CSS 파일을 서버에 요청하여 로드한 CSS 파일이나 style 태그 내의 CSS를 HTML과 동일한 파싱 과정(바이트 -> 문자 -> 토큰 -> 노드 -> CSSOM)을 거치며 CSSOM(CSS Object Model)을 생성한다. CSS 파싱을 완료하면 HTML 파싱이 중단된 지점부터 다시 HTML을 파싱하기 시작하여 DOM 생성을 재개한다.
1
2
3
4
5
6
7
body {
font-size: 18px;
}
ul {
list-style-type: none;
}
렌더 트리 생성
렌더링 엔진은 서버로부터 응답된 HTML과 CSS를 파싱하여 각각 DOM과 CSSOM를 생성한다. 그리고 DOM과 CSSOM은 렌더링을 위해 렌더 트리로 결합된다. 렌더 트리는 브라우저 화면에 렌더링되는 노드만으로 구성된다.
완성된 렌더 트리는 각 HTML 요소의 레이아웃(위치와 크기)을 계산하는 데 사용되며 브라우저 화면에 픽셀을 렌더링하는 페이팅(painting) 처리에 입력된다.
레이아웃 계산과 페인팅을 다시 실행하는 리렌더링은 비용이 많이 드는 작업이다. 가급적 리렌더링이 빈번하게 발생하지 않도록 주의할 필요가 있다.
자바스크립트 파싱과 실행
CSS 파싱 과정과 마찬가지로 렌더링 엔진은 HTML을 한 줄씩 순차적으로 파싱하며 DOM을 생성해 나가다가 자바스크립트 파일을 로드하는 script 태그나 자바스크립트 코드를 콘텐츠로 담은 script 태그를 만나면 DOM 생성을 일시 중단한다.
그리고 script 태그의 src 어트리뷰트에 정의된 자바스크립트 파일을 서버에 요청하여 로드한 자바스크립트 파일이나 script 태그 내의 자바스크립트 코드를 파싱하기 위해 자바스크립트 엔진에 제어권을 넘긴다.
자바스크립트 엔진은 자바스크립트 코드를 파싱하여 AST(추상적 구문 트리)를 생성하고 AST를 기반으로 인터프리터가 실행 할 수 있는 중간 코드인 바이트코드를 생성하여 실행한다.
1
<script defer src="extern.js"></script>
자바스크립트가 DOM API를 이용하여 DOM을 조작하는 경우, DOM이 완성되지 않았다면 에러가 발생할 수 있다. 이 경우에는 defer
어트리뷰트를 통해 파일을 로드하면 DOM이 생성된 이후에 JS를 실행을 보장하므로써 에러 발생을 막을 수 있다.