[React] React Query

 

 

01. React Query

React Query는 비동기 데이터를 다루는 라이브러리로, 주로 서버와의 데이터 fetching, caching, 동기화를 쉽게 처리할 수 있도록 도와주는 라이브러리이다. 간단히 말하자면, 프론트엔드에서 서버 데이터를 효과적으로 관리하고, API 호출 로직을 간소화할 수 있는 툴이다.

 

💡 React Query 장점

React Query는 프론트엔드 개발자가 서버 데이터 관리를 훨씬 간편하게 할 수 있도록 여러 기능을 제공한다.

  • 캐싱 기능 : 이미 가져온 데이터를 저장해 두었다가, 동일한 요청이 있을 때 서버에 다시 요청하지 않고 바로 사용할 수 있다. 즉, API 요청 횟수를 줄여 성능을 최적화 할 수 있다.
  • 자동 업데이트 : 만약 게시글 목록을 불러왔다면, 새 게시글을 추가하거나 수정할 때 자동으로 데이터를 다시 가져와 최신 상태를 유지한다.
  • 데이터 유효성 판단 : 데이터를 오래된 것으로 판단하면, 다시 서버에서 최신 데이터를 받아온다. 이를 invalidateQuaries 기능으로 자동화할 수 있다.
  • 중복 요청 방지 : 동일한 데이터를 여러 번 요청하면, 중복된 요청을 방지하고 한 번만 서버에 요청한다.
  • 무한 스크롤 : 많은 양의 데이터를 처리할 때 무한 스크롤 기능을 제공해 필요한 만큼의 데이터를 단계적으로 가져올 수 있다.
  • 비동기 처리의 선언적 관리 : 비동기 API 요청을 직관적으로 처리할 수 있어, 복잡한 로직을 간단한 코드로 구현할 수 있다.
  • React Hook과 유사한 구조 : React Query는 React의 Hooks과 매우 비슷한 방식으로 동작해 React 개발자가 쉽게 사용할 수 있다.

 

💡 「카카오페이 프론트엔드 개발자들이 React Query를 선택한 이유」 세줄요약

https://tech.kakaopay.com/post/react-query-1/

 

카카오페이 프론트엔드 개발자들이 React Query를 선택한 이유 | 카카오페이 기술 블로그

카카오페이 프론트엔드 개발자들이 React Query를 선택한 이유에 대해 설명합니다. 이 글은 연작 중 1편에 해당합니다. 1편: 카카오페이 프론트엔드 개발자들이 React Query를 선택한 이유, 2편: React Que

tech.kakaopay.com

1. React Query는 React Application에서 서버 상태를 불러오고, 캐싱하며, 지속적으로 동기화하고 업데이트하는 작업을 도와주는 라이브러리이다.
2. 복잡하고 장황한 코드가 필요한 다른 데이터 불러오기 방식과 달리 React Component 내부에서 단하고 직관적으로 API를 사용할 수 있다.
3. 더 나아가 React Query에서 제공하는 캐싱, Window Focus Refetching 등 다양한 기능을 활용하여 API 요청과 관련된 번잡한 작업 없이 “핵심 로직”에 집중할 수 있다.

 

 

 

 

02. React Query의 핵심 기능

#1. Caching(캐싱) & Refetching(데이터 갱신)

캐싱은 이미 불러온 데이터를 저장해두었다 재사용하는 기능이다.

만약 동일한 데이터를 다시 요청해야 하는 경우, 이미 저장된 캐싱 데이터를 사용하면 네트워크 요청을 줄일 수 있어 성능이 향상된다.

하지만 데이터가 최신의 것인지 아닌지에 대해 판별하는 것이 중요하다.

만약 서버 데이터를 불러와 캐싱한 후, 실제 서버 데이터를 확인했을 때 서버 상에서 데이터의 상태가 변경되었다면, 사용자는 변경된 데이터가 아닌 변경 전의 데이터를 볼 수 밖에 없다. 이는 사용자에게 잘못된 정보를 보여주는 문제를 발생시킨다.

 

