Skip to main content
Version: Next

Flatlist 설정 최적화

용어 설명

  • VirtualizedList: FlatList의 핵심 컴포넌트로, React Native에서 Virtual List 개념을 구현한 것이다.

  • 메모리 사용량(Memory consumption): 리스트 정보가 메모리에 얼마나 저장되는지를 나타낸다. 과도한 메모리 사용은 앱 충돌을 일으킬 수 있다.

  • 반응성(Responsiveness): 애플리케이션이 사용자 상호작용에 얼마나 빠르게 반응하는지를 의미한다. 예를 들어, 컴포넌트를 터치했을 때 즉각적인 반응이 없고 약간의 지연이 발생하면 반응성이 낮은 상태라고 할 수 있다.

  • 빈 공간(Blank areas): VirtualizedList가 항목을 충분히 빠르게 렌더링하지 못할 때 발생한다. 이 경우 리스트의 일부가 렌더링되지 않고 빈 공간으로 나타난다.

  • 뷰포트(Viewport): 화면에 렌더링되는 콘텐츠의 가시 영역을 말한다.

  • 윈도우(Window): 실제로 마운트되어야 하는 항목들이 위치하는 영역이다. 일반적으로 뷰포트보다 훨씬 큰 범위를 가진다.

Props

FlatList의 성능을 향상시킬 수 있는 주요 props 목록은 다음과 같다:

removeClippedSubviews

타입기본값
BooleanFalse

이 값을 true로 설정하면, 뷰포트 밖에 있는 뷰들이 네이티브 뷰 계층 구조에서 분리된다.

장점: 뷰포트 밖에 있는 뷰들을 네이티브 렌더링 및 그리기 과정에서 제외함으로써, 메인 스레드에서 소요되는 시간을 줄이고 프레임 드롭의 위험을 최소화할 수 있다.

단점: 이 구현에는 버그가 있을 수 있다. 특히 변환(transforms)이나 절대 위치 지정(absolute positioning)을 복잡하게 사용하는 경우, 콘텐츠가 누락되는 현상(주로 iOS에서 관찰됨)이 발생할 수 있다. 또한 뷰가 완전히 해제되지 않고 분리만 되기 때문에 메모리 절약 효과는 크지 않다는 점에 유의해야 한다.

maxToRenderPerBatch

타입기본값
Number10

이 프로퍼티는 VirtualizedList의 속성으로, FlatList를 통해 전달할 수 있다. 이 값은 스크롤할 때마다 렌더링되는 다음 아이템 묶음의 크기를 제어한다.

장점: 이 값을 크게 설정하면 스크롤 시 빈 공간이 덜 생기며, 화면이 더 빨리 채워진다.

단점: 한 번에 더 많은 아이템을 렌더링하면 JavaScript 실행 시간이 길어져, 클릭과 같은 다른 이벤트 처리가 지연될 수 있다. 이는 반응성을 떨어뜨릴 수 있다.

updateCellsBatchingPeriod

타입기본값
Number50

maxToRenderPerBatch가 한 번에 렌더링할 아이템의 수를 결정한다면, updateCellsBatchingPeriodVirtualizedList에게 배치 렌더링 사이의 지연 시간을 밀리초 단위로 지정한다. 이는 컴포넌트가 윈도우 내의 아이템을 얼마나 자주 렌더링할지를 결정한다.

장점: 이 프로퍼티를 maxToRenderPerBatch와 함께 사용하면, 더 많은 아이템을 적은 빈도로 렌더링하거나, 더 적은 아이템을 더 자주 렌더링하는 등의 유연한 조정이 가능하다.

단점: 렌더링 빈도가 낮을 경우 빈 영역이 발생할 수 있고, 빈도가 높을 경우 반응성이 떨어지는 문제가 생길 수 있다.

initialNumToRender

타입기본값
Number10

초기에 렌더링할 아이템의 수를 지정한다.

장점: 모든 기기에서 화면을 가릴 수 있는 정확한 아이템 수를 정의할 수 있다. 이는 초기 렌더링 성능을 크게 향상시킬 수 있다.

단점: initialNumToRender를 너무 작게 설정하면 초기 렌더링 시 화면을 가리기에 충분하지 않아 빈 공간이 생길 수 있다.

