PromleeBlog
sitemapaboutMe

posting thumbnail
React Native(Expo)에서 스크롤 화면 만들기 (ScrollView, FlatList)
Creating Scrollable Screens in React Native(Expo) (ScrollView, FlatList)

📅

🚀

들어가기 전에🔗

React Native 앱을 개발하다 보면 스크롤 가능한 화면을 만들어야 할 때가 많습니다. 이때 사용할 수 있는 두 가지 주요 컴포넌트가 있습니다. 각각 ScrollView와 FlatList인데요, 이 글에서는 각 컴포넌트의 특징과 사용법, 그리고 언제 어떤 컴포넌트를 선택해야 하는지 자세히 알아보겠습니다.

🚀

ScrollView🔗

ScrollView는 여러 컴포넌트와 뷰를 포함할 수 있는 일반적인 스크롤 컨테이너입니다. ScrollView는 모든 자식 컴포넌트를 한 번에 렌더링하므로, 적은 수의 항목을 표시할 때 유리합니다. 다만 대량의 데이터를 표시할 때는 한번에 모든 데이터를 로딩하므로 성능 문제가 발생할 수 있습니다.

사용 예시🔗

ScrollView는 다양한 종류의 컴포넌트를 포함할 수 있어 유연하게 사용할 수 있습니다. 다음은 ScrollView를 사용하여 간단한 스크롤 화면을 만드는 예시입니다. ScrollView 컴포넌트는 react-native 패키지에서 제공됩니다. Dump 데이터에 이미지 주소를 포함하여 ScrollView에 100개의 이미지를 네트워크에서 받아와 표시하는 예시입니다.
ScrollView의 사용법은 정말 간단합니다. 기존의 View 컴포넌트를
ScrollView로 감싸기만 하면 됩니다
. ScrollView는 자식 컴포넌트를 스크롤 가능하게 만들어주는 역할을 합니다. 이 코드에서는
배열 데이터를 map 함수로 순회하며
각 항목을 View 컴포넌트로 렌더링하고 있습니다.
/app/test.tsx
import { Image } from 'expo-image';
import { ScrollView, Text, View } from 'react-native';
 
const Test = () => {
  return (
    <ScrollView style={{ marginTop: 20, padding: 4 }}> // ScrollView로 감싸기
      {Dump.map((item, index) => ( // 배열 데이터 순회
        <View
          key={index} // 각 항목에 고유한 키 제공
          style={{
            padding: 10,
            backgroundColor: '#f5f5f5',
            marginBottom: 10,
          }}
        >
          <Text>{item.title}</Text>
          <Text>{item.description}</Text>
          <Image
            source={item.image}
            transition={1000}
            contentFit="cover"
            style={{
              marginTop: 10,
              width: '100%',
              height: 184,
            }}
          />
        </View>
      ))}
    </ScrollView>
  );
};
 
export default Test;
 
const Dump = Array.from({ length: 100 }, (_, index) => ({
  title: `Title ${index}`,
  description: `Description ${index}`,
  image: `https://picsum.photos/seed/${index}/3000/2000`,
}));
ScrollView 첫 로딩 시
ScrollView 첫 로딩 시

🚀

FlatList🔗

FlatList는 대량의 데이터를 효율적으로 표시하기 위한 컴포넌트입니다. FlatList는 화면에 보이는 항목만 렌더링하기 때문에 대량의 데이터를 표시할 때 유리합니다. 또한 스크롤 시 항목을 동적으로 렌더링하므로 성능이 향상됩니다.

사용 예시🔗

위의 ScrollView 예시와 동일한 데이터를 FlatList로 표시하는 예시입니다. FlatList 컴포넌트 또한 react-native 패키지에서 제공됩니다.
FlatList는 ScrollView와 비슷한 방식으로 사용할 수 있습니다. 다만 data 속성에 표시할 데이터를 전달하고, renderItem 속성에 각 항목을 렌더링하는 함수를 전달합니다. 또한 keyExtractor 속성을 사용하여 각 항목에 고유한 키를 제공합니다.
/app/test.tsx
import { Image } from 'expo-image';
import { FlatList, Text, View } from 'react-native';
 
