Search

브라우저의 렌더링 과정

브라우저는 다음과 같은 과정을 거쳐 렌더링을 수행합니다.
1.
브라우저는 HTML, CSS, JS, 이미지, 폰트 파일 등 렌더링에 필요한 리소스를 요청하고 서버로부터 응답을 받습니다.
2.
브라우저 렌더링 엔진은 서버로부터 응답된 HTML과 CSS를 파싱하여 DOM과 CSSOM을 생성하고 이들을 결합하여 렌더 트리를 생성합니다.
3.
브라우저의 JS 엔진은 서버로부터 응답된 JS를 파싱하여 AST(Abstract Syntax Tree)를 생성하고 바이트코드로 변환하여 실행합니다. 이때 JS는 DOM API를 통해 DOM이나 CSSOM을 변경할 수 있습니다. 변경된 DOM과 CSSOM은 다시 렌더 트리로 결합됩니다.
4.
렌더 트리를 기반으로 HTML 요소의 레이아웃(위치와 크기)을 계산하고 브라우저 화면에 HTML 요소를 페인팅합니다.

38.1 요청과 응답

해당 URI를 통해서 요청을 보내면 해당 경로에 맞는 파일이 오는데 이때 정적 파일과 동적 파일 둘 중 하나가 옵니다. 이때 css, js, img, font 등도 같이 오는데 HTML을 파싱하면서 외부 리소스를 가져오기 때문입니다.

38.2 HTTP 1.1과 HTTP 2.0

1.1의 경우 하나의 커넥션에 하나의 요청과 응답만 처리합니다. 2.0의 경우 커넥션당 여러개의 요청과 응답이 처리 가능해 50%정도 속도가 빨라진다고 합니다.

38.3 HTML 파싱과 DOM 생성

서버가 응답한 HTML 문서는 문자열로 이루어진 순수한 텍스트이기 때문에 이해할 수 있는 자료구조로 변환하여 메모리에 저장해야 합니다. 브라우저 엔진은 다음 그림과 같은 과정을 통해 응답받은 HTML 문서를 파싱하여 브라우저가 이해할 수 있는 자료구조인 DOM을 생성합니다.
1.
서버에 존재하던 HTML 파일이 브라우저 요청에 의해 응답합니다. 이때 서버는 HTML파일을 읽어 메모리에 저장된 데이터를 응답합니다.
2.
응답 받은 바이트를 HTML문서는 meta의 charset의 인코딩 방식을 기준으로 문자열로 변환됩니다.
3.
이를 문법적 의미를 갖는 최소 단위인 토큰들로 분해합니다.
4.
각 토큰들을 객체로 변환하여 노드들을 생성합니다. 문서노드, 요소 노드, 어트리뷰트 노드, 텍스트 노드가 생성됩니다.
5.
HTML 요소는 중첩 관걔를 갖기 때문에 모든 노드들을 트리 자료구조로 구성하는데 이를 DOM이라고 부릅니다.

38.4 CSS 파싱과 CSSOM 생성

렌더링 엔진은 DOM을 생성해 나가다가 link나 style태그를 만나면 DOM 생성을 일시 중단합니다. 이후 동일한 파싱 과정(바이트 → 문자 → 토큰 → 노드 → CSSOM)을 거치며 해석하여 CSSOM을 생성합니다.

38.5 렌더 트리 생성

렌더링 엔진은 서버로부터 응답된 HTML과 CSS를 파싱하여 DOM과 CSSOM을 만든 후 DOM과 CSSOM은 렌더링을 위해 렌더 트리로 결합됩니다.이때 화면에 렌더링 되지 않는 노드(meta, script)나 CSS에 의해 비표시(display: none)되는 노드들은 포함되지 않습니다.
이후 렌더 트리는 HTML 요소의 레이아웃을 계산하는데 사용되며, 브라우저 화면에 픽셀을 렌더링하는 페인팅 처리에 입력됩니다.
이러한 과정들은 반복 실행될 수 있는데
JS에 의한 노드 추가 삭제
브라우저 창의 리사이징에 의한 뷰포트 크기 변경
HTML 요소의 레이아웃에 변경을 발생시키는 width/height, margin, padding, border, display, position, top/right/bttom/left 등의 스타일 변경

