React Native의 주요 스레드는 3가지입니다.
- JS 스레드
- React 앱이 있고, API 호출과 터치 이벤트가 일어나는 곳
- 네이티브 지원 뷰에 대한 업데이트는 일괄 처리되어 프레임 마감일전에 이벤트 루프의 각 반복이 끝날 때 네이티브 코드에 전달됨
- 너무 무거운 컴포넌트를 리렌더링 할 때 100ms 보다 오래걸리면 JS로 제어되는 애니메이션은 정지된 것 처럼 보일 수 있음
- 보통 Navigator 전환 과정에서 발생함. 전환은 JS 스레드에 의해 제어되기 때문.
- JS 스레드가 바쁘면 네이티브 터치가 동작하지 않을 수 있음.
- 메인 스레드(네이티브 스레드)
- UI 렌더링 담당
- 애니메이션 프레임이 실행되는 스레드
- 기본적으로 모든 애니메이션은 메인 스레드에서 실행되는 거고, JS 스레드에서는 동적인 값을 통해 애니메이션을 제어할 때만 사용 됩니다.
- 백그라운드 스레드
- 이미지 처리, 네트워크 요청 등 무거운 작업 처리
네비게이터 전환이 느린 경우
InteractionManager 사용하는 것은 좋은 접근방법이 될 수 있지만, 애니메이션 중에 작업 지연시키기에 사용자 경험 비용이 높다면. LayoutAnimation 을 고려해보는게 좋습니다.
Animated API 는 사용자가 useNativeDriver 옵션을 사용하여 애니메이션 프레임에서 네이티브 코드를 호출할 수 있게 해주는데
이 옵션을 사용하지 않는 한 JS 스레드에서 각 키 프레임을 주문형으로 계산합니다.
- 항상 useNativeDriver:true 로 쓰면 되지 않는가 싶지만, 오직 transform과 opacity 같은 특정 스타일 속성만 지원됩니다.
- 레이아웃 관련 애니메이션(width, height, position 등)은 네이티브 드라이버로 처리할 수 없습니다
- JS에서 동적으로 애니메이션 값을 변경해야 하는 경우에는 사용할 수 없습니다
LayoutAnimation
- Core Animation 을 활용하며 JS스레드와 메인스레드 영향 받지 않습니다.
- Core Animation은 iOS의 메인 스레드(UI 스레드)에서 실행되는 프레임워크입니다.
- 최적화가 잘 되어있어 메인 스레드에 부하를 거의 주지 않고 하드웨어 가속을 활용합니다.
- 즉시 실행되는 정적 애니메이션에만 작동합니다.
- 중단이 가능해야 하는 경우 Animated API 를 사용해야 합니다.
- 이것을 사용하는 대표적인 예시는 모달에서 애니메이션 적용할 때 입니다.
화면에서 뷰를 이동하면(스크롤, 이동, 회전) UI 스레드
- 이미지 위에 투명 배경이 있는 텍스트 배치, 각 프레임에서 뷰를 다시 그리기 위해 알파 합성이 필요한 경우 shouldRasterizeIOS 속성을 활성화하면 renderToTextureAndroid 이 작업에 큰 도움 줌
- 과도하게 사용시 메모리 사용량 크게 증가함
이미지 크기를 애니메이션화하면 UI 스레드
- iOS에서 Image 구성 요소의 너비나 높이를 조정할 때마다 원본 이미지에서 다시 자르고 크기를 조정합니다. 이는 특히 큰 이미지의 경우 매우 비쌀 수 있습니다.
- 대신 transform: [{scale}]style 속성을 사용하여 크기를 애니메이션화합니다. 이를 수행할 수 있는 예는 이미지를 탭하여 전체 화면으로 확대할 때입니다.
구성요소 터치시 구성요소의 onPress 내에서 불투명도/하이라이트를 조정한다고 했을때 onPress 가 반환될 때까지 해당 효과를 볼 수 없을 것입니다. 이 경우도 JS 스레드가 바쁠 경우 많은 작업과 몇 개의 프레임이 삭제될 수 있기 때문이며. 이럴 때 requestAnimationFrame 을 사용하여 애니메이션이 중단되지 않도록 합니다.
requestAnimationFrame 를 사용하면 여전히 JS 스레드에서 실행이 되지만, JS 작업을 다음 프레임으로 연기하여 현재 프레임의 애니메이션이 방해받지 않도록 하는 것입니다.
handleOnPress() {
// 애니메이션 중단 방지
requestAnimationFrame(() => {
this.doExpensiveAnimation();
});
}
느린 네비게이터
- Navigator 애니메이션은 JS 스레드로 제어됩니다. (예를 들어 스텍에서 스와이프)
- 만약 JS 스레드가 바빠서 끊김이 발생하면, JS 기반 애니메이션을 메인 스레드로 보내야 합니다.
- React Navigation 라이브러리는 네이티브 구성 요소와 Animated 라이브러리를 사용해서 메인(네이티브) 스레드에서 실행되는 최소 60FPS 애니메이션을 제공합니다.
오프스레드
- RN 에서는 이미지 디코딩은 메인 스레드가 아닌 다른 스레드에서 수행됩니다.
- React Native는 백그라운드 스레드에서 이미지 디코딩을 수행합니다. 이는 메인 UI 스레드의 블로킹을 방지하고 앱의 성능과 반응성을 향상시키기 위한 설계입니다.
- 백그라운드 스레드는 메인 스레드와 별개로 동시에 실행되는 작업 단위입니다. 주로 시간이 많이 걸리는 작업(이미지 처리, 네트워크 요청 등)을 처리하여 메인 스레드의 부하를 줄이고 UI 응답성을 유지하는 데 사용됩니다.
- 이미지가 아직 다운로드되지 않은 경우를 위해 디코딩하는 동안 몇 개의 프레임에 대한 플레이스홀더를 표시해야 될 수 있습니다.
참고
모든 애니메이션에 대해 useNativeDriver
를 활성화하여 네이티브 스레드로 실행하면 JS 스레드가 바쁠때 애니메이션이 프레임을 건너뛰는 현상에 대해 전혀 걱정하지 않아도 되지 않을까 싶지만, useNativeDriver
는 transform과 opacity 같은 특정 스타일 속성만 지원됩니다.
width, height, top, left 등 요소의 레이아웃(크기나 위치)을 직접 변경하는 애니메이션 속성들은 Animated.View에서 useNativeDriver: true
로 처리할 수 없습니다.
리액트네이티브에 Animated API 와 별개로
모든 View에 사용될 애니메이션 전역 구성에 사용되는 LayoutAnimation
라는게 존재하는데 이것은 Core Animation
를 사용합니다.Core Animation
은 iOS의 메인 스레드(UI 스레드)에서 실행되는 프레임워크입니다. Core Animation
은 메인 스레드에서 실행되지만, 실제 애니메이션 계산과 렌더링은 별도의 렌더 서버 프로세스에서 처리됩니다. 따라서 메인 스레드가 바쁘더라도 애니메이션이 끊기지 않고 부드럽게 실행될 수 있습니다.
위에서 말한 레이아웃 속성(크키,위치)를 변경하는 것과는 완전 다른 이야기입니다. 헷갈리면 안됩니다.
특정 속성 추가하거나 계산하여 직접 애니메이션 하지 않고도 Flexbox 레이아웃 업데이트 수행하는데 유용. 더보기 확장 같은 상위 부모 요소에 영향 줄 수 있는 경우에 유용.
Android에서는 LayoutAnimation 을 사용하려면 UIManager.setLayoutAnimationEnabledExperimental(true)
를 먼저 설정해야 합니다. iOS의 Core Animation과 같은 최적화는 없지만, 기본적인 레이아웃 애니메이션은 동작합니다.
(편집됨)
chatGPT는 애니메이션 사용에 대해 이렇게 권장합니다.
- 단순한 애니메이션의 경우 Animated + useNativeDriver 사용
- 복잡한 애니메이션이나 제스처가 필요한 경우 Reanimated 사용 고려
'프론트엔드 개발 > ReactNative' 카테고리의 다른 글
react-native - expo sdk52 11/13 출시 (0) | 2024.11.14 |
---|