들어가며
지난번 React-Query에 대한 필요성을 간략하게 알아봤다.
이번에는 React-Query 자체에 대해서 정리를 해보려고 한다.
React-Query ?
서버에서 값을 가져오거나, 캐싱, 값 업데이트, 에러 핸들링 등 비동기 과정을 사용할 때
도움을 준다.
기존 Redux, Mobx, Recoil 같은 상태 관리 라이브러리가 있지만, 서버 데이터와 클라이언트
데이터를 분리시킬 수 있다는 장점이 있다.
React-Query의 장점
▶ 서버의 상태 업데이트, 데이터 패칭, 캐싱 등을 쉽게 할 수 있도록 도와준다.
▶ Redux 등, 전역 상태 관리를 통해서 서버의 데이터를 비동기로 가져오기 위해서는
추가적으로 작성해야하는 코드 많지만 React-Query는 간단하게 처리해준다.
▶ 코드가 간단해진다는 것은 유지 보수를 쉽게할 수 있다는 뜻이 된다.
▶ 같은 데이터를 여러번 요청할 경우 중복을 제거해준다.
▶ 백그라운드에서 자동으로 데이터를 업데이트시켜준다.
이 외에도 많지만 이것은 좀 더 React-Query와 친해지고 더 공부해보자!
React-Query의 라이프사이클
React Query는 5단계로 라이프 사이클이 구성되어 있다.
▶ Fetching : 데이터를 요청한 상태
▶ Fresh : 데이터가 만료되지 않은 상태
▷ 컴포넌트의 상태가 변경되어도 데이터를 다시 요청하지 않는다.
▷ 새로고침을 하면 다시 Fetching 한다.
▶ Stale : 데이터가 만료된 상태
▷ 서버에서 데이터를 받는 사이에 다른 유저가 데이터를 추가, 수정, 삭제등을 해서 최신화가
필요한 상태를 말한다.
▷ 컴포넌트가 마운트, 업데이트되면 다시 데이터를 요청한다.
▷ Fresh 상태에서 Stale 상태로 변경되는데 걸리는 시간이 StaleTime이다
▶ Inactive : 사용하지 않는 상태
▷ 일정 시간이 지나면 가비지 콜렉터가 캐시에서 제거한다.
▷ 기본값은 5분이다.
▷ Inactive 상태에서 캐싱된 상태로 남아있는 시간을 CacheTime이라고 한다.
▶ Delete : 가비지 콜렉터에 의해 캐시에서 제거된 상태
환경 설정
npx react-create-app client
cd client
npm install react-query
프론트 환경에서는 CRA를 사용해서 간단하게 만들고 react-query를 설치했다.
express server
서버 환경은 express 를 사용해서 간단하게 구성했다.
백엔드 준비
// routes/index.js
var express = require("express");
var router = express.Router();
const dummyList = [
{ title: "title1", contents: "text1", isFinish: false },
{ title: "title2", contents: "text2", isFinish: false },
{ title: "title3", contents: "text3", isFinish: true },
{ title: "title4", contents: "text4", isFinish: true },
{ title: "title5", contents: "text5", isFinish: false },
{ title: "title6", contents: "text6", isFinish: false },
];
/* GET home page. */
router.get("/api", function (req, res, next) {
// console.log(dummyList);
res.status(202).json(dummyList);
});
router.post("/api", function (req, res, next) {
dummyList.push(req.body);
res.status(201);
});
module.exports = router;
간단하게 " /api " 경로로 get 요청하면 dummyList 데이터를 넘겨주는 API이다.
post 요청의 경우 업데이트를 해주고 있다.
프론트엔드 설정하기
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { QueryClient, QueryClientProvider } from "react-query";
const queryClient = new QueryClient();
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</React.StrictMode>
);
QueryClient를 사용해서 client를 먼저 만들어줬습니다.
여기서 다양한 옵션을 설정할 수 있는데, 이것도 모두 공부하고 지나가면 좋겠지만
분량이 많아질 것 같아서 다음에 공부하자..! ( 점점 할게 많아지는.... )
useQuery
import "./App.css";
import { useQuery } from "react-query";
import axios from "axios";
const getTodos = async () => {
const { data } = await axios.get("/api");
return data;
};
function App() {
const query = useQuery("todos", getTodos);
console.log(query);
return (
<div className="App">
{query.data.map((data) => {
return <div key={data.title}>{data.title}</div>;
})}
</div>
);
}
export default App;
useQuery를 사용해서 데이터를 요청할 수 있다.
query 안에는 data, isLoading, isSuccess, isError 등 많은 정보를 가지고 있다.
만약 Redux였다면 직접 구현해야하는 부분을 React-Query는 알아서 제공을 해주고 있다.
API 요청이 완료되기 전에는 status 상태가 loading인데, 완료되면 success or failed로 나뉜다.
실제 결과 데이터는 data에 들어가있다.
useMutation
import "./App.css";
import { useMutation, useQuery } from "react-query";
import axios from "axios";
const getTodos = async () => {
const { data } = await axios.get("/api");
return data;
};
const postTodos = (data) => {
axios.post("/api", data);
};
function App() {
const query = useQuery("todos", getTodos);
const mutation = useMutation(postTodos, {
onSuccess: () => {
console.log("Update Todos");
},
});
const handleButtonClick = () => {
mutation.mutate({ title: "title7", contents: "text7", isFinish: false });
};
return (
<div className="App">
{query.status === "success" &&
query.data.map((data) => {
return <div key={data.title}>{data.title}</div>;
})}
<button onClick={handleButtonClick}>버튼</button>
</div>
);
}
export default App;
useMutation을 사용해서 데이터를 업데이트할 수 있다.
이때, 데이터를 추가하고 추가한 값은 실시간으로 업데이트 되고 있지 않다.
이것을 useMutation은 get을 한번 더 하는 방법과 기존 값을 업데이트하는 방법 2개를 선택할 수 있다.
한번 더 요청하기
function App() {
const queryClient = useQueryClient();
const query = useQuery("todos", getTodos);
const mutation = useMutation(postTodos, {
onSuccess: () => {
queryClient.invalidateQueries("todos");
},
});
// ...
}
useQueryClient를 사용하면 client에 접근할 수 있게 되는데, invalidateQueries는
기존 데이터를 무효화하고 다시 가져올 수 있게 해주는 함수이다.
키값 배열을 넣을 수 있고, 혹은 키값 하나만 넣을 수 있다.
여기서 사용하는 키는 useQuery를 사용할 때 첫 번째 매개변수이다.
기존 값 업데이트하기
const mutation = useMutation(postTodos, {
onMutate: (data) => {
const previousValue = queryClient.getQueryData("todos");
console.log("previousValue", data);
queryClient.setQueryData("todos", (old) => {
console.log("old", old);
return [...old, data];
});
return previousValue;
},
onSuccess: (result, variables, context) => {
console.log("성공 메시지:", result);
console.log("변수", variables);
console.log("onMutate에서 넘어온 값", context);
},
});
서버에 다시 요청하는 방법은 간단하지만 결국 서버의 리소스를 사용하는 것이라 꺼려지게 된다.
그래서 기존 값을 업데이트 하는 경우가 있을 텐데, React Query에서는 그나마 간단하게 가능하다.
queryClient의 getQueryData는 요청했던 데이터를 가져올 수 있고,
setQueryData는 해당 키의 데이터를 수정할 수 있다.
그리고 data는 mutation을 통해 넘긴 데이터를 가져올 수 있다.
이것을 활용해서 서버에 직접 요청을 할 필요없이 데이터를 업데이트할 수 있다.
이번에는 정말 간단하게 React-Query에 대해서 알아봤다.
한번에 최대한 많은 내용을 담는 것도 좋겠지만 큰 틀을 이해하고 하나씩 공부하는 것도 좋다고 생각해서
이번에 부족했던 부분은 다시 공부해서 작성하겠다.
'React > 실험실' 카테고리의 다른 글
[React] React-Query - QueryClient stale & cacheTime (0) | 2022.11.22 |
---|---|
[React] React-Query - 로그인 유지하기 (0) | 2022.11.21 |
[React] 벨로퍼트와 함께하는 React Testing - 자바스크립트 테스팅 기초 (0) | 2022.11.17 |
[React] 벨로퍼트와 함께하는 React Testing - 개요 (0) | 2022.11.16 |
[React] Test Coverage (0) | 2022.11.15 |