FlatList에 대하여 알아보자!
그 동안 나는 List를 뿌릴때에 아래와 같거나 비슷한 방식을 사용해왔다.
하지만 리엑트 네이티브를 공부하다 보니 그냥 mapping만 하는것이 아닌 여러가지 추가 기능들을 같이 제공해주는 FlatList 라는 컴포넌트가 있다는것을 알게 됐다.
FlatList 를 알아보기 전에 먼저 ScrollView에 대하여 간단히 알아보겠다.
- 간단한 스크롤 컨테이너다.
- ScrollView는 단순히 스크롤이 가능한 컨테이너를 제공한다.
- 뿌리는 모든 데이터를 랜더링한다.
- ScrollView는 모든 자식 컴포넌트를 렌더링하므로, 대규모 데이터를 다룰 때 성능이 저하될 수 있다.
- 1개든 10,000개든, 유저의 화면이 몇개의 데이터를 노출할 수 있건 모든 데이터를 랜더링한다.
즉, ScrollView는 랜더링되는 요소들이 화면의 높이를 벗어났을때, 그냥 Scroll을 생성하여 유저가 그 벗어난 데이터를 확인할 수 있도록 해주는 역할을 해주는 단순한 컴포넌트다.
ScrollView는 리엑트의 기본 컴포넌트인 View, Text, TextInput, Image, ScrollView를 정리할때, 다시 한번 상세히 작성하도록 하겠다.
위에서 언급했듯이, ScrollView는 대량이 아닌 데이터를 랜더링할때 좋다.
그렇다면 FlatList의 어떤점 때문에 그런것인지 알아보자!
ScrollView는 랜더링 시에 모든 리스트들을 모두 한번에 랜더링한다.
이때 모든 리스트가 10~20개 정도의 데이터면 모르지만 1,000개, 2,000개 의 데이터를 뿌리게 된다면, 이 모든 데이터를 한번에 랜더링 하는게 맞는것일까를 생각해보면 좋다.
즉, 한 화면에 보일수 있는 데이터의 개수는 작게 봐도 20개 정도의 데이터일텐데, 한번에 모든 데이터를 뿌리는것이 맞냐를 생각해보면 좋다.
이런 비효율성과 의문점에 기반하여 만들어진것이 이 FlatList 라는 것이다.
FlatList는 ScrollVeiw의 비효율성을 해결하기 위해, LazyLoading과 Recycling을 제공한다.
LazyLoading과 Recycling?
- LazyLoading
- LazyLoading은 화면에 보이지 않는 아이템은 미리 랜더링하지 않고, 사용자가 해당 아이템에 스크롤하여 접근할 때 동적으로 랜더링한다.
- 이를 통해 초기 로딩 시간을 줄이고 메모리 사용량을 최적화할 수 있다.
- FlatList는 가상화 기능을 내장하고 있어서 Lazy Loading을 자동으로 지원한다.
- 즉, 화면에 보이는 부분만 렌더링되고, 스크롤 시에만 필요한 아이템이 로드되어 성능이 향상된다.
- Recycling
- Recycling은 스크롤 중에 화면을 벗어난 아이템을 재사용하여 메모리 사용량을 최적화한다.
- FlatList는 화면에서 벗어난 아이템을 재사용하여 새로운 아이템을 렌더링하는 데 사용한다.
- 쉽게 설명하자면, 처음 화면이 랜더링 됐고 스크롤을 했다고 생각해보자, 이때 안보이는 아이템들이 생기는데 이 아이템들을 메모리에서 완전히 없애지 않고 그대로 유지한 채로, 아래로 내려오는 새로운 아이템으로 재사용된다는 것이다.
이를 통해 FlatList를 사용하면 유저에게 보다 나은 UX를 제공할 수 있다.
그럼 이제 FlatList를 사용하는 방법을 알아보자!
FlatList의 핵심 Props
- data
- 랜더링 할 데이터가 들어간다. 즉, 만들고 싶은 리스트의 소스다.
- renderItem
- data로 받은 데이터 아이템을 어떠한 뷰로 랜더링 해주는 콜백함수다.
- KeyExtractor
- 제일 위 내가 예시로 작성한 코드에서 map으로 데이터를 뿌릴때 key값을 직접 주어, 데이터 추적을 쉽게 하는데. 이 작업을 해주는 것이다.
간단 Tip
contentContainerStyle={{gap: 10}} 를 사용하여 각 리스트간 gap을 설정해줄수 있다.
이제 FlatList를 최적화하여 사용하는 방법에 대하여 알아보자!
대부분의 기본적인 설정이 FlatList에 default로 설정되어 있지만, 개발자가 조금 더 성능을 최적화 할 수 있다.
그 방법에 대하여 알아보겠다.
1. initialNumToRender
- default 값: 10
- 이름에서 알 수 있듯 초기에 랜더링할 아이템의 수를 지정하는것이다.
- 이를 통해 초기 랜더링 성능을 향상 시킬 수 있다.
주의점
- 하지만 너무 작은 숫자로 지정할 경우 초기에 공백이 발생할 수 있다.
2. getItemLayout
- 이름에서 느껴지듯 아이템들의 레이아웃을 가져오는것이다.
- 모든 목록의 각 구성 요소의 높이나 너비가 동일한 경우에,
**getItemLayout props**를 통해 제공하면 플랫리스트가 비동기 레이아웃 계산을 관리할 필요가 없다.
- 즉, 각 항목의 크기와 위치를 미리 알려주는 것입니다. 따라서 FlatList가 레이아웃 계산을 따로 하지 않고 알 수 있는것으로, 렌더링 효율성을 향상 시킵니다.
- Horizontal 일 경우 너비, Vertical일 경우 높이가 일정한 아이템일 때 사용하면 좋다.
3. WindowSize
- default 값: 21
- 이 숫자는 뷰포트의 갯수다. 즉 내가 보고 있는 화면 크기로 몇개일지를 정한다.
- 즉, 메모리에 미리 로드할 항목의 범위를 설정하는 Props다.
- 이 값이 크면 많은 항목이 메모리에 로드되기에 스크롤 시에 공백을 볼 확률이 내려가지만 메모리상으로 좋지 않다. 반대로, 너무 작은 값일 경우 메모리 사용량에는 이점이 있으나, 유저가 빈 영역을 보게 되는 경우가 발생할 수 있다.
4. maxToRenderPerBatch
- default 값: 10
- 말 그대로 한번에 랜더링 될 항목의 최대 개수를 정하는것이다.
- WindowSize와 마찬가지로 큰 숫자를 지정하면, 시각적 공백이 줄어드나, 너무 큰 숫자를 지정하면 비효율적으로 변하는 경우가 있다.
5. updateCellsBatchingPeriod
- default 값: 50
- maxToRenderPerBatch와 다르게 랜더링시에 걸리는 시간을 milliseconds 단위로 정하는것 입니다.
- 사용하여 한번에 많은 아이템을 렌더링하며 빈도를 줄이거나, 빈도를 잦게 하고 소수의 아이템을 렌더링하는 등의 설정을 할 수 있다.
- 빈도에 따라 빈영역이 발생하거나, 반응성 문제를 일으킬 수 있다고 하니 주의하여 사용하여야 한다.
6. item에 useMemo 사용
React.memo는 React 컴포넌트를 메모이징하여 성능을 향상시키는 데 사용된다.
- 메모이징은 이전에 렌더링된 결과를 저장하고, 같은 props로 다시 호출될 때 이전 결과를 반환하여 중복 계산을 피한다
- 이를 통해 컴포넌트의 불필요한 재렌더링을 방지하고 성능을 최적화할 수 있습니다.
- 즉, 리렌더링이 발생할 때, Recycle과 함께 기존의 컴포넌트를 메모이제이션 하여 재활용 할 수 있게 된다.
만약 렌더링 되는 FlatList 아이템들의 props가 자주 변하지 않는다면, 이를 활용하여 렌더링 효율성을 높일 수 있다.
- 성능 향상:
React.memo를 사용하여 불필요한 재랜더링을 방지하고, 이전에 랜더링된 결과를 재사용함으로써 성능을 향상시킨다. 특히, 복잡한 컴포넌트의 경우 성능 향상이 더욱 두드러진다.
- 리렌더링 감소: props가 변경되지 않은 경우에는 재랜더링이 발생하지 않기 때문에 컴포넌트의 리랜더링 횟수를 줄일 수 있다.
- 메모리 절약: 이전에 랜더링된 결과를 재사용함으로써 메모리 사용량을 줄인다. 특히, 컴포넌트가 자주 재렌더링되는 경우에 메모리 사용량을 크게 줄일 수 있다.
7. renderItem에 익명함수를 넣지 않고 외부에 빼기
- 랜더 함수가 호출될때마다 재생성되지 않도록 외부로 뺍니다.
renderItem={({item}) => <Text>{item.name}<Text/>}
이런식으로 사용하면 좋지 않다. 계속해서 재성생 되기때문!
react-native-fast-image 사용 이점
- 이미지 로딩 속도 향상:
react-native-fast-image는 네이티브 이미지 컴포넌트를 사용하여 이미지를 로드한다. 이는 네이티브 레벨에서 이미지를 처리하기 때문에 일반적인 Image 컴포넌트보다 더 빠르게 이미지를 로드하고 표시할 수 있다.
- 메모리 효율성: 이 라이브러리는 메모리를 효율적으로 관리하여 앱의 성능을 향상시킨다. 이미지가 메모리에 적재되는 방식이 최적화되어 있어서 메모리 부족이나 렌더링 성능 저하를 방지할 수 있다.
- 캐싱 기능: react-native-fast-image는 이미지 캐싱을 지원하여 이미지를 로컬 캐시에 저장하여 이후에 재로드할 때 빠르게 가져올 수 있다. 이는 네트워크 비용을 줄이고 사용자 경험을 향상시킨다.
- 이미지 처리 기능: 네이티브 이미지 컴포넌트를 사용하기 때문에 이미지 처리 기능이 향상된다. 이는 이미지 크기 조정, GIF 지원, 블러 처리 등의 기능을 더욱 효율적으로 제공할 수 있게 해준다.
- 이를 사용하여 이미지 사용에서 성능을 향상 시킬수 있다.
9. 이 외의 것들
- 가볍고 간단한 컴포넌트를 사용한다.
- list 아이템에 많은 로직과 중첩을 넣지 않는다.
- 용량이 큰 이미지를 사용하지 않고 잘라서 사용하거나 썸네일같이 작은 이미지를 사용한다.
- 즉, 복잡하고 무거운 컴포넌트를 사용하지 않는 것이다.
keyExtractor 를 사용한다.
- 캐싱과 아이템 재정렬을 추적하기 위한 React key로 사용하도록 한다.
지금까지 FlatList를 최적화 하는 방법에 알아보았다.이때 각자의 상황에 맞는 판단을 하여 사용하는것이 습관이 되어야한다. 또한 ScrollView를 사용할지 FlatList를 사용할지도 상황에 맞게 사용해야한다. 항상 상황에 맞는 방식을 사용하여 최적화하도록 하는 습관을 가지는것이 중요하다.
지금까지 FlatList 를 알아보았고, ScrollView를 기반으로 만들어진 SectionList라는것이 있는데, FlatList와 거의 동일하기에 간단하게 사용법과 차이점을 비교하여 다음 포스팅으로 정리하도록 하겠다.