useState와useEffect를 사용한 비동기 데이터 처리 방식은 관리할 상태가 많아질 수록 상태 관리가 복잡하고 코드 중복이 생겨 유지보수가 어렵다.- 리덕스에서 미들웨어로
서버 상태를 관리할 수 있지만 테스트하는 것이 복잡하고 보일러플레이트 코드가 많이 생기게된다.
그래서 TanstackQuery가 등장하게되었다.
TanstackQuery는 서버 상태 관리 라이브러리다. 이 한마디가 개념이다.
리덕스 미들웨어를 사용할 때 보다 훨~~~씬 쉽게 서버 상태 관리를 할 수 있고 유지보수성도 높일 수 있다.
- 데이터 캐싱: 동일한 DB에 여러 번 접속 할 필요없이 중간에 저장해 놓은(캐싱된)데이터에 접근하는 기능
- 자동 리페칭: 데이터의 최신 상태를 유지하기 위해 자동으로 서버에서 데이터를 다시 가져오는 기능
- 쿼리 무효화: 데이터가 추가, 삭제, 수정, 등의 이벤트가 발생하면 이 전데이터가 오래됬다는 것을 알려주면서 데이터를 동기화 시키는 기능
yarn add @tanstack/react-query
QueryClientProvider를 사용해 사용할 범위를 설정한다. RTK나 useContext에서 처럼 범위를 정해준다.
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient();
ReactDOM.createRoot(document.getElementById("root")).render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
);
⭐이때 중요한 것은 new QueryClient로 꼭 클라이언트를 생성해줘야 한다!!!⭐
useMutation(invalidateQueries)을 사용할 때는 반드시 위에서 생성한 QueryClient를 use해줘야한다!!
new QueryClient()를 하게된다면 위에서 설정해 놓은 범위가 의미가 없어지고 App컴포넌트에서 새로 만든 별개의 QueryClient가 되기 때문이다.
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import axios from "axios";
import { useState } from "react";
const App = () => {
//반드시 useQueryClient()를 해줘야 한다. new QueryClient()를 하면 안된다.
const queryClient = useQueryClient();
const [todoItem, setTodoItem] = useState("");
const fetchTodos = async () => {
const response = await axios.get("http://localhost:4000/todos");
return response.data;
};
const addTodo = async (newTodo) => {
await axios.post("http://localhost:4000/todos", newTodo);
};
const {
data: todos,
isPending,
isError,
} = useQuery({
queryKey: ["todos"],
queryFn: fetchTodos,
});
const { mutate } = useMutation({
mutationFn: addTodo,
onSuccess: () => {
// alert("데이터 삽입이 성공했습니다.");
queryClient.invalidateQueries(["todos"]);
},
});
if (isPending) {
return <div>로딩중입니다...</div>;
}
if (isError) {
return <div>데이터 조회 중 오류가 발생했습니다.</div>;
}
return (
<div>
<h3>TanStack Query</h3>
<form
onSubmit={(e) => {
e.preventDefault();
const newTodoObj = { title: todoItem, isDone: false };
// useMutation 로직 필요
mutate(newTodoObj);
}}
>
<input
type="text"
value={todoItem}
onChange={(e) => setTodoItem(e.target.value)}
/>
<button>추가</button>
</form>
<ul>
{todos.map((todo) => {
return (
<li
key={todo.id}
style={{
display: "flex",
alignItems: "center",
gap: "10px",
backgroundColor: "aliceblue",
}}
>
<h4>{todo.title}</h4>
<p>{todo.isDone ? "Done" : "Not Done"}</p>
</li>
);
})}
</ul>
</div>
);
};
export default App;
데이터를 가져오기 위한 훅이다. 쿼리 키와 함수를 인자로 받아 isPending, isError, data를 받아와 데이터를 조회하고있는지, 조회하는 도중에 에러가 났는지를 조건문을 통해 렌더링 해줄 수 있다.
const {
data,
isPending,
isError,
} = useQuery({
queryKey: ["todos"],
queryFn: fetchTodos,
});
if (isPending) {
return <div>로딩중입니다...</div>;
}
if (isError) {
return <div>데이터 조회 중 오류가 발생했습니다.</div>;
console.log(data)
}
데이터를 생성, 수정, 삭제, 등의 작업을 할 때 사용한다. 즉, CRUD기능을 할 때 사용한다. 또한 원하는 작업이 성공했을 때 아래에서 볼 invalidateQueries를 사용해 쿼리 무효화기능도 사용할 수 있다.
Mutation은돌연변이라는 뜻을 가지고 있다. 즉,변경한다는 것을 의미한다.
구조 분해 할당으로 mutate를 받아 Mutation을 한다.
const { mutate } = useMutation({
mutationFn: addTodo,
});
<form
onSubmit={(e) => {
e.preventDefault();
const newTodoObj = { title: todoItem, isDone: false };
// useMutation 로직 필요
mutate(newTodoObj);
}}
>
위에서 말했듯이 쿼리 무효화기능을 위해 사용한다.
useMutation에 onSuccess 속성을 추가하고 사용할 queryClient.invalidateQueries({queryKey:[쿼리키]})형식으로 사용하면 된다. queryKey:를 생략하고 바로 쿼리키를 작성해도 된다.
const { mutate } = useMutation({
mutationFn: addTodo,
onSuccess: () => {
// alert("데이터 삽입이 성공했습니다.");
queryClient.invalidateQueries(["todos"]);
},
});
오늘은 두 번째 팀프로젝트 시작날이다. 카카오지도API를 사용한 위치기반 일정관리 사이트를 기획했다. 팀원들과 으쌰으쌰해서 많이 배워갈 수 있었으면 좋겠다.