-
24.06.11 TIL TanStack Querysparta TIL 2024. 6. 11. 17:42
( 예전에 React Query 라고 불렀었고, 지금은 TanStack Query라고 한다. )
기존 비동기처리방법
▼ 데이터 패칭
useEffect(() => { const fetchData = async () => { const result = await axios("https://api.example.com/data"); setData(result.data); }; fetchData(); }, []);
▼ 로딩 및 에러 처리 : useState hook을 통해서 loading과 error를 핸들링 할 수 있도록 변경
import React, { useState, useEffect } from "react"; import axios from "axios"; const App = () => { 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("http://localhost:4000/todos"); 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>; return ( <div> <h1>Fetched Data</h1> <ul> {data && data.map((item) => ( <li key={item.id}> <h2>{item.title}</h2> <p>{item.isDone ? "Done" : "Not Done"}</p> </li> ))} </ul> </div> ); }; export default App;
여전히 발생하는 문제 !
1. 상태 관리의 복잡성
컴포넌트 내에서 직접 로딩상태, 에러상태, 데이터 자체를 관리하는 상태 등 여러 상태를 직접 관리해야 해서
상태 관리가 복잡하고, 각 상태에 따른 로직이 컴포넌트 내부에 분산되어 있어서 코드가 복잡해 진다.
2. 중복된 코드
여러 컴포넌트에서 동일한 데이터를 패칭해야 하는 경우, 각 컴포넌트마다 동일한 비동기 로직을 반복해서 작성해야 함
코드 중복은 유지보수성을 저하시킨다.
3. 비즈니스 로직의 분리 부족
비동기 로직이 컴포넌트 내부에 직접 포함되면, 비즈니스 로직과 UI 로직이 혼합되어 코드의 가독성과 유지보수성이 떨어짐
( 비즈니스 로직 : 애플리케이션의 핵심 동작을 정의하는 코드로 사용자가 데이터를 제출할 때의 검증이나 특정 조건에 따라 데이터를 필터링하는 작업 등이 포함되며, UI 로직과 분리되지 않으면 유지보수가 어려워짐 )
4. 서버 상태 관리의 어려움
서버 상태(ex : API로부터 패칭된 데이터)를 효율적으로 관리하기 어려움
데이터의 캐싱, 동기화, 리페칭 등의 기능을 구현하려면 정-말 많은 노력과 시간이 들어가야 하므로 굉장히 번거롭다.
이러한 이유로 Redux Middleware를 사용했지만, 이 역시 아래와 같은 이유로 문제점들이 있다 .
1. 복잡성 증가
비동기 로직이 복잡해질수록 액션 크리에이터와 리듀서의 코드가 길어짐에 따라 보일러플레이트 코드(반복적이고 틀에 박힌 코드)가 많아져 유지보수가 어려워진다.
2. 테스트가 복잡하다
비동기 로직을 포함한 액션 크리에이터를 테스트하는 것이 복잡하며, 다양한 응답 상태와 비동기 작업을 시뮬레이션하기 위한 별도의 장치가 필요해 테스트 코드가 복잡해진다.
( 비동기 로직의 테스트가 왜 복잡할까 ?
Redux Thunk를 사용하면 비동기 로직이 액션 크리에이터에 포함되는데, 이를 테스트하기 위해서는 다양한 응답 상태(로딩, 성공, 실패)를 시뮬레이션해야 한다. 이러한 비동기 작업을 모킹(mocking)하고, 상태 변화를 검증하는 테스트 코드를 작성하는 과정이 복잡해진다.
또한, 비동기 로직과 관련된 여러 상태를 관리해야 하므로 테스트 코드의 양도 많아진다.
반면 TanStack Query(React Query)등의 라이브러리를 사용하면 비동기 로직을 별도로 분리할 수 있어 테스트하기가 더 쉽다 !
React Query는 서버 상태 관리에 특화된 기능을 제공해 복잡한 비동기 로직을 단순화하고, 캐싱, 동기화, 리페칭 등의 기능을 쉽게 구현할 수 있다.
TanStack Query는 선언적인 데이터 패칭, 자동 리페칭, 캐싱 등 다양한 기능을 제공하여 서버 상태 관리를 더 쉽고 효율적으로 만들어준다.
⭐️ TanStack Query (서버상태관리 라이브러리)
( redux는 전역상태관리 이면서, 서버상태를 관리하는 추가적인 미들웨어를 사용했던 것 = 서버상태관리 전용은 아니었다 !! )
주요기능
1. 데이터 캐싱 : 동일한 데이터를 여러 번 요청하지 않도록 캐싱하여 성능을 향상시킨다.
2. 자동 리페칭 : 데이터가 변경되었을 때 자동으로 리페칭하여 최신 상태를 유지한다.
3. 쿼리 무효화 : 특정 이벤트가 발생했을 때 쿼리를 무효화하고 데이터를 다시 가져올 수 있다.yarn add @tanstack/react-query // 설치
세팅 끝 ▶︎ useQueryClient 훅으로 가져다 쓰면됨 테스트진행 세팅
yarn add json-server 설치 → 루트경로에 db.json생성 → db.json에 db내용입력 → yarn json-server db.json --port 4000 → yarn add axios
▼ useQuery
🔥 data, ispending, isError 기본제공이 되어서 로직 추가없이 사용할 수 있다 !!! 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", color: "black", width: "500px", }} > <h4>{todo.title}</h4> <p>{todo.isDone ? "Done" : "Not Done"}</p> </li> ); })} </ul> </div> ); }; export default App;
▼ useMutation ( create,update,delete역할 ), invalidateQueries ( 무효화=갱신 )
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import axios from "axios"; import { useState } from "react"; const App = () => { const queryClient = useQueryClient(); // main에 만들어 뒀던 것 ! 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"], // 서버에 재요청하지 않고 데이터 캐싱하는 기준(가지고있는)은 queryKey인 todos queryFn: fetchTodos, }); const mutation = useMutation({ mutationFn: addTodo, onSuccess: () => { queryClient.invalidateQueries(["todos"]); //queryKey를 무효화시켜줘서 갱신시켜주는게 invalidateQueries (반드시 queryKey를 넣어야함) }, }); if (isPending) { return <div>로딩중 ...</div>; } if (isError) { return <div>데이터 조회중 에러가 발생했습니다.</div>; } return ( <div> <h3>Tanstack Query</h3> <form onSubmit={(e) => { e.preventDefault(); mutation.mutate({ title: todoItem, isDone: false }); }} > <input type="text" value={todoItem} onChange={(e) => setTodoItem(e.target.value)} ></input> <button>추가하기</button> </form> <ul> {todos.map((todo) => { return ( <li key={todo.id} style={{ display: "flex", alignItems: "center", gap: "10px", backgroundColor: "aliceblue", color: "black", width: "500px", }} > <h4>{todo.title}</h4> <p>{todo.isDone ? "Done" : "Not Done"}</p> </li> ); })} </ul> </div> ); }; export default App;
강의 1-10까지
스터디플젝 회의 : 모든 인원이 의견을 내다보니 회의 무한 루프에 빠져서.. 기획 / 디자인으로 팀 나눔
'sparta TIL' 카테고리의 다른 글
24.06.팀플 TIL (0) 2024.06.18 24.06.14 TIL (0) 2024.06.15 24.06.10 TIL 동기와비동기/Promise/HTTP/json-server/axios (0) 2024.06.10 SpartaHub 리팩토링중 1 (0) 2024.06.09 24.06.03 TIL (0) 2024.06.03