PromleeBlog
sitemapaboutMe

posting thumbnail
React Native에서 데이터 로딩 시 Indicator 사용하기 (ActivityIndicator, react-native-skeleton-placeholder)
Using Indicator when loading data in React Native (ActivityIndicator, react-native-skeleton-placeholder)

📅

🚀

들어가기 전에🔗

이 포스트는 Expo 환경에서 작성되어 있지만 모든 React Native 환경에서 적용되는 내용입니다.
또한, 기본적인 셋팅이 되어있는 프로젝트를 기준으로 작성되었습니다.

Indicator란?🔗

Indicator는 사용자에게 어떤 작업이 진행 중임을 알려주는 UI 요소입니다. 데이터를 불러오거나, 페이지를 이동하는 등의 작업을 할 때 사용자에게 진행 중임을 알려주기 위해 사용됩니다. Indicator가 없으면 사용자는 작업이 진행 중인지 알 수 없기 때문에 사용성이 떨어질 수 있습니다.
Indicator에도 여러 가지 종류가 있지만, 이번 포스트에서는 대표적으로 사용되는 ActivityIndicatorreact-native-skeleton-placeholder를 사용해보겠습니다. ActivityIndicator는 기본적으로 제공되는 원 형태의 로딩 Indicator이며, react-native-skeleton-placeholder는 뼈대를 빈 상태로 미리 보여주는 Indicator입니다. 상황에 따라 적절한 Indicator를 사용하면 사용자에게 더 나은 경험을 제공할 수 있습니다.

🚀

테스트용 화면 만들기🔗

Indicator를 사용하기 위해 테스트용 화면을 만들어보겠습니다. App.js 파일을 열어 다음과 같이 코드를 작성합니다. 이 코드는 사용자가 화면을 새로고침할 때마다 2초 후에 현재 시간을 표시하는 화면을 만드는 코드입니다. 새로고침 작동 원리는 react native refreshcontrol 포스트를 참고해주세요.
import React, { useEffect, useState } from 'react';
import { Text, ScrollView, RefreshControl, View, SafeAreaView } from 'react-native';
 
const Card = () => {
  const [now, setNow] = useState('');
 
  useEffect(() => {
    setTimeout(() => {
      setNow(new Date().toLocaleString());
    }, 2000);
  }, []);
 
  return (
    <View style={{ flex: 1 }}>
      {Array.from({ length: 100 }).map((_, index) => (
        <Text key={index} style={{ textAlign: 'center', justifyContent: 'center', fontSize: 20 }}>{`${now}`}</Text>
      ))}
    </View>
  );
};
 
const List = () => {
  const [key, setKey] = useState(new Date().getTime());
  const [refreshing, setRefreshing] = useState(false);
 
  const handleRefresh = () => {
    setRefreshing(true);
    setKey(new Date().getTime());
    setRefreshing(false);
  };
 
  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: 'white', padding: 20 }}>
      <ScrollView
        refreshControl={
          <RefreshControl refreshing={refreshing} onRefresh={handleRefresh} colors={['blue', 'red', 'green']} />
        }
      >
        <View style={{ flex: 1, flexDirection: 'row' }}>
          <Card key={key} />
        </View>
      </ScrollView>
    </SafeAreaView>
  );
};
 
export default List;
새로고침 시 데이터가 로딩되기 전에는 빈 화면이 보이기 때문에 사용자에게 작업이 진행중인건지, 오류가 발생한 건지 알 수 없습니다.
image
이제 Indicator를 사용해 이를 개선해보겠습니다.

🚀

1. ActivityIndicator 사용하기🔗

ActivityIndicator는 기본적으로 제공되는 Indicator로, 원 형태의 로딩 Indicator입니다. ActivityIndicator는 기본적으로 제공되는 컴포넌트이기 때문에 별도의 설치가 필요하지 않습니다. ActivityIndicator를 사용하려면 다음과 같이 코드를 작성하면 됩니다.
새로고침 Indicator과 분리하기 위해 파란색으로 설정했습니다. 데이터를 불러오는 동안에만 Indicator가 보이도록 하기 위해 isLoading 상태를 추가했습니다.
import React, { useEffect, useState } from 'react';
import { Text, ScrollView, RefreshControl, View, SafeAreaView, ActivityIndicator } from 'react-native';
 
