TanStack Query (useQuery ,useMutate, invalidate Query)

하영·2024년 9월 6일
1

React

목록 보기
14/17
post-custom-banner

TanStack Query

사용 전 터미널 열고 설치하기!

yarn add @tanstack/react-query

TanStack Query의 개념

  • 데이터 캐싱: 동일한 데이터를 여러 번 요청하지 않도록 캐싱하여 성능을 향상 시킨다.
  • 자동 리페칭: 데이터가 변경되었을 때 자동으로 리페칭하여 최신 상태를 유지한다.
  • 쿼리 무효화: 특정 이벤트가 발생했을 때 쿼리를 무효화하고 데이터를 다시 가져올 수 있다.

🔥 TanStack Query에서 반드시 알고 외워야하는 것 3가지!

  • useQuery : get
  • useMutate : modify
  • invalidate Query : refresh

👩🏻‍💻 db.json 파일 세팅

  
    {
      "todos": [
        {
          "id": "1715926482394",
          "title": "리액트 공부하기",
          "isDone": true
        },
        {
          "id": "1715926492887",
          "title": "Node.js 공부하기",
          "isDone": true
        },
        {
          "id": "1715926495834",
          "title": "영화보기",
          "isDone": false
        }
      ]
    }

01. useQuery 사용하기

: 데이터를 가져오기 위해 사용되는 TanStack Query의 대표적인 훅

main.jsx에 QueryClient 생성하기

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.jsx";
import "./index.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
//import 시키기!

const queryClient = new QueryClient();

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </StrictMode>
);

Context의 Provider 처럼 App 컴포넌트 전체를 감싸주어서 전역에서 사용할 수 있도록 만든다.

⭐️ new QueryClient(); 는 한번만 만들어주면 된다.

다른 컴포넌트 가서 똑같이 new QueryClient(); 를 적어버리면 main.jsx에서 만든 QueryClient와 전혀 다른 것이 되어버리므로 주의!! → useQueryClient 사용하면 된다.


import { useQuery } from "@tanstack/react-query";
import axios from "axios"; //json server 데이터 get

function App() {
  const fetchTodos = async () => {
    const response = await axios.get("http://localhost:4000/todos"); //todos json의 서버 받아오기 (get)
    return response.data;
  };

  const {
    data: todos,
    isLoading,
    isError,
  } = useQuery({
    queryKey: ["todos"], // 캐싱된 데이터를 불러오기 때문에 DB까지 가서 데이터를 가져오지 않아도 된다 > cost 절감
    queryFn: fetchTodos, // queryFn 는 함수를 받아온다 (위에서 별도 작성)
  });

  if (isLoading) {
    return <div>로딩중</div>;
  }

  if (isError) {
    return <div>데이터 호출 중 오류 발생</div>;
  }

  // console.log("data", data);

  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;

02. useMutation 사용하기

: 데이터를 생성, 수정, 삭제하는 등의 작업에 사용되는 훅

import { useMutation, useQuery } from "@tanstack/react-query";
import axios from "axios"; //json server 데이터 get
import { useState } from "react";

function App() {
  const [todoItem, setTodoItem] = useState("");

  const fetchTodos = async () => {
    const response = await axios.get("http://localhost:4000/todos"); //todos json의 서버 받아오기 (get)
    return response.data;
  };

  const addTodo = async (newTodo) => {
    await axios.post("http://localhost:4000/todos", newTodo);
  };

  const {
    data: todos,
    isLoading,
    isError,
  } = useQuery({
    queryKey: ["todos"], 
    // 캐싱된 데이터를 불러오기 때문에 
    //DB까지 가서 데이터를 가져오지 않아도 된다 > cost 절감
    queryFn: fetchTodos,
  });

  const mutation = useMutation({ 
    //mutation 안에 mutate라는 메소드가 있으므로 {mutate} 로 적어도 됨
    mutationFn: addTodo,
  });
  // 생성, 추가, 삭제하는 역할이므로 버튼 onClick에 연결할 예정

  if (isLoading) {
    return <div>로딩중</div>;
  }

  if (isError) {
    return <div>데이터 호출 중 오류 발생</div>;
  }

  // console.log("data", data);

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

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

          // useMutation 로직 필요
          mutation.mutate({ title: todoItem, isDone: false });
        }}
      >
        <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;

이렇게 POST 로 얻어온 값들은 버튼을 입력하면 데이터를 뿌려준다.


03. invalidateQueries

: 특정 쿼리를 무효화하여 데이터를 다시 패칭하게 하는 함수

주로 useMutation과 함께 사용하여 데이터가 변경된 후 관련 쿼리를 다시 가져오게 해서 최신상태를 유지할 수 있게 한다.

👩🏻‍💻기존코드에 로직 추가

import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import axios from "axios"; //json server 데이터 get
import { useState } from "react";

function App() {
  const queryClient = useQueryClient(); // main.jsx에서 만들었던 거 사용

  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,
    isLoading,
    isError,
  } = useQuery({
    queryKey: ["todos"], 
    queryFn: fetchTodos,
  });

  const mutation = useMutation({
    mutationFn: addTodo,
    //데이터 무효화 처리
    onSuccess: () => {
      queryClient.invalidateQueries(["todos"]); 

    },
  });

  if (isLoading) {
    return <div>로딩중</div>;
  }

  if (isError) {
    return <div>데이터 호출 중 오류 발생</div>;
  }

  // console.log("data", data);

  return (
    <div>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          mutation.mutate({ title: todoItem, isDone: false });
        }}
      >
        <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;

json 데이터가 여러 개라면? (newPost의 객체가 여러개)

const addPost = (newPost) => {
    axios.post("http://localhost:4000/posts", {
      title: newPost.title, //newPost 안에 있는 title
      views: newPost.views, //newPost 안에 있는 views
    });
  };
<div>
  <button onClick={() => {
    mutation.mutate({
      title: newPost.title
      views: newPost.views
    });
  }}>제출</button>
</div>

💡 정리!

  • 데이터를 캐싱하는 기준은 queryKey
  • queryKey가 더이상 유효하지 않다고 알려주는 것 === invalidateQueries
  • invalidateQueries를 쓸 때에는 반드시 queryKey 넣어주기!
profile
왕쪼랩 탈출 목표자의 코딩 공부기록
post-custom-banner

1개의 댓글

comment-user-thumbnail
2024년 9월 6일

탱탱!

답글 달기