1. 핵심 요약
- 애니메이션 성능은 레이아웃(Layout) → 페인트(Paint) → 합성(Composite) 단계 중 어디를 유발하느냐로 결정된다.
- S-Tier = GPU 합성 단계만 실행 → 가장 부드러운 결과(60/120fps)
transform·opacity·filter·clip-path 등
- CSS 변수·레이아웃 변경·DOM 측정·쓰래싱(thrashing)은 성능 최악
- “빠른 애니메이션”의 기준은 단순 프레임레이트가 아니라 메인 스레드 차단 없이 즉각적인 반응성을 유지하는지 여부
2. 렌더링 파이프라인 이해 (Why)
브라우저가 한 프레임을 그릴 때 거치는 단계:
| 단계 | 의미 | 발생 시 비용 |
|---|
| Layout | 요소 크기·위치 계산 | 가장 비쌈 (재귀 영향 큼) |
| Paint | 픽셀 다시 그림 | Layout보다는 낮지만 비쌈 |
| Composite | 이미 그려진 레이어를 GPU에서 합성 | 가장 저렴함 |
- 상위 단계가 발생하면 아래 단계도 반드시 발생함
Layout → Paint → Composite
Paint → Composite
즉, Composite만 유발하는 기법이 최강이다.
3. 스레드 구조
메인 스레드
- JS 실행, 스타일 계산, 레이아웃, 페인트 등 대부분의 작업 담당
- 바쁘면 애니메이션 프레임 손실(jank) 발생
컴포지터 스레드
- transform/opacity 등 합성 단계만 필요할 때 애니메이션을 단독 실행
- 메인 스레드가 막혀도 애니메이션이 계속 부드럽게 유지
→ S-Tier = 컴포지터 스레드에서 돌 수 있는 애니메이션
4. 애니메이션 성능 티어 리스트 (What)
전체 기준:
- S: 합성 전용
- A: 메인 스레드 기반이지만 합성만 유발
- B: 측정 비용 1회 + A/S-tier
- C: Paint 발생
- D: Layout 발생
- F: Thrashing = 최악
5. S-Tier — 합성(Animation on Compositor)
사용 가능한 속성
- transform
- opacity
- filter
- clip-path
특징
- 메인 스레드와 분리 → GPU에서 60~120fps 유지
- CSS / WAAPI / Motion으로 작성 시 가장 안정적
주의점
-
브라우저 de-optimization
Safari처럼 compositor 기능이 제한될 경우 자동으로 메인 스레드로 폴백
-
레이어 크기 폭발
- transform/opacity를 위한 레이어 생성 → 너무 크면 GPU 메모리 초과
- 특히 무한 마키(ticker) 등은 레이어 폭이 매우 큼
-
filter: blur는 가속되지만 값이 커질수록 기하급수적 비용 증가
스크롤 기반 S-tier
- ScrollTimeline / ViewTimeline 활용 → 스크롤 자체가 컴포지터에서 실행
- scrollTop 읽기 기반 애니메이션은 D-tier (메인 스레드 개입)
6. A-Tier — 레이어 기반 합성(Main Thread-driven Composite)
조건:
- 대상 요소가 이미 레이어로 승격(promoted to layer) 되어 있을 것
레이어 승격 조건
- transform/opacity 애니메이션 존재
- 3D transform
- position: fixed/sticky
- backdrop-filter
- 또는 will-change: transform 등 힌트
주의: will-change는 과다 사용 시 GPU 메모리 초과 위험
예시
- GSAP / requestAnimationFrame 기반 transform 변경
- Motion의 독립 transform 애니메이션
속성
- GPU 합성만 트리거 → 보통 매우 빠름
- 단, 메인 스레드가 막히면 영향 받음
7. B-Tier — DOM 측정 1회 포함 + A/S 애니메이션
대표 예: FLIP 레이아웃 애니메이션
- First → Last → Invert → Play 기법
- transform으로 위치/사이즈를 보정하여 S-tier처럼 동작
- 하지만 초기 측정(레이아웃 읽기) 1회 필요
Motion의 layout 애니메이션이 이 카테고리.
8. C-Tier — Paint 발생
트리거
- background-color
- color
- border-radius
- mask-image / gradient
- CSS 변수 변경
CSS 변수 성능 문제
-
변경 시 항상 Paint 발생
-
전역 CSS 변수는 스타일 재계산 폭발(F-Tier로 격하)
- DOM 전체 1000개+ 요소 재계산 가능
- 실제 사례: 매 프레임 global var 업데이트 → 8ms 소모
해결책
- 변수 스코프 축소(html → 특정 섹션)
@property + inherits: false 사용하여 상속 차단
SVG 속성 변경
- d, cx, cy, r 등 변경 시 무조건 paint
View Transition API가 C~D에 있는 이유
- 기본 crossfade는 S-tier
- 하지만 width/height 기반 확장·축소는 D-tier
- 인터럽트 불가 → UI 반응성 저하 → C-tier 유지
9. D-Tier — Layout 발생
특징
- Layout은 가장 비싼 단계
- 형제·부모·자식 DOM 전체 영향을 받을 수 있음
예:
- width/height/left/top/bottom
- flex 값 변경
- grid-template 변경
- display 변경
최적화
- position: absolute/fixed를 사용해 레이아웃 영향 범위 축소
- contain: layout 적용
10. F-Tier — Thrashing (절대 금지)
정의
- Write → Read → Write → Read 반복
- Layout 강제 → 수십 fps 손실
예시:
element.style.width = "100px"
const w = element.offsetWidth
element.style.width = w * 2 + "px"
React useLayoutEffect에서 DOM 읽기/쓰기 조합할 때 흔히 발생.
해결책
- 측정과 업데이트를 프레임 단위로 배치(batch)
- Motion의
frame.read / frame.update 같은 API 사용
11. 결론 (How)
-
성능 문제의 90%는 레이아웃 또는 큰 blur/filter
-
나머지는 CSS 변수 스코프, 레이어 폭, Safari 폴백 등 섬세한 요소들
-
최적의 애니메이션 전략:
- 먼저 S-tier 가능한지 확인 (transform/opacity)
- 레이어 크기·픽셀 비용 고려
- CSS 변수·SVG·레이아웃 기반 애니메이션 최소화
- 불가피할 경우 FLIP(B-tier) 기법 활용
- 측정은 반드시 batching
원문 - The Web Animation Performance Tier List