이런 문제를 발생시키지 않는 좋은 캐싱 기능을 제공하는 것은 필요한 상황에 적절하게 데이터를 갱신해줄 수 있는 것과 같다.

React-Query는 서버 데이터가 변경되었을 때, 자동으로 데이터를 갱신하는 기능을 제공하며, 아래와 같은 상황에서 데이터 갱신이 자동으로 발생할 수 있다.

  • 브라우저 창에 다시 포커스가 들어올 때 (refetchOnWindowFocus)
  • 새로운 컴포넌트가 마운트될 때 (refetchOnMount)
  • 네트워크 연결이 재설정될 때 (refetchOnReconnect)

이를 위해서 React-Query에서는 기본적인 아래 옵션들을 제공한다.

refetchOnWindowFocus, //default: true
refetchOnMount, //default: true
refetchOnReconnect, //default: true
staleTime, //default: 0
cacheTime, //default: 5분 (60 * 5 * 1000)

 

 

💡 staleTime과 cacheTime

- staleTime

데이터가 fresh -> stale 상태로 변하는데 걸리는 시간이다. 이때 fres 상태의 데이터는 최신의 데이터, stale 상태의 데이터는 기존의 데이터를 말한다. fresh 상태일 때는 Refetch 트리거(브라우저 창에 다시 포커스가 들어올 때, 새로운 컴포넌트가 마운트될 때, 네트워크 연결이 재설정될 때)가 발생해도 Refetch가 일어나지 않는다. 기본값은 0이기 때문에 따로 설정해주지 않으면, Refetch 트리거가 발생했을 때 무조건 Refetch가 발생한다.

 

- cacheTime

cacheTime은 컴포넌트가 언마운트된 후에도 데이터를 캐싱한 상태로 얼마동안 유지할지를 결정한다. 즉, 데이터가 inactive한 상태일 때 캐싱된 상태로 남아있는 시간이다. 특정 컴포넌트가 unmount(페이지 전환 등으로 화면에서 사라질 때)되면 사용된 데이터는 inactive한 상태로 바뀌고, 이때 데이터는 cacheTime만큼 유지된다. 만약 기본값이 5분으로 설정되어 있으면, 이 시간 내에 다시 데이터를 요청하면 캐싱된 데이터를 보여주고, 시간이 지나면 데이터를 새로 가져온다. 즉, 캐싱된 데이터를 계속 보여주는 것이 아닌 fetch하는 동안 임시로 보여주는 것이다.

 

이외에도 사용자가 특정 이벤트가 발생했을 때 Refetching을 하도록 설정해줄 수 있다. React-Query의 이러한 기능들을 통해 사용자는 언제나 최선의 데이터를 제공받게 된다.

 

 

 

#2. Client 데이터와 Server 데이터 분리

React Query는 서버 상태를 관리하는데 특화되어 있어, 클라이언트 상태와 서버 상태를 명확하게 구분할 수 있다.

  • 클라이언트 상태 데이터 : 페이지 상태나 모달 상태와 같은 UI 관련 데이터는 상태 관리 라이브러리(Redux, Recoil)를 통해 관리
  • 서버 데이터 : API 요청을 통해 서버에서 가져오는 데이터는 React Query로 관리

이렇게 하면 UI에 필요한 상태와 서버에서 받아오는 데이터를 따로 관리하게 되어, 관리가 훨씬 수월해진다.

 

React Query는 서버 데이터를 불러오고 캐싱하는 작업을 자동화해주며, 개발자가 직접 구현해야 하는 복잡한 부분들을 대신 처리한다.

  • 캐싱: 한 번 가져온 데이터를 캐시해서, 불필요하게 다시 요청하지 않는다
  • 자동 리패치: 데이터가 변경되면 필요한 부분만 자동으로 다시 가져온다
  • 에러 처리: 서버 요청 실패 시, 간단한 에러 핸들링 로직을 제공한다

