transform과 opacity만 사용한 애니메이션인데 왜 첫 실행이 끊길까?

2026년 8월 7일

어느 화면에서 바텀싯을 위와 같은 css로 열고 있다.

.bottom-sheet {
  transform: translateY(100%);
  opacity: 0;
  transition:
    transform 300ms ease,
    opacity 300ms ease;
}

.bottom-sheet.open {
  transform: translateY(0);
  opacity: 1;
}

개발자는 transform과 opacity만 사용했으므로 “GPU 합성만 일어나고 메인 스레드와 무관하게 부드러울 것”이라고 판단했다.

하지만 저사양 Android 기기에서는 다음 현상이 발생했다.

  1. 애니메이션 시작 시 프레임이 잠깐 끊긴다.
  2. 패널 안에 이미지와 약관 텍스트가 많을수록 더 심하다.
  3. 애니메이션 도중 React에서 큰 상태 업데이트가 발생하면 끊김이 길어진다.
  4. will-change: transform을 추가하자 일부 기기에서는 개선됐지만, 여러 패널에 적용하니 메모리 사용량이 증가했다.

왜 그런것일까?


1. 애니메이션 시작 시 프레임이 잠깐 끊긴다.

여기서 시작시에 프레임이 끊긴다는 것을 유의해야한다. 기본적으로 transform과 opacity 같은 경우 조건이 부합한다면 style - layout - paint - raster 단계를 거치지 않고 바로 composition 단계에서 적용 될 수 있는 속성이다.

하지만 조건에 부합하지 않는다면 위의 렌더링 파이프 라인을 그대로 거친다. 즉, 대상 요소가 처음부터 컴포지션 요소로 준비가 되어야하는 조건이 있다. 만약에 준비가 안되어있다면 아래와 같은 단계를 거친다.

바텀시트 스타일 변경
페인트 명령 생성
패널 내부 콘텐츠 래스터화
GPU에서 사용할 텍스처 준비·업로드
컴포지션 레이어로 승격
transform·opacity 애니메이션 시작

래스터화

래스터화(rasterization)은 글자, 도형, 색상 같은 그리기 명령을 모니터에 표시할 수 있는 실제 픽셀 데이터로 바꾸는 작업이다.

.card {
  width: 200px;
  height: 100px;
  background: blue;
  border-radius: 12px;
}

위와 같은 css가 있다고 했을때, 브라우저는 먼저 좌표(100,200)에 크기가 200x100인 파란색 둥근 사각형을 그리라는 명령을 화면에 지시한다.

하지만 화면은 둥근 사각형의 개념을 정확하게 알지 못한다. 최종적으로 각 픽셀의 색상값이 필요하다.

(100, 200) → 파란색
(101, 200) → 파란색
(102, 200) → 파란색
...

화면이 실제로 그려질 수 있도록 데이터화하는걸 래스터화라고 한다.


정리해보면 바텀시트가 만약 처음에 컴포지션 레이어로 준비가 되어있지 않다면, 애니메이션 시작시의 위의 단계를 다 거쳐야하므로 첫 시작시에 애니메이션이 끊겨보일 수 있다.

2. 패널 안에 이미지와 약관 텍스트가 많을수록 더 심하다.

1번에서 설명했던 것 처럼, 컴포지션 레이어를 만들기 위해선 바텀시트 내부의 모든 컨텐츠들을 먼저 실제 픽셀로 만들어야한다.

컨텐츠가 많거나 이미지가 많다면 다음 비용들이 증가한다.

  1. 이미지 다운로드 및 디코딩, 리사이징
  2. 텍스트 조형 및 글리프 래스터화
  3. GPU 텍스처 생성 및 업로드
  4. 더 많은 메모리 사용

등등

위와 같은 이유로 인해서 컨텐츠가 많으면 많을수록 지연이 더 심해지는 것이다.

3. 애니메이션 도중 React에서 큰 상태 업데이트가 발생하면 끊김이 길어진다.

리액트 상태 업데이트는 보통 메인 쓰레드에서 처리된다.

상태 업데이트
React render
Reconciliation
DOM commit
Style·Layout·Paint

작업 자체가 길어지면 메인 스레드에 Long Task가 발생할 수 있다.

하지만 여기서도 컴포지션 레이어에서 실행되는 작업인 경우(transform이나 opacity)라면 메인 스레드가 아무리 바빠도 애니메이션은 실행 된다.

하지만 만약 직접 DOM을 변경하는 경우나, 주변 레이아웃이 변경되거나 픽셀이 무효화 된다면 래스터화를 재실행하기 때문에 끊김이 길어질 수 있다.

4. will-change: transform을 추가하자 일부 기기에서는 개선됐지만, 여러 패널에 적용하니 메모리 사용량이 증가했다.

will-change 속성은 브라우저한테

이 요소의 transform이 곧 변경될 가능성이 있으니, 필요한 최적화를 미리 준비해도 돼

라고 힌트를 준다.

그래서 애니메이션 전에

패널 Paint·Raster
합성 레이어 준비
GPU 텍스처 유지

애니메이션 시작
준비된 레이어의 위치만 변경

위와 같은 작업을 수행할 수 있다.

그렇기때문에 애니메이션 전에 한꺼번에 발생하던 컴포지션 레이어 작업이나, 래스터화가 앞당겨져서 첫 프레임의 끊김이 줄어들 수 있다.

하지만 will-change는 비용을 들이지 않고 성능개선을 해주는 것이 아니다.

컴포지션 레이어 관련 작업을 미리 한다면 아래와 같은 비용이 든다.

  1. 레이어별 픽셀 버퍼
  2. GPU 텍스쳐 메모리
  3. 래스터화 된 결과 캐시
  4. 레이어 관리 비용
  5. 최종 합성 비용

그렇기 때문에 메모리 사용량이 증가하는 것이다.

구글문서에서도 will-change 속성은 제한적으로 사용하라고 기재되어있다.