TanStack Query 알아보기

0
post-thumbnail

📌  TanStack Query

TanStack Query는 React Query라는 이름으로 시작했지만, v4 부터 React 외에도 다른 프레임워크에서도 활용할 수 있도록 기능이 확장되며 TanStack Query라는 이름으로 변경되었다.

TanStack Query는 React에서 서버 상태 가져오기, 캐싱, 동기화 및 업데이트를 보다 쉽게 다룰 수 있도록 도와주는 라이브러리이다. 클라이언트 상태와 서버 상태를 명확히 구분하기 위해 만들어졌다.

기존의 useState나 Redux Thunk 등을 사용한 비동기 데이터 요청 방법의 복잡성을 해결하고, 서버 상태 관리를 좀 더 용이하게 하기 위해 TanStack Query가 등장하게 되었다.

📍 useState hook을 사용한 비동기 데이터 페칭

  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const response = await axios.get(API_URL);
        setData(response.data);
      } catch (error) {
        setError(error);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, []);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  • 상태 관리의 복잡성
    컴포넌트 내에서 관리해야 할 상태가 많아져 상태 관리가 어려워지고, 각 상태에 따른 로직이 분산되어 코드가 복잡해짐
  • 중복된 코드
    여러 컴포넌트에서 동일한 데이터를 페칭하는 경우 각 컴포넌트마다 동일한 로직을 반복해야하므로 코드 중복이 발생하고 유지보수에 어려움을 줌
  • 비즈니스 로직의 분리 부족
    비동기 로직이 컴포넌트 내부에 포함되면, 비즈니스 로직과 UI 로직이 합쳐져 코드 가독성을 저해하고 유지보수성 측면에서 비효율적임
  • 서버 상태 관리 어려움
    서버 상태(API로부터 페칭된 데이터)를 효율적으로 관리하기 어렵고, 데이터의 캐싱, 동기화, 리페칭 등의 기능을 구현하기 까다로움

📍 Redux Thunk 사용한 비동기 데이터 페칭

const initialState = {
  isLoading: false,
  error: null,
  itemList: [],
  selectedItems: [], 
};

const itemSlice = createSlice({
  name: 'item',
  initialState,
  reducers: {
    startLoading: (state) => {
      state.isLoading = true;
    },
    hasError: (state, action) => {
      state.isLoading = false;
      state.error = action.payload;
    },
    getItemListSuccess: (state, action) => {
      state.isLoading = false;
      state.itemList = action.payload;
    },
    addToSelectedItems: (state, action) => {
      // ... existing code ...
    },
    removeFromSelectedItems: (state, action) => {
      // ... existing code ...
    },
  },
});

export const {
  startLoading,
  hasError,
  getItemListSuccess,
  addToSelectedItems,
  removeFromSelectedItems,
} = itemSlice.actions;

// ------------------------------------------------------------

export const fetchItemList = () => async (dispatch) => {
  dispatch(startLoading());
  try {
    const response = await axios.get('/api/items');
    dispatch(getItemListSuccess(response.data.items));
  } catch (error) {
    dispatch(hasError(error.message));
  }
};

export default itemSlice.reducer;
  • 복잡성 증가
    보일러플레이트 코드가 긴 Redux 에 더 긴 코드들을 추가해야 되기 때문에 코드가 복잡해지고 유지보수에 어려움을 줌
  • 테스트 복잡성 증가
    비동기 로직을 포함한 액션 크리에이터를 테스트하는 것이 복잡하며, 다양한 응답상태와 비동기 작업을 시뮬레이션 하기위한 별도의 장치가 필요해 테스트 코드가 복잡해짐

💡 TanStack Query 사용법

1. 핵심 기능

  • useQuery - GET 요청
  • useMutataion - POST, PUT/PATCH, DELETE 요청
  • invalidateQueries - refetch를 통한 데이터 최신 상태 유지

2. 기본 설정

