react-query 를 쓰라고 해서 쓰긴 쓰는데.. 이걸 왜 써야 하는 걸까?
필자는 프론트엔드 개발을 계속 진행하던 도중, 개발자 커뮤니티에서 무한 스크롤 구현을 쉽게 하시려면 react query
와 Intersection Observer를 잘 버무려서 구현하면 좋다는 말을 듣고 무작정 react query를 시작하였다.
물론 리액트 쿼리를 사용한 입장으로서 만족감은 최상이었다, 기존의 useState
와 전역 상태관리 라이브러리를 활용하여 서버에서부터 데이터를 받아와 이를 보관하는 과정은 종종 예상치 못한 에러를 내뿜기 마련이었고, 해당 코드를 일관성 있게 작성하는 것은 더더욱 어려웠다. 하지만 리액트 쿼리는 이러한 단점을 많이 상쇄시켜준 고마운 친구였다.
하지만 이 고마운 친구가 정확히 어떤 일을 하는 건지, 그리고 나는 이걸 왜 써야 하는지에 대한 심도 있는 고찰이 필요한 시점이 바로 지금이라고 생각하여, 리액트 쿼리가 어쩌다 만들어졌고 이걸 써야 하는 이유와, 리액트 쿼리를 사용함에 있어 필요한 기본 지식을 정리하고자 한다.
fetching, caching, synchronizing and updating server state in your React applications - tanstack
A query is a declarative dependency on an asynchronous source of data that is tied to a unique key
useQuery
, useInfiniteQuery
Hook을 쓴다.useMutation
Hook을 사용해야 한다.function Todos() {
const { isLoading, isError, data, error } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodoList,
});
if (isLoading) {
// if (state === 'loading')
return <span>Loading...</span>;
}
if (isError) {
// if (state === 'error')
return <span>Error: {error.message}</span>;
}
// 이 지점까지 코드가 도달했다면, 정상적으로 쿼리가 success state를 가짐.
return (
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
queryKey
는 refetch, caching 등과 같은 작업을 할때 쓰이는 고유한 키다. v4 부터는 배열로만 key를 선언해야 한다.queryFn
의 경우 서버로부터 데이터를 가져오기 위해 쓰이는 비동기 함수를 의미하며 반드시 Promise를 리턴해야 한다.retry
옵션과 retryDelay
옵션을 별도로 설정하자.useQuery
가 리턴한 결과 객체의 state
프로퍼티로 쿼리의 상태를 파악한다.state
속성의 경우 현재 mount 된 쿼리 인스턴스의 상태를 파악할 때 쓰인다.fetchState
속성의 경우 쿼리가 사용한 queryFn
의 동작 결과를 나타낸다.queryFn
이 동작하지 않고 있음을 의미.state
와 fetchState
를 모두 확인하여 쿼리의 상태를 유추할 수 있다.staleTime
옵션을 통해 fresh한 데이터가 stale 한 상태가 되기까지 걸리는 시간을 지정할 수 있음.unmount
되어 현재 비활성화 되었음을 의미.inactive
한 상태가 된 후 5분이 지나면 해당 쿼리는 자동으로 GC에 수집됨.cacheTime
옵션을 통해 inactive 된 쿼리 데이터가 GC에게 수집되기까지 걸리는 시간을 지정할 수 있음.useQuery
혹은 useInfiniteQuery
로 생성된 쿼리 인스턴스는 기본적으로 캐싱된 데이터를 stale
상태로 둔다.staleTime
속성을 통해 해당 쿼리에 종속된 데이터의 유효 시간을 설정할 수 있다. (단위 : ms)const { data, hasNextPage, fetchNextPage } = useInfiniteQuery(
['question', { sortOption, answeredOption }],
({ pageParam = 1 }) => getQuestionsAsync(pageParam, amount, sortOption, answeredOption),
{
getNextPageParam: (lastPage) => {
if (!lastPage || !lastPage.isSuccess || lastPage.result.isLast) {
return undefined;
}
return lastPage.result.nextPage;
},
// staleTime은 5분으로 설정함.
staleTime: 30000,
}
);
stale
한 쿼리를 즉시 refetch 한다.defaultOptions
옵션으로 조정할 수 있다.const [queryClient] = useState(
new QueryClient({
defaultOptions: {
queries: {
refetchOnReconnect: false,
refetchOnWindowFocus: false,
},
},
})
);
1. 새로운 쿼리 인스턴스가 mount 되었을 경우. (refetchOnMount)
2. 사용자가 창을 클릭하여 focus 하였을 경우. (refetchOnWindowFocus)
3. 네트워크가 재연결 되었을 경우. (refetchOnReconnect)
4. 사용자가 특정 주기마다 refetch를 진행하도록 한 경우 (refetchInterval)
inactive
로 변환되고, 설정된 cacheTime
(기본 5분) 이 지났다면 해당 쿼리는 GC에 의해 소거된다.fresh
상태인지 stale
상태인지는 중요하지 않다. 오직 inactive
된 시점을 기준으로 cacheTime을 체크한다.
좋은글 감사합니다 저도 프로젝트에 react-query 도입을 고민하고 있었는데 이 글로 도입 문제를 해결 한거 같습니다!