(1) 등장배경
(1)-1. 비동기 로직의 복잡성 해결 필요
- 기존의 useEffect와 useState를 사용한 비동기 데이터 처리 방식은 상태 관리가 복잡하고 코드 중복이 많아 유지보수가 어려웠어요.
- Redux Thunk와 같은 미들웨어를 사용해도 비동기 로직의 테스트가 복잡하고 보일러플레이트 코드가 많이 생기기 때문에 더 효율적인 도구가 필요했습니다.
(1)-2. 서버 상태 관리의 어려움
- 서버 상태는 클라이언트 상태와 달리 캐싱, 동기화, 재검증 등 관리해야 할 요소가 많아 기존 방법으로는 관리가 어려웠어요.
- 이를 해결하기 위해 등장한 것이 TanStack Query로, 서버 상태 관리를 쉽게 해 주고 복잡한 비동기 로직을 단순화해 줍니다.
(2) 개념
(2)-1. 서버 상태 관리 라이브러리
- TanStack Query는 서버 상태를 관리하기 위한 라이브러리로, 데이터를 패칭하고 캐싱, 동기화, 무효화 등의 기능을 제공합니다.
- 개발자는 이전에 비해 ‘훨씬’ 비동기 로직을 간편하게 작성하고 유지보수성을 높일 수 있습니다.
(2)-2. 주요 기능
- 데이터 캐싱: 동일한 데이터를 여러 번 요청하지 않도록 캐싱하여 성능을 향상합니다.
- 자동 리페칭: 데이터가 변경되었을 때 자동으로 리페 칭하여 최신 상태를 유지합니다.
- 쿼리 무효화: 특정 이벤트가 발생했을 때 쿼리를 무효화하고 데이터를 다시 가져올 수 있습니다.
(3) 사용방법 - useQuery
(시작하기 전) 세팅
- 프로젝트 생성
yarn create vite tanstack-query-app --template react
- 설치
yarn add @tanstack/react-query
- 적용할 범위(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>
);
(3)-1. useQuery의 개념
- useQuery는 데이터를 가져오기 위해 사용되는 TanStack Query의 대표적인 훅입니다. 쿼리 키와 비동기 함수(패칭 함수)를 인자로 받아 데이터를 가져오고, 로딩 상태, 오류 상태, 그리고 데이터를 반환합니다.
💡 로딩 상태, 오류 상태 등을 자동으로 반환하기 때문에 redux-thunk에서처럼 일일이 모든 상태를 직접 세팅할 필요가 없음.
- 이 훅을 통해 서버에서 데이터를 가져오고, 가져온 데이터를 컴포넌트에서 쉽게 사용할 수 있습니다
(3)-2. useQuery 기본 사용법
fetchTodos와 같은 비동기 함수는 별도 파일에 분리 보관하는 것이 유지보수 측면에서 좋지만 지금은 기본 사용법을 익히는 데에 집중하기 위해 한 파일에서 봅니다.
(테스트 환경은 json-server 사용, 아래는 db.json 파일)
{
"todos": [
{
"id": "1715926482394",
"title": "리액트 공부하기",
"isDone": true
},
{
"id": "1715926492887",
"title": "Node.js 공부하기",
"isDone": true
},
{
"id": "1715926495834",
"title": "영화보기",
"isDone": false
}
]
}
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;
(4) 사용방법 - useMutation
(4)-1. useMutation의 주요 개념
- useMutation은 데이터를 생성, 수정, 삭제하는 등의 작업에 사용되는 훅입니다. CUD에 대한 비동기 작업을 쉽게 수행하고, 성공 또는 실패 시에 추가적인 작업을 실행할 수 있기 때문에 useQuery와 함께 가장 대표적인 TanStack Query hook이라고 할 수 있어요.
- 비동기 작업을 쉽게 처리한다는 말 안에는 작업이 완료된 후에 관련된 쿼리를 무효화하는 과정이 포함되는데 이 역시도 TanStack Query의 핵심 개념이라고 할 수 있습니다. 최신 데이터를 유지하는 데에 필수적인 요소예요.
(4)-2. useMutation 기본 사용법
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;
(5) 사용방법 - invalidateQueries
(5)-1. invalidateQueries의 개념
- invalidateQueries는 특정 쿼리를 무효화하여 데이터를 다시 패칭 하게 하는 함수입니다. 주로 useMutation과 함께 사용하여 데이터가 변경된 후 관련 쿼리를 다시 가져오도록 합니다.
- 이를 통해 데이터가 항상 최신 상태로 유지될 수 있도록 도와줍니다. 예를 들어, 새로운 할 일을 추가한 후 기존의 할 일 목록을 다시 가져오도록 할 수 있습니다.
(5)-2. invalidateQueries 기본 사용법
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;
'ReactJS' 카테고리의 다른 글
React - Zustand (1) | 2024.09.25 |
---|---|
React - ( React Hooks 6 - Custom Hooks ) (2) | 2024.09.13 |
React - ( React Hooks 5 - memoization ) (0) | 2024.09.10 |
React - ( React Hooks 4 - useContext ) (0) | 2024.09.06 |
React - ( React Hooks 3 - useRef ) (1) | 2024.09.05 |