Hooks은 (함수들은) 값의 재사용이 아니라 로직의 재사용을 위한 것이기 때문에 데이터가 캐시되지 않습니다. 동일한 데이터를 여러곳에서 사용할 경우 hook을 호출할 때마다 데이터를 다시 불러오게 되는 것이죠.
또한 로딩이나 에러처리를 직접 구현해야한다는 번거로움이 있을 수도 있습니다. 이러한 문제들을 개선하기 위해 react-query를 사용할 수 있습니다.
세분화된 상태 관리, 수동 다시오기, 끝없는 비동기 스파게티 코드를 버리세요. TanStackQuery는 개발자와 사용자 경험을 모두 직접적으로 개선하는 선언적이고 최신의 자동 관리 쿼리와 변형을 제공합니다.
아무튼 비동기 데이터 처리를 손쉽게 할 수 있다는 것 같아요.
그럼 한 번 직접 프로젝트에 적용해봅시다!
1. 설치
npm i @tanstack/react-query
or
yarn add @tanstack/react-query
2. 최상위 컴포넌트를 QueryClientProvider
로 감싸줍니다.
import {
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<Todos />
</QueryClientProvider>
)
}
3. 기존의 커스텀 훅을 react-query로 변경
queryKey
는 배열 형태로 데이터의 이름을 붙여주면 됩니다. (자세한 내용은 아래에 👇)
데이터를 가지고 오는 곳에 따라 queryFn
를 설정해주세요.
저는 firebase에서 데이터를 가지고 왔습니다.
// 기존
// const { products, loading, error } = useProducts();
const { data: products, error, isLoading } = useQuery({
queryKey: ['products'],
queryFn: async () => {
console.log('데이터 불러오기...')
const querySnapshot = await getDocs(collection(db, 'products'));
const data = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
return data;
}
});
이것만으로 react-query로 데이터를 가져오는데 성공했습니다! 😲
react-query를 적용하기는 했지만 좀 더 효율적인 데이터 관리를 위하여 몇가지를 더 살펴볼 수 있습니다!
react-query는 기본적으로 queryKey
에 따라 캐싱을 관리합니다. 동일한 Key의 이미 캐싱된 데이터가 있다면 데이터를 새로 가져오지 않고 캐싱된 데이터를 가지고 오는 것이죠.
queryKey는 항상 배열이여야합니다. 배열의 요소는 하나일수도 여러개일 수도 있고, 객체일 수도 있습니다.
아까 작성한 코드를 ['products', saleState]
와 같이 수정해서 판매여부를 추가해볼 수도 있겠네요.
# 이런 복잡한 형식도 사용할 수 있어요.
useQuery({ queryKey: ['todo', 5, { preview: true }], ...})
react-query에서는 전용 개발툴을 지원합니다.
이것을 프로젝트에 적용하여 데이터의 상태를 모니터링 할 수 있습니다.
# 설치
npm i @tanstack/react-query-devtools
# 최상위 경로
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
<QueryClientProvider client={queryClient}>
{/* The rest of your application */}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
initialIsOpen
를 true
로 설정하면 페이지에 접속하면 개발툴이 바로 보여지게 됩니다.
짜잔 내 프로젝트에서 react-query 개발툴을 확인할 수 있습니다.
아까 프로젝트에 호출한 [”products”, true]
를 살펴볼까요?
앞에 표기된 1
은 현재 [”products”]
키를 가진 데이터가 1개의 컴포넌트가 보고있다 라는 의미입니다.
노란색은 stale
그러니까 ‘신선하지 않다. 조금 오래되었다.’를 의미합니다.
상태가 이상하지 않나요?
막 잡아온 신선한 데이터가 호출하자마자 신선하지 않은 상태가 되어버렸습니다 🤨
데이터가 신선한 상태여야 새롭게 데이터를 호출하지 않고 이전에 캐시된 데이터를 사용할텐데 말이죠.
Query instances via useQuery or useInfiniteQuery by default consider cached data as stale.
공식 문서를 확인해보면, useQuery 또는 useInfiniteQuery를 사용하여 가져온 데이터는 stale
상태로 가져온다고 명시 되어있습니다.
이 사항을 변경하기 위해서는 글로벌로 사용하거나 staleTime
옵션을 사용하여 신선함이 유지되는 시간을 설정해야합니다.
우리의 데이터를 신선한 상태로 유지하기 위해 staleTime
, 유통기한을 설정해 봅시다.
const { data: products, error, isLoading } = useQuery({
queryKey: ['products', saleState],
queryFn: async () => {데이터 호출 함수},
staleTime: 5000, # 5초 (밀리세컨드 기준)
});
유통기한 staleTime
을 설정해주면 데이터가 Fresh
상태로 받아와지고 상태가 변경되어도 데이터를 새롭게 불러오지 않다가, 5초가 지나면 stale
상태로 변경되고 데이터를 새로 가지고 오는 것을 확인할 수 있습니다.
다음과 같은 경우 stale
상태의 데이터가 백그라운드에서 새롭게 데이터를 가져옵니다.
해당 기능은 refetchOnMount
, refetchOnWindowFocus
, refetchOnReconnect
, refetchInterval
옵션을 사용하여 변경할 수 있습니다.
📗 Window focus refetching no longer listens to the focus event
React-Query v5에서는 refetchOnWindowFocus가 기본적으로 false로 변경되어 윈도우 포커스가 변경되어도 데이터를 다시 호출하지 않습니다.
데이터의 신선도가 얼마나 중요한지에 따라 staleTime을 설정하고, 효율적으로 데이터를 관리할 수 있습니다!