예를 들어, useQuery를 통해 데이터를 가져오고 성공했을 때 onSuccess, 실패했을 때 onError로 결과를 처리한다.

const { data, isLoading, isError } = useQuery('userData', fetchUserData, {
  onSuccess: (data) => {
    // 성공했을 때 처리
  },
  onError: (error) => {
    // 실패했을 때 처리
  },
});

 위 코드에서는 컴포넌트 내부에서 위와 같은 로직을 통해 Server에서 받은 데이터를 가져오며, onSuccess와 onError 함수를 통해 fetch 성공과 실패에 대한 분기를 아주 간단하게 구현할 수 있다. 이는 Server 데이터를 불러오는 과정에서 구현해야 할 추가적인 설정들을 작성할 필요가 없는 것이다.

 

React Query로 서버 데이터를 관리하면, 전역 상태 관리 라이브러리로 불필요한 서버 데이터를 관리할 필요가 없어진다. 예를 들어, 서버 데이터를 상태로 저장해놓고 반복적으로 리패치하는 것보다는, React Query가 알아서 필요한 데이터를 다시 요청하게 두는 게 더 효율적일 수 있다. 결론적으로, 클라이언트 상태와 서버 데이터를 각기 다른 도구로 분리해 관리하는 것이 큰 프로젝트에서는 매우 유용하다.

 

 

 

 

03. React Query 사용법

1️⃣ react-query 패키지 설치

npm install @tanstack/react-query

 

 

2️⃣ QueryClient 설정

React Query는 주로 useQuery와 useMutaion 훅을 통해 서버 데이터를 가져오고 업데이트하는 역할을 한다.

서버 데이터는 QueryClient라는 객체를 통해 관리되며, 이 객체는 React의 Context API를 기반으로 동작한다.

즉, 컴포넌트 트리 내에서 데이터를 공유하고, 관리할 수 있게 해주는 핵심 도구이다.

 

React Query를 사용하기 위해서는 먼저 QueryClient를 설정하고, 이를 전체 애플리케이션에 제공해야 한다.

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';

// QueryClient 생성
const queryClient = new QueryClient();

const App: React.FC = () => {
  return (
    <QueryClientProvider client={queryClient}>
      <YourComponent />
    </QueryClientProvider>
  );
};

export default App;

 

 

 

3️⃣ useQuery로 데이터 가져오기

서버에서 데이터를 가져오는 가장 일반적인 방식은 useQuery 훅을 사용하는 것이다.

useQuery는 GET 요청을 통해 서버 데이터를 가져오고, 이를 캐시하고 관리해준다.

import { useQuery } from '@tanstack/react-query';

// API에서 받아올 데이터 타입 정의
interface RepoData {
  name: string;
  description: string;
  stargazers_count: number;
  forks_count: number;
  subscribers_count: number;
}

// 비동기 함수: API 호출
const fetchRepoData = async (): Promise<RepoData> => {
  const response = await fetch('https://api.github.com/repos/tannerlinsley/react-query');
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
};

const Example: React.FC = () => {
  const { data, error, isLoading } = useQuery<RepoData>({
    queryKey: ['repoData'],
    queryFn: fetchRepoData,
  });

  if (isLoading) return <p>Loading...</p>;
  if (error instanceof Error) return <p>An error occurred: {error.message}</p>;

  return (
    <div>
      <h1>{data?.name}</h1>
      <p>{data?.description}</p>
      <strong>👀 {data?.subscribers_count}</strong>{' '}
      <strong>✨ {data?.stargazers_count}</strong>{' '}
      <strong>🍴 {data?.forks_count}</strong>
    </div>
  );
};

export default Example;

 

 💡 useQuery의 핵심 파라미터

  • queryKey: 이 데이터의 고유한 식별자로, 동일한 queryKey를 사용하면 캐싱된 데이터를 활용
  • queryFn: 실제 데이터를 불러오는 비동기 함수. 주로 fetch, axios 등의 라이브러리와 함께 사용
  • isLoading, error, data: API 요청 상태와 결과를 쉽게 확인할 수 있는 값들. 로딩 중인지, 에러가 발생했는지, 성공적으로 데이터를 받아왔는지를 간단하게 처리

