최근 Motion 블로그 글 중 The Web Animation Performance Tier List을 읽게 되었다.
같은 효과를 내는 애니메이션이더라도 렌더링 경로에 따라 성능이 달라진다는 내용이었다.
정확한 이해를 위해 브라우저 렌더링 과정을 다시 한번 복습해보기로 했다.
🖥️ 브라우저 렌더링 과정
브라우저는 아래와 같은 순서로 웹페이지를 렌더링한다.
- HTML 파싱 후 DOM Tree 생성
- CSS를 파싱 후 CSSOM Tree 생성
- DOM Tree와 CSSOM Tree를 합쳐 Render Tree 생성
- 레이아웃(Layout)
- 페인트(Paint)
- 합성(Compositing)
각 과정이 어떻게 이루어지는지 자세히 알아보자.
1. HTML 파싱하고 DOM Tree 만들기
브라우저 렌더링 엔진은 HTML 문서를 파싱하고 DOM(Document Object Model) 트리 구조로 변환한다.
파싱 중 async나 defer 설정이 되어있지 않은 <script> 태그를 만나면 파싱을 중단하고 스크립트가 실행된다.
따라서 실행 순서가 중요하지 않은 스크립트에는 async나 defer 설정을 해주는 것이 좋다.
sync: 다운로드는 파싱과 병행, 다운로드 끝나는 즉시 실행(순서 보장 X)defer: 다운로드는 파싱과 병행, HTML 파싱 끝난 뒤 실행(순서 보장 O)
2. CSS 파싱하고 CSSOM Tree 만들기
HTML 파싱과 동시에 브라우저는 CSS 파일을 파싱하고 CSSOM(CSS Object Model) 트리 구조로 변환한다.
3. Render Tree 만들기
DOM과 CSSOM 트리 완성되면 이 둘을 합쳐 렌더 트리를 만든다.
이 과정에서 CSS Cascade 규칙에 따라 스타일이 적용되며 display: none 이 적용되어 실제 화면에 나타나지 않은 요소는 렌더 트리에 포함하지 않는다.
4. 레이아웃
렌더 트리를 기반으로 처음 노드의 크기와 위치를 계산하고 뷰포트 내에서의 정확한 배치를 수행하는 것을 레이아웃이라고 부른다. 레이아웃이 한번 실행된 이후 다시 노드의 크기와 위치를 계산하는 것을 리플로우라고 한다.
5. 페인트
레이아웃에서 계산된 각 노드를 텍스트, 색상, 테두리, 그림자 등을 포함하여 실제 화면의 픽셀로 변환한다.
브라우저는 효율적인 렌더링을 위해 일부 요소를 별도의 Compositor Layer(합성 레이어)로 분리하게된다.
Compositor Layer로 승격된 요소는 GPU에서 처리되어 성능이 향상되는데, transform, opacity, will-change, video, canvas 등의 속성을 가진 요소가 합성 레이어로 승격된다.
⚠️ 레이어는 성능을 향상시키지만, 메모리 관리 측면에서는 비싼 작업이므로 과하게 사용하지 말아야한다.
6. 컴포지팅(합성)
페인트된 레이어들을 순서대로 합성하여 최종 화면을 만든다. GPU를 활용한 합성 레이어는 이 단계에서 효율적으로 처리된다.
👍🏻 가장 성능이 좋은 웹 애니메이션은 뭘까?
Motion에서 설명하는 가장 성능이 좋은 웹 애니메이션은 Layout/Paint를 건드리지 않고 Composite만 트리거하는 속성(transform, opacity, filter, clip-path)을 애니메이션하는 것이다.
⚠️
filter,clip-path는 상황에 따라 Paint가 다시 일어날 수 있다.
Composite을 트리거하는 속성이더라도 requestAnimationFrame 기반 애니메이션은 메인 스레드에 의존하지만, CSS Animation/Transition이나 Web Animations API는 오프로드 가능한 조건(주로 transform/opacity 등)을 만족하면 compositor에서 처리되어 메인 스레드가 바빠도 더 안정적으로 동작할 수 있다.