windowSize

타입기본값
Number21

여기에 전달하는 숫자는 측정 단위로, 1은 뷰포트 높이와 동일하다. 기본값은 21로 설정되어 있으며, 이는 위쪽으로 10개, 아래쪽으로 10개, 그리고 중간에 1개의 뷰포트를 의미한다.

장점: windowSize를 크게 설정하면 스크롤 시 빈 공간이 보일 확률이 줄어든다. 반면, windowSize를 작게 설정하면 동시에 마운트되는 아이템 수가 줄어들어 메모리를 절약할 수 있다.

단점: windowSize가 클수록 메모리 사용량이 증가한다. windowSize가 작을수록 빈 공간이 보일 확률이 높아진다.

리스트 아이템에 대한 팁

리스트 아이템 컴포넌트는 리스트의 핵심 요소다. 따라서 빠른 성능을 보장해야 한다.

컴포넌트가 복잡할수록 렌더링 속도가 느려진다. 리스트 아이템에 많은 로직과 중첩을 피하는 것이 좋다. 앱에서 이 리스트 아이템 컴포넌트를 자주 재사용한다면, 큰 리스트 전용 컴포넌트를 만들고 가능한 한 로직과 중첩을 최소화한다.

가벼운 컴포넌트를 사용하면 렌더링 속도가 빨라진다. 무거운 이미지를 피하고, 목록 아이템에는 가능한 한 작은 크기의 잘린 버전이나 썸네일을 사용한다. 디자인 팀과 협의해 목록에 효과와 상호작용, 정보를 최소한으로 줄인다. 세부 정보는 아이템의 상세 페이지에서 보여준다.

memo() 사용하기

React.memo()는 컴포넌트에 전달된 props가 변경될 때만 리렌더링되는 메모이제이션된 컴포넌트를 생성한다. 이 함수를 사용해 FlatList 내부의 컴포넌트를 최적화할 수 있다.

tsx
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;

이 예제에서는 MyListItemtitle이 변경될 때만 리렌더링되도록 설정했다. React.memo()의 두 번째 인자로 비교 함수를 전달해 특정 prop이 변경될 때만 컴포넌트가 리렌더링되도록 했다. 비교 함수가 true를 반환하면 컴포넌트는 리렌더링되지 않는다.

캐시된 최적화 이미지를 사용하려면 커뮤니티 패키지(예: @DylanVannreact-native-fast-image)를 활용할 수 있다. 리스트의 모든 이미지는 new Image() 인스턴스로 생성된다. 이미지가 loaded 훅에 도달하는 속도가 빠를수록 JavaScript 스레드가 더 빨리 해제된다.

모든 리스트 아이템 컴포넌트의 높이(또는 가로 방향 리스트의 경우 너비)가 동일하다면, getItemLayout 프로퍼티를 사용해 FlatList가 비동기 레이아웃 계산을 수행하지 않도록 할 수 있다. 이는 성능 최적화를 위해 매우 유용한 기법이다.

만약 컴포넌트의 크기가 동적으로 변하고 성능이 중요하다면, 디자인 팀과 상의해 성능을 개선할 수 있는 리디자인을 고려해볼 수 있다.

FlatList 컴포넌트에 keyExtractor를 설정할 수 있다. 이 프로퍼티는 캐싱과 아이템 재정렬을 추적하기 위한 React key로 사용된다.

아이템 컴포넌트 내에서 key 프로퍼티를 직접 사용할 수도 있다.

렌더링 함수에서 익명 함수 사용 피하기

함수형 컴포넌트에서는 renderItem 함수를 JSX 반환값 외부로 이동한다. 또한, 매 렌더링마다 함수가 재생성되지 않도록 useCallback 훅으로 감싼다.

클래스 컴포넌트에서는 renderItem 함수를 렌더 함수 외부로 이동한다. 이렇게 하면 렌더 함수가 호출될 때마다 함수가 재생성되지 않는다.

tsx
const renderItem = useCallback(({item}) => (
<View key={item.key}>
<Text>{item.title}</Text>
</View>
), []);

return (
// ...

<FlatList data={items} renderItem={renderItem} />;
// ...
);