const Test = () => {
  return (
    <FlatList // FlatList로 변경
      data={Dump} // 표시할 데이터 전달
      renderItem={({ item, index }) => ( // 각 항목 렌더링 함수 (map 함수 대신 사용)
        <View
          key={index}
          style={{
            padding: 10,
            backgroundColor: '#f5f5f5',
            marginBottom: 10,
          }}
        >
          <Text>{item.title}</Text>
          <Text>{item.description}</Text>
          <Image
            source={item.image}
            transition={1000}
            contentFit="cover"
            style={{
              marginTop: 10,
              width: '100%',
              height: 184,
            }}
          />
        </View>
      )} 
      keyExtractor={(item, index) => index.toString()} // 각 항목에 고유한 키 제공
    />
  );
};
 
export default Test;
 
const Dump = Array.from({ length: 100 }, (_, index) => ({
  title: `Title ${index}`,
  description: `Description ${index}`,
  image: `https://picsum.photos/seed/${index}/3000/2000`,
}));
FlatList 첫 로딩 시
FlatList 첫 로딩 시

🚀

ScrollView vs FlatList🔗

각각의 기능이 어떤 방식으로 로드되는지는
스크롤 바를 확인
해보면 알 수 있습니다. 위의 두 구현 화면을 비교해 보있을 떄 ScrollView로 구현된 화면은 스크롤 바가 지체 없이 가장 아래쪽 까지 이동하는 반면, FlatList로 구현된 화면은 스크롤 바가 일정 구간에 도달하면 추가적인 데이터를 로드하여 다시 위로 올라가는 것을 확인할 수 있습니다.
ScrollViewFlatList
모든 항목을 한 번에 로드화면에 보이는 항목만 로드
초기 로딩 시 시간이 걸릴 수 있음초기 로딩 속도가 빠름
부드러운 스크롤 가능빠른 스크롤 시 약간의 지연 발생 가능
메모리 사용량 증가메모리 사용량 일정하게 유지

🚀

FlatList 속성 설명🔗

FlatList는 ScrollView에 비해 내부 렌더링 방식이 한정적입니다. 그렇기에 FlatList는 다양한 속성을 제공하여 렌더링 방식을 조절할 수 있습니다. FlatList의 주요 속성은 다음과 같습니다.
필수 속성 두 가지와 keyExtractor 속성은 위 예시에서 이미 사용했으므로 간략하게만 설명하겠습니다.
data: 표시할 데이터 배열 (필수)
renderItem: 각 항목을 렌더링하는 함수 (필수)
keyExtractor: 각 항목에 고유한 키를 제공하는 함수

ListEmptyComponent🔗

ListEmptyComponent 속성은 데이터가 없을 때 표시할 컴포넌트를 지정할 수 있습니다. 데이터가 없을 때 표시할 컴포넌트를 지정하면 사용자에게 더 나은 사용자 경험을 제공할 수 있습니다.
/app/test.tsx
// ...
<FlatList
	data={[]}
	renderItem={() => <View />}
	keyExtractor={(_, index) => index.toString()}
	ListEmptyComponent={
		<Text style={{ textAlign: 'center', padding: 20 }}>No data</Text> // 데이터가 없을 때 표시할 컴포넌트
	}
/>
// ...
image

ItemSeparatorComponent🔗

ItemSeparatorComponent 속성은 각 항목 사이에 표시할 구분선을 지정할 수 있습니다. 이 속성을 사용하면 각 항목 사이에 구분선을 추가하여 리스트의 항목을 구분할 수 있습니다.
/app/test.tsx
// ...
<FlatList
	// ...
	ItemSeparatorComponent={() => (
		<View style={{ height: 1, backgroundColor: '#e0e0e0' }} /> // 각 항목 사이에 표시할 구분선
	)}
