브라우저 렌더링 과정을 (더 깊게) 알아보자

·2023년 9월 7일
47

프론트엔드

목록 보기
7/11

여러분들은 브라우저 렌더링에 대해서 얼마나 알고 계시나요 ? 우리가 흔히 알고 있는 브라우저 렌더링 지식은 아주 간단하게 설명해보자면

dom 파싱 -> cssom 생성 -> 렌더트리 생성 -> DOM API사용해 랜더 트리 변경 리플로우 -> 리페인트

정도 였을 것입니다. 렌더링 성능 개선을 하기 위해서 브라우저 렌더링에 대해서 더 깊이 있게 알아야 한다고 생각했습니다. 여러 블로그 글들과 유튜브 [10분 테코톡] 슬링키의 브라우저 렌더링 파이프라인 을 참고해서 작성된 글입니다. 보다 더 깊게 공부하고 싶은 욕심이 있으시다면 유튜브 영상을 시청한 뒤 제 블로그 글과 레퍼런스에 있는 글들을 참고하시면 더 좋을 것 같습니다!

🔉브라우저 렌더링 맛보기

1. paint 는 화면에 그리는 단계가 아니다.

paint 는 어떻게 그려야 하는지 정보를 담고 있는 레코드를 생성하는 과정입니다.

2. Chromium 은 멀티 프로세스 아키텍처이다

프로세스프로세스가 제어하는 부분
브라우저 프로세스주소 표시줄, 북마크 막대, 뒤로 가기 버튼, 앞으로 가기 버튼 등 애플리케이션의 "chrome" 부분을 제어한다.네트워크 요청이나 파일 접근과 같이 눈에 보이지는 않지만 권한이 필요한 부분도 처리한다.
렌더러 프로세스탭 안에서 웹 사이트가 표시되는 부분의 모든 것을 제어한다.
플러그인 프로세스웹 사이트에서 사용하는 플러그인(예: Flash)을 제어한다.
GPU 프로세스GPU 작업을 다른 프로세스와 격리해서 처리한다. GPU는 여러 애플리케이션의 요청을 처리하고 같은 화면에 요청받은 내용을 그리기 때문에 GPU 프로세스는 별도 프로세스로 분리되어 있다.

🖌️ 브라우저 렌더링을 과정을 탐구해봅시다.

0. Overview


(출처:소영님 블로그)

렌더러 프로세스

렌더러 프로세스는 웹 콘텐츠를 처리하고 탭 내부에서 발생하는 모든 작업을 담당합니다. 렌더러 프로세스의 메인 스레드가 브라우저로 전송된 대부분의 코드를 처리합니다. 렌더러 프로세스의 주요 역할은 HTML과 CSS, JavaScript를 사용자와 상호작용을 할 수 있는 웹 페이지로 변환하는 것입니다.

Main스레드

Parsing, Style, Layout , PrePaint , Paint , Layerize , Commit

Composite스레드

Tiling , Raster , DrawQuard

Raster 스레드

조금 생소한 용어들이 많이 나오지만 사실 이해하기 엄청나게 어려운 개념이 아니기 때문에 천천히 따라가봅시다.


🎨 Part 1 : Main Thread

js 실행, 레이어 생성 등


1. Parsing

HTML 데이터를 수신하기 시작하면 렌더러 프로세스의 메인 스레드는 문자열(HTML)을 파싱해서 DOM(document object model)으로 변환합니다.

HTML 파싱

  • 서버로부터 받은 문서를 바이트로 응답 받습니다.
  • 응답받은 데이터를 문자열로 변환합니다.
  • 문바열로 변환된 HTML 문서를 문법적 의미를 갖는 토큰들로 분해합니다.
  • 각 토큰들을 객체로 변환하여 노드를 생성합니다.
  • 노드들을 트리 자료구조로 구성한 DOM(Doucment Object Model)이 완성됩니다.

CSSOM 생성

HTML과 동일한 파싱 과정(바이트 -> 문자 -> 토큰 -> 노드 -> CSSOM)을 거치며 해석하여 CSSOM(CSS Object Model)을 생성합니다. DOM Tree파싱 이후 브라우저는 CSS를 파싱해서 세 가지 단계로 각 DOM노드의 스타일을 계산합니다.

