[React-query] TanStack-Query (a.k.a react-query) 다뤄보기

Bomin·2023년 7월 12일
2

[React]

목록 보기
1/5
post-thumbnail
post-custom-banner

개인 프로젝트이던 팀 프로젝트이던 사용하는 데이터 통신들이 복잡해질수록 react-query의 필요성을 크게 느끼고 적용해왔다. 하지만 공식문서를 정독하거나, 효율적으로 사용을 한 건 아닌 것 같다. 따라서 다시 제대로 공부하고 정리하는 시간을 가지며 프로젝트에 더 좋은 코드를 적용하고 싶다. 🥺

TanStack-Query v4

강력한 비동기 상태 관리 라이브러리

Tanstack Query는 개발자에게 보다 간편하고 효율적으로 data fetching을 할 수 있도록 해준다.

데이터를 어디서 가져올 지와 필요한 신선도(데이터의 fresh한 상태를 얼마나 유지할 지)를 알려주면 나머지는 자동으로 처리한다. 캐싱, 백그라운드 업데이트, 오래된 데이터 처리 등도 자동으로 처리해준다.

또 Promise나 async/await을 안다면 바로 사용할 수 있다. 전역 상태를 관리하거나 리듀서, 정규화 시스템 또는 복잡한 구성을 이해할 필요가 없이, 함수를 전달하면(또는 오류를 던져주면), 나머지는 😉

그리고 각 쿼리의 옵저버 인스턴스에 대해 맞춤형 설정과 옵션으로 구성할 수 있다. Devtools, Infinite-loading API 및 데이터 업데이트를 위한 일급 Mutation tools이 제공되어 작업자의 편의를 높여준다.

react-query 사용하기

프로젝트에 설치하기

react-query 설치 (devtool은 선택)

$ npm i @tanstack/react-query @tanstack/react-query-devtools
# or
$ yarn add @tanstack/react-query @tanstack/react-query-devtools

App에 Provider 씌우기

  1. const queryClient = new QueryClient() : 쿼리 클라이언트를 생성한다.

  2. <QueryClientProvider client={queryClient}> : 프로바이더로 쿼리를 사용할 앱에 씌워준다.

  3. <ReactQueryDevtools initialIsOpen={false} /> : Devtools 사용 시 추가한다.

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

// 1. Create a client
const queryClient = new QueryClient();
function App() {
  return (
    // 2. Provide the client to your App
    <QueryClientProvider client={queryClient}>
		// 3. Devtools 사용 시 
		<ReactQueryDevtools initialIsOpen={false} />
      <Home />
    </QueryClientProvider>
  );
}
export default App;

useQuery

useQuery({ queryKey: ['고유 키'], queryFn: fetch 함수(비동기),옵션})

  • 반환한 쿼리 결과에는 useQuery 템플릿 및 기타 데이터 사용에 필요한 쿼리에 대한 모든 정보가 들어있다.
  • 네트워크 통신하는 곳에서 사용하면 된다.
import { useQuery } from '@tanstack/react-query'