💡 동기적으로 데이터 가져오기 (enabled 옵션)

어떤 조건이 만족될 때만 데이터를 가져오고 싶다면, enabled 옵션을 사용할 수 있다.

enabled가 true일 때만 쿼리가 활성화된다.

const { data: todos } = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  enabled: !!userId, // userId가 있을 때만 데이터를 가져옴
});

 

 

 

 

4️⃣ useQueries로 여러 쿼리 한 번에 실행하기

useQueries는 여러 개의 쿼리를 한 번에 실행하고 싶을 때 유용하다.

이를 통해 여러 서버 요청을 병렬로 실행하거나, 여러 쿼리의 결과를 한꺼번에 받아볼 수 있다.

const ids = [1, 2, 3];
const results = useQueries({
  queries: ids.map((id) => ({
    queryKey: ['post', id],
    queryFn: () => fetchPost(id),
  })),
});

여기서 반환된 results는 배열로 묶여 있으며, 각 쿼리의 데이터를 한 번에 처리할 수 있다. 

 

 

 

5️⃣ useMutation으로 데이터 변경하기

useMutation은 POST, PUT, DELETE 같은 서버 데이터 변경 작업에 사용된다.

데이터를 추가하거나 수정할 때 서버에 요청을 보내고, 성공 또는 실패 시 처리 로직을 쉽게 작성할 수 있다.

import { useMutation } from '@tanstack/react-query';
import axios from 'axios';

function App() {
  const mutation = useMutation({
    mutationFn: (newTodo) => axios.post('/todos', newTodo),
    onSuccess: () => {
      console.log('Todo successfully added!');
    },
  });

  return (
    <button onClick={() => mutation.mutate({ title: 'New Task' })}>
      Add Todo
    </button>
  );
}

 

 💡 useMutation 주요 파라미터

  • mutationFn: 서버에 데이터를 변경하는 함수. 이 함수는 Promise를 반환해야 하며, 서버 요청 후에 데이터를 추가, 수정, 삭제하는 역할
  • onSuccess, onError: 요청이 성공했을 때나 실패했을 때 실행되는 콜백 함수. 성공 시 데이터를 캐싱하거나 에러 처리

 

 

 

6️⃣ QueryClient 메서드를 통한 데이터 관리

React Query는 QueryClient를 통해 데이터를 직접 관리할 수 있는 다양한 메서드를 제공한다.

const queryClient = new QueryClient();
const cachedData = queryClient.getQueryData(['user', 1]); // 캐시된 데이터 가져오기
queryClient.setQueryData(['user', 1], { name: 'John Doe' }); // 캐시된 데이터 업데이트
  • getQueryData: 현재 캐시된 데이터를 가져온다.
  • setQueryData: 데이터를 수동으로 설정할 수 있다. (예: 캐시 업데이트)
  • fetchQuery: 특정 키에 대한 데이터를 강제로 다시 가져온다.

 

 

 

7️⃣ 캐싱과 데이터의 자동 갱신

React Query의 가장 큰 장점 중 하나는 캐싱과 자동 refetch 기능이다. 서버에서 데이터를 가져오고 캐싱하여, 동일한 요청을 반복할 필요가 없게 만들어준다. 또한 특정 조건이 만족되면 자동으로 데이터를 다시 가져온다.
const { data } = useQuery({
  queryKey: ['user', userId],
  queryFn: fetchUserData,
  staleTime: 10000, // 10초 동안 데이터가 유효함
  cacheTime: 50000, // 50초 동안 캐시 데이터를 유지
});
  • staleTime: 데이터가 얼마나 오래 유효한지 설정. 이 시간이 지나면 데이터를 다시 가져온다
  • cacheTime: 캐시된 데이터가 메모리에서 얼마나 오래 유지될지를 결정

 

 

반응형