article thumbnail image
Published 2021. 5. 28. 01:29
[NPM] https://www.npmjs.com/package/react-query
[공식 문서] https://react-query.tanstack.com/overview
[참고 포스팅] React Query - 왜, 그리고 어떻게 사용할 수 있을까?

들어가기 전..

Query(쿼리)란?

Query란 '데이터베이스에 있는 정보에 대한 요청'을 의미합니다. (참고: What is a Query? Database Query Explained)


React Query(쿼리)란?

React Query의 공식 문서에 따르면 React Query는 리액트용 데이터 fetching 라이브러리로서, 구체적으로는 데이터 fetch, 캐싱, 동기화, 리액트 애플리케이션의 서버 상태 업데이트 등 데이터 요청과 관련된 주요 작업들을 간편하게 할 수 있도록 하는 라이브러리라고 합니다.

 

React Query의 사용 이점

React Query를 사용하면 데이터를 fetch 해오는 로직을 간결하게 작성할 수 있습니다. 기존에는 데이터 fetch에 관련된 상태들(isLoading, isError, data 등), 즉 서버 상태(server state)를 제어하기 위해 useState, useEffect 등 hook을 여러개 사용해야 했지만, React Query는 useQuery라는 하나의 hook만으로도 이러한 상태들을 제어할 수 있어 편리합니다.

const { isLoading, error, data } = useQuery('repoData', () =>
     fetch('https://api.github.com/repos/tannerlinsley/react-query')
     	.then(res => res.json());
);

// 출처: 공식 문서(https://react-query.tanstack.com/overview#enough-talk-show-me-some-code-already)

 

기존 방식과 React Query의 차이점

리액트에서 비동기 요청을 통해 데이터를 불러올 때, 요청과 관련한 상태들을 보통은 전역 상태(state)로써 관리합니다. 또는 여러 상태를 한번에 관리하는 custom hook을 만들어 관리하는 방법도 있고 이것 역시 좋은 방법이라고 합니다. 하지만 React Query를 사용하면 이러한 custom hook을 직접 만들지 않아도 된다는 이점이 있습니다. 뿐만 아니라 React Query는 isFetching이라는 값을 통해 쿼리가 데이터를 다시 불러오고 있는지 아닌지도 확인할 수 있다고 합니다.

 

주요 API

useQuery

- 서버에서 데이터를 가져오고 캐싱하는 데 사용합니다.

- 첫 번째 인자 ⇒ query key / 두 번째 인자 ⇒ query function(데이터를 불러오는 비동기 함수) / 세 번째 인자 ⇒ 옵션

- 반환값을 통해 데이터를 비롯하여, 애플리케이션의 현재 상태(요청 관련 상태)를 나타내는 값들도 확인할 수 있습니다.

const {
   data,
   dataUpdatedAt,
   error,
   errorUpdatedAt,
   failureCount,
   isError,
   isFetched,
   isFetchedAfterMount,
   isFetching,
   isIdle,
   isLoading,
   isLoadingError,
   isPlaceholderData,
   isPreviousData,
   isRefetchError,
   isStale,
   isSuccess,
   refetch,
   remove,
   status,
 } = useQuery(queryKey, queryFn?, {
   cacheTime,
   enabled,
   initialData,
   initialDataUpdatedAt
   isDataEqual,
   keepPreviousData,
   notifyOnChangeProps,
   notifyOnChangePropsExclusions,
   onError,
   onSettled,
   onSuccess,
   queryKeyHashFn,
   refetchInterval,
   refetchIntervalInBackground,
   refetchOnMount,
   refetchOnReconnect,
   refetchOnWindowFocus,
   retry,
   retryOnMount,
   retryDelay,
   select,
   staleTime,
   structuralSharing,
   suspense,
   useErrorBoundary,
 })
 
 // or using the object syntax
 
 const result = useQuery({
   queryKey,
   queryFn,
   enabled,
 })

 

useMutation

- 서버에 있는 데이터를 생성/업데이트/삭제, 즉 mutate하기 위해 사용합니다.

- 첫 번째 인자 ⇒ 데이터 업데이트를 위한 비동기 함수 / 두 번째 인자 ⇒ 옵션

