리액트 쿼리는 fetching, caching, updating 등을 다양한 옵션과 간단한 로직으로 해결할 수 있게 해줍니다.
그 중에서 useQuery는 GET 요청을 보낼 때 사용합니다.
초기 설정은 Context API와 비슷합니다.
import React from 'react'
import styled from 'styled-components'
import { QueryClient, QueryClientProvider } from 'react-query'
function App() {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
refetchOnMount: false,
},
},
})
return (
<QueryClientProvider client={queryClient}>
<Main>
...
</Main>
</QueryClientProvider>
)
}
Context API에서 Provider로 감쌌다면 리액트 쿼리에서는 QueryClientProvider로 감싸야 하고 client라는 prop에 위에서 설정한 queryClient를 넣어주어야 합니다.
queryClient에서는 공통적으로 적용할 설정을 해줄 수 있습니다.
제가 설정한 것은 아래와 같습니다.
다음은 리액트 쿼리의 옵션과 결과값입니다.
const {
data,
dataUpdatedAt,
error,
errorUpdatedAt,
failureCount,
isError,
isFetched,
isFetchedAfterMount,
isFetching,
isIdle,
isLoading,
isLoadingError,
isPlaceholderData,
isPreviousData,
isRefetchError,
isRefetching,
isStale,
isSuccess,
refetch,
remove,
status,
} = useQuery(queryKey, queryFn?, {
cacheTime,
enabled,
initialData,
initialDataUpdatedAt
isDataEqual,
keepPreviousData,
meta,
notifyOnChangeProps,
notifyOnChangePropsExclusions,
onError,
onSettled,
onSuccess,
placeholderData,
queryKeyHashFn,
refetchInterval,
refetchIntervalInBackground,
refetchOnMount,
refetchOnReconnect,
refetchOnWindowFocus,
retry,
retryOnMount,
retryDelay,
select,
staleTime,
structuralSharing,
suspense,
useErrorBoundary,
})
정말 많은 옵션과 세분화된 결과값을 받을 수 있습니다.
number
번까지만 재요청합니다.number
millisecond 후 stale 상태로 처리할 것인지 설정합니다. (default: 0)number
millisecond 동안 캐시 데이터가 메모리에 남아있게 됩니다. 이 이후 가비지 컬렉션에서 이 데이터를 처리합니다. (default: 5 60 1000 => 5 min)다음과 같이 사용할 수 있습니다.
const [searchValue, setSearchValue] = useState<string>('')
const [page, setPage] = useState(1)
const fetcher = (ctx: QueryFunctionContext) => {
if (ctx.queryKey[1] === '') {
return { items: [], total_count: -1 }
}
return get('repositories', { q: `${ctx.queryKey[1]} in:name`, page })
}
async function get(url: string, params: Params) {
const { data } = await customAxios.get(url, {
params,
})
return data
}
const { data, isFetching } = useQuery([page, searchValue], fetcher, {
staleTime: 60 * 1000,
keepPreviousData: true,
})
useQuery의 첫 번째 인자로 쿼리 키를 넣어주고, 두 번째로는 fetcher를 넣어줍니다.
세 번째에서 옵션을 설정해주고 결과값을 반환 받습니다. 저는 data와 isFetching을 받았습니다.
이제 검색어와 페이지가 달라지면서 상태가 변경되므로 알아서 uri가 변경되어 API 호출을 하게 됩니다.
저는 페이지가 마운트되자마자 데이터가 필요하지 않아서 fetcher 안에서 조건문을 통해서 검색어가 존재할 경우에만 호출을 하도록 하였습니다.
staleTime을 1분으로 두어서 1분 안에 호출했던 쿼리 키로 다시 호출한 경우에는 API를 호출하지 않고 캐시에 있는 데이터를 다시 가져와 사용할 수 있도록 하였습니다.
처음에는 저렇게 fetcher 안에서 분기를 통해 마운트되자마자 API을 호출하지 않는 방법이 아니었습니다.
enabled 옵션을 false로 주고 useEffect로 searchValue와 page가 바뀔 때마다 refetch를 호출하는 방식으로 하였습니다.
그러니까 리액트 쿼리의 핵심인 stale, cache를 전혀 사용하지 않고 무조건 강제로 refetching하게 되어서 위와 같이 개선하였습니다.
리액트 쿼리를 사용하여 여러 옵션들과 캐싱, 유효 시간 등을 설정할 수 있어서 편리하게 비동기 호출을 처리할 수 있었습니다.
특히, 저렇게 {data, isFetching} 처럼 받으면 추가적으로 상태를 선언하지 않아도 되어서 코드가 간결해져서 이전에 있던 코드의 상당 부분을 지워도 되어서 놀랐습니다.
이번에는 useQuery만 써봤는데 다음에는 useMutation을 비롯한 다른 API도 사용해보고 싶은 생각이 들었습니다.