react-query 설치, 사용
React query는 react App 에서 서버의 상태를 불러오고, 캐싱하며, 지속적으로 동기화하고 업데이트 하는 비동기 로직을 지원하는 라이브러리이다.
server state 의 메모리를 관리하고 garbage collection 을 지원하고, 동일 데이터를 여러번 요청하면 알아서 한번만 요청한다.
사용법이 react 의 hook 과 비슷하다.
pagination 및 lazy loading 에 최적화 되어있고, 인피니트 스크롤을 손쉽게 만들 수 있다.
설치
yarn add @tanstack/react-query
QueryClient
react query 를 사용하기 위해 클라이언트를 생성한다.
src 폴더에 queryClient.ts 로 파일을 생성했다.
import { QueryClient } from '@tanstack/react-query';
// Create a client
export const getClient = (()=>{
let client: QueryClient | null = null;
return ()=>{
if (!client) client = new QueryClient({});
return client;
}
})();
defaultOptions
QueryClient 인스턴스 생성 시 defaultOptions 값으로 공통된 기본 쿼리를 지정할 수 있다.
let client: QueryClient | null = null;
if (!client) client = new QueryClient({
defaultOptions:{
queries: {
cacheTime: 1000 * 60 * 60 * 24,
staleTime: 1000 * 60,
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
}
}
});
return client;
cacheTime
캐시로 가지고 있는 시간을 설정한다.
기본값은 300000(5분)으로, 이 시간동안 활동이 없으면 garbage collector 에 의해 저장된 캐시가 삭제된다.
number 또는 Infinity 를 값으로 갖는다.
staleTime(number | Infinity)
fetch 로 받아온 데이터는 일정 시간이 지난 후 fresh 상태에서 stale 상태로 변경된다.
fresh 상태에서는 쿼리 인스턴스가 새롭게 mount 되어도 fetch 를 실행하지 않으며, stale 상태가 되어야 fetch 한다.
기본값은 0이며 쿼리 인스턴스가 mount 될 때 마다 fetch 를 실행함을 의미한다.
staleTime
은 fresh 상태에서 stale 상태로 변경되는데 걸리는 시간을 설정해 잦은 중복 호출을 방지한다.
number 또는 Infinity 를 값으로 갖는다.
refetchOnMount
데이터가 stale 상태일 경우 mount 마다 refetch 를 실행하는 옵션이다.
기본값은 true 이며, 만약 always 로 설정하면 mount 시 매번 refetch 를 실행한다.
boolean 또는 ‘always’ 를 값으로 갖는다.
refetchOnWindowFocus
데이터가 stale 상태일 때, 윈도우가 포커싱 될 때 마다 refetch를 실행한다는 옵션이다.
기본값은 true 이며, always 로 설정하면 윈도우가 포커싱 될 때 마다 refetch를 실행한다.
boolean 또는 ‘always’ 를 값으로 갖는다.
refetchOnReconnect
데이터가 stale 상태일 경우 다시 연결 될 때 refetch를 실행한다는 옵션이다.
기본값은 true 이며, always 시 쿼리가 매번 연결될 때 마다 refetch를 실행한다.
boolean 또는 ‘always’ 를 값으로 갖는다.
QueryClientProvider
query client 를 전역에서 사용하기 위해 app을 QueryClientProvider 로 감싸준다.
이때 전달할 client 속성값을 위에서 생성한 getClient 로 전달한다.
// _app.ts
import { QueryClientProvider } from '@tanstack/react-query';
import { getClient } from './queryClient';
function App() {
const element = useRoutes(routes);
const queryClient = getClient();
return (
<QueryClientProvider client={queryClient}>
{element}
</QueryClientProvider>
);
}
export default App;
devtools
react-query 는 전용 개발자도구를 제공해 편리한 개발을 돕는다.
개발자도구를 사용하기 위해 먼저 react-query-devtools 를 설치한다.
yarn add @tanstack/react-query-devtools
QueryClientProvider 내부에 ReactQueryDevtools 을 추가한다.
<QueryClientProvider client={queryClient}>
{element}
<ReactQueryDevtools initialIsOpen={false}></ReactQueryDevtools>
</QueryClientProvider>
run dev 를 실행하면 화면 좌측 하단에 개발자도구가 추가된 것을 확인할 수 있다.
query key
쿼리를 지정해 실행하기 위한 query key 를 생성한다.
queryClient.ts 파일 내 객체형태로 정해둔다.
export const QueryKeys = {
PRODUCTS: 'PRODUCTS',
}
fetcher
client 에서 보내는 요청을 받아 처리할 fetcher 를 만든다.
먼저 전달받을 값에 대한 type 을 생성한다.
type TypeMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
type TypeBodyOBJ = { [key: string]: any };
실제로 통신을 전달받을 함수를 생성한다.
매개변수로 method 로 요청 종류를 받고, path 로 상세 경로를 지정하고, body 및 params 로 데이터를 전달받는다.
비동기통신의 동기화를 위해 async, await 로 함수를 생성하고, try, catch 문으로 fetch 를 실행한다.
export const fetcher = async ({
method,
path,
body,
params,
}: {
method: TypeMethod;
path: string;
body?: TypeBodyOBJ;
params?: TypeBodyOBJ;
}) => {
try {
const url = `${BASE_URL}${path}`;
const fetchOptions: RequestInit = {
method,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': BASE_URL
}
}
const res = await fetch(url, fetchOptions);
const json = await res.json();
return json;
} catch(err) {
console.error(err);
}
}
RequestInit 는 request 의 초기화를 위해 ts 에서 기본적으로 제공하는 interface 이다.
useQuery
client 에서 실제로 server 와 통신하기 위해 사용하는 react-query 의 메서드이다.
useQuery 를 사용하기 위해 먼저 react-query 로부터 useQuery()
를 import
하고, 위에서 생성한 fetcher, QueryKey 도 가져온다.
import { useQuery } from "@tanstack/react-query";
import { fetcher, QueryKeys } from "../../queryClient";
useQuery()
를 사용해 데이터를 GET 한다.
파라미터를 객체 형태로 넘겨서 key 와 fetcher 를 전달한다.
여기서 key 는 객체 형태로 전달되어야 한다.
const { data } = useQuery({
queryKey: [QueryKeys.PRODUCTS],
queryFn: () => fetcher({
method: 'GET',
path: '경로',
})
})
data 를 콘솔에 찍어보면 데이터가 잘 전달되는 것을 확인할 수 있다.