
이번 개인 프로젝트때 주로 사용했던 Tanstack Query에 대해 알아보자. 리액트는 정말 훅이 많다..고 항상 느낀다.. 이번 TIL은 리액트 쿼리에 대해 깊이는 아니지만 내가 이해한만큼 써보려고 한다.
Tanstack Query❓
💡 fetching, caching, 서버 데이터와의 동기화를 지원해주는 라이브러리
서버 상태를 관리하기 위한 라이브러리로, 데이터를 패칭하고 캐싱, 동기화, 무효화 등의 기능을 제공한다. 복잡하고 장황한 코드가 필요하지 않고 리액트 컴포넌트 내부에서 간단하고 직관적으로 API를 사용할 수 있으며 이전보다 비동기 로직을 간편하게 작성하고 유지보수성을 높일 수 있다.
사용하는 이유
비동기 로직의 복잡성 해결 필요성
기존의 방식은 Fetching 코드를 작성하고 데이터를 담아 둘 상태(useState) 생성, useEffect를 이용해 컴포넌트 마운트시 데이터를 Fetching 한 뒤 상태에 저장하였다. 리액트 쿼리를 사용하면 해당 로직의 복잡함을 해결할 수 있다.
서버 상태 관리의 어려움
서버 상태는 클라이언트 상태와 달리 캐싱, 동기화, 재검증 등 관리해야 할 요소가 많아 기존 방법으론 관리가 어려움.
주요기능
- 데이터 캐싱: DB서버에 여러번 동일한 데이터를 요청하지 않고 캐싱하여 데이터를 빠르게 가져온다.
- 자동 리페칭: 데이터가 변경되었을 때 자동으로 리페칭하여 최신 상태를 유지한다.
- 쿼리 무효화: 특정 이벤트가 발생했을 때 쿼리를 무효화하고 데이터를 다시 가져온다.
데이터를 가져오고, 수정하고, 리프레시한다. 무조건 외우기!!
📌 get / Modify / refresh
get = useQuery
Modify = useMutation
refresh = invalidateQueries
라이브러리 설치
yarn add @tanstack/react-query
리액트 쿼리를 사용하려면 백엔드 데이터 베이스가 필요해서 json-server로 임시 데이터 베이스를 만들어준다.
yarn add json-server
단축명령어 설정
📂package.json에서 아래와 같이 설정해준다. 그러면 yarn json 입력시 서버가 켜진다.
{
"scripts": {
"json": "json-server --watch db.json --port 5000",
}
}
단축 명령어 없이 서버를 키는 명령어는 아래와 같다.
json-server --watch db.json
db.json
📂src > 📄db.json
{
"expenses": [
{
"id": "f70630fd-852d-4566-a830-9d318c22046a",
"date": "2024-06-18",
"item": "📚",
"amount": 50000,
"desc": "리액트",
"month": 5,
"user": "wnswns"
},
]
}
QueryClient 생성
useQuery를 사용할 곳에 쿼리 클라이언트를 생성해준 뒤 Provider를 적용해준다. 이렇게하면 전역에서 사용할 수 있다.
import React from "react";
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>
);
데이터 가져오기 : useQuery()
useQuery에 쿼리 키와 비동기 함수를 인자로 받아 데이터를 가져오고, 로딩 상태, 오류 상태, 그리고 데이터를 반환한다.
여기서 쿼리키는 배열로 들어간다.
useQuery({ queryKey: [쿼리키], queryFn: 비동기 함수 })
실무에서 자주 사용하는 쿼리키 이름
💡 꼭 알아야 하는 것
- 다른 API는 다른 쿼리키를 사용한다. (ex. 목록 API와 상세 API는 다른 쿼리키를 사용한다)
- 목록 페이지에선 `["posts"]` or `["movies"]` or `["todos"]`이렇게 쓰는 경우가 많음.
- 상세 페이지에서는 `["posts", id]` 이렇게 쓰는 경우가 많음.
- 페이지네이션의 쿼리키는 `["posts", page]` 이렇게 쓰는 경우가 많음
서버에서 데이터를 가져오는 API 함수를 만들어보자.
import axios from "axios";
const JSON_SERVER_HOST = "http://localhost:4000";
export const getExpense = async () => {
try {
const response = await axios.get(`${JSON_SERVER_HOST}/expenses`);
return response.data;
} catch (error) {
console.error(error);
alert("데이터를 가져오지 못했습니다.");
}
};
위에서 만든 API 함수를 useQuery에 적용해보자. 아래 코드로 적용하면 데이터 가져오는 로직 완성.
useQuery({ queryKey: ["expense"], queryFn: getExpense })
위 코드를 아래 코드처럼 쓸 수 있다.
- data : 화면에 보여줄 데이터 (queryFn의 return 값)
- isLoading : 데이터 로딩 여부 (true, false)
- error : 데이터 로딩 중 발생한 에러 데이터
const { data, isLoading, error } = useQuery({
queryKey: ["expense"],
queryFn: getExpense,
});
데이터를 받아오기 전 로딩중으로 뜨고, 데이터를 가져오지 못할 경우 예외 처리를 해줄 수 있다.
if (isLoading) {
return <div>로딩중입니다.</div>;
}
if (error) {
return <div>데이터를 불러오지 못했습니다.</div>;
}
데이터 추가, 수정, 삭제하기 : useMutation()
export const postExpense = async (newExes) => {
try {
const response = await axios.post(`${JSON_SERVER_HOST}/expenses`, newExes);
return response.data;
} catch (error) {
console.error(error);
alert("데이터를 생성하지 못했습니다.");
}
};
다른 컴포넌트에서 리액트 쿼리를 써야할 경우 ✔️ useQueryClient() 훅을 사용해줘야한다. invalidateQueries 속성을 사용해주기 위해선 필수이다. 생성 후 데이터를 다시 가져와야하기 때문에 invalidateQueries를 사용해줬다. 기존에 있던(데이터를 생성하기전) 데이터는 무효화시키고 최신 상태(데이터가 추가된)를 가져온다
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: postExpense,
onSuccess: () => {
queryClient.invalidateQueries(["expenses"]);
},
});
새로운 데이터가 추가되는 함수에 mutation.mutate 사용해주면 추가하는 로직도 완성이다. 차례대로 수정과 삭제도 같은 방법으로 하면 된다!
const onInsert = useCallback(
(date, item, amount, desc) => {
const newExes = {
id: uuidv4(),
date,
item,
amount,
desc,
month: selectedMonth,
user: userInfo.id,
};
mutation.mutate(newExes);
},
[mutation, userInfo]
);
'React' 카테고리의 다른 글
[리액트] Zustand 사용법 (0) | 2024.06.20 |
---|---|
[리액트] Tailwind CSS 사용해보기 (0) | 2024.06.19 |
[리액트] Thunder Client 사용해보기 (0) | 2024.06.11 |
[supabase] Supabase로 프로젝트 시작하기 (1) | 2024.06.03 |
[리액트] memoization, useMemo, useCallback (1) | 2024.05.29 |