React-Native(RN)

React Native에서 네트워크 요청 최적화 (캐싱, 로딩 처리, 성능 개선)

rn-guide 2025. 2. 7. 12:00

안녕하세요! 이번 글에서는 React Native에서 네트워크 요청을 최적화하는 방법을 다뤄보겠습니다.

처음에는 API 요청을 그냥 fetch나 axios로 하면 끝이라고 생각했어요.
작은 프로젝트에서는 큰 문제가 없었죠.
그런데 앱이 커지고 네트워크 요청이 많아지면서, 로딩 속도가 느려지거나 같은 데이터를 여러 번 요청하는 비효율적인 상황이 생겼어요.
특히, 사용자가 스크롤을 빠르게 내릴 때마다 새로운 요청이 발생하거나, 네트워크가 불안정할 때 앱이 멈추는 문제도 경험했어요.

그래서 이번 글에서는 네트워크 요청을 최적화하는 다양한 방법(캐싱, 로딩 처리, 성능 개선)을 정리해보겠습니다.


1. 불필요한 API 요청 줄이기

네트워크 요청이 많아지면 앱 성능이 떨어질 수밖에 없습니다.
특히, 같은 데이터를 여러 번 요청하는 경우가 많다면 불필요한 API 호출을 줄이는 방법이 필요합니다.

📌 해결 방법:

  • 이전에 불러온 데이터를 다시 사용할 수 있도록 캐싱
  • 불필요한 네트워크 요청을 방지하는 Debounce & Throttle 적용

1️⃣ 캐싱을 활용해서 같은 요청 반복 방지

캐싱이란 한 번 불러온 데이터를 저장해두고, 다시 요청하지 않고 저장된 데이터를 활용하는 방식입니다.
React Native에서는 React Query, SWR 같은 데이터 관리 라이브러리를 활용해서 쉽게 구현할 수 있습니다.

📌 React Query로 데이터 캐싱 적용 예제

import React from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';

const fetchPosts = async () => {
  const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts');
  return data;
};

const QueryExample = () => {
  const { data, isLoading, error } = useQuery(['posts'], fetchPosts, {
    staleTime: 1000 * 60 * 5, // 5분 동안 데이터 캐싱
  });

  if (isLoading) return <ActivityIndicator size="large" color="blue" />;
  if (error) return <Text>데이터를 불러오는 데 실패했습니다.</Text>;

  return (
    <FlatList
      data={data}
      keyExtractor={(item) => item.id.toString()}
      renderItem={({ item }) => <Text>{item.title}</Text>}
    />
  );
};

export default QueryExample;

 

📌 코드 설명

  • useQuery(['posts'], fetchPosts, { staleTime: 1000 * 60 * 5 })
    • staleTime을 설정하면, 5분 동안 같은 API 요청을 반복하지 않음.
    • 즉, 앱을 다시 실행하거나 화면을 새로고침해도 캐싱된 데이터를 우선 사용하고, 필요할 때만 API 요청을 보냄.

👉 이렇게 하면 같은 데이터를 불필요하게 여러 번 요청하지 않아서 성능이 훨씬 좋아짐.


2️⃣ Debounce & Throttle 적용하기 (검색 & 빠른 요청 방지)

📌 Debounce: 사용자가 입력을 멈춘 후 일정 시간이 지나면 요청을 보냄.
📌 Throttle: 일정 시간마다 한 번씩 요청을 보냄.

예를 들어, 검색창에 입력할 때마다 API 요청을 보내면 비효율적이기 때문에, Debounce를 적용하면 불필요한 요청을 줄일 수 있음.

📌 Debounce 적용 예제 (lodash 사용)

import React, { useState, useCallback } from 'react';
import { View, TextInput, FlatList, Text } from 'react-native';
import axios from 'axios';
import debounce from 'lodash.debounce';

const SearchComponent = () => {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);

  const fetchSearchResults = async (text) => {
    const response = await axios.get(`https://jsonplaceholder.typicode.com/posts?q=${text}`);
    setResults(response.data);
  };

  const debouncedSearch = useCallback(debounce(fetchSearchResults, 500), []);

  return (
    <View>
      <TextInput
        placeholder="검색어 입력"
        onChangeText={(text) => {
          setQuery(text);
          debouncedSearch(text);
        }}
      />
      <FlatList
        data={results}
        keyExtractor={(item) => item.id.toString()}
        renderItem={({ item }) => <Text>{item.title}</Text>}
      />
    </View>
  );
};

export default SearchComponent;

 

📌 코드 설명

  • debounce(fetchSearchResults, 500) → 사용자가 입력을 멈춘 후 500ms 뒤에 API 요청을 보냄.
  • 입력할 때마다 API 요청을 보내지 않아서, 네트워크 부하를 줄이고 성능을 최적화할 수 있음.

👉 검색창, 버튼 클릭, 스크롤 이벤트 등에서도 Debounce & Throttle을 활용하면 성능 최적화에 유용함!


2. 로딩 처리 최적화 (Skeleton UI 활용)

네트워크 요청이 느려지면, 사용자가 아무 반응 없이 빈 화면을 보게 되는 문제가 발생할 수 있습니다.
이를 해결하려면 로딩 상태를 사용자에게 자연스럽게 보여줄 방법이 필요합니다.

📌 Skeleton UI란?

  • API 요청이 끝날 때까지, 임시로 비슷한 모양의 UI를 먼저 보여주고, 데이터가 로드되면 실제 내용을 표시하는 방식
  • 앱을 더 빠르게 느껴지게 만드는 효과가 있음.

📌 React Native에서 Skeleton UI 구현 예제

import React, { useState, useEffect } from 'react';
import { View, Text, ActivityIndicator, StyleSheet } from 'react-native';

const SkeletonExample = () => {
  const [loading, setLoading] = useState(true);
  const [data, setData] = useState(null);

  useEffect(() => {
    setTimeout(() => {
      setData("API 데이터 로드 완료!");
      setLoading(false);
    }, 3000);
  }, []);

  return (
    <View style={styles.container}>
      {loading ? (
        <View style={styles.skeletonBox} />
      ) : (
        <Text>{data}</Text>
      )}
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  skeletonBox: { width: 200, height: 30, backgroundColor: '#ddd', borderRadius: 5 },
});

export default SkeletonExample;

👉 이렇게 하면, 데이터가 로드되기 전까지 빈 화면 대신 "로딩 중" UI를 보여줄 수 있음!


정리 및 다음 단계

React Query를 활용한 API 캐싱 → 불필요한 요청 방지
Debounce & Throttle 적용 → 빠른 요청 반복 방지
Skeleton UI 적용 → 사용자 경험 향상

 

📌 출처 및 참고 자료