브라우저의 렌더링 과정

귤양·2021년 6월 30일
0

Javascript

목록 보기
10/10
post-thumbnail

아래 내용은 학원 수업과 "모던 자바스크립트 Deep Dive : 이웅모 저"를 읽고 정리한 내용입니다.

자바스크립트는 브라우저에서 실행되기 위해서 만들어졌으며 HTML, CSS와 함께 실행된다. 따라서 브라우저가 파일들을 어떻게 화면에 렌더링 하는지를 알아두는 것이 도움이 될 수 있다.

요청과 응답

브라우저는 HTML, CSS, 자바스크립트와 같은 파일(리소스)을 서버에 요청하고, 응답받은 파일(리소스)들을 화면에 렌더링 한다.
브라우저에 있는 주소창에는 서버의 위치를 가르키는 주소(URL)을 입력할 수 있고, 이 URL은 DNS를 통해 IP 주소로 변환되어 해당 서버에 요청을 전송한다.

이렇게 브라우저와 서버간에 요청과 응답을 하는 것을 통신을 한다고 표현하는데, 이 통신을 위한 프로토콜(규약)을 HTTP라고 한다. HTTP는 1.1과 2.0으로 나뉘게 되는데, 이 두가지는 큰 차이점이 있다. 바로 한번의 커넥션당 처리할 수 있는 파일의 갯수이다. 1.1은 한번에 하나의 파일만 요청하고 응답받을 수 있다. 때문에 요청해야 할 리소스의 개수가 많으면 응답 시간이 길어지는 단점을 가지고 있다. 반면 2.0은 한 커넥션에서 다중 요청과 응답이 가능하기 때문에 속도가 더 빠르다.

HTML 파싱과 DOM 생성

브라우저가 서버에 요청해서 응답받은 HTML 파일은 브라우저가 이해할 수 있도록 변환해야 하는데 이 과정을 파싱을 한다고 하며, 파싱을 통해 생성된 자료구조를 DOM이라고 한다.

DOM

DOM이란 HTML 문서를 토큰(문법적 의미를 갖는 코드의 최소 단위)으로 분해하고, 이 토큰으로 DOM을 구성하는 노드를 생성한다. 이 노드들은 문서 노드(document), 요소 노드(HTML, body, div 등), 어트리뷰트 노드(요소의 추가 정보를 넣을 때 사용하는 요소), 텍스트 노드(텍스트)로 분류할 수 있다.

HTML을 작성해본 사람들이라면 쉽게 알 수 있지만, HTML 파일은 각 요소들이 중첩 관계를 가지고 있다. 따라서 DOM을 생성할 때에도 이런 중첩 관계들을 반영하게 되는데, 이 모습 때문에 DOM 트리라고 부르기도 한다.

HTML 파싱 과정

브라우저의 렌더링 엔진은 HTML 파일을 위에서부터 순서대로 파싱해서 DOM 트리를 만들게 되는데, DOM 트리를 생성하다가 CSS를 로드하는 link 태그 혹은 style 태그를 만나면 DOM 생성을 일시 중단하고, CSS 파일을 요청하여 응답 받은 뒤 HTML 파일과 마찬가지로 파싱하여 CSSOM을 생성하고 완료되면 다시 HTML 파일 파싱을 시작하게 된다.

이 때 만들어지는 CSSOM은 CSS가 상속을 통해 최종적으로 반영해야할 CSS를 바탕으로 만들어지게 된다. 이렇게 만들어진 CSSOM은 DOM과 합쳐져서 렌더 트리를 만들게 되고, 렌더 트리는 말 그대로 브라우저에 렌더링을 하기 위한 자료구조이기 때문에 화면에 렌더링 되지 않는 노드나, CSS에서 display:none을 통해 화면에 표시되지 않도록 하는 노드들은 포함하지 않으며, 요소 노드들을 화면에 표시하기 위한 레이아웃을 계산할 때 사용되어 브라우저에 픽셀을 렌더링 하는 페인팅 처리에 입력된다.

HTML 파일을 파싱하는 과정에서 Javascript 파일을 만나면 CSS 파일과 마찬가지로 HTML의 파싱을 중지하고, 자바스크립트 코드를 파싱하기 위해서 자바스크립트 엔진에 제어권을 넘기게 되고, 자바스크립트의 파싱이 끝난 뒤에 다시 브라우저 렌더링 엔진으로 제어권이 넘어오게 된다.

자바스크립트 엔진은 자바스크립트를 해석하여 ATS(Abstract Syntax Tree, 추상적 구문 트리)를 생성한다. 생성된 AST는 바이트 코드로 변환되어 실행되는데, 자바스크립트 코드가 DOM, CSSOM 을 변경하게 되면 변경된 DOM, CSSOM 트리가 렌더 트리로 결합되어 레이아웃과 페인트 과정을 거쳐 다시 브라우저에 렌더링 되는데 이 과정을 리플로우 리페인트라고 한다.

좀 더 자세히 설명하자면 리플로우는 레이아웃 계산을 다시 하는 것을 말하며, 노드 추가/삭제, 요소의 크기/위치 변경 윈도우 리사이징 등 레이아웃에 영향을 주는 경우에 실행되고, 리페인트는 재결합된 렌더트리를 다시 페인트 하는 것을 말한다. 따라서 레이아웃에 변경이 없다면 리플로우는 실행되지 않고, 리페인트만 실행되게 된다.

이 렌더링 과정은 자바스크립트, 브라우저 창의 리사이징, HTML 요소의 레이아웃을 변경하는 스타일에 변경이 일어날 경우 다시 반복해서 실행(리렌더링)된다. 하지만 이 과정은 성능에 악영향을 주는 작업이므로 리렌더링이 발생하지 않도록 하는 것이 좋다.

렌더링 엔진과 자바스크립트 엔진은 위에서 아래 방향의 직렬적 방향으로 파싱하고 실행한다. 때문에 자바스크립트를 사용하는 script 태그의 위치가 중요하다. 자바스크립트를 실행하기 위해서는 DOM, CSSOM트리의 생성이 중단되게 되는데, 실행하는 자바스크립트에 DOM, CSS를 변경하는 코드가 있을 경우 DOM과 CSSOM이 이미 생성되어 있어야 문제가 발생하지 않게 되기 떄문이다. 때문에 script 태그의 경우 body 태그의 가장 아래쪽에 위치하는 것이 좋다. 하지만 HTML5가 나오면서 추가된 async, defer 어트리뷰트를 사용하면 HTML 파싱과 자바스크립트 파일의 로드가 비동기적으로 동시에 진행된다.

이 때 async와 defer에는 차이가 있는데 async의 경우 script 태그의 로드가 완료는 순간 자바스크립트가 바로 실행 되므로 순서가 보장되지 않기 때문에 순서 보장이 필요한 경우에는 async 어트리뷰트를 사용하는 것은 올바르지 않다.

이와는 다르게 defer 어트리뷰트는 자바스크립트 파일의 로드가 완료되었더라도 DOM 생성까지 완료된 이후에 자바스크립트가 실행된다. 따라서 script 파일이 body 태그의 가장 마지막에 있는 것과 같은 효과를 기대할 수 있다.

다만 이 두 어트리뷰트는 모드 IE10이상에서만 정상적으로 지원되므로, IE 구형 브라우저를 지원해야 하는 경우에는 사용할 수 없다.

profile
디자인하는 Frontend Developer.

0개의 댓글