react-query의 staleTime과 gcTime
react-query에서 useQuery를 사용하면 staleTime과 gcTime 을 이용해 데이터를 효율적으로 다룰 수 있다. 둘의 차이점을 알아보자.
staleTime
The time in milliseconds after data is considered stale. This value only applies to the hook it is defined on.
If set to
Infinity
, the data will never be considered stale
staleTime
은 불러온 데이터가 fresh한 시간을 나타낸다. defaultValue는 0으로, 리액트 쿼리에서는 기본적으로 불러온 데이터는 fresh하지 않다고 간주한다.
기본 값을 사용하게 된다면 해당 hook을 사용하는 컴포넌트가 mount 될 때마다 data fetching을 시도할 것이다.
데이터의 stale여부에 따라서 적절하게 조절해줌으로써 최적화 해줄 수 있다.
아래 예시는 until.lbog 에서 사용하는 특정 블로그 정보를 가져오는 훅이다.
import { useSuspenseQuery } from '@tanstack/react-query'
import { GetBlogResponse, createGetBlog } from 'src/apis/blog/[username]'
import { queryKeys } from 'src/constants/queryKeys'
const useBlogQuery = () => {
const username = useUsername();
const { data } = useSuspenseQuery<GetBlogResponse>({
queryKey: queryKeys.blog.username.body(username),
queryFn: createGetBlog(username),
staleTime: Infinity,
})
return data
}
export default useBlogQuery
staleTime을 Infinity
로 설정한 이유는 무엇일까?
기본적으로 블로그 정보는 서버사이드에서 불러온 이후 변경되는 경우가 거의 없다. 해당 훅은 블로그에서 전반적으로 사용하고 있기 때문에 staleTime이 0일 경우에는 화면을 전환될 때마다 비효율적인 데이터 패칭이 계속해서 일어날 것이라고 판단하였다.
프로필 수정, 팔로우, 팔로잉와 같이 데이터가 더이상 fresh하지 않은 경우에는 queryClient.invalidateQuery
를 통해 명시적으로 갱신해줌으로써 해결할 수 있다.
물론 위 방식의 경우 외부적인 요인에 의해 특정 블로그 정보가 변경되는 경우에는 해당 블로그에 재 접속하지 않는 이상 갱신되지 않을 수 있다는 점은 감안해야한다.
아래 예시는 게시글 리스트를 가져오는 훅이다.
import { useSuspenseInfiniteQuery } from '@tanstack/react-query'
import { queryKeys } from 'src/constants/queryKeys'
import {
GetArticlesParams,
createGetUserArticles,
} from 'src/apis/blog/[username]/articles'
import useBlogData from 'src/blog/hooks/useBlogData'
import useArticleParams from './useArticleParams'
const useArticlesInfiniteQuery = () => {
const blogData = useBlogData()
const { order, query, tag } = useArticleParams()
const params: GetArticlesParams = {
order: order || undefined,
query: query || undefined,
tagSlug: tag || undefined,
}
const { data, ...infiniteQuery } = useSuspenseInfiniteQuery({
queryKey: queryKeys.blog.username.articles.list(blogData.username, params),
queryFn: async ({ pageParam }) => {
const getUserArticles = createGetUserArticles(blogData.username)
return await getUserArticles({ page: pageParam, ...params })
},
initialPageParam: 0,
getNextPageParam: (data) => (data.hasMore ? data.page + 1 : undefined),
})
if (!data) throw new Error('Suspense Error')
return { data, ...infiniteQuery }
}
export default useArticlesInfiniteQuery
위 훅에서는 반대로 staleTime
을 설정하지 않고 기본값(0)을 사용했다.
그 이유는 훅의 영향 범위가 useBlogQuery
와 같이 전반적인 블로그 페이지에서 사용되는 것이 아닌 하나의 특정된 페이지에서만 사용하기에 때문이다
gcTime (cacheTime)
The time in milliseconds that unused/inactive cache data remains in memory. When a query's cache becomes unused or inactive, that cache data will be garbage collected after this duration. When different garbage collection times are specified, the longest one will be used.
gcTime
은 데이터가 사용되지 않는 상황에서 memory에 유지될 수 있는 시간을 의미하고, defaultValue는 클라이언트 사이드에서는 5분, 서버사이드에서는 Infinity로 지정된다.
일반적으로 클라이언트 사이드에서 해당 값을 변경하는 경우는 크게 없었던 것 같다.
데이터 패칭에 직접적인 영향을 주지 않기 때문이다.
다만 서버사이드에서는 Infinity로 설정되기 때문에 얘기가 달라진다. 메모리에 계속해서 데이터가 누적되는 경우에는 해당 값을 적절히 지정해 주는 것이 필요할 것 같다.
결론
어떻게 잡아주더라도 http 통신을 하는 웹페이지 안에서 데이터는 불일치하는 경우는 발생할 수 밖에 없다.
데이터의 특성에 따라 적절한 캐싱 시간을 조절함으로써 불일치하는 경우를 최소화 할 수 있도록 하는것이 최선인 것 같다.