브라우저 동작과정 최종정리


:raising_hand: 프론트엔드 개발관련 공부내용을 기록하는 포스트 입니다.


1. 개요


브라우저 동작과정에 대해서 그동안 공부해왔던 모든 것들을 최종적으로 정리해본다. 렌더링` 해당 포스팅은 네이버 D2 블로그 관련 내용을 토대로 정리하였습니다.

2. 브라우저 동작과정


브라우저의 주요 기능은 웹 상에서 사용자가 선택한 자원을 서버에 요청하고 브라우저에 표시하는 것이다.

2-1. 브라우저의 기본 구조

image

  • 사용자 인터페이스 : 우리가 브라우저에서 볼수 있는 주소표시줄, 버튼 등 사용자가 컨트롤 할 수 있는 부분
  • 브라우저 엔진 : 사용자 인터페이스렌더링 엔진 사이 동작을 제어
  • 렌더링 엔진
    • 사용자가 요청한 URI브라우저 엔진 으로부터 받아 서버에 요청한다.
    • 서버로 URI 요청하여 응답받은 데이터(HTML, CSS , JavaScript) 를 받아서 파싱 후 렌더링한다.(Chrome webkit)
  • 통신 : 렌더링 엔진으로부터 HTTP 요청 등을 받아서 네트워크 처리 후 응답을 전달
  • 자바스크립트 해석기 : JavaScript 를 파싱한다(chrome V8)
  • 자료 저장소 : 쿠키 등의 자료를 컴퓨터 하드디스크저장한다. (HTML5 부터 Web Database 에 저장가능)
  • UI 백엔드 : render tree 를 Browser 에 그리는 역할을 담당

위와 같은 역할을 하는 브라우저의 각 기능별로 자세히 알아본다.

2-2. Rendering 과정

브라우저의 렌더링 엔진은 구글이 사용하는 웹킷(Webkit) 을 기준으로 담는다.

image

image

위 과정을 Critical Rendering Path 로 불리운다.

  1. HTML 마크업을 처리하고 DOM 트리를 만든다.
    • HTML 파싱
  2. CSS 마크업을 처리하고 CSSOM 트리를 만든다.
    • Stylesheet 파싱
  3. DOMCSSOM을 결합하여 렌더 트리를 만든다.
    • 스타일 계산
  4. 렌더 트리에서 레이아웃을 실행하여 각 노드를 기하학적 형태를 계산한다.
  5. 개별 노드를 화면에 그린다(Paint).
    • Paint
    • Raster
    • Composit Layer

위 프로세스의 전체 혹은 일부를 거쳐 화면이 그려지게 되며 이에 따라 성능이 다르게 나올 수 밖에 없다.

DOM 트리 생성

DOM 트리생성은 아래와 같은 과정으로 진행된다.

Bytes > Characters > Tokens > Nodes > DOM
  1. 변환(Bytes -> Characters)

    • 브라우저가 HTML의 원시 Byte디스크 혹은 네트워크에서 읽어와서, 해당 파일에 지정된 인코딩에 따라 개별 문자로 변환
  2. 토큰화(Character -> Tokens)

    • 브라우저가 문자열을 고유 토큰으로 변환하며, 각 토큰은 특별한 의미와 고유한 규칙을 가진다.
    • 토큰화는 파싱을 통해 진행된다.
  3. 렉싱(Tokens -> Nodes)

    • 구문 분석기노드를 파싱 트리에 추가하기 위해 어휘 분석기 즉, 토큰화된 토큰을 요청하여 노드 객체로 변환
  4. DOM 생성(Nodes -> DOM)

    • 마지막으로 HTML 마크업이 상위 / 하위 관계가 포함된 트리로 생성
DOM 트리 구축을 위한 HTML 파싱

문서 파싱은 브라우저가 코드를 이해하고 사용할 수 있는 구조로 변환하는 것을 의미한다.

파싱은 어휘 분석(어휘 분석기)구문 분석(파서) 두가지로 구분할 수 있다.

어휘 분석은 자료를 토큰으로 분해하는 역할을 진행한다.

토큰은 구문의 단어를 의미한다 생각하면 될 것이며, 유효한 토큰으로 분해하면서 어휘 분석기는 공백줄 바꿈 같은 의미 없는 문자를 제거한다.

이후 구문 분석을 진행하는데 이는 어휘 분석기로부터 변환된 새로운 유효한 토큰을 받아 구문 규칙과 일치하는지 확인한다.

이후 규칙에 맞는다면 토큰에 해당하는 노드가 파싱 트리에 추가되고, 맞지 않으면 토큰을 내부적으로 저장 후 어휘 분석기에게 일치하는 규칙이 발견될까지 요청한다.

규칙이 없는 경우는 예외 처리를 진행하며 문제가 없으면 다음 토큰을 요청한다.

이러한 파싱을 통해 마크업들을 분석하여 DOM 트리로 변환하게 된다.

이후 CSS 정보 또한 파싱을 통해 CSSOM 트리로 변환이 된다.

이 과정에서 <script> 태그를 만나게 되면 파싱은 일시적으로 중단을 하고 우선적으로 스크립트를 파싱한 후에 다시 진행이된다.

이러한 부분으로 인해 발생하는 문제점과 지연(defer), 비동기(async) 를 통해서 해결하는 방법은 별도의 포스팅으로 남긴다.

CSSOM 트리 구축

수신된 CSS 규칙을 브라우저가 이해하고 처리할 수 있는 형식으로 변환하기 위해 DOM 트리생성과 동일하게 CSSOM 트리를 생성한다.

렌더트리 구축

DOM 트리가 구축되는 동안 브라우저렌더 트리를 구축한다.

이는 표시해야 할 순서와 문서의 시각적인 구성 요소로써 올바른 순서로 내용을 그려낼 수 있도록 하기 위한 목적을 가진다.

웹킷에서는 이 구성요소를 렌더러(renderer) 또는 렌더 객체(render object)라는 용어를 사용한다.

웹킷렌더러의 기본 클레스는 아래와 같다.

class RenderObject { virtual
    void layout(); virtual
    void paint(PaintInfo); virtual
    void rect repaintRect();
    Node * node; //the DOM node
    RenderStyle * style; // the computed style
    RenderLayer * containgLayer; //the containing z-index layer
}

각각의 렌더러CSS2 명세에따라 CSS 박스에 부합하는 사각형을 표시하며, 너비, 높이 그리고 위치와 같은 기하학적 정보를 포함한다.

즉, 렌더러는 화면에 전체적인 구조를 잡는다고 생각하면 될 것이다.

이러한 렌더러를 만드는 과정을 어태치먼트(attachment)라 부른다. 모든 DOM 노드에는 attach 메서드가 있으며, DOM 트리에 노드가 추가되면 새 노드의 attach 메서드를 호출한다.

결과적으로 정리를 해본다면 이렇다.

렌더 트리를 구축하기 위해 html, body태그 를 처리하면서 렌더 트리 루트를 구성하고 이후 파서가 DOM 트리를 구축하면서 DOM 노드가 추가되면 동기적으로 attach 메서드를 호출해서 렌더 트리의 나머지 부분을 구축하는 방식인것이다.

이후 최적화된 과정 즉, 규칙에 따라서 렌더 트리를 구축하는 과정에서 DOM 노드스타일 시트정보를 규칙에 따라 적용하여 DOM의 시각적 요소와 스타일요소가 규칙에 따라 정의된 트리를 만들어 낸다.

즉, 정리하면 아래와 같다.

1. CSSOM 의 트리 정보와 DOM 트리의 정보를 활용해서 렌더링엔진이 렌더트리생성
2. 렌더 트리는 화면에 표시되어야할 모든 콘텐츠와 스타일을 가지고 있는 트리
3. 만들어지는 과정은 DOM 트리의 최상단 노드인 루트노드(document 노드)부터 각 노드를 순회하면서
각 노드에 맞는 CSSOM 안의 노드를 찾아서 규칙을 적용하여 만든다.
4. meta, display:none 과 같은 부분은 렌더 트리에 포함되지 않는다.

DOM트리와 렌더트리의 관계

image

렌더러는 모든 DOM 트리 요소1:1대응은 아니며, head 요소 및 display 속성이 none값이 할당된 요소와 같은 비시각적 DOM 요소렌더 트리에 추가되지 않는다.

예를들면, ` select``의 경우 ` 표시영역, 드롭다운 목록, 버튼 표시를 위한 3개의 렌더러가 해당 DOM 트리 노드에 대응 될 것이다.

Layout(배치) 과정

렌더러가 생성되면 attach 과정을 통해 DOM 요소에 스타일 정보를 입힌 렌더 트리가 생성된다.

결과적으로 생성된 렌더 트리노드 와 노드스타일만 계산되어 있다.

하지만, 앞서말한 렌더러가 생성되어 트리에 추가될때 크기나 위치정보에 대한 계산은 없었는데 이 배치과정에서 이를 계산하게되며 배치 또는 리플로라고 한다.

배치<html>요소에 해당하는 최상위 렌더러에서 시작되어 각 노드를 반복하며 각 렌더러에 필요한 크기위치 정보를 계사한다.

페이지에서 각 객체의 정확한 크기위치를 파악하기 위해 브라우저는 렌더 트리 루트에서부터 렌더 트리를 탐색하며 각 노드의 Box Type을 확인한다.

TypeBlock이면 세로, Inline 이면 가로로 Box위치를 계산한다.

  • 더티 비트 체제

초기 생성 외 소소한 변경 때문에 전체를 다시 배치하지 않기 위해 브라우저더티 비트 체제를 사용한다.

렌더러는 다시 배치할 필요가 있는 변경 요소 또는 추가된 것과 그 자식을 더티라고 표시한다.

더티 플레그에는 dirtychildren are Dirty 두가지 플레그가 존재하며, 후자의 경우 자식 중 재배치할 필요가 있는 경우에 사용된다.

  • 전역 배치와 점증 배치

배치에는 전역 배치점증 배치가 존재한다.

전역 배치는 모든 렌더러에 영향을 줄 수 있는 경우 실행된다.

1. 글꼴 크키 변경
2. 화면 크기 변경

점증 배치렌더러더티일때 비동기적으로 일어난다.

전역 배치는 보통 동기적으로 실행된다.

반면, 점증 배치의 경우 웹킷점증 배치를 실행하는 타이머를 두고 트리를 탐색하여 더티 렌더러를 배치하게 된다.

배치크기 변경 또는 렌더러 위치 변화로 인해 실행되는 경우 렌더러는 크기를 다시 계산하지 않고 캐시로부터 가져온다.

전체적인 과정은 아래와 같다.

1. 부모 렌더러가 자신의 너비를 결정.

2. 부모가 자식을 검토.
  2-1. 자식 렌더러를 배치(자식의 x와 y를 설정)
  2-2. (부모와 자식이 더티하거나 전역 배치 상태이거나 또는 다른 이유로 필요하다면  자식 배치를 호출하여 자식의 높이를 계산한다.

3. 부모는 자식의 누적된 높이와 여백, 패딩을 사용하여 자신의 높이를 설정한다. 이 값은
  부모 렌더러의 부모가 사용하게 된다.

4. 더티 비트 플래그를 제거한다.

이때 너비가 고정된 경우 값은 캐시에 저장된다.

그리기(Paint) 과정


Layout(배치)과정을 거쳐 화면에 UI를 표현하기 위한 계산식이 끝나면 Paint과정이 시작된다.

Layout과정에서 Render Layer 가 2개이상 생성되면 각각의 LayerPainting한 뒤 하나의 이미지로 Composite하는 과정을 거쳐 브라우저에 표현한다.

image

  • Paint

    • PaintRender 트리픽셀로 변환하는 프로세스이다.
    • 텍스트, 색, 이미지, 경계 및 그림자 등 요소의 모든 시각적 부분을 그리는 작업을 포함
    • 흔히 painting또는 rasterizing 이라고 하며 실제로 픽셀을 채우는 작업은 rasterize라고 하며, paint는 그릴 호출 목록을 생성한다.
  • Composite

    • 사용된 HTML이나 CSS속성에 따라 여러개의 Layer가 생성된 경우, 생성된 Layer들을 합성하여 한장의 비트맵dmfh aksemsms rhkwjd
그리기 상세 과정

Layout(배치) 과정이 끝나게 되면 화면에 내용을 표시하기 위해 렌더 트리탐색이 되고 렌더러paint메서드가 호출된다.

그리기 또한 배치와 마찬가지로 전역, 점증방식으로 수행된다.

점증 그리기에서 일부 렌더러는 전체 트리에 영향을 주지 않는 방식으로 변경된다.

Paint 순서는 아래와 같은 순으로 진행된다.

1. 배경 색
2. 배경 이미지
3. 테두리
4. 자식
5. 아웃라인

만약에 RePainting이 실행이 실행된다면 그 전에 웹킷은 기존의 정보를 비트맵에 저장하고 변경된 정보와 비교하여 차이가 있는 부분만 다시 그린다.

동적 변경


초기에 렌더링과정을 통해 화면에 노출되고 JS인터렉션 등으로 인해 시각적인 화면의 변경이 발생할 경우 아래와 같은 프로세스를 타게 된다.

  • JS / CSS > 스타일 > 레이아웃 > 페인트 > 합성

레이아웃 너비, 높이, 왼쪽 또는 상단 위치 등 요소의 기하학적 형태에 영향을 주는 ‘layout’ 속성을 변경하면 브라우저가 다른 모든 요소를 확인하고 페이지에 대해 ‘리플로우’를 수행해야 합니다. 영향을 받은 영역이 있으면 다시 페인트해야 하고 최종적으로 페인트한 요소는 다시 합성해야 합니다.

  • JS / CSS > 스타일 > 페인트 > 합성

페이지의 레이아웃에 영향을 주지 않는 배경 이미지, 텍스트 색상 또는 그림자 등의 ‘paint only’ 속성을 변경하면, 브라우저가 레이아웃을 건너뛰되 페인트 작업은 여전히 수행합니다.

  • JS / CSS > 스타일 > 합성

레이아웃과 페인트가 필요 없는 속성을 변경하면 브라우저가 합성 단계로 건너뜁니다. 이 최종 버전은 앱의 수명 주기에서 애니메이션이나 스크롤처럼 많은 부담을 주는 시점에 가장 이상적이고 비용이 가장 적게 드는 버전입니다.

결과적으로 브라우저는 변경에 대해 가능한 최소한의 동작으로 반응하기 때문에 요소색깔이 바뀌면 해당 요소리페인팅이 발생한다.

요소위치가 바뀌면 요소자식그리고 형제리페인팅재배치가 발생한다.

DOM 노드가 추가되게 되면 재배치(ReFlow)리페인팅(RePainting)이 모두 발생하며, html요소의 글꼴 크기를 변경하는 것과 같은 큰 변경은 캐시를 무효화 하고 트리 전체배치리페인팅이 발생한다.

참고 사이트