npm i @tanstack/react-query
yarn add @tanstack/react-query
// main.tsx
import { createRoot } from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import "./index.css";

const queryClient = new QueryClient();

createRoot(document.getElementById("root")!).render(
	<QueryClientProvider client={queryClient}>
		<RouterProvider router={router} />
    </QueryClientProvider>
);

3. GET 요청 (useQuery)

import { useQuery } from "@tanstack/react-query";
import axios from "axios";

const App = () => {
  const fetchData = async () => {
    const response = await axios.get(API_URL);
    return response?.data;
  };

  const { data, isLoading, isError, error } = useQuery({
    queryKey: ["data"],
    queryFn: fetchData,
  });

  if (isLoading) return <div>Loading...</div>;
  if (isError) return <div>Error: {error.message}</div>;

  return (
    <div>
      <h3>TanStack Query</h3>
      <ul>
        {data.map((item) => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    </div>
  );
};

export default App;
  • useQuery는 인자로 단 하나의 객체만 받는다. 객체에는 queryKey, queryFn가 필수 값이다.
  • queryKey는 배열로 지정해 줘야 하며 이는 단일 문자열만 포함된 배열이 될 수도 있고, 여러 문자열과 중첩된 객체로 구성된 복잡한 형태일 수도 있다.
  • queryFn는 Promise를 반환하는 함수를 넣어야 한다.

4. POST, PUT/PATCH, DELETE 요청 (useMutation)

import { useMutation, useQuery } from "@tanstack/react-query";
import axios from "axios";
import { useState } from "react";

const App = () => {
  const [item, setItem] = useState("");
  const fetchData = async () => {
    //...
  };

  const { data, isLoading, isError, error } = useQuery({
    queryKey: ["data"],
    queryFn: fetchData,
  });
  
  const addItem = async (newItem) => {
    await axios.post(API_URL, newItem);
  };
  
  const { mutate } = useMutation({
    mutationFn: addItem,
    onMutate() {
      /* ... */
    },
    onSuccess(data) {
      console.log(data);
    },
    onError(err) {
      console.log(err);
    },
    onSettled() {
      /* ... */
    },
  });

  return (
    <div>
      <h3>TanStack Query</h3>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          const newItemObj = { title: item, isDone: false };
          mutate(newItemObj);
        }}
      >
        <input
          type="text"
          value={item}
          onChange={(e) => setItem(e.target.value)}
        />
        <button>추가</button>
      </form>
      <ul>
        {data?.map((entry) => (
          <li key={entry.id}>{entry.title}</li>
        ))}
      </ul>
    </div>
  );
};

export default App;
  • useMutation의 반환 값인 mutation 객체의 mutate 메서드를 이용해서 요청 함수를 호출할 수 있다.
  • mutate는 onSuccess, onError 메서드를 통해 성공했을 시, 실패했을 시 response 데이터를 핸들링할 수 있다.
  • onMutate는 mutation 함수가 실행되기 전에 실행되고, mutation 함수가 받을 동일한 변수가 전달된다.
  • onSettled는 try-catch-finally 구문의 finally 처럼 요청이 성공하든 에러가 발생하든 상관없이 마지막에 실행된다.

5. 쿼리 최신화/무효화 (invalidateQueries)

const { mutate } = useMutation({
    mutationFn: addItem,
	onSuccess: () => {
	  queryClient.invalidateQueries(["data"]);
	},
});
  • invalidateQueries는 화면을 최신 상태로 유지하는 가장 간단한 방법이다.
  • useMutation onSuccess에 queryClient의 invalidateQueries 메소드를 사용하면 된다. 이때 해당 컴포넌트에서 사용한 queryKey를 사용한다.
  • 예를 들면, 게시판 목록에서 어떤 게시글을 작성하거나 게시글을 제거했을 때 화면에 보여주는 게시판 목록을 실시간으로 최신화해야 할 때 사용한다.

0개의 댓글

관련 채용 정보