Tanstack Query
서버 상태를 관리하는 라이브러리.
QeuryClient()
캐싱 데이터와 상호작용할 수 있게 해주는 객체를 제공하는 생성자 함수.
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const App = ({ Component, pageProps }) => {
const queryClient = new QueryClient();
return (
<QueryClientProvier client={queryClient}>
<Component {...pageProps} />
</QueryClientProvier>
);
};
export default App;
QueryClient() 생성자함수로 생성된 queryClient 객체는 쿼리 관리, 캐싱, 오류 처리, 인터셉터 등, 여러 기능을 제공하는 객체이다. 생성한 queryClient 객체를 자식 컴포넌트에서 사용할 수 있게 queryClientProvider로 감싼 후, client prop에 생성한 queryClient 객체를 넘겨준다.
queryClient Option
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const App = ({ Component, pageProps }) => {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 밀리초 단위, 기본값은 0
gcTime: 5 * 60 * 1000 // 기본값은 5 * 60 * 1000
}
}
});
return (
<QueryClientProvier client={queryClient}>
<Component {...pageProps} />
</QueryClientProvier>
);
};
export default App;
defaultOptions 프로퍼티는 쿼리의 기본 설정을 할 수 있다.
생성된 queryClient 사용하기
useQueryClient
import { useQueryClient } from '@tanstack/react-query';
const SomeComponent = () => {
const queryClient = useQueryClient();
return(<div></div>);
};
useQueryClient는 QueryClient 생성자 함수로 생성한 queryClient 객체를 반환한다.
생성된 queryClient 사용하기
fetchQuery
prefetchQuery
import { useQueryClient } from '@tanstack/react-query';
const SomeComponent = () => {
const queryClient = useQueryClient(); // QueryClient생성자 함수로 만든 queryClient 객체를 반환.
return(<div></div>);
};
QueryClientProvider의 prop으로 내린 queryClient객체를 반환한다.
useQuery
데이터를 가져오는 메서드.
const BASE_URL = 'https://learn.codeit.kr/api/codestudit';
const useGetPosts = async () => {
const response = await fetch(`${BASE_URL}/posts`);
return response.json();
};
export { useGetPosts };
import { useQuery } from "@tanstack/react-query";
import { useGetPosts } from "../query/useHook";
const Home = () => {
const response = useQuery({ queryKey: ["posts"], queryFn: useGetPosts });
return <div></div>;
};
export default Home;
1. useQuery()는 인자로 객체를 넣는다.
2. queryKey는 배열의 형태로, 쿼리를 식별하는 유일한 키다. 쿼리 결과를 캐싱하고 관리하는 장소이다.
3. queryFn은 데이터를 가져오는 비동기 함수이며, queryFn 뒤에는 promise객체를 반환하는 함수의 참조가 와야 한다.
4. useQuery()가 반환한 객체의 data 프로퍼티에는 queryFn이 반환한 promise 객체의 PromiseResult 값이 들어있다.
5. refetch 메서드는 현재 쿼리를 다시 실행하여 최신 데이터를 가져온다 (매개변수 x).
QueryKey?
1. 쿼리를 식별하는 유일한 키로, 쿼리 결과를 캐싱하고 관리하는 장소.
2. queryKey 배열의 요소에 변수를 넣게 되면 해당 변수가 바뀔 때 마다 캐싱 장소가 변경된다 (하나의 쿼리 키당 하나만 캐싱하기 때문).
3. queryKey의 변수의 값이 변경되면 해당 쿼리는 다시 실행되고 새로 받아온 데이터를 변경된 queryKey에 캐싱한다.
queryFn은 항상 실행되지 않는다
1. 컴포넌트가 렌더링 되면 useQuery()의 queryFn 함수가 바로 실행되진 않는다.
2. queryKey에 캐싱되어 있는 데이터가 있으면 실행되지 않고, 캐싱되어 있는 데이터가 없으면 queryFn이 실행되고, queryKey에 데이터를 캐싱한다.
useMutation
데이터를 전송하는 메서드.
import { useMutation } from "@tanstack/react-query";
const BASE_URL = "https://learn.codeit.kr/api/members";
const usePostUser = () =>
useMutation({
mutationFn: (requestBody) =>
fetch(`${BASE_URL}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
}),
});
export { usePostUser };
import { usePostUser } from "../query/useHook";
const Home = () => {
const mutationHandler = usePostUser();
const submit = (e) => {
e.preventDefault();
const requestBody = {
name: e.target[0].value,
email: e.target[1].value,
department: e.target[2].value
}
mutationHandler.mutate(requestBody);
}
return (
<form onSubmit={submit}>
<input placeholder="name" />
<input placeholder="email" />
<input placeholder="department" />
<button>Send</button>
</form>
);
};
export default Home;
1. useMutation은 인자로 객체를 넣으며, 객체에 mutationFn 를 포함한 여러 프로퍼티, 메서드들을 넣을 수 있다.
2. mutationFn은 Promise를 반환하는 함수의 참조여야 한다.
3. mutationFn 메서드의 매개변수는 useMutation() 이 반환하는 객체의 mutate 메서드의 인자다.
4. mutate 메서드는 백엔드의 데이트를 변경시키지만, 현재 캐시에 저장된 데이터는 refetch를 하지 않으면 기존의 데이터.가 그대로 캐싱되어 있으므로 queryKey를 따로 refetch 해줘야 한다.
5. useMutation 이 반환한 객체의 data 프로퍼티에는 서버로부터 응답받은 promise 객체의 [[PromiseResult]] 가 들어간다.
useMutation 의 여러 옵션
1. onSuccess : 뮤테이션이 성공했을 때 실행할 메서드, 매개변수는 서버로 부터의 응답 데이터.
2. onError : 뮤테이션이 실패했을 때 실행할 메서드, 매개변수는 오류 객체.
3. onSettled : 뮤테이션이 완료 되었을 때 (성공, 실패 여부x) 실행할 메서드, 매개변수는 성공시 응답데이터, 실패시 오류 객체.
4. onMutate : 뮤테이션이 실행되기 전에 실행할 메서드, 매개변수는 mutate 메서드의 인자.
refetch, invalidateQueries
데이터를 다시 요청하는 메서드.
refetch
import { useQuery, useMutation } from "@tanstack/react-query";
const BASE_URL = "https://learn.codeit.kr/api/members";
const Home = () => {
const response = useQuery({
queryKey: ["posts"],
queryFn: async () => {
const response = await fetch(`${BASE_URL}`);
return response.json();
},
});
const request = useMutation({
mutationFn: (requestBody) =>
fetch(`${BASE_URL}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
}),
onSuccess: () => {
response.refetch();
},
});
const submit = (e) => {
const requestBody = {
name: e.target[0].value,
email: e.target[1].value,
department: e.target[2].value,
};
request.mutate(requestBody);
};
...
1. query Status와 관계없이 항상 데이터를 가져온다.
invalidateQueries
import { useQuery, useMutation, useQueryClient } from "@/tanstack/react-query";
import { getPosts, uploadPost } from "./api";
const Home = () => {
const queryClient = useQueryClient();
const { data: postData } = useQuery({
queryKey: ["posts"],
queryFn: getPosts,
});
const uploadPostMutation = useMutation({
mutationFn: (newPost) => uploadPost(newPost),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["posts"] });
}
})
...
1. 인자로 받은 queryKey의 캐시를 무효화 시키고 stale 상태로 만든다.
2. refetch처럼 항상 데이터를 가져오는게 아닌, 새로운 데이터를 가져오게끔 유도한다.
Query Status
현재 쿼리의 상태를 표현하는 3가지 값.
1. Query Status는 pending(아직 데이터를 받아오지 못했을 때), success(데이터를 성공적으로 받아왔을 때), error(데이터를 받아오는 중에 에러가 발생했을 때) 3가지 상태 값으로 나뉜다.
2. isPendding, isSuccess, inError 프로퍼티 중, 현재 상태에 해당하는 프로퍼티의 값으로 true 가 할당된다.
3. status 프로퍼티 값으로 현재 상태가 할당된다.
fetch Status
queryFn 함수의 실행 상태를 말해주는 값.
1. fetching : 쿼리 함수가 실행중일 때
2. pasued : 쿼리 함수가 시작은 됐지만, 실제로 실행되고 있지 않을 때
3. idle : 쿼리함수가 어떤 작업도 하고 있지 않을 때
4. fetch status는 데이터를 성공적으로 가져왔는지 여부에 상관없이, 쿼리 함수의실행이 끝나면, idle 상태가 되고, 그 후에 데이터 서버에서 다시 받아오는 refetch 작업이 발생하면 쿼리 함수가 재실행되면서 다시 fetching 상태로 가게 된다
5. query status와 fetch status는 서로 독립적인 상태이기 때문에, 상황에 따라 query status와 fetch status가 다양한 조합의 형태로 나타날 수 있다. 따라서, query status와 fetch status의 다양한 조합에 맞춰 디테일한 구현을 할 수 있다.
Cashe
데이터를 미리 복사해 놓은 임시 저장소.
1. 데이터를 가져오는 속도가 아주 빠르다.
2. 웹 브라우저는 기본으로 cashe를 사용해서 속도를 높이고 네트워크 비용을 아끼는데, 사이트에 접속했을 때 받아 온 데이터를 저장해서 사용자가 같은 도메인에 접속하면 서버에 매번 데이터를 다시 요청하는게 아니라 저장해 놓은 데이터를 유저에게 보여준다.
캐싱된 데이터의 3가지 상태
1. fresh : 백엔드에서 이제 막 데이터를 받아와 캐시에 저장한 상태, useQuery 는 캐시에 fresh 상태의 캐싱 데이터를 반환한다.
2. stale : stale time 이라고 불리는 특정 시간이 지난 상태, 백그라운드에서 refetch 를 진행한다.
○ 4가지 stale 상태.
1. 새로운 쿼리 인스턴스가 마운트.
2. 브라우저 창에 다시 포커스.
3. 네트워크가 다시 연결.
4. 미리 설정해 둔 refetch interval 시간이 지남.
3. inactive : 컴포넌트가 언마운트되어 해당 데이터가 쓰이지 않는 상태.
dependant query
의존 관계의 쿼리들이 특정 순서대로 혹은, 특정 조건이 충족된 이후에 실행되어야 하는 쿼리.
const { data: user } = useQuery({
queryKey: ['user', email],
queryFn: getUserByEmail,
});
const userId = user?.id
const {
data: projects,
} = useQuery({
queryKey: ['projects', userId],
queryFn: getProjectsByUser,
enabled: !!userId,
});
enabled 는 프로퍼티 값이 true일 때 useQuery를 실행시킨다.