FEInterview Prep

css · high priority

CSS 애니메이션 — *transition · animation · 60fps* 의 원리

*렌더링 파이프라인* 위에서 *Composite-only* 속성으로 부드러움을 만든다

intermediate 난이도4시간토스당근카카오네이버배민라인쿠팡
시작 전
이해도
매우 낮음

학습 개요

탄생 배경

쉬운 설명

복잡한 개념을 실생활 비유로 설명합니다.

*무대 조명 vs 소품 옮기기*

60fps 애니메이션은 *공연장의 무대 운영* 과 같습니다. *opacity* 와 *transform* 은 *조명* 입니다 — 조명 색을 바꾸거나 비추는 각도를 돌리는 일은 *무대 자체를 다시 짓지 않고도* 즉시 가능합니다 (컴포지터 스레드). 반면 *width/top/left* 같은 속성은 *소품을 옮기는 일* 입니다 — 한 소품을 옆으로 밀면 옆에 놓인 소품도 같이 자리를 *재배치* 해야 하고 (Layout), 페인트도 다시 칠해야 합니다 (Paint). 공연 한 막 (≈ 16.7ms) 안에 모든 소품 재배치가 끝나야 *프레임 드롭이 없는 부드러움* 이 됩니다. *will-change* 는 *"이 소품 곧 움직일 거야 — 옆에 미리 두고 준비해 둬"* 라는 *메모* 와 같지만, 모든 소품에 메모를 붙이면 *백스테이지가 메모로 가득 차* 오히려 운영이 느려집니다.

핵심 개념

*속성별 트리거 단계* — 비용 순
속성 예시트리거 단계비용메모
width height top left margin paddingLayout + Paint + Composite🔴 매우 높음*reflow* 발생 — 형제·자손까지 영향
color background box-shadow border-radiusPaint + Composite🟡 중간레이아웃은 그대로, 픽셀만 다시
transform opacity filter (compositor 가능 시)Composite only🟢 낮음*컴포지터 스레드* 에서 처리 — 메인 스레드 독립

*"transform/opacity 만 애니메이션하라"* 의 진짜 의미

두 속성은 *합성 단계만* 다시 돌리고 *컴포지터 스레드* 에서 GPU 가 처리할 수 있다. 메인 스레드가 *무거운 작업* 으로 막혀도 *애니메이션은 살아남는다*. 반대로 top 으로 같은 위치 변화를 만들면 매 프레임 *전체 레이아웃* 이 다시 돌아 메인 스레드 부하 + 형제 요소까지 흔들 수 있다.

같은 시각 효과, *왼쪽으로 이동*

❌ `left` 로 움직이기
  • *Layout* 부터 다시 — 부모/형제까지 영향
  • 메인 스레드 사용 → 다른 작업과 경합
  • 60fps 깨지기 쉬움
1.box {
2 position: absolute;
3 transition: left 300ms ease;
4}
5.box.move { left: 200px; }
✅ `transform` 으로 움직이기
  • *Composite* 만 — 레이아웃 영향 없음
  • 컴포지터 스레드 → 메인 스레드와 독립
  • GPU 텍스처로 처리 → 60fps 유지
1.box {
2 transition: transform 300ms ease;
3}
4.box.move { transform: translateX(200px); }

실무 적용

어떤 상황에서 사용하는가

커머스 상세 페이지 — *상품 카드 호버 시 살짝 떠오름*, *장바구니 담기 클릭 후 카운트 배지 펄스*, *모달 등장 (배경 페이드 + 본문 슬라이드)*, *이미지 그리드 → 상세 뷰로 매끄러운 전환 (FLIP/View Transitions)*, *접근성 (Reduce Motion) 준수*.

어떻게 적용하는가

(1) *호버* 는 `transform: translateY(-4px)` 와 `box-shadow` 의 *transition* 으로 — `top` 사용 금지. (2) *펄스 배지* 는 `@keyframes` + `transform: scale` 무한 반복, 단 *3 회 후 멈추는 것이 UX 에 더 좋음* (시선 분산 방지). (3) *모달* 은 `opacity` + `transform: translateY(20px) → 0` 의 *동시 transition* 으로 *합성 트랙* 만 사용. (4) *그리드 → 상세* 전환은 *View Transitions API* (`document.startViewTransition(() => mutateRoute())`) 를 우선 시도, 미지원 브라우저는 *수동 FLIP* 폴백. (5) `@media (prefers-reduced-motion: reduce)` 분기로 *transition 제거* 또는 *opacity 만* 남기기. (6) `will-change: transform` 은 *호버/모달 시작 직전* 에만 켜고 `transitionend` 에서 *해제* — `useEffect` cleanup 에서도 보장. (7) *Performance 탭* 으로 정기 측정, *frame drop* 보이면 *paint flashing* 으로 페인트 영역 확인.

