본문 바로가기

브라우저 렌더링, 렌더러 프로세스, requestAnimationFrame

by kicksky 2021. 5. 8.

웹의 기능과 역할이 확대되고 universal platform이 되리라는 전망도 커지고 있다. 브라우저의 렌더링 파이프라인을 제대로 이해하는 것에서 시작.

페이지 렌더링

웹 페이지 로드는 주소창에 URL을 입력하는 것부터 시작. (사실 HTML 요청이 있기 전에도 많은 일들이 일어나긴 함) HTML 파일이 로드 되었을 때 발생하는 일 (픽셀-화면 파이프라인):

1. HTML 파싱

  • HTML 문서가 파싱되고, DOM 트리 생성
  • HTML 데이터 받기 시작하면서 메인 스레드가 텍스트 문자열(HTML)을 파싱하고 이를 DOM으로 변환 (페이지에 대한 브라우저의 내부 표현이자 웹 개발자가 JS 통해 상호 작용할 수 있는 데이터 구조 및 API)
  • 사전 로드 스캐너는 외부 리소스(이미지나 CSS, JS 등)가 요청되면 브라우저 프로세스에 있는 네트워크 스레드에 요청
  • HTML 파서가 script 태그를 발견할 때 파싱을 중단하고 JS 코드를 로드 - 파싱 - 실행하는 이유 : JS가 DOM 구조를 바꿀 수 있기 때문이다.

document.write()를 사용하지 않는다면 async(백그라운드에서 다운로드 되고, 완료되면 바로 실행된다. 이 때 HTML 파싱은 중단된다. 방문자 수 카운터나 광고 관련 스크립트처럼 각각 독립적인 역할을 하는 서드 파티 스크립트에 사용) 혹은 defer(백그라운드에서 다운로드 되고, 페이지 구성되고 DOM 준비될 때까지 기다림) 속성을 추가할 수 있다. 이 경우 js 파일을 비동기로 로드하고 파싱을 중단시키지 않는다. <link rel='preload' />는 반대로 리소스가 현재 탐색에서 반드시 필요할 때 사용할 수 있다.

2. 스타일 계산

  • 메인 스레드가 CSS를 파싱해서 셀렉터에 따라 요소에 CSS 규칙을 적용한다. CSSOM 생성
  • 렌더 트리 생성: 렌더 트리는 화면에 보일 DOM 트리 노드를 포함한다. 따라서 <head>, <style>, <script>는 제외된다.display: none 인 엘리먼트도 무시된다. :after와 같은 pseudo 엘리먼트는 포함된다. 모든 텍스트 라인은 단일 블록이 된다.

3. 레이아웃(리플로우)

  • 화면에서 얼마의 공간을 차지하고 어디에 배치되는지 계산하는 단계. 각 요소들의 기하학적 구조를 찾는 과정.
  • 뷰포트 내에서의 위치와 크기. 상대값들이 절대적인 값, 픽셀로 변환.
  • 한 요소가 다른 요소에 영향을 줄 수 있기 때문에 이 레이아웃 과정이 중간에 들어가는 듯
  • 레이아웃 트리는 페이지에 보이는 정보만 담는다. display: none인 요소는 포함되지 않는다. 반대로 DOM에 없지만 레이아웃 트리에 포함되는 컨텐츠도 있다.
  • 렌더 트리에서 만들어진 블록을 모아서 레이아웃을 정하고, 의존적인 블록 dimension들이 계산된다.

4. 페인트

  • 메인 스레드는 레이아웃 트리를 따라 페인트 기록을 생성한다.
  • 여러 레이어 표면에서 실행된다.
  • 블록과 텍스트에 대한 구획화(rasterizing). 이미지 디코딩 및 리사이징. 그리기 호출 목록을 생성.
  • 래스터라이징Rasterizing : 앞선 단계들에서 얻은 정보들을 스크린의 픽셀로 바꾸는 과정. 픽셀 채우기.

5. 레이어 컴포지션

  • 페이지가 정확히 렌더링 되기 위해서는 화면에 정확한 순서로 그려져야 할 필요가 있다.
  • 독립적으로 가공되고 채색되는 비주얼 레이어들을 컴포지션, 즉 합체 ! 페이지를 그리는 단계.
  • 컴포지팅Compositing : 한 페이지의 부분들을 여러 레이어로 나누고 그것들을 '각각' 래스터하여 컴포지터 스레드에서 페이지를 합성하는 기술. 만약 스크롤 발생했다면 레이어들은 이미 래스터 되었으므로 새로운 프레임을 합성하면 된다.

requestAnimationFrame

복잡한 애니메이션이나 지속적인 DOM 업데이트가 있을 때는 이 과정을 거치며 전부 변화한다. 새로운 프레임은 1초에 60번 렌더링 된다. (60 FPS) 대부분의 기기가 초당 60회 새로고침을 하며, 브라우저는 한 프레임을 위해 16 밀리초가 필요하다는 의미이다. 실제로는 10 밀리초 내에 모든 작업이 완료되어야 한다고 한다. 이보다 오래 걸리면 FPS는 감소하고 애니메이션은 버벅거리며 유저는 화가 남.

구 브라우저에서는 애니메이션에 setTimeout이나 setInterval 함수로 구동되었는데 이 함수들의 본 목적과 전혀 부합하지 않는 케이스였다. 이 함수들을 호출할 때 브라우저의 리플로우가 고려되지 않으므로 파이프 라인 중간에 비주얼 변화가 요청되고 이전 프레임의 작업의 마지막 단계가 취소되거나 재계산이 발생하곤 했다. 콜백이 프레임에서특정 시점(종료 시)에 실행되고, 종종 프레임이 누락되어 버벅거림 현상이 발생했다. jQuery의 기본animate동작이setTimeout을 사용했다고 한다. ( 인턴으로 일할 때 제발 setTimeout 쓰지 말라고 신신당부를 들었는데 이것 때문이었는듯 )

모던 브라우저에서는 requestAnimationFrame이라는 browser API가 애니메이션을 효과적으로 처리할 수 있다. 브라우저에서 정확한 시간(프레임 시작 시)에 작업이 수행될 수 있고, 이는 자바스크립트가 프레임 시작 시 실행되도록 보장하는 유일한 방법이다. 이 함수를 호출하면 다음 스타일을 계산하기 직전에 브라우저가 콜백 함수를 호출해서 다음 프레임 프로세싱이 중간에 방해받는 일이 없다. 하길 원합니다.

강제 리플로우

offsetWidth,getComputedStyle() 등 브라우저가 레이아웃 state을 알려준다.

렌더러 프로세스

브라우저 탭에서 발생하는 모든 일을 담당, 메인 스레드가 코드 대부분을 처리한다. (+컴포지터, 래스터 스레드) HTML, CSS, Javascript를 사용자가 상호작용할 수 있는 웹 페이지로 만드는 것


'' 카테고리의 다른 글

[TIR] 2022년 웹 개발 Baseline  (0) 2022.06.20
[네트워크] HTTPS, TLS/SSL, 인증서 갱신, acme  (0) 2022.05.14
SPA  (0) 2021.05.19

댓글