|
| 1 | +### Chapter 38 - 브라우저의 렌더링 과정 |
| 2 | + |
| 3 | +대부분의 프로그래밍 언어는 운영체제(OS)나 가상 머신(VM) 위에서 실행되지만 |
| 4 | +웹 어플리케이션의 클라이언트 사이드 자바스크립트는 브라우저에서 HTML과 CSS와 함께 실행된다. |
| 5 | +> 브라우저가 HTML, CSS, 자바스크립트로 작성된 텍스트 문서를 어떻게 파싱(해석)하여 브라우저에 렌더링되는지 알아보자. |
| 6 | +
|
| 7 | +<br> |
| 8 | + |
| 9 | +**※ 파싱(Parsing)** |
| 10 | +파싱(구문 분석, Syntax Analysis)은 프로그래밍 언어의 문법에 맞게 작성된 텍스트 문서를 읽어 들여 실행하기 위해 |
| 11 | +텍스트 문서의 문자열을 토큰(Token)으로 분해하고, 토큰에 문법적인 의미와 구조를 반영하여 |
| 12 | +트리 구조의 자료구조인 Parse Tree를 생성하는 일련의 과정을 의미한다. |
| 13 | + |
| 14 | +일반적으로 파싱이 완료된 이후에는 Parse Tree를 기반으로 중간 언어인 바이트코드(Bytecode)를 생성하고 실행한다. |
| 15 | + |
| 16 | +**※ 렌더링(Rendering)** |
| 17 | +랜더링은 HTML, CSS, 자바스크립트로 작성된 문서를 파싱하여 <font color='orange'>브라우저에 시각적으로 출력</font>하는 것을 의미한다. |
| 18 | + |
| 19 | +<br> |
| 20 | + |
| 21 | +브라우저는 아래와 같은 과정을 거쳐 랜더링을 수행한다. |
| 22 | +1) 브라우저는 HTML, CSS, 자바스크립트, 이미지, 폰트 파일 등 렌더링에 필요한 리소스를 요청하고 서버로부터 응답을 받는다. |
| 23 | +2) 브라우저의 렌더링 엔진은 서버로부터 응답된 HTML과 CSS를 파싱하여 DOM, CSSOM을 생성하고 이들을 결합하여 렌더 트리를 만든다. |
| 24 | +3) 브라우저의 자바스크립트 엔진은 서버로부터 응답된 자바스크립트를 파싱하여 AST(Abstract Syntax Tree)를 생성하고 바이트코드로 변환하여 실행한다. 이때 자바스크립트는 DOM API를 통해 DOM이나 CSSOM을 변경할 수 있다. 변경된 DOM과 CSSOM은 다시 렌더 트리로 결합된다. |
| 25 | +4) 렌더 트리를 기반으로 HTML 요소의 레이아웃(위치와 크기)을 계산하고 브라우저 화면에 HTML 요소를 페인팅한다. |
| 26 | + |
| 27 | +<br><br> |
| 28 | + |
| 29 | +### 요청과 응답 |
| 30 | +**브라우저의 핵심 기능**은 필요한 리소스(HTML, CSS, 자바스크립트, 이미지, 폰트 파일 등)를 서버에 요청하고 서버로부터 응답받은 리소스를 파싱하여 렌더링하는 것이다. |
| 31 | + |
| 32 | +<br> |
| 33 | + |
| 34 | +**※ 요청(Request)** |
| 35 | +요청(Request)은 클라이언트에서 서버로 보내는 데이터를 의미한다. |
| 36 | + |
| 37 | +**※ 응답(Response)** |
| 38 | +응답(Response)은 서버에서 클라이언트로 보내는 데이터를 의미한다. |
| 39 | + |
| 40 | +<br> |
| 41 | + |
| 42 | +※ 요청과 응답은 개발자 도구의 Network 패널에서 확인할 수 있다. |
| 43 | + |
| 44 | +<br><br> |
| 45 | + |
| 46 | +### HTTP 1.1와 HTTP 2.0 |
| 47 | +HTTP/1.1은 기본적으로 커넥션(Connection)당 하나의 요청과 응답만 처리한다. |
| 48 | +> 여러 개의 요청을 한 번에 전송할 수 없고, 응답 또한 순차적으로 처리된다. |
| 49 | +
|
| 50 | +이와 같은 특징으로 HTTP/1.1은 동시 전송이 불가능하여 |
| 51 | +요청할 리소스의 개수에 비례하여 응답 시간도 증가한다는 단점이 있다. |
| 52 | + |
| 53 | +<br> |
| 54 | + |
| 55 | +HTTP/2는 커넥션 당 여러 개의 요청과 응답, 즉 다중 요청/응답이 가능하다. |
| 56 | +따라서 HTTP/2.0은 여러 리소스의 동시 전송이 가능하므로 HTTP/1.1보다 페이지 로드 속도가 약 50% 정도 빠르다고 알려져 있다. |
| 57 | + |
| 58 | +<br><br> |
| 59 | + |
| 60 | +### HTML 파싱과 DOM 생성 |
| 61 | +브라우저의 요청에 의해 서버가 응답한 HTML 문서는 순수 텍스트이다. |
| 62 | +순수 텍스트인 HTML 문서를 브라우저에 시각적인 픽셀로 렌더링하려면 HTML 문서를 브라우저가 이해할 수 있는 자료구조로 변환하여 메모리에 저장해야 한다. |
| 63 | + |
| 64 | +예를 들어서 아래와 같은 index.html이 서버로부터 응답되어싿고 가정해보자. |
| 65 | + |
| 66 | +``` html |
| 67 | +<!DOCTYPE html> |
| 68 | +<html lang="en"> |
| 69 | +<head> |
| 70 | + <meta charset="UTF-8"> |
| 71 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 72 | + <title>Document</title> |
| 73 | +</head> |
| 74 | +<body> |
| 75 | + <h1>Hello World</h1> |
| 76 | + <script src="app.js"></script> |
| 77 | +</body> |
| 78 | +</html> |
| 79 | +``` |
| 80 | + |
| 81 | +브라우저의 렌더링 엔진은 아래의 그림과 같은 과정을 거쳐 응답받은 HTML 문서를 파싱하여 |
| 82 | +브라우저가 이해할 수 있는 자료구조인 DOM(Document Object Model)을 생성한다. |
| 83 | + |
| 84 | +<img src="https://github.com/user-attachments/assets/ce843bce-b7fd-4fb4-bc97-14f5de42ab3e"> |
| 85 | + |
| 86 | +<br> |
| 87 | + |
| 88 | +1) 서버에 존재하던 HTML 파일이 브라우저의 요청에 의해 응답된다. 이때 서버는 브라우저가 요청한 HTML 파일을 읽어 들여 메모리에 저장한 다음 메모리에 저장된 바이트(2진수)를 응답한다. |
| 89 | +2) 응답된 바이트 형태의 HTML 문서는 meta 태그의 `charset` 어트리뷰트에 의해 지정된 인코딩 방식(UTF-8 등)을 기준으로 문자열로 변환된다. |
| 90 | +3) 문자열로 변환된 HTML 문서를 읽어 들여 문법적 의미를 갖는 코드의 최소 단위인 토큰(Token)으로 분해된다. |
| 91 | +4) 각 토큰들은 객체로 변환되어 노드(Node)가 된다. 토큰의 내용에 따라 문서 노드, 요소 노드, 어트리뷰트 노드, 텍스트 노드가 생성되고 이들을 트리 자료구조인 DOM을 형성한다. |
| 92 | +5) *모든 노드들은 트리 자료구조로 구성된다. 이 노드들로 구성된 트리 자료구조를 DOM(Document Object Model)이라 한다.* |
| 93 | + |
| 94 | +<br><br> |
| 95 | + |
| 96 | +### CSS 파싱과 CSSOM 생성 |
| 97 | +렌더링 엔진은 HTML을 한줄 씩 순차적으로 파싱하며 DOM을 생성한다. |
| 98 | +DOM을 생성해 나가다가 CSS를 로드하는 `link` 태그나 `style` 태그를 만나면 DOM 생성을 일시 중단한다. |
| 99 | + |
| 100 | +그리고 `link` 태그의 `href` 어트리뷰트에 지정된 CSS 파일을 서버에 요청하여 |
| 101 | +로드한 CSS 파일이나 `style` 태그 내의 CSS를 HTML과 동일한 파싱 과정을 거치며 해석하여 CSSOM(CSS Object Model)을 생성한다. |
| 102 | + |
| 103 | +아래의 예제를 살펴보자. |
| 104 | + |
| 105 | +``` html |
| 106 | +<!DOCTYPE html> |
| 107 | +<html lang="en"> |
| 108 | +<head> |
| 109 | + <meta charset="UTF-8"> |
| 110 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 111 | + <link rel="stylesheet" href="style.css"> |
| 112 | + <title>Document</title> |
| 113 | +</head> |
| 114 | +<body> |
| 115 | + <h1>Hello World</h1> |
| 116 | + <script src="app.js"></script> |
| 117 | +</body> |
| 118 | +</html> |
| 119 | +``` |
| 120 | + |
| 121 | +위 예제에서 CSS 파일을 로드하는 `link` 태그가 있다. |
| 122 | +렌더링 엔진은 `meta` 태그까지 HTML을 순차적으로 해석한 다음, `link` 태그를 만나면 DOM 생성을 일시 중단하고 |
| 123 | +`link` 태그의 `href` 어트리뷰트에 지정된 CSS 파일을 서버에 요청한다. |
| 124 | + |
| 125 | +서버로부터 CSS 파일이 응답되면 렌더링 엔진은 HTML과 동일한 해석 과정을 거쳐 CSS를 파싱하여 CSSOM을 생성한다. |
| 126 | + |
| 127 | +<br><br> |
| 128 | + |
| 129 | +### 렌더 트리 생성 |
| 130 | +렌더링 엔진은 서버로부터 응답된 HTML과 CSS를 파싱하여 각각 DOM과 CSSOM을 생성한다. |
| 131 | +그리고 DOM과 CSSOM은 렌더링을 위해 렌더 트리(render tree)로 결합된다. |
| 132 | + |
| 133 | +렌더 트리는 렌더링을 위한 트리 구조의 자료구조이다. |
| 134 | +> 즉, 브라우저 화면에 렌더링 되지 않는 노드(meta 태그, script 태그 등)와 CSS에 의해 비표시(display: none)되는 노드들은 포함되지 않는다. |
| 135 | +
|
| 136 | +<br> |
| 137 | + |
| 138 | +이후 완성된 렌더 트리는 각 HTML 요소의 레이아웃을 계산하는 데 사용되며 |
| 139 | +브라우저 화면에 픽셀을 렌더링하는 페인팅(painting) 처리에 입력된다. |
| 140 | + |
| 141 | +<img src="https://github.com/user-attachments/assets/878a2422-8aef-4bb9-ab38-b0ed2c0c8d24"> |
| 142 | + |
| 143 | +<br> |
| 144 | + |
| 145 | +렌더링 과정은 반복해서 실행될 수 있다. |
| 146 | +아래와 같은 상황에서는 반복해서 레이아웃 계산과 페인팅이 재차 실행된다. |
| 147 | +- 자바스크립트에 의한 노드 추가 또는 삭제 |
| 148 | +- 브라우저 창의 리사이징에 의한 뷰포트(viewport) 크기 반영 |
| 149 | +- HTML 요소의 레이아웃(위치, 크기)에 변경을 발생시키는 `width`, `height`, `margin`, `padding` 등의 스타일 변경 |
| 150 | + |
| 151 | +<br> |
| 152 | + |
| 153 | +<font color='orange'>레이아웃 계산과 페인팅을 다시 수행하는 리렌더링은 비용이 많이 드는, 성능에 영향을 주는 작업이다.</font> |
| 154 | + |
| 155 | + |
| 156 | +<br><br> |
| 157 | + |
| 158 | +### script 태그의 async/defer 어트리뷰트 |
| 159 | + |
| 160 | +앞에서 살펴본 자바스크립트 파싱에 의해 DOM 생성이 중단(blocking)되는 문제를 근본적으로 해결하기 위해 |
| 161 | +HTML5부터 script 태그에 `async` 어트리뷰트와 `defer` 어트리뷰트가 도입되었다. |
| 162 | + |
| 163 | +<br> |
| 164 | + |
| 165 | +#### async 어트리뷰트 |
| 166 | +HTML 파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 동시에 진행된다. |
| 167 | +단, 자바스크립트의 파싱과 실행은 자바스크립트 파일의 로드가 완료된 직후 진행되며, 이때 HTML 파싱이 중단된다. |
| 168 | + |
| 169 | +여러 개의 `script` 태그에 `async` 어트리뷰트를 지정하면 `script` 태그의 순서와는 상관없이 로드가 완료된다. |
| 170 | +> 순서 보장이 필요한 `script` 태그에는 `async` 어트리뷰트를 지정하지 않는 것이 좋다. |
| 171 | +
|
| 172 | +<br> |
| 173 | + |
| 174 | +#### defer 어트리뷰트 |
| 175 | +HTML 파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 동시에 진행된다. |
| 176 | +단, 자바스크립트의 파싱과 실행은 HTML 파싱이 완료된 직후, 즉 DOM 생성이 완료된 이후에 진행된다. |
| 177 | + |
| 178 | +여러 개의 `script` 태그에 `defer` 어트리뷰트를 지정하면 `script` 태그의 순서대로 로드되며, 로드가 완료된 직후 파싱이 시작된다. |
| 179 | +> 순서 보장이 중요한 `script` 태그에는 `defer` 어트리뷰트를 지정하는 것이 좋다. |
| 180 | +
|
| 181 | +<br> |
| 182 | + |
| 183 | +끝. |
0 commit comments