티스토리 뷰

React-Query란?

React Query는 React 애플리케이션에서 비동기 작업(데이터를 가져오기 등)을 관리하기 위한 간편하고 강력한 라이브러리입니다. 주요 특징으로는 간편한 데이터 캐싱, 자동 재요청 및 캐시 갱신, 에러 핸들링 및 재시도 등이 있습니다.

 

장점

  1. 간편한 데이터 관리: React Query를 사용하면 useQuery 훅을 이용하여 데이터를 가져오고 캐싱할 수 있습니다. 복잡한 상태 관리 로직 없이도 데이터를 효율적으로 관리할 수 있습니다.
  2. 자동 캐싱 및 캐시 관리: React Query는 데이터를 자동으로 캐싱하고 필요할 때 캐시된 데이터를 사용합니다. 이를 통해 네트워크 요청의 중복을 방지하고 성능을 향상시킵니다.
  3. 자동 재요청 및 캐시 갱신: 데이터가 변화하면 React Query가 자동으로 해당 데이터에 대한 요청을 다시 보내고, 캐시를 갱신하여 최신 데이터를 유지합니다.
  4. 에러 핸들링 및 재시도: 네트워크 요청에서 발생하는 에러를 쉽게 처리하고 재시도할 수 있습니다. 이를 통해 강건한 애플리케이션을 구축할 수 있습니다.

 

먼저, React-Query를 쓰지 않고 React에서 데이터를 불러오는 코드입니다.

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

export default function App() {
  return (
    <Example />
  );
}

function Example() {
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);
  const [repoData, setRepoData] = useState(null);

  useEffect(() => {
    fetchRepoData();
  }, []);

  const fetchRepoData = () => {
    fetch('https://api.github.com/repos/TanStack/query')
      .then(response => {
        if (!response.ok) {
          throw new Error('Failed to fetch data');
        }
        return response.json();
      })
      .then(data => {
        setRepoData(data);
        setIsLoading(false);
      })
      .catch(error => {
        setError(error);
        setIsLoading(false);
      });
  };

  if (isLoading) return 'Loading...';

  if (error) return 'An error has occurred: ' + error.message;

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

 

위 코드에서는 useState를 사용하여 로딩 상태(isLoading), 에러 상태(error), 그리고 데이터 상태(repoData)를 관리합니다. 그리고 useEffect를 사용하여 컴포넌트가 마운트되었을 때 한 번만 데이터를 가져오도록 처리합니다. 데이터를 가져오는 fetchRepoData 함수에서는 fetch API를 사용하여 데이터를 요청하고, 요청이 성공하면 상태를 업데이트합니다. 요청이 실패하면 에러를 처리합니다.

그리고 로딩 중(isLoading이 true일 때)에는 'Loading...'을 화면에 표시하고, 에러가 발생하면 에러 메시지를 표시합니다. 모든 것이 정상적으로 처리되면 불러온 데이터를 화면에 표시합니다.

 

데이터를 불러오고 화면에 보여주는 코드일 뿐인데 useState를 통해 상태 관리 변수를 3개 만들고 useEffect까지 사용해서 구현해야합니다. 하지만 React-Query를 사용하면 위에 코드보다 더 간결하고 쉽게 구현이 가능합니다.

 

 

React-Query에서 제공해주는 useQuery를 통해 데이터를 불러오는 코드입니다.

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

const queryClient = new QueryClient()

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Example />
    </QueryClientProvider>
  )
}

function Example() {
  const { isLoading, error, data } = useQuery({
    queryKey: ['repoData'],
    queryFn: () =>
      fetch('https://api.github.com/repos/TanStack/query').then((res) =>
        res.json(),
      ),
  })

  if (isLoading) return 'Loading...'

  if (error) return 'An error has occurred: ' + error.message

  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>
  )
}

기존 코드보다 더 간결하고 쉽게 구현된 것을 확인하실 수 있습니다.

 

이제 각 코드가 어떤 의미를 하는지 설명해보도록 하겠습니다.

 

QueryClientProvider

React-Query를 사용하기 위해서는 QueryClientProvider를 통해 사용하고자 하는 컴포넌트를 감싸주 QueryClient 값을 props로 넣워줘야 합니다.

const queryClient = new QueryClient()

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Example />
    </QueryClientProvider>
  )
}


