[TIL] React - tanstackQuery

JongYeon·2025년 2월 26일

TIL

목록 보기
38/69
post-thumbnail

등장 배경

  • useStateuseEffect를 사용한 비동기 데이터 처리 방식은 관리할 상태가 많아질 수록 상태 관리가 복잡하고 코드 중복이 생겨 유지보수가 어렵다.
  • 리덕스에서 미들웨어로 서버 상태를 관리할 수 있지만 테스트하는 것이 복잡하고 보일러플레이트 코드가 많이 생기게된다.
    그래서 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로 꼭 클라이언트를 생성해줘야 한다!!!⭐

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;

useQuery

데이터를 가져오기 위한 훅이다. 쿼리 키와 함수를 인자로 받아 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)
 }

useMutation

데이터를 생성, 수정, 삭제, 등의 작업을 할 때 사용한다. 즉, CRUD기능을 할 때 사용한다. 또한 원하는 작업이 성공했을 때 아래에서 볼 invalidateQueries를 사용해 쿼리 무효화기능도 사용할 수 있다.
Mutation은 돌연변이라는 뜻을 가지고 있다. 즉, 변경한다는 것을 의미한다.

구조 분해 할당으로 mutate를 받아 Mutation을 한다.

  const { mutate } = useMutation({
    mutationFn: addTodo,
  });


 <form
        onSubmit={(e) => {
          e.preventDefault();

          const newTodoObj = { title: todoItem, isDone: false };

          // useMutation 로직 필요
          mutate(newTodoObj);
        }}
      >

invalidateQueries

위에서 말했듯이 쿼리 무효화기능을 위해 사용한다.
useMutation에 onSuccess 속성을 추가하고 사용할 queryClient.invalidateQueries({queryKey:[쿼리키]})형식으로 사용하면 된다. queryKey:를 생략하고 바로 쿼리키를 작성해도 된다.

 const { mutate } = useMutation({
    mutationFn: addTodo,
    onSuccess: () => {
      // alert("데이터 삽입이 성공했습니다.");
      queryClient.invalidateQueries(["todos"]);
    },
  });

하루를 마치며

오늘은 두 번째 팀프로젝트 시작날이다. 카카오지도API를 사용한 위치기반 일정관리 사이트를 기획했다. 팀원들과 으쌰으쌰해서 많이 배워갈 수 있었으면 좋겠다.

profile
프론트엔드 공부중

0개의 댓글