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 |
FlatList
를 통해 전달할 수 있는 VirtualizedList
의 속성이다. 이 값은 스크롤할 때마다 렌더링되는 다음 항목 묶음의 크기를 제어한다.
장점: 더 큰 값을 설정하면 스크롤 시 빈 공간이 줄어들어 화면 채우기 속도가 빨라진다.
단점: 한 번에 더 많은 항목을 렌더링하면 JavaScript 실행 시간이 길어져, 클릭과 같은 이벤트 처리가 지연될 수 있다. 이는 반응성이 떨어지는 결과를 초래할 수 있다.
updateCellsBatchingPeriod
타입 | 기본값 |
---|---|
Number | 50 |
maxToRenderPerBatch
가 한 번에 렌더링할 아이템의 수를 지정한다면, updateCellsBatchingPeriod
는 VirtualizedList
에게 배치 렌더링 간의 지연 시간(밀리초 단위)을 설정한다. 이 값은 컴포넌트가 윈도우 내 아이템을 얼마나 자주 렌더링할지를 결정한다.
장점: 이 프로퍼티를 maxToRenderPerBatch
와 함께 사용하면, 예를 들어 더 많은 아이템을 덜 자주 렌더링하거나, 더 적은 아이템을 더 자주 렌더링하는 등의 유연한 제어가 가능하다.
단점: 배치 렌더링 간격이 너무 길면 빈 공간이 발생할 수 있고, 너무 짧으면 반응성이 떨어질 수 있다.
initialNumToRender
타입 | 기본값 |
---|---|
Number | 10 |
최초로 렌더링할 아이템의 수를 지정한다.
장점: 모든 기기에서 화면을 덮을 만큼의 정확한 아이템 수를 정의할 수 있다. 이는 초기 렌더링 성능을 크게 향상시킬 수 있다.
단점: initialNumToRender
값을 너무 낮게 설정하면 초기 렌더링 시 화면을 덮기에 충분하지 않아 빈 공간이 생길 수 있다.
windowSize
타입 | 기본값 |
---|---|
Number | 21 |
여기에 전달하는 숫자는 측정 단위로, 1은 뷰포트 높이와 동일하다. 기본값은 21로 설정되어 있다. (위쪽 10개 뷰포트, 아래쪽 10개 뷰포트, 그리고 중간에 1개)
장점: windowSize
를 크게 설정하면 스크롤 시 빈 공간이 보일 확률이 줄어든다. 반면, windowSize
를 작게 설정하면 동시에 마운트되는 아이템 수가 줄어들어 메모리를 절약할 수 있다.
단점: windowSize
를 크게 설정하면 메모리 사용량이 증가한다. windowSize
를 작게 설정하면 빈 공간이 보일 확률이 높아진다.
리스트 아이템 컴포넌트 활용 팁
리스트 아이템 컴포넌트는 리스트의 핵심 요소이므로 빠른 성능을 보장해야 한다. 아래는 리스트 아이템을 효율적으로 구성하는 데 도움이 되는 주요 팁이다.
컴포넌트가 복잡할수록 렌더링 속도가 느려진다. 리스트 아이템에 많은 로직이나 중첩을 사용하지 않도록 주의한다. 앱에서 이 리스트 아이템 컴포넌트를 자주 재사용한다면, 큰 리스트를 위한 별도의 컴포넌트를 만들고 가능한 한 적은 로직과 중첩으로 구성한다.
가벼운 컴포넌트 사용하기
컴포넌트가 무거울수록 리렌더링 속도가 느려진다. 무거운 이미지는 피하고, 리스트 항목에는 가능한 한 작은 크기의 자른 버전이나 썸네일을 사용한다. 디자인 팀과 협의해 리스트에는 최소한의 효과와 상호작용, 정보만 포함시킨다. 자세한 내용은 항목의 상세 페이지에서 보여준다.
React.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 사용하기
모든 리스트 아이템 컴포넌트의 높이(또는 가로 리스트의 경우 너비)가 동일하다면, getItemLayout
prop을 사용하면 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} />;
// ...
);