브라우저의 렌더링 과정
- webkit rendering engine process
- render tree = DOM + CSSOM
용어
- 파싱(parsing)
- 프로그래밍 언어의 문법에 맞게 작성된 텍스트 문서를 읽어 들여 실행하기 위해 텍스트 문서의 문자열을 토큰(token)으로 분해하고, 토큰에 문법적 의미와 구조를 반영하여 트리 구조의 자료 구조인 파스 트리(parse tree/syntax tree)를 생성하는 일련의 과정
- 일반적으로 파싱이 완료된 이후에는 파스 트리를 기반으로 중간 언어인 바이트코드(bytecode)를 생성하고 실행함
- 렌더링(rendering)
- HTML, CSS, JavaScript로 작성된 문서를 파싱하여 브라우저에 시각적으로 출력하는 것
1. 요청과 응답
- 브라우저의 핵심 기능은 필요한 리소스를 서버에 요청하고 서버로부터 응답받아 브라우저에 시각적으로 렌더링하는 것임
- 서버에 요청을 전송하기 위해 브라우저는 주소창을 제공함
- 브라우저의 주소창에 URI를 입력하고 엔터 키를 누름
- URI의 호스트 이름이 DNS를 통해 IP 주소로 변환됨
- 이 IP 주소를 갖는 서버에게 요청을 전송함
-
루트 요청
- 스킴(scheme)과 호스트(host)만으로 구성된 URI에 의한 요청
- 일반적으로 서버는 루트 요청에 대해 암묵적으로 index.html을 응답하도록 기본 설정되어 있음
- 예)
https://poiemaweb.com
= https://poiemaweb.com/index.html
-
정적 파일 요청
- index.html이 아닌 다른 정적 파일을 서버에 요청하려면, 브라우저의 주소창에 요청할 정적 파일의 경로와 파일 이름을 URI의 호스트 뒤의 패스(path)에 기술하여 서버에 요청함
- 예)
https://poiemaweb.com/assets/data/data.json
: 루트 폴더의 assets/data 폴더 내에 있는 정적 파일 data.json을 요청함
-
개발자 도구의 Network 패널에서 요청과 응답을 확인할 수 있음
- 개발자 도구의 Network 패널을 활성화하기 이전에 브라우저가 이미 응답을 받은 경우, 응답된 리소스가 표시되지 않음
- 이 경우, 페이지를 새로고침해야 응답된 리소스를 확인할 수 있음
-
루트 요청시, index.html뿐만 아니라 CSS, JavaScript, 이미지, 폰트 파일등도 응답됨
- 이유 : 브라우저의 렌더링 엔진이 HTML(index.html)을 파싱하는 도중에 외부 리소스를 로드하는 태그를 만나면 HTML의 파싱을 일시 중단하고 해당 리소스 파일을 서버에 요청하기 때문임
- 외부 리소스를 로드하는 태그의 종류
- CSS 파일을 로드하는 link 태그
- 이미지 파일을 로드하는 img 태그
- JavaScript를 로드하는 script 태그
2. HTTP 1.1과 HTTP 2.0
-
HTTP
- 웹에서 브라우저와 서버가 통신하기 위한 프로토콜(규약)
- 1989년, 팀 버너스 리 경이 HTTP를 URL과 함께 고안함
- 1991년, HTTP가 최초 문서화됨
- 1990년, HTTP/1.1이 발표됨
- 2015년, HTTP/2가 발표됨
-
HTTP/1.1
-
HTTP/2.0
- 커넥션 당 다중 요청과 응답이 가능함
- 여러 리소스의 동시 전송이 가능하므로, HTTP/1.1에 비해 페이지 로드 속도가 약 50% 정도 빠름
3. HTML 파싱과 DOM 생성
4. CSS 파싱과 CSSOM 생성
- 렌더링 엔진은 meta 태그까지 HTML을 순차적으로 파싱하여 DOM을 생성해 나가다가, CSS를 로드하는 link 태그나 style 태그를 만나면 DOM 생성을 일시 중단함
- link 태크의 href 어트리뷰트에 지정된 CSS 파일을 서버에 요청함
- 서버로부터 응답받은 CSS 파일이나 style 태그 내의 CSS를 HTML과 동일한 과정(바이트->문자->토큰->노드->CSSOM)을 거치며 파싱하여 CSSOM(CSS Object Model)을 생성함
- CSS 파싱을 완료하면 HTML 파싱이 중단된 지점부터 다시 HTML을 파싱하기 시작하여 DOM 생성을 재개함
body {
font-size: 18px;
}
ul {
list-style-type: none;
}
5. 렌더 트리 생성
- 렌더 트리의 사용
- 각 HTML 요소의 레이아웃(위치와 크기)를 계산하는데 사용됨
- 브라우저 화면에 픽셀을 렌더링하는 페인팅(painting) 처리에 입력됨
6. JavaScript 파싱과 실행
-
렌더링 엔진은 HTML을 순차적으로 파싱하며 DOM을 생성해 나가다가, JavaScript 파일을 로드하거나 JavaScript 코드를 컨텐츠로 담은 script 태그를 만나면 DOM 생성을 일시 중단함
- script 태그의 src 어트리뷰트에 정의된 JavaScript 파일을 서버에 요청함
- 서버로부터 응답받은 JavaScript 파일이나 script 태그 내의 JavaScript 파일을 파싱하기 위해 JavaScript 엔진에 제어권을 넘김
- JavaScript 파싱과 실행이 종료되면, 렌더링 엔진으로 다시 제어권을 넘겨 HTML 파싱이 중단된 지점부터 다시 HTML 파싱을 시작하여 DOM 생성을 재개함
-
JavaScript 엔진이 JavaScript의 파싱과 실행을 처리함
- JavaScript 코드를 파싱하여 CPU가 이해할 수 있는 저수준 언어로 변환하고 실행하는 역할을 함
- JavaScript를 파싱하여 AST(Abstract Syntax Tree, 추상적 구문 트리)를 생성함
- AST를 기반으로 인터프리터가 실행할 수 있는 중간 코드인 바이트코드를 생성하여 실행함
-
토크나이징(tokenizing)
- 단순한 문자열인 JavaScript 소스코드를 어휘 분석(lexical analysis)하여 문법적 의미를 갖는 최소 단위인 토큰(token)들로 분해함
-
파싱(parsing)
- 토큰들의 집합을 구문 분석(syntactic analysis)하여 AST(Abstract Syntax Tree, 추상적 구문 트리)를 생성함
-
바이트코드의 생성과 실행
- 생성된 AST는 인터프리터가 실행할 수 있는 중간 코드인 바이트코드로 변환되고 인터프리터에 의해 실행됨
7. 리플로우와 리페인트
- JavaScript 코드에 DOM이나 CSSOM을 변경하는 DOM API가 사용된 경우, DOM이나 CSSOM이 변경됨
- 이때 변경된 DOM과 CSSOM은 다시 렌더 트리로 결합됨
- 변경된 렌더트리를 기반으로 레이아웃과 페인트 과정을 거쳐 브라우저의 화면을 리렌더링함
- 리플로우(reflow)
- 레이아웃 계산을 다시 하는 것
- 노드 추가/삭제, 요소의 크기/위치 변경, 윈도우 리사이징 등 레이아웃에 영향을 주는 변경이 발생한 경우에 한해서 실행됨
- 리페인트(repaint)
- 재결합된 렌더 트리를 기반으로 다시 페인트하는 것
- 레이아웃의 변경이 없는 변경의 경우, 리페인트만 실행됨
JavaScript 파싱에 의한 HTML 파싱 중단
-
브라우저는 동기적(synchronous)으로 HTML, CSS, JavaScript를 파싱하고 실행함
- script 태그의 위치에 따라 HTML 파싱이 블로킹되어 DOM 생성이 지연될 수 있음
- JavaScript 코드에서 DOM이나 CSSOM을 변경하는 DOM API를 사용할 경우, DOM이나 CSSOM이 이미 생성되어 있어야 함
- 만약 DOM API를 사용할 때 DOM 생성이 완료되지 않은 상태라면 문제가 발생할 수 있음
-
body 요소의 가장 아래에 JavaScript를 위치시키는 것이 좋음
- JavaScript가 실행될 시점에는 이미 렌더링 엔진이 HTML 요소를 모두 파싱하여 DOM 생성을 완료한 이후임
- 따라서, DOM이 완성되지 않은 상태에서 JavaScript가 DOM을 조작하는 에러가 발생할 우려가 없음
- JavaScript가 실행되기 이전에 DOM 생성이 완료되어 렌더링되므로 페이지 로딩 시간이 단축됨
9. script 태그의 async/defer 어트리뷰트
- HTML5부터는 JavaScript 파잇에 의한 DOM 생성이 중단되는 문제를 해결하기 위해 script 태그에 async와 defer 어트리뷰트가 추가됨
- src 어트리뷰트를 통해 외부 JavaScript 파일을 로드하는 경우에만 async과 defer 어트리뷰트를 사용 할 수 있음
- async/defer 어트리뷰트를 사용하면, HTML 파싱과 외부 JavaScript 파일의 로드가 비동기적으로 동시에 진행됨
| async 어트리뷰트 | defer 어트리뷰트 |
---|
HTML 파싱과 외부 JavaScript 파일의 로드 | 비동기적으로 동시에 진행됨 | 비동기적으로 동시에 진행됨 |
JavaScript의 파싱과 진행 | JavaScript 파일의 로드가 완료된 직후 진행됨, HTML 파싱이 중단됨 | HTML 파싱이 완료된 직후, 즉 DOM 생성이 완료된 직후(DOMContentLoaded 이벤트 발생) 진행됨 |
참고 문헌
- 모던 자바스크립트 Deep Dive: 자바스크립트의 기본 개념과 동작 원리(위키북스, 이응모) Chapter. 38 브라우저의 렌더링 과정