1. Css-> 스타일 시트

link태그로 로드하는 CSS, style, 그리고 inline style의 정보를 바탕으로 브라우저가 해석할 수 있는 스타일 시트를 생성합니다.

2. 단위변환

width: 50%
padding: 2em 0
font-size: 1rem
CSS는 px, %, em, rem등 다양한 단위로 작성할 수 있는데, rem등 상대적인 값은 픽셀로 치환되어 계산됩니다.

3. 스타일 계산

CSS 오버라이딩 등을 고려하여 요소의 최종 스타일을 계산합니다.

preload scanner

모든 브라우저는 파일을 토큰화 하고 객체 모델로 처리하는 HTML 파서를 기본적으로 가지고 있습니다. 작업은 블로킹 리소스를 만나기 전 까지 계속 됩니다. css파일 같은 경우는 스타일링이 적용되지 않은 콘텐츠가 잠깐 뜨는 현상을 방지하기 위해 파싱과 렌더링이 차단됩니다. 그리고 script 태그를 만나게 되어도 렌더링 작업이 중단됩니다.

하지만 이러한 작업은 다른 중요한 리소서를 찾는 과정을 지연시킴으로써 퍼포먼스를 저하 시킬 수 있습니다. preload scanner를 사용해서 필요한 요청을 병렬적으로 처리합니다. 예를들면 HTML 문서에 img 또는 link 와 같은 태그가 있으면 프리로드 스캐너는 HTML 파서가 생성한 토큰을 확인하고 브라우저 프로세스의 네트워크 스레드에 요청을 보냅니다.

3. layout

DOM과 CSSOM이 생성되면 렌더링을 위해 결합(attachment)하여 렌더 트리(Render Tree)를 구성합니다. 렌더 트리는 웹 페이지에 보이는 요소 정보만 포함. 즉, 렌더트리는 브라우저 화면에 렌더링되는 노드만으로 구성되어기 때문에 display: none 으로 처리된 요소는 포함되지 않습니다.

4. Pre-paint

DOM을 파싱하고 Style 과정 거치고 Layout 과정을 거치면서 어떤 좌표에 어떤 크기로 데이터들이 들어갈지에 대한 정보 (LayoutObject)를 만들었습니다. 단, 여기서 처리되지 않은 css 속성 몇가지가 있습니다.

tansform / clip / opacity / scroll 등..
해당 속성들은 Property Tree는 GPU 에서 직접 처리를 하게 됩니다. (따로 Tree 형태를 만들어둔다고 이해하시면 됩니다.)


[10분 테코톡] 💡슬링키의 브라우저 렌더링 파이프라인
출처 : https://www.youtube.com/watch?v=idgsruQl9f4

해당 그림은 Pre-paint를 시각화한 그림입니다. 이렇게 Pre-paint 과정에서 해당 css 속성별로
Property Tree를 생성함으로써 이후 레이어를 합치는 단계에서 필요한 효과를 빠르게 적용할 수 있습니다. 이 개념을 이해하기 위해서는 레이어에 대한 선행 이해가 필요합니다.

5. paint

💡paint 과정은 실제로 화면을 그리는 과정이 아니라 어떻게 그려야하는지에 대한 정보를 담고 있는 페인트 레코드(Paint Records)를 생성하는 과정입니다.

6. Layerize


Layerize과정은 paint과정의 결과물을 사용해서 Composited Layer List라는 데이터를 생성하는 단계입니다. 간단하게 화면을 특정한 레이어들로 쪼개는 과정입니다.

layout단계에서 Layout Object로 구성된 Layout Tree가 생성 되고, 아래 조건을 만족하면 별도의 Paint Layer 가 생성됩니다.

별도의 Paint Layer가 생성되는 조건

  • 최상위 요소(root element)
  • position: relative, absolute 사용
  • 3D(translate3d, preserve-3d, ,..)나 perspective transform 사용
  • video, canvas 태그 사용
  • CSS filter나 alpha mask 사용

Paint Layer중 Compositing Trigger를 가지고 있거나 스크롤 가능한 컨텐츠가 있을 경우 별도의 Graphics Layer가 생성됩니다.