QueryClient를 생성하고 QueryClientProvider로 애플리케이션을 감싸는 이유는 React Query가 데이터를 관리하고 캐싱하기 위한 필요한 환경을 제공하기 위함입니다. 이를 통해 React Query를 사용하여 데이터를 효율적으로 관리할 수 있습니다. 여기에는 몇 가지 이유가 있습니다.

 

  1. 전역 상태 관리: QueryClientProvider는 React Query의 컨텍스트를 제공하여 애플리케이션 전역에서 데이터를 캐싱하고 관리할 수 있도록 합니다. 이는 여러 컴포넌트에서 동일한 데이터에 접근하고 사용할 수 있도록 해줍니다.
  2. 캐시 관리 및 재요청: QueryClientProvider는 React Query의 QueryClient를 제공하여 데이터를 캐싱하고, 캐시된 데이터를 사용하여 UI를 업데이트하고 필요할 때 백그라운드에서 재요청할 수 있도록 합니다. 이를 통해 네트워크 요청의 중복을 방지하고, 애플리케이션의 성능을 향상시킬 수 있습니다.
  3. React Query 훅 사용 가능: QueryClientProvider를 사용하면 하위 컴포넌트에서 React Query의 훅을 사용하여 데이터를 요청하고 관리할 수 있습니다. 이를 통해 간편하고 효율적으로 데이터를 처리할 수 있습니다.
  4. 효율적인 코드 관리: QueryClientProvider를 사용하면 React Query 관련 설정을 한 곳에서 관리할 수 있습니다. 이는 코드의 가독성을 높이고 유지보수를 용이하게 만듭니다.

useQuery

데이터를 가져올 때는(get 요청) useQuery 함수를 통해 데이터를 가져올 수 있습니다.

const { isLoading, error, data } = useQuery({
    queryKey: ['repoData'],
    queryFn: () =>
      fetch('https://api.github.com/repos/TanStack/query').then((res) =>
        res.json(),
      ),
  })

 

 queryKey는 데이터를 식별하는 데 사용되는 키입니다. 이 키는 배열 형태로 지정되며, 데이터를 고유하게 식별하는 데 사용됩니다. 즉 queryKey를 통해 해당 데이터를 캐싱하게 됩니다.

 

queryFn은 데이터를 요청하는 함수를 나타냅니다. 이 함수는 비동기로 실행되며, 데이터를 가져오는 데 사용됩니다. 

 

isLoadingerrordata 변수를 통해 로딩 상태, 에러 상태, 데이터를 관리하고 활용할 수 있게 됩니다.

 

이 외에도 useQuery에는 다양한 기능을 제공합니다.

const {
  data,
  dataUpdatedAt,
  error,
  errorUpdatedAt,
  failureCount,
  failureReason,
  fetchStatus,
  isError,
  isFetched,
  isFetchedAfterMount,
  isFetching,
  isInitialLoading,
  isLoading,
  isLoadingError,
  isPaused,
  isPending,
  isPlaceholderData,
  isRefetchError,
  isRefetching,
  isStale,
  isSuccess,
  refetch,
  status,
} = useQuery(
  {
    queryKey,
    queryFn,
    gcTime,
    enabled,
    networkMode,
    initialData,
    initialDataUpdatedAt,
    meta,
    notifyOnChangeProps,
    placeholderData,
    queryKeyHashFn,
    refetchInterval,
    refetchIntervalInBackground,
    refetchOnMount,
    refetchOnReconnect,
    refetchOnWindowFocus,
    retry,
    retryOnMount,
    retryDelay,
    select,
    staleTime,
    structuralSharing,
    throwOnError,
  },
  queryClient,
)

이 중에서 많이 사용되는 몇 가지 기능에 대해 설명하도록 하겠습니다.

 

enabled

데이터 요청을 활성화 또는 비활성화할 때 사용됩니다. 기본값은 true이며, 데이터 요청이 자동으로 활성화됩니다. 그러나 enabled를 false로 설정하면 초기 렌더링 시에 데이터 요청이 자동으로 시작되지 않습니다.

예를 들어, 버튼을 클릭했을 때 데이터를 가져오고 싶은 경우에는 enabled를 false로 설정하고, 버튼을 클릭할 때 enabled를 true로 변경하여 데이터 요청을 할 수 있습니다. 이렇게 하면 컴포넌트가 마운트될 때 데이터를 불필요하게 가져오는 것을 방지할 수 있습니다.

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