/>
// ...
ItemSeparatorComponent
ItemSeparatorComponent

ListHeaderComponent, ListFooterComponent🔗

ListHeaderComponentListFooterComponent 속성은 각각 리스트의 상단과 하단에 표시할 컴포넌트를 지정할 수 있습니다. 이 속성을 사용하면 리스트의 상단과 하단에 추가 정보를 표시할 수 있습니다.
/app/test.tsx
 
<FlatList
	// ...
	ListHeaderComponent={
		<Text style={{ textAlign: 'center', padding: 20 }}>Header</Text> // 리스트의 상단에 표시할 컴포넌트
	}
	ListFooterComponent={
		<Text style={{ textAlign: 'center', padding: 20 }}>End of list</Text> // 리스트의 하단에 표시할 컴포넌트
	}
/>
// ...
const Dump = Array.from({ length: 2 }, (_, index) => ({
  title: `Title ${index}`,
  description: `Description ${index}`,
  image: `https://picsum.photos/seed/${index}/3000/2000`,
}));
ListHeaderComponent, ListFooterComponent
ListHeaderComponent, ListFooterComponent

getItemLayout🔗

getItemLayout 속성은 항목의 높이가 고정된 경우 항목의 높이를 미리 계산하여 성능을 향상시킬 수 있습니다. 이 속성을 사용하면 항목의 높이를 미리 계산하여 스크롤 시 항목의 높이를 계산하는 시간을 줄일 수 있습니다.
/app/test.tsx
// ...
<FlatList
	// ...
	getItemLayout={(data, index) => ({
		length: 214, // 항목의 높이
		offset: 214 * index, // 항목의 높이 * 인덱스
		index, // 인덱스
	})}
/>
// ...

horizontal🔗

horizontal 속성은 리스트를 가로로 스크롤할 수 있도록 설정할 수 있습니다.
/app/test.tsx (가로로 스크롤)
// ...
<FlatList
	// ...
	horizontal // 리스트를 가로로 스크롤
/>
// ...
horizontal
horizontal

numColumns🔗

numColumns 속성은 그리드 형태로 리스트를 표시할 때 한 줄에 표시할 항목의 개수를 지정할 수 있습니다.
/app/test.tsx (그리드 형태)
import { Image } from 'expo-image';
import { FlatList, Text, View } from 'react-native';
 
const Index = () => {
  return (
    <FlatList
      data={Dump}
      renderItem={({ item, index }) => (
        <View
          key={index}
          style={{
            flex: 1,
            padding: 10,
            backgroundColor: '#f5f5f5',
            marginBottom: 10,
          }}
        >
          <Text>{item.title}</Text>
          <Text>{item.description}</Text>
          <Image
            source={item.image}
            transition={1000}
            contentFit="cover"
            style={{
              marginTop: 10,
              width: '100%',
              height: 184,
            }}
          />
        </View>
      )}
      numColumns={3}
      keyExtractor={(_, index) => index.toString()}
    />
  );
};
 
export default Index;
 
const Dump = Array.from({ length: 100 }, (_, index) => ({
  title: `Title ${index}`,
  description: `Description ${index}`,
  image: `https://picsum.photos/seed/${index}/3000/2000`,
}));
numColumns: 3
numColumns: 3

🚀

결론🔗

ScrollView와 FlatList는 각각 고유한 용도와 장점이 있습니다. 적절한 상황에 맞는 컴포넌트를 선택하여 사용하면 효율적이고 성능이 좋은 React Native 앱을 개발할 수 있습니다. 항상 앱의 요구사항과 데이터의 특성을 고려하여 최적의 선택을 하시기 바랍니다.
저는 단순히 화면을 넘어가는 메뉴 형태는 ScrollView를, 대량의 데이터를 fetching 후 표시해야 하는 경우에는 FlatList를 사용할 예정입니다.

더 생각해 보기🔗

참고 자료🔗