const Card = () => {
  const [now, setNow] = useState('');
  const [isLoading, setIsLoading] = useState(true); // isLoading 추가
 
  useEffect(() => {
    setIsLoading(true); // 데이터 로딩 시작
    setTimeout(() => {
      setNow(new Date().toLocaleString());
      setIsLoading(false); // 데이터 로딩 완료
    }, 2000);
  }, []);
 
  return (
    <View style={{ flex: 1 }}>
      {isLoading ? (
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', height: 200 }}>
          <ActivityIndicator size="large" color="blue" /> // ActivityIndicator 추가
        </View>
      ) : (
        Array.from({ length: 100 }).map((_, index) => (
          <Text key={index} style={{ textAlign: 'center', justifyContent: 'center', fontSize: 20 }}>{`${now}`}</Text>
        ))
      )}
    </View>
  );
};
 
const List = () => {
  const [key, setKey] = useState(new Date().getTime());
  const [refreshing, setRefreshing] = useState(false);
 
  const handleRefresh = () => {
    setRefreshing(true);
    setKey(new Date().getTime());
    setRefreshing(false);
  };
 
  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: 'white', padding: 20 }}>
      <ScrollView
        refreshControl={
          <RefreshControl refreshing={refreshing} onRefresh={handleRefresh} colors={['blue', 'red', 'green']} />
        }
      >
        <View style={{ flex: 1, flexDirection: 'row' }}>
          <Card key={key} />
        </View>
      </ScrollView>
    </SafeAreaView>
  );
};
 
export default List;
다음과 같이 2초간 데이터를 불러오는 동안 Indicator가 보이고, 데이터를 불러온 후에는 사라지고 원하는 데이터가 보이게 됩니다.
image

🚀

2. react-native-skeleton-placeholder 사용하기🔗

react-native-skeleton-placeholder는 뼈대를 빈 상태로 미리 보여주는 Indicator입니다. react-native-skeleton-placeholder를 사용하려면 먼저 설치해야 합니다. 다음 명령어를 둘 중 하나 실행하여 설치합니다.
npm 사용 시
npm install @react-native-masked-view/masked-view react-native-linear-gradient --save
npm install react-native-skeleton-placeholder --save
yarn 사용 시
yarn add @react-native-masked-view/masked-view react-native-linear-gradient 
yarn add react-native-skeleton-placeholder
react-native-skeleton-placeholder를 사용하려면 다음과 같이 코드를 작성하면 됩니다.
 
import React, { useEffect, useState } from 'react';
import { Text, ScrollView, RefreshControl, View, SafeAreaView } from 'react-native';
import SkeletonPlaceholder from 'react-native-skeleton-placeholder';
 
const Card = () => {
  const [now, setNow] = useState('');
  const [isLoading, setIsLoading] = useState(true);
 
  useEffect(() => {
    setIsLoading(true);
    setTimeout(() => {
      setNow(new Date().toLocaleString());
      setIsLoading(false);
    }, 2000);
  }, []);
 
  return (
    <View style={{ flex: 1 }}>
      {isLoading ? (
        <SkeletonPlaceholder borderRadius={4}>
          <Text style={{ marginTop: 6, fontSize: 14, lineHeight: 18 }}>Hello world</Text>
        </SkeletonPlaceholder>
      ) : (
        Array.from({ length: 100 }).map((_, index) => (
          <Text key={index} style={{ textAlign: 'center', justifyContent: 'center', fontSize: 20 }}>{`${now}`}</Text>
        ))
      )}
    </View>
  );
};
 
const List = () => {
  const [key, setKey] = useState(new Date().getTime());
  const [refreshing, setRefreshing] = useState(false);
 
  const handleRefresh = () => {
    setRefreshing(true);
    setKey(new Date().getTime());
    setRefreshing(false);
  };
 
  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: 'white', padding: 20 }}>
      <ScrollView
        refreshControl={
          <RefreshControl refreshing={refreshing} onRefresh={handleRefresh} colors={['blue', 'red', 'green']} />
        }
      >
        <View style={{ flex: 1, flexDirection: 'row' }}>
          <Card key={key} />
        </View>
      </ScrollView>
    </SafeAreaView>
  );
};
 
export default List;
현재 ERROR Warning: TypeError: Cannot read property 'bubblingEventTypes' of null 에러가 있어 이를 해결하는 방법을 찾고 있습니다. 이에 대한 해결 방법을 찾으면 업데이트하겠습니다.
github issue

🚀

결론🔗

더 생각해 보기🔗

참고🔗