Compositing Trigger

  • 3D 변형: translate3d, translateZ …
  • video, canvas, iframe 요소
  • position: fixed
  • CSS 트랜지션과 애니메이션을 사용해 구현한 transform과opacity 애니메이션
  • position: fixed
  • will-change
  • filter

Graphics Layer

분리된 Graphics Layer들은 독립적인 픽셀화가 가능하며 프레임마다 후에 설명할 단계인 래스터(raster)과정을 다시 실행할 필요 없이 GPU연산이 가능하기 때문에 빠른 스크롤링과 애니메이션이 가능합니다.

💡 layout Object , Paint Layer , Graphics Layer가 생성되는 조건들을 한 번 더 떠올려보세요.

네이버의 레이어 화면 (레이아웃) 훔쳐보기


보는 방법 : 네트워크탭 => 더보기 (점 세개) 클릭=> More Toole => Layers

7. Commit

Layerize 과정이 끝나고 나서 그 결과물들을 Composite Thread 로 옮기는 과정입니다. Composited Layer List는 PrePaint단계에서 생성한 Property Tree와 함께 합성 스레드(Composite Thread)로 복사됩니다.


🎨 Part 2 : Composite Thread & Raster Thread

GPU 프로세스와 상호작용하며 작업 처리입니다.Compositor thread 에서 메인 스레드와 별개로 레이어를 합성하고 사용자의 입력을 처리합니다.

브라우저가 화면에 그리는 실제 렌더링을 하기 위해서는 HTML 구조와 기하학적 속성과 스타일에 대한 정보를 픽셀로 변환해야 합니다. 이 작업을 픽셀화(레스터화, rasterizing)라고 합니다.
최신 브라우저에서는 보다 정교한 기술인 합성이라는 기술을 사용합니다. 합성은 웹 페이지의 각 부분을 레이어로 분리해 별도로 픽셀화 하고 합성 스레드에서 합성 하는 기술입니다.


8. Tilling

이제 메인 스레드에서 넘겨받은 각 레이어들을 픽셀화(레스터화) 하는데, 이 때 레이어의 크기가 클 수 있기 때문에 레이러를 Tile 형태로 분할합니다. 각 타일에는 draw 과정에서 생성한 PaintRecord 가 포함됩니다.

9. Raster

Raster 단계에서는 Raster Thread에서 처리됩니다. Paint 과정에서 만들었던 draw 명령어들을 실행하는 과정입니다. Blink엔진에서는 Skia라는 그래픽 라이브러리를 사용하여 비트맵 이미지를 생성하고 이를 GPU 메모리에 저장합니다. 명령어 실행 후 그에 대한 결과는 DrawQuad 라는 명령어로 변형됩니다.

드로쿼드와 합성 프레임

드로 쿼드메모리에서 타일의 위치와 웹 페이지 합성을 고려해 타일을 웹 페이지 어디에 그려야 하는지에 관한 정보를 가지고 있음
합성 프레임웹 페이지의 프레임을 나타내는 드로 쿼드의 모음

10. Activate

합성 스레드는 pending tree와 active tree를 가지고 swap 하는 형태의 멀티 버퍼링 배턴을 가지고 있습니다. 픽셀화 (래스터 작업) 은 비동기로 진행됩니다. 컴포지터 스레드에서 이전 작업을 처리 중일 때 새로운 커밋 (변경점) 이 들어올 경우 새 커밋에 대한 작업을 진행하기 전 이전 커밋에 대한 내용을 보여주어야 하기 때문입니다.

activate된 쿼드들을 하나로 묶어 Compositor Frame 이라는 데이터로 묶여 GPU프로세스로 전달됩니다.

11. Display

마지막으로 CompositorFrame 들을 단일 CompositorFrame으로 합치고 화면에 렌더링됩니다.

📂 레퍼런스

https://velog.io/@radin/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%95

https://so-so.dev/web/browser-rendering-process/

https://d2.naver.com/helloworld/9274593

프리로드 스캐너
https://yceffort.kr/2022/06/preload-scanner

profile
My Island

2개의 댓글

comment-user-thumbnail
2023년 9월 7일

좋은 자료 감사합니다!

1개의 답글