- mutation 실행을 위한 mutate 함수를 비롯하여 서버 상태를 확인할 수 있는 다양한 값들을 반환합니다.

const {
   data,
   error,
   isError,
   isIdle,
   isLoading,
   isPaused,
   isSuccess,
   mutate,
   mutateAsync,
   reset,
   status,
 } = useMutation(mutationFn, {
   mutationKey,
   onError,
   onMutate,
   onSettled,
   onSuccess,
   useErrorBoundary,
 })
 
 mutate(variables, { // variables: mutate 함수에 전달될 객체
   onError,
   onSettled,
   onSuccess,
 })

- 다만 mutate를 한다고 해서 데이터가 그 즉시 갱신되는 것은 아니어서, React Query가 데이터를 다시 fetch하도록 하기 위해 QueryClientinvalidateQueries 메서드를 사용해야 한다고 합니다. (참고)

// Invalidate every query in the cache
queryClient.invalidateQueries()
// Invalidate every query with a key that starts with `todos`
queryClient.invalidateQueries('todos')

// invalidateQueries 메서드는 기존 쿼리를 무효화 시켜서
// react query가 데이터를 다시 불러오도록 한다.
공식 문서에 따르 invalidateQueries 메서드는 쿼리를 무효화하고 캐시 내의 쿼리를 refetch하기 위해 사용하는 메서드이며, 데이터에 대한 mutation이 완료된 직후 그와 관련한 쿼리들을 refetch하기 위해 사용한다고 합니다.

- QueryClient는 쿼리의 추가적인 조작을 위한 다양한 함수를 포함하는 객체로서, 캐시와 interact할 때 사용한다고 합니다. 이를 사용하려면 현재의 QueryClient의 인스턴스를 반환하는 useQueryClient 메서드를 사용해야 합니다. (참고1, 참고2)

- 캐시와 관련하여, QueryCache는 전체 데이터 및 메타 정보 등을 다 담고 있으며 보통은 우리가 이것과 직접적으로 interact할 일은 없기 때문에 QueryCache 대신에 특정 캐시와 interact할 수 있는 QueryClient를 사용하라고 하네요~ (참고)

- QueryClientinvalidateQueries 뿐만 아니라 캐시와 interact할 수 있는 다양한 메서드들을 제공한다고 합니다.

 

Query key 관련

React Query는 각 쿼리가 갖는 유니크한 key인 query key에 기반하여 쿼리 캐싱을 관리합니다. 이때 query key는 단순한 문자열이 될 수도 있고, 문자열 및 중첩 객체를 요소로 갖는 배열이 될 수도 있습니다. 공식 문서에 따르면 둘 중 어떤 유형이든간에, 직렬화할 수 있고(serializable) 쿼리의 데이터에 대한 유니크한 값이라면 query key로 사용할 수 있다고 합니다.

직렬화(serialization)
직렬화란, 데이터 구조 및 오브젝트 상태를 나중에 재구성할 수 있는 포맷으로 변환하는 과정이다.

역직렬화(deserialization)
역직렬화란, 일련의 바이트로부터 데이터 구조를 추출하는 과정이다.

(출처: 위키백과)

공식 문서 내용을 추가로 정리해보자면, query key로서 문자열을 전달할 경우, 이 값은 내부적으로 배열로 변환이 되며 이때 이 배열은 해당 문자열만을 유일한 요소로 갖는다고 합니다.

// A list of todos
 useQuery('todos', ...) // queryKey === ['todos']
 
 // Something else, whatever!
 useQuery('somethingSpecial', ...) // queryKey === ['somethingSpecial']

한편 데이터를 설명하기 위한 더 많은 정보가 필요할 때는 배열을 query key로서 전달하는 것이 좋은데, 이때 배열의 요소로는 문자열 및 직렬화가 가능한 여러 객체들이 포함될 수 있습니다.

// An individual todo
 useQuery(['todo', 5], ...)
 // queryKey === ['todo', 5]
 
 // And individual todo in a "preview" format
 useQuery(['todo', 5, { preview: true }], ...)
 // queryKey === ['todo', 5, { preview: true }]
 
 // A list of todos that are "done"
 useQuery(['todos', { type: 'done' }], ...)
 // queryKey === ['todos', { type: 'done' }]

 

 

 

복사했습니다!