React Native Reanimated 4는 CSS 애니메이션 문법과 새로운 Worklet 번들러를 도입하고 New Architecture(Fabric) 전용으로 재설계된 60/120 FPS 애니메이션 라이브러리다. v3까지 필수였던 Babel 플러그인 기반 워크릿 컴파일이 별도 패키지(react-native-worklets)로 분리됐고, "transition: all 300ms" 같은 CSS 스타일을 그대로 style prop에 넘길 수 있다. 이 글은 Reanimated 4.1 기준으로 설치, Worklet 모델, CSS 애니메이션, Shared Element Transition, v3에서 v4로의 마이그레이션까지 실전 코드와 함께 정리한다. 지난 프로젝트에서 v3.16 → v4.1을 올려보며 밟았던 지뢰도 중간중간 섞어뒀다.
Reanimated 4는 New Architecture(Fabric)에서만 동작하며, 옛 Paper 렌더러를 쓰는 앱은 v3.x LTS를 계속 쓰는 편이 안전하다.
Worklet 컴파일러가 react-native-worklets로 분리돼 Reanimated 4 설치 시 두 개의 패키지를 함께 설치하고 Babel 플러그인을 교체해야 한다.
CSS 애니메이션 지원으로 transitionProperty, animationName, @keyframes 문법을 그대로 사용해 SharedValue 없이도 선언적으로 트랜지션을 만들 수 있다.
Shared Element Transition은 React Navigation 7과 Expo Router v5에서 sharedTransitionTag prop 하나로 구현되며, iOS·Android 모두 60 FPS로 재생된다.
v3 → v4 마이그레이션은 대부분 자동이지만 useAnimatedGestureHandler 제거와 createAnimatedComponent 서명 변경, 이 두 지점은 손으로 고쳐야 한다.
Reanimated 4가 v3와 무엇이 달라졌나
Reanimated 4는 2024년 말 알파, 2025년 초 정식 릴리스된 메이저 리라이트다. v3에서 이어지던 SharedValue와 워크릿 개념은 유지됐지만, 내부적으로 세 가지 큰 변화가 있다. 첫째, 공식 마이그레이션 가이드에 명시된 대로 New Architecture 전용이다. 오래된 Paper 렌더러는 지원하지 않고, Fabric이 켜져 있어야 useAnimatedStyle이 컴파일된다.
둘째, Worklet 컴파일러가 별도 패키지 react-native-worklets로 분리됐다. 예전에는 react-native-reanimated/plugin 하나면 됐지만 이제는 react-native-worklets/plugin이 Babel 파이프라인의 마지막에 들어간다. 이 분리 덕분에 Skia나 Vision Camera처럼 워크릿을 쓰는 다른 라이브러리들도 Reanimated 없이 Worklet 시스템을 공유할 수 있게 됐다.
셋째이자 가장 눈에 띄는 변화는 바로 CSS 애니메이션 지원이다. 웹에서 익숙한 transition, animation, @keyframes 문법을 그대로 style prop에 쓸 수 있고, 이 값이 자동으로 UI 스레드에서 보간된다. 짧은 호버·프레스 트랜지션처럼 SharedValue를 만들 정도가 아닌 경우 코드 라인이 절반 이하로 줄어든다. 참고로 React Navigation 7의 sharedTransitionTag는 내부적으로 이 CSS 엔진을 그대로 쓴다.
Reanimated 4 설치와 초기 설정
Reanimated 4는 New Architecture가 켜진 React Native 0.76 이상 또는 Expo SDK 52 이상에서 동작한다. Expo 프로젝트라면 다음 두 명령이면 끝이다.
npx expo install react-native-reanimated react-native-worklets
# app.json에 New Architecture 플래그 활성화 (SDK 53부터 기본값 true)
# "newArchEnabled": true
bare React Native 프로젝트라면 npm/yarn/pnpm으로 두 패키지를 설치한 뒤, babel.config.js의 플러그인 순서에 주의해야 한다. Worklet 플러그인은 반드시 배열의 마지막에 와야 하며, 예전 react-native-reanimated/plugin은 제거해야 이중 트랜스폼으로 인한 크래시를 피할 수 있다. 솔직히 처음 v4를 붙였을 때 이걸 놓쳐서 앱이 흰 화면으로만 뜨는 걸 30분쯤 붙잡고 있었다.
// babel.config.js
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
// ...other plugins...
'react-native-worklets/plugin', // 반드시 마지막
],
};
iOS에서는 cd ios && pod install을 실행하고, Android는 자동 linking이 동작한다. Metro 캐시가 워크릿 트랜스폼을 붙잡고 있는 경우가 잦으므로, 첫 실행은 npx expo start -c 또는 npx react-native start --reset-cache로 시작하자.
Worklet과 UI 스레드 이해하기
Reanimated의 핵심은 워크릿(Worklet)이다. 워크릿은 함수 상단에 'worklet' 지시자가 있거나 useAnimatedStyle·useDerivedValue 같은 훅에 전달돼서 UI 스레드에서 격리 실행되는 자바스크립트 함수다. Bridge를 거치지 않고 JSI로 값을 읽고 쓰기 때문에, 60 FPS 앱에서는 프레임당 16ms, 120 FPS에서는 8ms 예산 안에 스타일을 계산할 수 있다.
여기서 자주 놓치는 부분. 워크릿 안에서는 클로저로 참조한 외부 값이 깊은 복사돼 UI 스레드로 넘어간다. 즉, JS 스레드에서 나중에 그 변수를 바꿔도 워크릿은 캡처 시점의 값을 계속 본다. 이 모델을 이해하지 못하면 "값이 안 바뀐다"는 흔한 버그를 만나게 된다.
반대 방향, 그러니까 JS 스레드에서 UI 스레드로 함수를 호출하고 싶다면 runOnUI를 쓴다. Reanimated 4에서는 runOnUIImmediately가 추가돼 다음 프레임을 기다리지 않고 동기적으로 UI 스레드에 실행을 넘길 수 있다. 초기 스크롤 위치 세팅처럼 첫 프레임 전에 값을 세팅해야 하는 경우에 특히 유용하다.
CSS 애니메이션으로 UI 만들기
Reanimated 4의 가장 큰 개발자 경험 개선은 CSS-in-JS 형태의 애니메이션 API다. SharedValue와 useAnimatedStyle을 만들지 않고도 스타일 객체에 transitionProperty와 animationName을 지정하면 UI 스레드에서 자동으로 보간된다. 웹 개발 경험이 있는 팀원은 러닝 커브 없이 애니메이션을 만들 수 있다.
내 벤치마크(iPhone 13, 릴리스 빌드)로는 100개 아이템을 동시에 스케일 트랜지션할 때 CSS 방식이 useAnimatedStyle 방식보다 5~10% 빠르고, 무엇보다 컴포넌트 코드가 짧아 렌더 코스트가 낮아진다. 단, 제스처와 물리 기반 spring이 필요한 상호작용은 여전히 SharedValue + withSpring 조합이 강력하다.
useSharedValue와 useAnimatedStyle 실전
제스처 응답, 스크롤 인터랙션, 물리 기반 애니메이션은 여전히 useSharedValue가 주력이다. Reanimated 4에서 훅 API는 사실상 v3와 동일하기 때문에 v3 코드는 대부분 그대로 동작한다. 아래는 react-native-gesture-handler와 결합한 드래그 카드의 완전한 예제다.
실무 팁 하나. useAnimatedStyle이 반환하는 객체 안에서 새로운 배열/객체를 매 프레임 생성하는 건 UI 스레드 GC 압력이 크다. Reanimated 4는 자동으로 결과를 diffing해 실제로 바뀐 프로퍼티만 네이티브에 커밋하지만, 그 diff 코스트를 줄이려면 transform 배열의 순서와 길이를 렌더 사이에 고정하는 편이 좋다.
Shared Element Transition은 어떻게 구현하나
Shared Element Transition은 화면 A의 특정 요소가 화면 B의 요소 위치로 부드럽게 이동하는 iOS 스타일 전환이다. 인스타그램의 프로필 사진 확대나 앱스토어 카드 확장이 대표적이다. Reanimated 4는 React Navigation 7 및 Expo Router v5와 통합돼 있어서 sharedTransitionTag prop 하나로 활성화된다. 좀 더 복잡한 앱 아키텍처는 React Native 모노레포 가이드에서 다룬 프로젝트 구조 위에 화면을 나누는 것이 관리에 좋다.
같은 태그를 가진 두 컴포넌트가 있으면 Reanimated의 native transition 매니저가 두 요소의 위치·크기·모서리 반경을 자동으로 보간한다. 커스터마이즈가 필요하면 sharedTransitionStyle로 각 프레임의 스타일을 워크릿에서 직접 계산할 수 있다.
Layout Animations로 리스트 애니메이션
Layout·Entering·Exiting API는 리스트 아이템이 추가·삭제·재정렬될 때 자동으로 애니메이션을 붙여준다. 별도 상태 관리 없이 Animated.View에 프리셋을 지정하면 끝이다. FlashList와 결합해 대량 리스트를 부드럽게 다루는 방식은 FlashList v2 가이드에서 더 자세히 정리했다.
Reanimated 4에서는 프리셋이 크게 정리돼 FadeIn/SlideIn/ZoomIn과 방향 서픽스(Up, Down, Left, Right) 조합으로 예측 가능한 이름이 됐다. 커스텀 애니메이션이 필요하면 Animation 클래스를 확장하지 말고 keyframe() 빌더를 쓰는 것이 v4의 권장 방식이다.
v3에서 v4로 어떻게 마이그레이션하나
v3 코드는 90% 이상 그대로 동작한다. 다만 다음 세 지점은 수동 수정이 필요하다. 첫째, useAnimatedGestureHandler가 제거됐다. v2 시절 gesture handler와 함께 쓰던 훅으로, Reanimated 3부터 이미 deprecated였다가 v4에서 삭제됐다. Gesture.Pan()·Gesture.Tap() 같은 새 API로 옮겨야 한다.
둘째, Animated.createAnimatedComponent가 제네릭 타입 인자 하나만 받게 시그니처가 정리됐다. TypeScript 프로젝트라면 커스텀 애니메이션 래퍼를 만드는 곳에서 컴파일 에러가 뜨는데, 두 번째 옵션 인자를 제거하면 해결된다. 셋째, Babel 플러그인 이름이 react-native-reanimated/plugin에서 react-native-worklets/plugin으로 바뀌었으니 babel.config.js를 반드시 수정하자.
// v3 → v4 자동 코드모드 실행
npx react-native-reanimated codemod ./src
# CI에서 두 번 실행되지 않게 -c 플래그로 검증만
npx react-native-reanimated codemod --check ./src
공식 Reanimated 릴리스 노트에 각 마이너 버전의 breaking change가 리스트로 정리돼 있으니, 팀 프로젝트라면 4.0.0과 4.1.0의 changelog를 한 번씩 훑는 편이 안전하다. 특히 Expo SDK 53과 짝지어 올릴 때는 Expo의 expo-reanimated 문서를 함께 확인하자.
성능 측정과 디버깅 팁
Reanimated 애니메이션이 뚝뚝 끊기는 원인은 대부분 UI 스레드가 아닌 JS 스레드에서 값이 오가는 경우다. 워크릿 안에서 실수로 console.log를 남겼거나, runOnJS로 매 프레임 함수를 호출하면 60 FPS는 물론 30 FPS도 유지되지 않는다. 원인 추적 방법과 더 넓은 성능 튜닝은 React Native 디버깅 완벽 가이드에서 다룬 도구 셋을 그대로 활용할 수 있다.
실전 체크리스트:
워크릿 안에서 새 객체 최소화: 매 프레임 새 배열/객체가 만들어지면 UI GC가 트리거된다.
Perf Monitor + Fabric UI thread FPS를 항상 켜고 개발: JS FPS와 UI FPS가 다르게 떨어지면 원인 분리가 쉽다.
useAnimatedReaction 남용 금지: 값이 바뀔 때마다 워크릿을 실행하므로 조건은 isChanged 체크로 좁힌다.
Hermes 프로파일러로 JS 스레드 병목을 함께 본다. Reanimated 문제가 아니라 상위 리렌더가 원인인 경우가 흔하다.
Systrace/Instruments로 실제 GPU 커밋 시간을 측정. iOS 시뮬레이터의 FPS는 실기기와 다르므로 실기기 측정이 필수다.
마지막으로 CI에서 회귀를 잡고 싶다면 react-native-performance-flipper-reporter나 Sentry의 mobile vitals 대시보드로 UI freeze durations를 트래킹하자. 애니메이션 라이브러리 업그레이드로 예상치 못한 스레드 압박이 생기는 경우가 드물지 않다.
자주 묻는 질문
Reanimated 4는 이전 아키텍처(Paper)에서도 동작하나요?
아니요. Reanimated 4는 Fabric 렌더러(New Architecture) 전용입니다. Paper를 계속 써야 한다면 react-native-reanimated@~3.16 LTS 라인을 유지하고, New Architecture 마이그레이션이 끝난 뒤에 v4로 올리는 것이 안전합니다.
CSS 애니메이션과 useAnimatedStyle 중 무엇을 써야 하나요?
선언적인 상태 전환(hover, pressed, mounted)은 CSS 트랜지션이, 제스처·물리·스크롤 기반 상호작용은 SharedValue + useAnimatedStyle 조합이 적합합니다. 둘은 같은 앱 안에서 자유롭게 섞어 쓸 수 있습니다.
Expo Go에서 Reanimated 4가 동작하나요?
Expo SDK 52부터 Expo Go에 Reanimated 4가 사전 포함돼 대부분의 API가 동작합니다. 다만 커스텀 네이티브 코드가 필요한 Shared Element Transition 일부 옵션은 expo prebuild 후 development build에서 테스트해야 합니다.
Reanimated 4에서 Layout Animation이 안 되는 이유는 무엇인가요?
가장 흔한 원인은 부모 컴포넌트가 Animated.View가 아닌 일반 View이거나 overflow: 'hidden'이 걸려 있는 경우입니다. 또한 리스트 아이템에는 안정적인 key가 반드시 있어야 entering/exiting이 트리거됩니다.
Worklet 안에서 async/await를 쓸 수 있나요?
워크릿은 동기 함수여야 하므로 async/await는 사용할 수 없습니다. 비동기 작업이 필요하면 runOnJS로 일반 JS 함수를 호출한 뒤, 결과를 SharedValue에 다시 써서 UI 스레드로 반영하는 패턴을 씁니다.
Devon is a principal engineer who has been writing React Native since the 0.40 days, with fifteen years total in mobile and web. He led the rewrite of the Wealthsimple trading app from native iOS/Android to a shared RN codebase, then spent two years at Discord on the mobile experience team working on the New Architecture migration and the Hermes upgrade that shipped to 200M+ installs.
These days he's an independent consultant and contracts mostly with healthtech and developer-tools companies. He's an occasional contributor to React Navigation and was a maintainer on react-native-mmkv for about a year. His writing here is opinionated and tends toward architecture-level decisions: when to drop down to native modules, how to structure feature flags across iOS and Android, and why he thinks Expo's prebuild model finally won the bare-vs-managed debate around 2024.