Flatlist 설정 최적화
용어 설명
-
VirtualizedList:
FlatList
의 핵심 컴포넌트로, React Native에서 Virtual List 개념을 구현한 것이다. -
메모리 사용량(Memory consumption): 리스트 정보가 메모리에 얼마나 저장되는지를 나타낸다. 과도한 메모리 사용은 앱 충돌을 일으킬 수 있다.
-
반응성(Responsiveness): 애플리케이션이 사용자 상호작용에 얼마나 빠르게 반응하는지를 의미한다. 예를 들어, 컴포넌트를 터치했을 때 즉각적인 반응이 없고 약간의 지연이 발생하면 반응성이 낮은 상태라고 할 수 있다.
-
빈 공간(Blank areas):
VirtualizedList
가 항목을 충분히 빠르게 렌더링하지 못할 때 발생한다. 이 경우 리스트의 일부가 렌더링되지 않고 빈 공간으로 나타난다. -
뷰포트(Viewport): 화면에 렌더링되는 콘텐츠의 가시 영역을 말한다.
-
윈도우(Window): 실제로 마운트되어야 하는 항목들이 위치하는 영역이다. 일반적으로 뷰포트보다 훨씬 큰 범위를 가진다.
Props
FlatList
의 성능을 향상시킬 수 있는 주요 props 목록은 다음과 같다:
removeClippedSubviews
타입 | 기본값 |
---|---|
Boolean | False |
이 값을 true
로 설정하면, 뷰포트 밖에 있는 뷰들이 네이티브 뷰 계층 구조에서 분리된다.
장점: 뷰포트 밖에 있는 뷰들을 네이티브 렌더링 및 그리기 과정에서 제외함으로써, 메인 스레드에서 소요되는 시간을 줄이고 프레임 드롭의 위험을 최소화할 수 있다.
단점: 이 구현에는 버그가 있을 수 있다. 특히 변환(transforms)이나 절대 위치 지정(absolute positioning)을 복잡하게 사용하는 경우, 콘텐츠가 누락되는 현상(주로 iOS에서 관찰됨)이 발생할 수 있다. 또한 뷰가 완전히 해제되지 않고 분리만 되기 때문에 메모리 절약 효과는 크지 않다는 점에 유의해야 한다.
maxToRenderPerBatch
타입 | 기본값 |
---|---|
Number | 10 |
이 프로퍼티는 VirtualizedList
의 속성으로, FlatList
를 통해 전달할 수 있다. 이 값은 스크롤할 때마다 렌더링되는 다음 아이템 묶음의 크기를 제어한다.
장점: 이 값을 크게 설정하면 스크롤 시 빈 공간이 덜 생기며, 화면이 더 빨리 채워진다.
단점: 한 번에 더 많은 아이템을 렌더링하면 JavaScript 실행 시간이 길어져, 클릭과 같은 다른 이벤트 처리가 지연될 수 있다. 이는 반응성을 떨어뜨릴 수 있다.
updateCellsBatchingPeriod
타입 | 기본값 |
---|---|
Number | 50 |
maxToRenderPerBatch
가 한 번에 렌더링할 아이템의 수를 결정한다면, updateCellsBatchingPeriod
는 VirtualizedList
에게 배치 렌더링 사이의 지연 시간을 밀리초 단위로 지정한다. 이는 컴포넌트가 윈도우 내의 아이템을 얼마나 자주 렌더링할지를 결정한다.
장점: 이 프로퍼티를 maxToRenderPerBatch
와 함께 사용하면, 더 많은 아이템을 적은 빈도로 렌더링하거나, 더 적은 아이템을 더 자주 렌더링하는 등의 유연한 조정이 가능하다.
단점: 렌더링 빈도가 낮을 경우 빈 영역이 발생할 수 있고, 빈도가 높을 경우 반응성이 떨어지는 문제가 생길 수 있다.
initialNumToRender
타입 | 기본값 |
---|---|
Number | 10 |
초기에 렌더링할 아이템의 수를 지정한다.
장점: 모든 기기에서 화면을 가릴 수 있는 정확한 아이템 수를 정의할 수 있다. 이는 초기 렌더링 성능을 크게 향상시킬 수 있다.
단점: initialNumToRender
를 너무 작게 설정하면 초기 렌더링 시 화면을 가리기에 충분하지 않아 빈 공간이 생길 수 있다.
windowSize
타입 | 기본값 |
---|---|
Number | 21 |
여기에 전달하는 숫자는 측정 단위로, 1은 뷰포트 높이와 동일하다. 기본값은 21로 설정되어 있으며, 이는 위쪽으로 10개, 아래쪽으로 10개, 그리고 중간에 1개의 뷰포트를 의미한다.
장점: windowSize
를 크게 설정하면 스크롤 시 빈 공간이 보일 확률이 줄어든다. 반면, windowSize
를 작게 설정하면 동시에 마운트되는 아이템 수가 줄어들어 메모리를 절약할 수 있다.
단점: windowSize
가 클수록 메모리 사용량이 증가한다. windowSize
가 작을수록 빈 공간이 보일 확률이 높아진다.
리스트 아이템에 대한 팁
리스트 아이템 컴포넌트는 리스트의 핵심 요소다. 따라서 빠른 성능을 보장해야 한다.
컴포넌트가 복잡할수록 렌더링 속도가 느려진다. 리스트 아이템에 많은 로직과 중첩을 피하는 것이 좋다. 앱에서 이 리스트 아이템 컴포넌트를 자주 재사용한다면, 큰 리스트 전용 컴포넌트를 만들고 가능한 한 로직과 중첩을 최소화한다.
가벼운 컴포넌트를 사용하면 렌더링 속도가 빨라진다. 무거운 이미지를 피하고, 목록 아이템에는 가능한 한 작은 크기의 잘린 버전이나 썸네일을 사용한다. 디자인 팀과 협의해 목록에 효과와 상호작용, 정보를 최소한으로 줄인다. 세부 정보는 아이템의 상세 페이지에서 보여준다.
memo()
사용하기
React.memo()
는 컴포넌트에 전달된 props가 변경될 때만 리렌더링되는 메모이제이션된 컴포넌트를 생성한다. 이 함수를 사용해 FlatList 내부의 컴포넌트를 최적화할 수 있다.
import React, {memo} from 'react';
import {View, Text} from 'react-native';
const MyListItem = memo(
({title}: {title: string}) => (
<View>
<Text>{title}</Text>
</View>
),
(prevProps, nextProps) => {
return prevProps.title === nextProps.title;
},
);
export default MyListItem;
이 예제에서는 MyListItem
이 title
이 변경될 때만 리렌더링되도록 설정했다. React.memo()
의 두 번째 인자로 비교 함수를 전달해 특정 prop이 변경될 때만 컴포넌트가 리렌더링되도록 했다. 비교 함수가 true
를 반환하면 컴포넌트는 리렌더링되지 않는다.
캐시된 최적화 이미지를 사용하려면 커뮤니티 패키지(예: @DylanVann의 react-native-fast-image)를 활용할 수 있다. 리스트의 모든 이미지는 new Image()
인스턴스로 생성된다. 이미지가 loaded
훅에 도달하는 속도가 빠를수록 JavaScript 스레드가 더 빨리 해제된다.
모든 리스트 아이템 컴포넌트의 높이(또는 가로 방향 리스트의 경우 너비)가 동일하다면, getItemLayout
프로퍼티를 사용해 FlatList
가 비동기 레이아웃 계산을 수행하지 않도록 할 수 있다. 이는 성능 최적화를 위해 매우 유용한 기법이다.
만약 컴포넌트의 크기가 동적으로 변하고 성능이 중요하다면, 디자인 팀과 상의해 성능을 개선할 수 있는 리디자인을 고려해볼 수 있다.
FlatList
컴포넌트에 keyExtractor
를 설정할 수 있다. 이 프로퍼티는 캐싱과 아이템 재정렬을 추적하기 위한 React key
로 사용된다.
아이템 컴포넌트 내에서 key
프로퍼티를 직접 사용할 수도 있다.
렌더링 함수에서 익명 함수 사용 피하기
함수형 컴포넌트에서는 renderItem
함수를 JSX 반환값 외부로 이동한다. 또한, 매 렌더링마다 함수가 재생성되지 않도록 useCallback
훅으로 감싼다.
클래스 컴포넌트에서는 renderItem
함수를 렌더 함수 외부로 이동한다. 이렇게 하면 렌더 함수가 호출될 때마다 함수가 재생성되지 않는다.
const renderItem = useCallback(({item}) => (
<View key={item.key}>
<Text>{item.title}</Text>
</View>
), []);
return (
// ...
<FlatList data={items} renderItem={renderItem} />;
// ...
);