Skip to main content

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

FlatList를 통해 전달할 수 있는 VirtualizedList의 속성이다. 이 값은 스크롤할 때마다 렌더링되는 다음 항목 묶음의 크기를 제어한다.

장점: 더 큰 값을 설정하면 스크롤 시 빈 공간이 줄어들어 화면 채우기 속도가 빨라진다.

단점: 한 번에 더 많은 항목을 렌더링하면 JavaScript 실행 시간이 길어져, 클릭과 같은 이벤트 처리가 지연될 수 있다. 이는 반응성이 떨어지는 결과를 초래할 수 있다.

updateCellsBatchingPeriod

타입기본값
Number50

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

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

단점: 배치 렌더링 간격이 너무 길면 빈 공간이 발생할 수 있고, 너무 짧으면 반응성이 떨어질 수 있다.

initialNumToRender

타입기본값
Number10

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

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

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

windowSize

타입기본값
Number21

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

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

단점: windowSize를 크게 설정하면 메모리 사용량이 증가한다. windowSize를 작게 설정하면 빈 공간이 보일 확률이 높아진다.

리스트 아이템 컴포넌트 활용 팁

리스트 아이템 컴포넌트는 리스트의 핵심 요소이므로 빠른 성능을 보장해야 한다. 아래는 리스트 아이템을 효율적으로 구성하는 데 도움이 되는 주요 팁이다.

컴포넌트가 복잡할수록 렌더링 속도가 느려진다. 리스트 아이템에 많은 로직이나 중첩을 사용하지 않도록 주의한다. 앱에서 이 리스트 아이템 컴포넌트를 자주 재사용한다면, 큰 리스트를 위한 별도의 컴포넌트를 만들고 가능한 한 적은 로직과 중첩으로 구성한다.

가벼운 컴포넌트 사용하기

컴포넌트가 무거울수록 리렌더링 속도가 느려진다. 무거운 이미지는 피하고, 리스트 항목에는 가능한 한 작은 크기의 자른 버전이나 썸네일을 사용한다. 디자인 팀과 협의해 리스트에는 최소한의 효과와 상호작용, 정보만 포함시킨다. 자세한 내용은 항목의 상세 페이지에서 보여준다.

React.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 사용하기

모든 리스트 아이템 컴포넌트의 높이(또는 가로 리스트의 경우 너비)가 동일하다면, getItemLayout prop을 사용하면 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} />;
// ...
);