상권's

TIL 68 (브라우저 렌더링 과정) 본문

~2022 작성 글/TIL

TIL 68 (브라우저 렌더링 과정)

라마치 2022. 3. 15. 22:07

앞 선 TIL 54에서 모든 자바스크립트 파일을 브라우저에서 한 번에 로딩할 경우의 문제점에 대해서 간단하게 알아봤었습니다. 오늘은 조금 더 구체적으로 브라우저 렌더링 과정에 대해서 알아보고 script 태그의 async / defer 어트리뷰트도 함께 다뤄보겠습니다.

 

브라우저의 요청에 의해 서버가 응답한 HTML 문서는 문자열로 이루어진 순수한 텍스트입니다. 이러한 HTML 파일을 브라우저의 렌더링 엔진이 파싱하고 브라우저가 이해할 수 있는 자료구조인 DOM을 생성합니다.

 

DOM이 생성되는 순서를 알아보겠습니다.

 

1. HTML 파일을 읽어 들여 메모리에 저장한 다음 메모리에 저장된 바이트를 인터넷을 경유하여 응답

2. 응답된 바이트 형태의 HTML 문서는 meta 태그의 charset 어트리뷰트에 의한 인코딩 방식을 기준으로 문자열로 변환

3. 이를 문법적 의미를 갖는 코드의 최소 단위인 토큰으로 분해

4. 토큰을 객체로 변환하여 노드 생성

5. 중첩 관계에 의해 노드를 트리 자료구조로 구성 => DOM

 

이처럼 DOM은 HTML 문서를 파싱한 결과물입니다.

 

HTML의 DOM처럼 CSS 또한 CSSOM을 생성합니다.

CSS를 로드하는 link나, style 태그를 만나면 DOM 생성을 일시 중단하는데 HTML과 동일한 파싱 과정으로 바이트 -> 문자 -> 토큰 -> 노드 순으로 CSSOM이 생성됩니다.

 

HTML과 CSS로 DOM과 CSSOM이 생성되면 렌더링을 위해 렌더 트리로 결합됩니다. 렌더 트리는 렌더링을 위한 트리 구조의 자료구조로, 브라우저 화면에 렌더링 되는 노드만으로 구성됩니다.

 

완성된 렌더 트리는 각 HTML 요소의 레이아웃을 계산하는 데 사용되며 브라우저 화면에 픽셀을 렌더링하는 페인팅 처리에 입력됩니다. 레이아웃 계산과 페인팅 처리는 자바스크립트나 브라우저 창의 리사이징, 스타일의 변경 등으로 반복해서 실행될 수 있습니다. 하지만, 성능에 악영향을 주는 작업으로, 빈번하게 발생하지 않도록 주의할 필요가 있습니다.

 

CSS 파싱과 동일하게 렌더링 엔진은 자바스크립트 파일을 로드하는 script 태그를 만나면 DOM 생성을 일시 중단합니다. 그리고 자바스크립트 엔진으로 제어권 넘겼다가, 자바스크립트 파싱과 실행이 종료되면 렌더링 엔진으로 다시 제어권을 넘겨 남은 HTML 파싱이 이루어집니다.

 

자바스크립트 엔진이 렌더링 엔진으로부터 제어권을 넘겨 받으면 자바스크립트를 해석합니다. 단순한 문자열인 자바스크립트 소스코드를 문법적 의미를 가지는 코드의 최소 단위인 토큰들로 분해합니다. 이 과정을 토크나이징이라고 합니다. 토큰들의 집합을 구문 분석하여  AST(Abstract Syntax Tree, 추상적 구문 트리)를 생성합니다. 그리고 ASP를 기반으로 인터프리터(해석기)가 실행할 수 있는 중간 코드인 바이트 코드를 생성하여 실행합니다.

 

만약 자바스크립트 코드에 의해 DOM이나 CSSOM을 변경하는 DOM API가 사용될 경우, DOM과 CSSOM의 변경됩니다. 이후 다시 렌더 트리로 결합되고 변경된 렌더 트리를 기반으로 레이아웃과 페인트 과정을 거쳐 브라우저의 화면에 다시 렌더링합니다. 이를 리플로우리페인트라고 합니다.

 

이렇게 브라우저 렌더링이 이루어집니다. 여기서 주의할 것은 HTML 파싱 과정은 병렬적으로 실행되지 않고 CSS나 자바스크립트를 만나게 될 경우 멈추게 됩니다. 이것은 script 태그의 위치에 따라 HTML 파싱이 블로킹되어 DOM 생성이 지연도리 수 있다는 것을 의미합니다. 만약 script 태그가 DOM 생성 이전에 실행되는데, 이때 DOM API를 사용할 경우 DOM 생성이 이루어지지 않았기 때문에 에러가 발생합니다.

 

이러한 이유로 HTML의 body 요소의 가장 아래에 자바스크립트를 위치 시키는 것을 추천합니다.

이는 앞 서 말씀드린 것 처럼 DOM이 완성되지 않은 상태에서 자바스크립트가 DOM을 조작하면 에러가 발생하는 문제 해결자바스크립트 로딩/파싱/실행으로 인해 HTML 요소들의 렌더링에 지장 받는 일이 발생하지 않아 페이지 로딩 시간이 단축되기 때문입니다. 

 

하지만 이는 자바스크립트 파싱에 의한 DOM 생성 중단되는 문제를 근본적으로 해결하는 것은 아닙니다. HTML5부터 이러한 문제를 해결하기 위한 script의 async와 defer 어트리뷰트가 추가되었습니다.

 

async와 defer 어트리뷰트는 src 어트리뷰트를 통해 외부 자바스크립트 파일을 로드하는 경우에만 사용할 수 있습니다.

 

async와 defer 어트리뷰트의 차이에 대해서 알아보겠습니다.

 

async 어트리뷰트의 경우에는 HTML 파싱과 외부 자바스크립트 파일의 로드가 비동적으로 동시에 진행됩니다. 여기서 로드만 동시에 진행되기 때문에 자바스크립트 파싱과 실행은 기존처럼 HTML 파싱을 중단하게 됩니다. 그리고 여러 개의 script 태그에 async 어트리뷰트를 지정하게 되면 script 태그의 순서와는 상관없이 로드된 순서대로 실행되기 때문에 자바스크립트 실행 순서를 보장하지 못합니다.

 

반면 defer 어트리뷰트는 HTML 파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 동시에 진행되는 것은 async와 동일하지만 자바스크립트의 실행는 HTML 파싱이 완료된 직후, 즉 DOM 생성이 이뤄진 후 진행됩니다.

 

오늘은 이렇게 브라우저의 렌더링 과정에 대해서 알아보았습니다. 확실히 개념을 세울 수 있는 기회가 된 것 같습니다. 이러한 개념들을 실무에서 활용할 수 있는 개발자가 되기 위해 노력하겠습니다.

'~2022 작성 글 > TIL' 카테고리의 다른 글

TIL 71 (함수)  (0) 2022.03.21
TIL 70 (async/await)  (0) 2022.03.19
TIL 67 (클로저)  (0) 2022.03.01
TIL 66 (렉시컬 환경)  (0) 2022.03.01
TIL 65 (실행 컨텍스트)  (0) 2022.02.27
Comments