요즘 공고를 보다보면 react-query
사용자가 우대한다는 글이 많다.
도대체 리액트쿼리가 뭐기에 이렇게 많이들 요구하는 걸까?
그래서 공부해본다!
리액트 애플리케이션에서 데이터를 가져오기 위한(fetching data) 라이브러리다.
useEffect
과 loading, error, data 결과값과 같이 컴포넌트의 상태를 유지하기 위햇 useState
를 사용했다.Client state
클라이언트가 자체적으로 생성한 상태로 사용자의 UI 업데이트가 동기화되기 때문에
서버에서 일어나는 일과는 관계가 없는 상태를 의미한다.
Server state
클라이언트에 표시하는 데 필요한 서버 데이터를 가져오거나 업데이트하기 위해 비동기 API가 필요하다.
그러나 캐시나 동일한 데이터에 대한 여러가지 중복된 요청을 제거하는것, 그리고 백그라운드에서 오래된 데이터들을 업데이트하고, 성능을 최적화할때 어려움을 겪을 수 있다.
따라서 React Query는 리액트 애플리케이션에서 서버의 응답에 대한 상태인 fetch
, caching
, update
와 같은 동작을 쉽게 다룰 수 있도록 한다.
그리고 Client state과 Server state를 분리해서 관리할 수 있도록 해주는 라이브러리다.
npx create-react-app [프로젝트 이름]
https://www.npmjs.com/package/json-server
에서 JSON Server를 설치해준다.
터미널에서 npm install json-server
이후 mock data를 만들기 위해 db.json
을 만든다.
그리고 안에 데이터를 만들어준다.
{
"products": [
{
"id": 1,
"name": "dress",
"price": "219000",
},
{
"id": 2,
"name": "shoes",
"price": "159000",
},
{
"id": 3,
"name": "pants",
"price": "109000",
}
]
}
package.json
파일에서 "scripts"
에 다음과 같이 추가해준다."server-json": "json-server --watch db.json --port 4000"
port 번호는 React port 번호와 다르게 설정하는데 보통 4000번이나 3001번으로 설정한다.
터미널에서 아래와같이 입력한 뒤
json-server --watch ./db.json --port 4000;
주소창에 http://localhost:4000/products를 입력하면 앞에서 입력한 mock 데이터들을 확인할 수 있다.
react-router
와 몇가지 라우터들을 설정한다.npm i react-router-dom
그리고 App.js에서 라우터를 설정한다.
(라우터에 관해서는 포스팅하지 않겠음.)
useEffect
와 useState
를 사용하여 기존 방식으로 데이터를 가져온다.fetch
또는 axios
를 사용하면 된다.axios
를 사용하기 때문에 axios
를 설치해준다.npm i axios
useEffect
와 useState
를 사용해서 데이터를 가져온다.// fetch data using react-query
import axios from "axios";
import { useState, useEffect } from "react";
const Products = () => {
const [isLoading, setIsLoading] = useState(true);
const [data, setData] = useState([]);
useEffect(() => {
axios.get("http://localhost:4000/products").then((res) => {
console.log(res);
setData(res.data);
setIsLoading(false);
});
}, []);
if (isLoading) {
return <h1>Loading....</h1>;
}
return (
<>
<h1>Products Page</h1>
{data.map((product) => {
return <div key={product.name}>{product.name}</div>;
})}
</>
);
};
export default Products;
json-server --watch ./db.json --port 4000;
그리고 다시 새로고침해보면 axios에러가 사라지고 해당 products의 이름이 출력되는걸 볼 수 있다.
react-query
를 설치해준다.npm install @tanstack/react-query
npm i @tanstack/react-query-devtools
설치가 끝났다면 이제 index.js에서 관련 속성을 적용해준다. Redux를 사용해봤다면 조금 비슷할 것 같다.
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
const queryClient = new QueryClient();
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
<QueryClientProvider client={queryClient}>
<App />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</BrowserRouter>
);
React Query Devtools을 설정하면 화면 아래에 이쁜 꽃 모양이 나온다.
클릭해보면 fetch한 API에 대한 정보를 받아올 수 있다.
useMutations
를 사용해야한다.useQueries
를 사용한다.useQuery는 기존의 방법과 조금 달라졌다.
useQuery
사용 방법const fetchData = async () => {
const res = await axios.get('');
return res.data;
}
const queryfetch = useQuery('query-key', fetchtempData, {
onSuccess: (data)=> {
console.log(data)
},
})
이전에는 query-key
가 문자열로 선언하면 되지만, 현재는 배열안에 query key
값을 넣어줘야한다.
useQuery
사용 방법const queryfetch = useQuery(['query-key'], fetchtempData, {
onSuccess: (data)=> {
console.log(data)
},
})
이를 통해 useQuery
를 사용해서 위에서 설정한 products를 불러오는 코드를 아래와 같이 작성해줬다.
// fetch data using react-query
import axios from "axios";
import { useQuery } from "@tanstack/react-query";
const Products = () => {
const fetchData = async () => {
const res = await axios.get("http://localhost:4000/products");
return res.data;
};
const { status, data, error } = useQuery(["products"], fetchData);
if (status === "loading") {
return <h1>Loading...</h1>;
}
if (status === "error") {
return <h1>Error: {error.message}</h1>;
}
return (
<>
<h1>React Query Products</h1>
{data &&
data.map((product) => {
return <div key={product.name}>{product.name}</div>;
})}
</>
);
};
export default Products;
status
: 쿼리의 상태로 isLoading, isSuccess와 같은 상태를 status로 처리할 수 있다. loading
, error
, success
3가지 종류가 있다.data
: fetch한 데이터로, 데이터가 fetch될 때까지 undefined
값을 가지게 된다.error
: 오류가 발생한 경우를 말하며, 오류가 없을 시 undefined
의 값을 가지게 된다.isFetching
: 쿼리가 fetching 중인 여부를 boolean
값으로 나타낸다.return문을 보면 &&
로 되어있지만 처음에는 data?.data로 코드를 만들었다.
{data?.data.map((product) => {
return <div key={product.name}>{product.name}</div>;
})}
그렇게해서 실행시켰더니 TypeError: Cannot read property 'map' of undefined 발생.
React는 렌더링이 커밋된 후 효과를 실행한다. 만약 data?.data.map
으로 반복 실행하게 된다면, 첫 번째턴은 undefined
로 아직 데이터가 들어와있지 않은 상태에서 렌더링이 실행되기 때문에 오류가 발생한다.
초기값 설정
첫번째 렌더링이 될 때 데이터가 들어와있지 않기 때문에 값이undefined
가 되면서 발생하는 에러이기 때문에 초기값을 설정해주면 된다.
const [data, setData] = useState([]);
위의 방법처럼 data의 초기값을 []
비어있는 배열로 설정해주면 해결할 수 있다.
&&
따라서 &&
로 변경시켜주면 된다.
{data &&
data.map((product) => {
return <div key={product.name}>{product.name}</div>;
})}
&&
는 true일 경우에만 그 뒤의 코드가 실행되고, 만약 false일 경우에는 뒤의 요소가 출력되지 않기 때문에 무시하고 건너뛴다.
따라서 오류가 발생하지 않게 된다.