React-query로 데이터 패칭 최적화하기

React 애플리케이션에서 데이터를 가져오는 작업, 즉 데이터 페칭(Data Fetching)은 핵심적인 부분입니다. 많은 개발자가 useEffectuseState, 그리고 Axios 같은 HTTP 클라이언트를 조합하여 이 문제를 해결합니다. 하지만 이 방식은 애플리케이션이 복잡해질수록 여러 문제점을 드러냅니다.

이 글에서는 전통적인 데이터 페칭 방식의 한계를 살펴보고, React-query가 어떻게 이 문제들을 해결하고 데이터 페칭을 최적화하는지 알아봅니다.

목차

  1. 전통적인 데이터 페칭의 문제점
  2. 서버 상태 관리 라이브러리, React-query
  3. 핵심 API로 최적화 시작하기
    • useQuery: 데이터 조회와 캐싱의 자동화
    • useMutation: 간편한 데이터 변경
    • queryClient.invalidateQueries: 데이터 동기화의 마법
  4. 사용자 경험을 극대화하는 고급 패턴
    • Optimistic Updates (낙관적 업데이트)
  5. 결론: 왜 React-query를 선택해야 하는가?

1. 전통적인 데이터 페칭의 문제점

useEffectuseState를 사용해 데이터를 가져오는 코드를 생각해봅시다.

import React, { useState, useEffect } from 'react';
import axios from 'axios';

function TodoList() {
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);
  const [data, setData] = useState(null);

  useEffect(() => {
    setIsLoading(true);
    axios.get('/api/todos')
      .then(response => {
        setData(response.data);
      })
      .catch(error => {
        setIsError(true);
      })
      .finally(() => {
        setIsLoading(false);
      });
  }, []);

  if (isLoading) return <div>로딩 중...</div>;
  if (isError) return <div>에러 발생!</div>;

  // ...
}

이 코드에는 몇 가지 비효율적인 부분이 있습니다.

  • 보일러플레이트 코드: isLoading, isError, data 상태를 모두 직접 선언하고 관리해야 합니다. 컴포넌트마다 이 코드가 반복됩니다.
  • 캐싱의 부재: 다른 컴포넌트에서 동일한 데이터를 요청하면, 불필요한 API 호출이 다시 발생합니다.
  • 상태 동기화의 어려움: 사용자가 할 일을 추가(Create)하거나 삭제(Delete)했을 때, 목록을 최신 상태로 다시 불러오는(refetch) 로직을 수동으로 구현해야 합니다.

2. 서버 상태 관리 라이브러리, React-query

React-query는 자신을 “데이터 페칭 라이브러리”가 아닌 “서버 상태(Server State) 관리 라이브러리”라고 소개합니다. 서버에서 받아온 비동기 데이터를 클라이언트의 UI 상태와 분리하여, 캐싱, 동기화, 업데이트를 매우 효율적으로 관리해줍니다.

3. 핵심 API로 최적화 시작하기

3.1. useQuery: 데이터 조회와 캐싱의 자동화

useQuery는 데이터 조회를 위한 기본 Hook입니다. 위의 장황한 코드를 아래와 같이 단 몇 줄로 줄일 수 있습니다.

import { useQuery } from 'react-query';
import axios from 'axios';

const fetchTodos = () => axios.get('/api/todos').then(res => res.data);

function TodoList() {
  const { data, isLoading, isError } = useQuery('todos', fetchTodos);

  if (isLoading) return <div>로딩 중...</div>;
  if (isError) return <div>에러 발생!</div>;

  // ...
}

useQueryisLoading, isError, data 상태를 알아서 관리해줄 뿐만 아니라, 강력한 캐싱 기능을 제공합니다.

  • useQuery('todos', ...)의 첫 번째 인자인 'todos'는 이 데이터의 고유한 쿼리 키(Query Key)입니다.
  • React-query는 이 키를 기준으로 데이터를 캐시에 저장합니다.
  • 다른 컴포넌트에서 동일한 'todos' 키로 useQuery를 호출하면, API를 다시 호출하는 대신 캐시된 데이터를 즉시 반환하여 불필요한 네트워크 요청을 막습니다.
  • stale-while-revalidate 전략을 기본으로 사용하여, 캐시된 데이터를 먼저 보여준 후 백그라운드에서 데이터를 최신으로 업데이트하여 사용자 경험과 데이터 정합성을 모두 잡습니다.

3.2. useMutation: 간편한 데이터 변경

데이터를 생성, 수정, 삭제할 때는 useMutation을 사용합니다.

import { useMutation } from 'react-query';

const addTodo = (newTodo) => axios.post('/api/todos', newTodo);

function AddTodoComponent() {
  const mutation = useMutation(addTodo);

  const handleAddTodo = () => {
    mutation.mutate({ title: '새로운 할 일', completed: false });
  };

  // ...
}

3.3. queryClient.invalidateQueries: 데이터 동기화의 마법

useMutation의 가장 강력한 기능은 다른 쿼리와의 연동입니다. 할 일이 추가된 후, 목록을 어떻게 새로고침할 수 있을까요? invalidateQueries를 사용하면 됩니다.

import { useMutation, useQueryClient } from 'react-query';

function AddTodoComponent() {
  const queryClient = useQueryClient();

  const mutation = useMutation(addTodo, {
    onSuccess: () => {
      // 'todos' 쿼리를 무효화시켜 자동으로 다시 가져오게 합니다.
      queryClient.invalidateQueries('todos');
    },
  });

  // ...
}

mutation이 성공하면 onSuccess 콜백이 실행되고, queryClient.invalidateQueries('todos')'todos'라는 키를 가진 useQuery를 “오래된 데이터(stale)”로 만듭니다. 해당 쿼리를 사용하는 모든 컴포넌트는 자동으로 데이터를 리เฟetch하여 UI를 최신 상태로 업데이트합니다. 더 이상 수동으로 상태를 관리할 필요가 없습니다.

4. 사용자 경험을 극대화하는 고급 패턴

Optimistic Updates (낙관적 업데이트)

서버의 응답을 기다리지 않고, mutation이 성공할 것이라고 “낙관”하여 UI를 먼저 업데이트하는 기법입니다. 사용자는 즉각적인 피드백을 받게 되어 앱이 매우 빠르게 느껴집니다. useMutationonMutateonError 옵션을 사용해 구현할 수 있으며, 만약 mutation이 실패하면 이전 상태로 롤백합니다.

5. 결론: 왜 React-query를 선택해야 하는가?

React-query는 단순히 코드를 줄여주는 것을 넘어, 애플리케이션의 데이터 페칭 로직을 근본적으로 최적화합니다.

  • 성능 향상: 자동 캐싱과 불필요한 API 호출 방지로 뛰어난 성능을 제공합니다.
  • 개발 경험 향상: 반복적인 로딩/에러 상태 관리를 제거하고, 선언적으로 데이터를 다룰 수 있게 해줍니다.
  • 사용자 경험 향상: stale-while-revalidate, Optimistic Updates 등의 패턴으로 사용자에게 빠르고 부드러운 인터페이스를 제공합니다.

React 애플리케이션의 데이터 페칭을 한 단계 끌어올리고 싶다면, React-query는 단연 최고의 선택입니다.

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다