[Web] 브라우저의 렌더링 과정

초코침·2023년 3월 14일
0

Web

목록 보기
3/5

HTML 문서가 어떤 과정을 거쳐 사용자에게 시각적으로 보여지게 될까?


우선, HTML은 문서이며 문자열로 이루어진 순수한 텍스트다. 따라서 이러한 문자열 자체를 브라우저가 이해할 수 있게 변환해야만 비로소 시각적으로 보여질 수 있게 된다.

이때 문자열을 브라우저가 이해할 수 있는 것으로 변환하는 과정을 파싱이라고 한다.

HTML 파싱

HTML 파싱은 브라우저 렌더링 엔진에 의해 다음 순서대로 이루어진다.

  1. 서버에 HTML 문서를 요청하고 바이트 형태로 응답 받는다.
  2. 바이트 형태의 HTML 문서를 문자열로 변환한다.
    이때 HTML 문서 meta 태그의 charset 어트리뷰트에 선언된 인코딩 방식을 이용한다.

  1. 문자열로 변환된 HTML 문서를 문법적 의미를 갖는 최소 단위인 토큰들로 분해한다.
  2. 분해한 각 토큰들을 객체로 변환하여 노드를 생성한다.
  3. HTML 요소 간의 부모-자식 관계를 반영해 모든 노드들을 트리 자료구조로 엮어 DOM을 구성한다.

렌더링 엔진은 HTML을 첫 번째 라인부터 순차적으로 파싱한다. 따라서 위에서부터 차례대로 읽어 나가다가 CSS를 로드하는 link 태그나 style 태그를 만나면 DOM 생성을 잠시 중단하고 CSS를 파싱한다.

CSS 파싱

렌더링 엔진은 CSS 파일을 서버에 요청해 받은 다음 HTML과 동일한 파싱 과정(바이트 → 문자 → 토큰 → 노드 → CSSOM)을 거쳐 CSSOM을 생성한다. (style 태그의 경우 서버에게 요청하는 과정이 없고 바로 파싱)

CSSOM란 CSS 객체 모델이다.

CSSOMCSS의 상속을 반영하여 생성된다. 상속을 반영하기 때문에, 예시로 부모 태그인 body에 font-size 속성을 지정한 경우 body의 자식 요소도 body와 동일한 font-size 속성을 갖게 된다.


렌더링 엔진이 HTML과 CSS를 각각 파싱하면 DOM과 CSSOM이 생성된다고 했다.

이 다음으로는, 이때 생성된 객체 모델들(DOM과 CSSOM)을 렌더 트리로 결합한다.

렌더 트리

렌더 트리란 렌더링을 위한 트리 구조의 자료구조다. 브라우저 화면에 표시할 노드들을 담고 있으며 CSS에 비표시되는 노드들은 포함하지 않는다.

렌더 트리는 브라우저 화면에 렌더링될 노드들로만 구성되어 있다.


렌더 트리는 각 요소의 위치나 크기 등 레이아웃을 계산하는데 사용되고, 브라우저 화면에 픽셀을 렌더링하는 페인팅 처리에 입력된다.

페인팅이란, 브라우저 화면에 픽셀을 렌더링하는 작업이다.


렌더링 과정을 요약하자면,

HTMLCSS를 파싱하여 DOM 트리CSS 트리를 생성하고,

두 트리를 하나로 합쳐 렌더 트리를 생성한 다음,

렌더 트리를 바탕으로 레이아웃을 계산해 브라우저에 페인팅한다.

이때, DOM이나 CSS에 조작(변경)이 발생한다면 어떻게 될까?

노드를 추가/삭제하거나 요소의 크기/위치를 변경하거나 윈도우의 사이즈를 변경하는 등 레이아웃에 영향을 주는 변경이 생기면 reflow가 발생한다.

reflow란 레이아웃을 다시 계산하는 것을 말한다.

reflow가 발생하면 변경한대로 다시 브라우저에 그리는 repaint가 발생한다.

repaint란 재결합된 렌더 트리를 기반으로 다시 페인트를 칠하는 것을 말한다.

만약 레이아웃에 영향이 없는 변경이라면 repaint만 발생하지만, 어찌됐든 reflowrepaint 즉, 리렌더링은 비용이 많이 드는 작업이다. 즉, 성능에 악영향을 줄 수 있다.

[참고] 브라우저의 Reflow를 최소화하려면?

구글에서는 웹페이지의 reflow를 최소화하기 위한 가이드라인을 제시하고 있다.

  1. 불필요한 DOM의 depth를 줄인다.
  2. CSS 규칙을 최소화하고, 사용하지 않는 규칙은 삭제한다.
  3. 애니메이션 같은 복잡한 렌더링 변경이 필요하다면 흐름 밖에서 변경하도록 하며, 변경할 때는 절대 위치나 고정 위치를 사용한다.
  4. CPU를 많이 사용하는 불필요하고 복잡한 CSS 셀렉터를 사용하지 않는다.

JavaScript 파싱

HTML 파일에는 CSS 이외에도 JavaScript 파일을 로드할 수 있다. 그렇다면 JavaScript는 어떻게 로드될까?

마찬가지로 렌더링 엔진이 순차적으로 파싱하다가 script를 만나면 DOM 생성을 일시 중단한다. 이때 CSS와 다른 점은 렌더링 엔진이 아닌 자바스크립트 엔진이 자바스크립트를 파싱한다는 점이다.

자바스크립트 엔진은 자바스크립트 코드를 파싱하여 AST를 생성한다.

AST란 추상적 구문 트리(Abstract Syntax Tree)를 말한다.

이 AST를 기반으로 인터프리터가 실행할 수 있는 바이트 코드를 생성해 실행한다.

즉, 자바스크립트 엔진은 자바스크립트 코드를 파싱해 저수준 언어로 변환하고 실행한다.

[참고] script 태그의 위치 (script를 body의 마지막에 위치시켜야 하는 이유)

렌더링 엔진과 자바스크립트 엔진은 병렬적으로 파싱하지 않고 직렬적으로 파싱한다.

즉, 렌더링 엔진의 파싱과 자바스크립트 엔진의 파싱은 동시에 일어날 수 없다.

렌더링 엔진은 DOM을 생성하다가 script 태그를 만나면 HTML 파싱은 블로킹되고 자바스크립트 코드를 파싱하기 시작한다.

자바스크립트 코드에서 DOM을 조작하려면 HTML이 파싱되어 DOM이 생성되어 있어야만 한다. 자바스크립트 코드가 아직 생성되지 않은 DOM을 조작하려 한다면 에러가 발생한다.

따라서 script 태그는 DOM이 모두 생성된 시점일 body의 마지막에 위치시키는 것이 바람직하다.

뿐만 아니라 script 태그를 body의 마지막에 위치시킨다면 자바스크립트 코드의 로딩/파싱/실행 즉, 블로킹으로 인해 HTML 요소들의 렌더링에 지장받는 일이 발생하지 않아 페이지 로딩 시간이 단축될 수 있다.

profile
블로그 이사중 🚚 (https://sungjihyun.vercel.app)

0개의 댓글