export default function MusicList() {

	const query = useQuery({
	    queryKey: ['musics'],
	    queryFn: async () => {
	      const res = await fetch(`data/musics.json`);
	      return res.json();
	    },
	  });
 console.log(query);
...

  • useQuery로 반환되어 꺼내쓸 수 있는 api 데이터들을 확인할 수 있다.

이제 데이터를 사용해서 화면에 나타내보자.

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

export default function MusicList() {

  const { data: musics } = useQuery<Music[]>({
    queryKey: ['musics'],
    queryFn: async () => {
      const res = await fetch(`data/musics.json`);
      return res.json();
    },
  });
...
  • { data: '받아올이름'} : data를 musics이라는 이름으로 가져왔다. (data로만 사용해도 된다.)
  • 호출하는 컴포넌트 두개지만 네트워크에서 데이터 받아오는 페칭이 한 번 일어났다!👍
  • 키 배열에 checked 추가하여 checked의 상태에 따라 다르게 fetching 할 수 있다.
    • queryKey: ['musics', checked ]

isLoading, isError

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

type Music = { title: string; singer: string; id: string };

export default function MusicList() {
  const [checked, setChecked] = useState(false);

  const { isLoading, isError, data: musics } = useQuery<Music[]>({
    queryKey: ['musics', checked],
    queryFn: async () => {
      const res = await fetch(`data/${checked ? 'new_' : ''}musics.json`);
      console.log('💡데이터 받아옴');
			// 일부러 error를 던져줌!
      throw new Error('error');
    },
  });
  if (isLoading) return <p>Loading...</p>;

  if (isError) return <span>Error</span>;
...

  • 첫 실패 후 3번 retry(default) 후 에러로 넘어간다.

💦 그런데 문제가 있다…?

  1. 데이터는 변한 게 없는데 체크박스에 다시 체크만해도 다시 페칭이 일어난다.
  2. 다른 윈도우갔다오기만해도 다시 페칭이 일어난다…

왜?

공식 문서에 따르면

  1. useQueryuseInfiniteQuery로 만들어진 쿼리 인스턴스는 기본적으로 캐시된 데이터를 stale(오래된) 한 것으로 간주한다.

  2. 따라서 이를 바꾸려면 staleTime 옵션을 사용해야한다.

    🔆 stale queries는 자동적으로 백그라운드에서 refetch한다.
    When :
    - 새로운 쿼리가 마운트되었을 때 == 컴포넌트가 다시 쿼리를 이용했을 때
    - 윈도우가 다시 자동으로 포커스되었을 때
    - 네트워크가 다시 연결되었을 때
    - 쿼리가 refetch interval이 설정이 되었을 때 (몇초간격으로 리페치할 건지)

  • 즉, 캐시는 되어있어서 바로 UI 상에서 보여지긴하지만, 다시 네트워크에 요청해서 데이터를 받아와서 업데이트가 이루어진다는 것이다.

staleTime

  • tanstack query의 기본 설정으로는 fetch 후 바로 데이터가 stale(오래된)로 바뀐다!
  • staleTime을 설정해서 리프레쉬===신선한 상태의 시간을 정해주자!
const {
   ...
  } = useQuery<Music[]>({...,
    staleTime: 5000, //5초
  });
  • 5초간은 윈도우가 포커스되어도, 체크박스가 체크되어도 fresh한 상태이므로 refetching이 일어나지 않는다!

staleTime 은 네트워크 요청의 성격에 따라 세심하게 생각하고 설정하자.

  • 데이터가 백엔드에서 빈번하게 업데이트가 되는가
  • 오랫동안 사용자가 캐시하고 있어도 되는 데이터인가 등

예를 들어 쇼핑몰의 물건 데이터들은 좀 길게 해도 될 것이다. 바로바로 물건들이 업데이트 되거나 하지 않기 때문에!

업데이트는?

데이터가 추가되면 지금 화면에서는 어떻게 나타날까?

  • 사용자가 데이터를 추가하자마자 화면에 나타난 데이터는 오래된 데이터일 것이다.
  • 따라서 이 데이터에 관한 모든 캐시는 invalidate, 즉 무효화해주어 새롭게 가져온, refresh한 데이터를 가져와야한다.
const queryClient = useQueryClient()
...
queryClient.invalidateQueries({ queryKey: ['musics'] })
  • 캐시된 데이터가 보관 중이지만 서버에 어떤 데이터를 추가했을 때, 수동적으로 invalidateQuries를 할 수 있다. === 캐시 업데이트! === 새로고침

마치며

이상으로 tanstack query 로 간단한 예제를 구현해보며 기본적인 사용법을 익혀보았다. 다음 포스팅에서는 데이터를 생성/업데이트/삭제하거나 서버 부작용을 수행하는 데 사용되는 Mutation에 대해서 자세히 다뤄볼 예정이다.

profile
Frontend-developer
post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 7월 13일

감사합니다. 다음 글이 기다려지네요.

답글 달기