export default function Example() {
  const [enabled, setEnabled] = useState(false);

  const { isLoading, error, data } = useQuery({
    queryKey: ['repoData'],
    queryFn: () =>
      fetch('https://api.github.com/repos/TanStack/query').then((res) =>
        res.json()
      ),
    enabled: enabled, // enabled 상태에 따라 요청 활성화 여부 결정
  });

  const handleClick = () => {
    // 버튼 클릭 시 데이터 요청을 활성화
    setEnabled(true);
  };

  return (
    <div>
      <button onClick={handleClick}>Load Data</button>
      {isLoading ? (
        <p>Loading...</p>
      ) : error ? (
        <p>An error has occurred: {error.message}</p>
      ) : (
        <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>
      )}
    </div>
  );
}

refetchOnMount, refetchOnReconnect, refetchOnWindowFocus

refetchOnMount, refetchOnReconnect, refetchOnWindowFocus는 React Query에서 제공하는 옵션으로, 데이터를 자동으로 다시 요청하는 시점을 제어하는 데 사용됩니다.

refetchOnMount: 컴포넌트가 마운트될 때 마다 데이터를 자동으로 다시 요청하는 옵션입니다. 기본값은 true입니다.


refetchOnReconnect: 네트워크가 재연결될  데이터를 자동으로 다시 요청하는 옵션입니다. 기본값은 true입니다.


refetchOnWindowFocus: 사용자가 다른 탭에서 다시 해당 탭으로 이동 했을 때 데이터를 자동으로 다시 요청하는 옵션입니다. 기본값은 true입니다.

 

여기서 중요한 점은 데이터를 자동으로 다시 불러오는 경우에는 해당 데이터가 stale 상태일 때 불러옵니다.

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

export default function Example() {
  const { isLoading, error, data } = useQuery({
    queryKey: ['repoData'],
    queryFn: () =>
      fetch('https://api.github.com/repos/TanStack/query').then((res) =>
        res.json()
      ),
    refetchOnMount: false,
    refetchOnReconnect: false,
    refetchOnWindowFocus: false,
  });

}

staleTime & cacheTime

staleTime은 캐시된 데이터가 신선하지 않은 상태가 되기 까지의 시간을 나타냅니다. 기본값은 0입니다. 데이터가 신선하지 않은 상태가 되면 캐시된 데이터를 사용하여 UI를 업데이트하고, 백그라운드에서 데이터를 재요청해 캐시를 갱신합니다. 

 

예를 들어, staleTime을 10초로 설정하면, 캐시된 데이터가 10초가 지나지 않았다면 새로운 요청을 보내지 않고 캐시된 데이터를 사용할 수 있습니다. 하지만 10초가 지나면 자동으로 새로운 요청(refetch)을 보내고 캐시를 갱신합니다.

const { isLoading, error, data } = useQuery({
    queryKey: ['repoData'],
    queryFn: () =>
      fetch('https://api.github.com/repos/TanStack/query').then((res) =>
        res.json()
      ),
      {
        staleTime:10000 // 10초
      }
  });

 


cacheTime은 캐시된 데이터가 유지되는 시간을 나타냅니다. 기본 값은 5분입니다. 이 시간이 지나면 해당 데이터는 자동으로 캐시에서 삭제(가비지 컬렉팅)됩니다.

const { isLoading, error, data } = useQuery({
    queryKey: ['repoData'],
    queryFn: () =>
      fetch('https://api.github.com/repos/TanStack/query').then((res) =>
        res.json()
      ),
      {
        cacheTime: 600000 // 10분
      }
  });

 

const { isLoading, error, data } = useQuery({
    queryKey: ['repoData'],
    queryFn: () =>
      fetch('https://api.github.com/repos/TanStack/query').then((res) =>
        res.json()
      ),
      {
        cacheTime: 600000 // 10분
        staleTime: 1000 // 10초
      }
  });

 

이렇게 사용하면 캐시된 데이터가 10초 이전에 가져온 것이라면 캐시된 데이터를 사용하고, 캐시된 데이터는 10분 동안 유지됩니다. 10초 이후에는 백그라운드에서 데이터를 재요청하고 캐시를 갱신합니다.

 

 

 

개발자는 staleTime을 적절하게 조절하여 무분별한 refeching이 일어나지 않도록 해야합니다.

 

지금까지 React-Query와 useQuery에 대해 알아보았습니다.

 

 

참고 : https://tanstack.com/query/v5/docs/framework/react/reference/useQuery

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함