흔한 실수와 안티패턴

  • `width`/`height`/`top`/`left` 로 애니메이션 → *Layout 매 프레임* — `transform` 으로 대체.
  • `transition: all` 사용 → *예상 못한 속성까지* 보간되어 *페이지 첫 페인트* 가 흔들림. 명시적 속성만.
  • `will-change` 를 *영구 적용* → GPU 메모리 폭증. 시작 시 켜고 끝나면 해제.
  • `prefers-reduced-motion` 미고려 → 접근성 위반 + 어지럼증 호소.
  • `transform: scale` 안에 *텍스트* → 폰트 *재페인트* 비용. 보통 무시 가능하지만 *서브픽셀 흐림* 이 보일 수 있음.
  • JS `setInterval` 로 매 16ms 스타일 변경 → *메인 스레드 점거*. `requestAnimationFrame` 또는 CSS 로.

흔한 오해

오해

*"transform 은 무조건 GPU 에서 처리된다"*

교정

*조건부* 다. 브라우저가 *애니메이션 중* 인 transform 을 발견하면 자동으로 레이어를 분리한다. 단순히 정적인 `transform: rotate(45deg)` 는 *합성 단계는 가지만 별도 레이어가 아닐 수 있다*. *애니메이션 트리거* 가 있어야 GPU 텍스처로 분리된다.

왜 중요

레이어 승격은 *비용이 있는 결정* 이라 브라우저가 필요한 시점에만 한다.

오해

*"`transform: translateZ(0)` 해킹은 여전히 표준이다"*

교정

*레거시 트릭* 이다. 과거에는 강제 레이어 분리 트릭으로 썼지만, *명시적 의도 표현* 이 가능한 `will-change: transform` 이 W3C 표준이다. 새 코드에는 `will-change` 또는 *애니메이션을 자연스럽게 트리거* 하는 방식 사용.

왜 중요

translateZ(0) 은 *우연히* 동작하는 부수효과일 뿐, 명세에 정의된 동작이 아니다.

오해

*"animation-duration 은 짧을수록 좋다"*

교정

*인지 한계* 가 있다. 너무 짧으면 (≤ 80ms) *깜박임* 으로 인식되어 변화 자체를 놓치고, 너무 길면 (≥ 600ms) *지루함* 을 준다. 마이크로 인터랙션은 *150~250ms*, 대형 전환은 *300~500ms* 가 통상.

왜 중요

인간 지각의 *연속 변화 최소 시간* 과 *주의 지속* 사이의 균형.

면접 질문

중급토스카카오네이버배민당근

답변 방향 힌트

"Layout/Paint/Composite", "컴포지터 스레드", "GPU 메모리".

반드시 언급할 키워드

  • `top` → *Layout 부터* — 부모/형제까지 reflow
  • `transform` → *Composite only* — 별도 레이어 + 컴포지터 스레드
  • 컴포지터 스레드는 *메인 스레드와 독립* → JS 가 무거워도 60fps
  • `will-change` 는 *힌트* — 브라우저가 미리 레이어 준비
  • *항상 켜두면 GPU 메모리 폭증* — 직전 켜고 직후 끄기
  • `transitionend` / `animationend` 에서 `will-change: auto` 로 해제

예상 꼬리 질문

  • `opacity` 도 *Composite-only* 인 이유를 픽셀 합성 모델로 설명해 주세요.
  • iOS Safari 에서 `position: fixed` 안의 `transform` 이 *떨림 (jitter)* 을 보이는 원인은?

자기 점검

`Layout → Paint → Composite` 중 `transform` 이 트리거하는 단계는?

기대 키워드

Composite only컴포지터 스레드

자주 하는 오해

*"transform 은 Layout 부터 다시 돈다"* — 합성 단계만 다시 돌고 *컴포지터 스레드* 가 처리.

`will-change: transform` 을 *영구 적용* 하면 생기는 부작용은?

기대 키워드

GPU 메모리VRAM레이어 폭증

자주 하는 오해

*"많이 쓸수록 빠르다"* — 별도 텍스처를 모두 메모리에 올려 오히려 느려진다.

FLIP 의 4 단계를 한 단어씩 답하라.

기대 키워드

FirstLastInvertPlay

자주 하는 오해

*"순서가 중요하지 않다"* — F → L → I → P 순서가 핵심.

*전정 장애* 사용자를 위한 표준 CSS 분기는?

기대 키워드

`prefers-reduced-motion: reduce``@media`

자주 하는 오해

*"줄이는 것만으론 부족하다"* — 큰 방향성 모션은 *제거 또는 opacity 대체* 가 표준.

학습 자료