[React] 리액트 쿼리에 대해 알아보자 (React Query)

mangsuyo·2024년 5월 2일
post-thumbnail

커스텀 훅의 문제점

필요성

만약 fetch를 이용해서 얻어야 하는 데이터라면, 그 데이터가 필요한 곳마다 계속해서 fetch를 해주어야 한다!
그렇기 때문에, 이러한 fetch된 데이터들(값)의 재사용을 위해 Hook을 반복 호출한다는 것은 비효율적이다.

이러한 데이터들의 캐싱을 도와주고, 에러가 발생했을 때 retry를 도와주는 것이 리액트 쿼리이다.


리액트 쿼리 이용하기

사용

QueryClientProvider로 감싸준뒤 useQuery()를 이용하면 된다.

첫번 째 인자로는 고유한 Key를, 두번 째 인자로는 실제 호출하고자 하는 비동기 함수(fetch)가 들어간다.

최종 반환 값은 API의 성공, 실패여부, 데이터를 포함한 객체이다.

App.jsx : Provider 생성

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

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

Example.jsx : useQuery 이용

export default function Example() {
  const { isLoading, error, data : products } = useQuery(
// 배열 안의 값이 하나라도, 배열 안에 넣어줘야 합니다.
 ['repoData'], async () => {
      fetch('https://api.github.com/repos/TanStack/query').then(
        (res) => res.json();}
  );
useQuery(첫번째 인자 : Key(배열 형태), 두번째 인자 : fetch함수)

** 참고
매개변수를 받을 때 이름을 바꾸고 싶다면, data : products 처럼
:을 붙이고 내가 사용하고 싶은 이름을 사용할 수 있다.

export default function Videos() {
  const { isPending, error, data } = useQuery({
    queryKey: ["videos"],
    queryFn: async () => getMostPopularVidoes(),
  });

  return (
    <section className="grid gap-2 grid-cols-1 justify-items-center md:grid-cols-2 lg:grid-cols-4 2xl:grid-cols-5">
      {isPending && <div>Loading..</div>}
      {error && <div>Something wrong 🥹</div>}
      {data && data.map((video) => <VideoCard key={video.id} video={video} />)}
    </section>
  );
}

Query Keys

고유한 Key로 네트워크 통신을 구분한다.

즉 A라는 Key를 가진 컴포넌트가 fetch를 이용할 때 동일한 Key A로 캐시된 데이터를 공유한다.

만약 A라는 Key가 ['product']이고 B라는 Key가 ['product', 'sale']이라면 두개의 키는 다른 캐싱을 이용한다.
물론 A라는 Key를 다른 컴포넌트에서 사용해도, Key가 동일하므로 같은 캐싱을 이용할 것이다.

export default function Example() {
  const { isLoading, error, data : products } = useQuery(
 ['products', checked], async () => {
      fetch(`https://api.github.com/repos/TanStack/query/${checked ? 'sale_' : ''}`).then(
        (res) => res.json();}
  );

개발툴

React-Query에서는 공식적으로 react-query/devtools 를 통해 Devtool을 지원한다. 개발 모드에서만 사용하며, devtools를 통해 좀 더 확실하게 데이터의 흐름을 파악할 수 있다.


React Query 제대로 사용하기

함정

좋은 캐싱을 유지한다는 것은 적절하게 데이터를 갱신해줄 수 있다는 말과도 같다.
그렇다면 React Query는 언제 데이터를 갱신할까?

  1. 브라우저에 포커스가 들어오는 경우 (refetchOnWindowFocus)
  2. 새로운 컴포넌트 마운트가 발생한 경우 (refetchOnMount)
  3. 네트워크 재연결이 발생한 경우 (refetchOnReconnect)
  4. Refetch의 주기가 되었을 경우

staleTime - cacheTime

staleTime은 데이터가 fresh에서 stale 상태로 변경되는 데 걸리는 시간이다.

staleTime은 기본 값이 0이므로, Refetch가 일어나는 4가지 경우에 대해서 무조건 Refetch가 발생한다.
cacheTime은 데이터가 inactive한 상태일 때 캐싱된 상태로 남아있는 시간이다.

특정 컴포넌트가 페이지 전환 등으로 인해 화면에서 사라지면 사용된 데이터는 inactive되고, 이때 데이터는 cacheTime만큼 유지된다.


적절한 staleTime 설정하기

Refetch가 발생하는 이유는, staleTime이 기본적으로 0으로 설정되어있기 때문이다.

따라서, staleTime을 설정하면, 위의 4가지 이유에도 Refetch가 일어나지 않는다.

export default function Example() {
  const { isLoading, error, data : products } = useQuery(
 ['products', checked], async () => {
  fetch(`https://api.github.com/repos/TanStack/query/${checked ? 'sale_' : ''}`).then(
  (res) => res.json();
}, {
	stailTime : 1000 * 60 * 5, // 5분동안은 데이터를 stail 처리하지 않는다.
}
  );

React Query 동작 원리

해당 키의 데이터가 캐싱되어 있지 않을 때

products라는 key로 데이터를 요청하고 React Query안에 products라는 key를 가진 데이터가 없으므로 server에게 데이터를 받아와 내부적으로 저장합니다.

몇초 후 다시 products라는 key로 데이터를 요청했을 때, 어떻게 될까요?

캐싱된 데이터가 있을 때

결과적으로는 re-fetch가 일어납니다.

Query안에서 캐싱된 데이터를 가지고 있는데 왜 re-fetch가 일어났을까요?
먼저, 캐싱된 데이터를 가지고 있으므로 즉각적으로 re-fetch없이 UI를 업데이트 해줍니다.

그러나, 상태가 stale이기 때문에, 오래된 데이터라고 인식해서, 서버를 통해 새로운 데이터를 fetch하고 다시 UI를 업데이트 해줍니다.

이 현상을 피하고 싶다면, 앞의 예시와 같이 staleTime을 따로 선언해주면 됩니다.


데이터 업데이트하기

만약 우리가 가지고 있는 캐싱된 데이터에 변동이 일어났다면 어떻게 해야할까요?

업데이트가 일어났다면 기존에 가지고 있는 데이터는 오래된 데이터임을 명시하고, 사용자에게 업데이트된 데이터를 보여줘야 합니다.

const client = useQueryClient();
client.invalidateQueries(['products', false]);
// Key가 ['products', false]인 데이터를 Refetch해준다.

profile
한망수요

0개의 댓글