서버 상태를 관리하기 위한 라이브러리로, 데이터를 패칭하고 캐싱, 동기화, 무효화 등의 기능을 제공합니다. 비동기 로직을 간편하게 작성하고 유지보수성을 높일 수 있습니다.
1. 설치
yarn add @tanstack/react-query
2. 적용
적용할 범위(ex: 전역)에 Provider를 이용하여 적용합니다.
App.jsx 또는 main.jsx(index.jsx)에 세팅하는 것을 권장합니다.
// main.jsx
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>
);
TanStack Query에서 데이터를 가져오는 대표적인 훅은 useQuery입니다. 이 훅은 쿼리 키와 비동기 함수(패칭 함수)를 인자로 받아 서버에서 데이터를 가져오며, 로딩 상태, 오류 상태, 그리고 데이터를 반환합니다. useQuery를 사용하면 데이터를 쉽게 컴포넌트에서 활용할 수 있을 뿐만 아니라, 캐싱, 자동 리패칭, 백그라운드 데이터 동기화 등 다양한 기능을 지원해 효율적인 데이터 관리가 가능합니다.
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
const App = () => {
const fetchTodos = async () => {
const response = await axios.get("http://localhost:4000/todos");
return response.data;
};
const {
data: todos,
isPending,
isError,
} = useQuery({
queryKey: ["todos"],
queryFn: fetchTodos,
});
if (isPending) {
return <div>로딩중입니다...</div>;
}
if (isError) {
return <div>데이터 조회 중 오류가 발생했습니다.</div>;
}
return (
<div>
<h3>TanStack Query</h3>
<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;
useMutation은 데이터를 생성, 수정, 삭제하는 작업(CUD)에 사용되는 훅입니다. 이 훅을 통해 비동기 작업을 간편하게 수행할 수 있으며, 작업이 성공하거나 실패했을 때 추가적인 후속 작업을 실행할 수 있어 useQuery와 함께 TanStack Query의 대표적인 훅으로 꼽힙니다.
특히, 비동기 작업을 쉽게 처리한다는 말에는 작업이 완료된 후 관련된 쿼리를 무효화하는 과정이 포함됩니다. 이는 최신 데이터를 유지하는 데 필수적이며, TanStack Query의 핵심 개념 중 하나입니다.
import { useMutation, useQuery } from "@tanstack/react-query";
import axios from "axios";
import { useState } from "react";
const App = () => {
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,
});
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;
invalidateQueries는 특정 쿼리를 무효화하여 데이터를 다시 패칭하게 하는 함수입니다. 주로 useMutation과 함께 사용되어, 데이터가 변경된 후 관련된 쿼리를 다시 가져오도록 합니다. 이를 통해 데이터가 항상 최신 상태로 유지되도록 보장할 수 있습니다.
예를 들어, 새로운 할 일을 추가한 후 invalidateQueries를 사용해 기존의 할 일 목록을 다시 패칭하여 최신 상태로 업데이트할 수 있습니다.
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import axios from "axios";
import { useState } from "react";
const App = () => {
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;