react-query는 서버의 상태를 다루기에 정말 좋은 라이브러리라고 생각합니다.
react-query의 공식 홈페이지에서도 서버의 상태를 위한 라이브러리로 설명되어있습니다.
* React Query는 서버 상태 라이브러리입니다. 서버와 클라이언트의 비동기적인 동작을 관리하기에 적합합니다.
* Redux, MobX, Zustand, etc 는 클라이언트 상태 라이브러리 입니다. 비동기 데이터를 저장할수는 있지만 React Query에 비해서는 비효율적입니다.
이러한 점을 염두에 두고 React Query는 클라이언트 상태에 위치한 캐시데이터를 관리하기 위한 boiler plate코드와 관련 로직들을 코드 몇 줄 만으로 대체한다는 것입니다!
Redux에서 서버의 api요청 응답을 저장해두고 있을때는 아래의 사항들이 걸렸습니다.
1. 너무 많은 boiler plate 코드
2. 언제 응답되었는지를 알수 없는 데이터 로드 시기
3. 전역 클라이언트 상태와 같이 관리되어지는 서버 상태
그래서 React Query를 도입했었고 axios, fetch등과 같이 비동기 요청을 보내주는 도구들과 함께 효율적으로 사용했었습니다.
아래에서는 간단한 기본 개념과 쓰임을 알아보려 합니다.
먼저 stale에 대하여 알아보려 합니다.
브라우저에서는 header의 cache-control를 통해 네트워크 요청의 캐시를 관리합니다.
캐시를 이용하는 목적은 동일한 요청이라면 서버로의 요청이 아니라 메모리에서 가져와 더 빠른시간내에 응답을 하기 위해서 사용합니다.
cache-control의 값에는 max-age, no store, no cache 등이 들어갈 수 있지만 stale-while-revalidate라는 값 또한 들어갈 수 있습니다. (stale은 신선하지 않은 이라는 뜻입니다.)
예시를 들어보겠습니다. cache-control: "max-age= 60, stale-while-revalidate= 60" 으로 응답헤더에 포함되어 있다면
1~60초에 요청이 들어오면 캐시는 신선하므로 바로 가져가 사용합니다.
61~120초 요청에 들어오면 캐시는 stale 상태 신선하지 않은 상태가 됩니다. 그래서 우선 캐시된 값을 내보내고 서버로 값을 refetch, 즉 fresh 한 상태로 바꾸기 위해 요청을 보내고 응답을 받아오면 캐시된 값을 교체해 줍니다.
react-query에서 기본 stale time은 0입니다. cache-time의 default는 5분 입니다.
이 설정을 바꾸고 싶다면 개별 요청에서만 옵션을 변경해주어 보내주거나 queryClient에서 설정해주면 모든 요청에 기본 요청으로 설정됩니다.
stale 쿼리들은 아래와 같은 상황에서는 백그라운드 상태에서 자동으로 요청됩니다.
- New instances of the query mount(잘 모르겠습니다...)
- window가 다시 focus 되었을때
- network가 재연결 되었을때
- query가 설정한 retch 간격에 따라
그외 기본 사항들
- inactive 상태인 쿼리들은 5분 이후에 garbage collect 됩니다.
- 요청에 실패할 경우 3번 까지 요청을 보내고 모두 실패하였을시 화면에 표시합니다. 이때 요청간 delay를 설정할 수 있습니다.
- 기본적으로 Json 형식의 쿼리 결과는 데이터가 실제로 변경되었는지 여부를 감지하기 위해 구조적으로 공유되어 집니다. 실제로 변경되지 않은 경우 데이터 참조는 변경되지 않은 상태로 유지되어 useMemo및 useCallback과 관련하여 값 안정화에 더 도움이 됩니다.
QueryClient Provider
import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient();
function App(){
return (
<QueryClientProvider client={queryClient}>
<Component />
</QueryClientProvider>
)
}
useQuery
import {useQuery} from 'react-query';
const PeopleWithFetch = () => {
const { isLoading, error, data } = useQuery(
["todo", 1],
async () => await fetch("https://jsonplaceholder.typicode.com/todos/1").then(r => r.json()),
)
if (isLoading) return (<>"is Loading"</>)
if (error) return (<>"somethin wrong"</>)
return (
<div>
{data.title}
</div>
)
}
export default PeopleWithFetch;
isLoading, error와 같이 따로 설정이 필요하게 됩니다.
이러한 경우 Suspense, ErrorBoundary의 사용을 검토해 볼 수 있습니다.
만약 useQuery를 복수개 요청하였고 모든 데이터의 유효성이 검증되어야 한다면 코드는 복잡해질수 있습니다.
Suspense를 사용하여 loading 상태를 대체할 수 있고 ErrorBoundary를 사용하여 error 상태를 대체할 수 있습니다.
react-query에서는 errorResetBoundary를 제공해주고 있습니다.
react-error-boundary 라이브러리와 같이 사용하면 errorBoundary 역할을 할수 있습니다.
저는 typescript에서 react 18, react-dom18을 사용하여 suspense, react-query를 사용하려 하였으나 아직 @types/react-query내부에서 참조하고 있는 react가 17을 기반으로 하고 있어 그런지 typescript 적용에는 실패하였습니다.
추후에 react js 에서 시도해보고 안되면 다른 방법을 찾아볼 예정입니다.
복수의 useQuery 사용
한 컴포넌트에서 여러 요청이 복수로 parallel하게 수행되어져야 할 경우 아래와 같이 작성할 수 있습니다.
function App () {
// The following queries will execute in parallel
const usersQuery = useQuery('users', fetchUsers)
const teamsQuery = useQuery('teams', fetchTeams)
const projectsQuery = useQuery('projects', fetchProjects)
...
}
parallel하게 수행되어 져야 하는 요청이 복수개일 경우 useQueries의 사용을 고려할 수 있습니다.
useQueries는 쿼리키, 쿼리함수를 복수개를 받을 수 있습니다.
function App({ users }) {
const userQueries = useQueries(
users.map(user => {
return {
queryKey: ['user', user.id],
queryFn: () => fetchUserById(user.id),
}
})
)
}
Dependent Query 사용시
a요청이 수행되어 지고 a요청의 결과를 가지고 b 요청이 수행되어 져야 할 경우를 의존적 쿼리라 칭하겠습니다.
이런 의존적 쿼리가 필요할 경우 enabled 옵션을 사용하면 편리합니다.
// Get the user
const { data: user } = useQuery(['user', email], getUserByEmail)
const userId = user?.id
// Then get the user's projects
const { isIdle, data: projects } = useQuery(
['projects', userId],
getProjectsByUser,
{
// The query will not execute until the userId exists
enabled: !!userId,
}
)
// isIdle will be `true` until `enabled` is true and the query begins to fetch.
// It will then go to the `isLoading` stage and hopefully the `isSuccess` stage :)
이 외에 useInfiniteQuery등과 옵션중 keepPreviousData등 편리한 옵션이 많아서 매우 유용합니다!!
useMutation
get 요청을 보낼때는 useQuery를 사용합니다.
post, delete, patch, put 과 같은 요청을 이용할때는 useMutation 훅을 사용합니다.
useQuery에 비해 적은 옵션을 사용합니다.
DevTool
ReactQuery에서는 DevTool을 제공합니다.
키와 데이터 값을 같이 볼수 있고
fresh, fetching, stale 등 데이터의 상태를 눈으로 볼수 있고
refetch, invalidate, reset, remove 등의 동작을 수행할 수 있습니다.
데이터의 상태를 다룰수 있어 편리한 도구 입니다.
출처 : https://react-query.tanstack.com/guides/does-this-replace-client-state
https://web.dev/i18n/ko/stale-while-revalidate/
https://www.stevy.dev/react-state-management-guide/
'Frontend > Javascript' 카테고리의 다른 글
Javascript Promise, async/await MicroTask queue 동작차이 🖇 (1) | 2022.04.15 |
---|---|
Javascript Prototype 🤢 (0) | 2022.04.15 |
Javascript 실행 컨텍스트(Execution Context) (0) | 2022.04.03 |
npm, yarn (0) | 2022.04.03 |
Javascript custom array 🌹 (0) | 2022.04.02 |