38.6 JS 파싱과 실행

JS의 파싱과 실행은 브라우저 엔진이 아닌 JS 엔진이 처리하는데, JS를 저수준 언어로 변환하고, 실행합니다. JS 엔진도 DOM과 CSSOM을 만들듯이 ATS(Abstract Syntax Tree)를 생성합니다. 그리고 이를 기반으로 인터프리터가 실행할 수 있는 중간 코드인 바이트코드를 생성하여 실행합니다.

토크나이징 Tokenizing

단순 문자열인 소스코드를 문법적 의미를 갖는 최소 단위인 토큰으로 분회합니다.

파싱 Parsing

토큰들의 집합을 구문 분석하여 ATS 트리를 생성합니다. AST를 통해 TS, Babel, Prettier 같은 트랜스파일러도 구현할 수 있습니다.

바이트코드 생성과 실행

AST는 인터프리터가 실행할 수 있는 중간 코드인 바이트코드로 변환되고 인터프리터에 의해 실행됩니다.

38.7 리플로우와 리페인트

JS 코드에 DOM이나 CSSOM을 변경하는 DOM API가 사용된 경우 DOM이나 CSSOM이 변경됩니다. 이때 변경된 DOM이나 CSSOM은 렌더 트리로 결합되고 이를 기반으로 레이아웃과 페인트 과정을 거쳐 브라우저 화변에 다시 렌더링되는데 이를 리플로우, 리페인트라고 합니다.

리플로우

레이아웃 계산을 다시하는 것을 말하며, 노드 추가/삭제, 요소의 크기/위치 변경, 위도우 리사이징 등 레이아웃에 영향을 주는 변경이 발생한 경우에 한하여 실행됩니다.

리페인트

재결합된 렌더 트리를 기반으로 다시 페인트 하는 것을 이야기합니다. 레이아웃 영향이 ㅇ벗는 변경은 리플로우 없이 리페인트만 실행됩니다.

38.8 JS 파싱에 의한 HTML 파싱 중단

브라우저는 동기적으로 실행을 하기 때문에 script 태그의 위치에 따라 HTML 파싱이 블로킹되어 DOM 생성이 지연될 수 있습니다. 따라서 body 요소의 가장 아래에 JS를 위치시키는 것이 좋습니다.
DOM이 완성되지 않은 상태에서 JS가 DOM을 조작시 에러가 발생할 수 있습니다.
JS 로딩/파싱/실행으로 인해 HTML 요소들의 렌더링에 지장받는 일이 발생하지 않아 페이지 로딩 시간이 단축됩니다.

38.9 script 태그의 async/defer 어트리뷰트

DOM 생성이 중단되는 문제를 근본적으로 해결하기 위해 HTML5부터 script태그에 asyn와 defer 속성이 추가되었습니다. 이는 src를 통해 외부 JS 파일을 로드할 경우에만 사용할 수 있습니다. 이는 HTML 파싱과 외부 JS 파일의 로드가 비동기적으로 동시에 진행됩니다만 실행 시점에 차이가 있습니다.

async Attr

HTML 파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 동시에 진행됩니다. 단, JS의 파싱과 실행은 JS 파일의 로드가 완료된 직후에 진행되며 HMTL 파싱이 중단됩니다. 또한 여러 script 태그에 async 지정 시 순서와 상관없이 로드가 완료된 순서대로 실행되어 순서가 보장되지 않습니다.

defer Attr

async와 마찬가지로 HTML 파싱과 로드가 비동기적으로 동시에 진행됩니다. 단 JS 파싱과 실행은 HTML 파싱이 완료된 직후, 즉 DOM 생성이 완료된 직후에 진행됩니다.