RN에서 스크롤 관련 된 View를 보여줄 때 자주 사용되는 태그가 몇 개 존재한다.
그 중 이번에 다룰 것은 바로 ScrollView
와 FlatList
이다.
어느 때와 다를 것 없이 하나의 Screen에서 root를 ScrollView
로 잡고 내부에 배열로 갖춰진 데이터를 뿌릴 일이 있어 FlatList
로 구현하였다.
<ScrollView>
...
<FlatList
data={data}
renderItem={<Something />}
/>
...
</ScrollView>
하지만, 다음과 같은 두 가지 문제점이 발생했다.
다음과 같은 에러 메시지를 볼 수 있을 것이다.
VirtualizedLists should never be nested inside plain ScrollViews with the same orientation because it can break windowing and other functionality - use another VirtualizedList-backed container instead.
이 메시지는 React Native에서 가상화된 리스트 컴포넌트인 VirtualizedLists를 사용할 때, 그 안에 동일한 스크롤 방향을 가진 ScrollView
를 중첩해서 사용하지 말아야 한다는 경고이다.
VirtualizedLists의 종류로 SectionList
와 FlatList
가 있는데, 이 리스트들은 이미 스크롤 관련 로직을 처리하며, 중첩된 ScrollView
와 함께 사용하면 예상치 못한 동작이 발생할 수 있다는 뜻이다.
FlatList
의 최대 강점은 바로 "메모리 효율성" 이다.
ScrollView
는 첫 렌더링에서 모든 리스트들을 전부 렌더링 시켜 버린다. 즉, 아직 화면에 보이지도 않는 저~~~기 밑에 데이터들도 이미 그려놨다는 뜻이다. 이는 앱 퍼포먼스 면에서 굉장히 비효율적이다.
그래서 나온 리스트가 바로 FlatList
이다.
보여줘야 할 컨텐츠 양이 많은 리스트들을 렌더링할 때 이러한 메모리 소비를 막기 위해서 FlatList
를 사용한다.
FlatList
는 스크롤을 내리면서 현재 보여지는 영역에 필요한 데이터들만 그때 그때 불러온다. 따라서 렌더링 시 메모리 소비를 감소시켜 퍼포먼스를 향상 시킨다.
하지만, root에 ScrollView
로 감싸져 있는 FlatList
는 현재 스크린에서의 메모리 계산이 어려워져 메모리 적인 이득을 전혀 볼 수 없다. 사실상 map
함수로 화면을 그린 것과 결과값이 다르지 않게 된 것이다.
뿐만 아니라, infinity scroll 구현을 위해 있는 대표적인 옵션인 onEndReached
등 다른 기능 역시 사용할 수가 없다. 유명무실 한 기능이 되는 셈이다.
FlatList
를 중첩하여 사용하면 된다.
부모 FlatList
내의 ListEmptyComponent
옵션 안에서 다시 FlatList
를 그려주면 위 문제점들을 모두 해결할 수 있다.
ListEmptyComponent
는 본래 기능은 FlatList
가 아무것도 렌더하지 않을 때 대체하여 보여줄 컴포넌트이다.
따라서 부모 FlatList
의 data와 renderItem은 값을 비워두어야 동작한다.
<FlatList
data={[]}
renderItem={null}
ListEmptyComponent={
...
<FlatList
data={data}
renderItem={<Something />}
/>
...
}
/>