React Query (TanStack Query)
https://tanstack.com/query/v3/
(react query가 아니고 tanstack query...? 라는 의문은 일단 접어두기)
쉽게 말하자면!
React에서 API 요청과 상태 관리를 쉽게 해주는 도구!
React Application에서 데이터 Fetching, Caching, 동기화, 서버의 상태를 업데이트하는 등의 작업을 쉽게 할 수 있도록 도와주는 라이브러리이다.
우리에게 친숙한 Hook을 사용하여 React Component 내부에서 자연스럽게 서버(또는 비동기적인 요청이 필요한 Source)의 데이터를 사용할 수 있는 방법을 제안해 준다.
React Query가 도입되기 까지는 ...
태초 API 통신과 비동기 데이터 관리에는 Redux가 주로 사용되는 있는 트렌드가 있었다.
일반적인 state를 활용하는 경우
1) 비동기 데이터를 State로 보관하게 될 경우 다수의 Component의 Lifecycle에 따라 비동기 데이터가 관리되기 때문에 캐싱 등 최적화를 수행하기 어렵다.
2) 다수의 Component에서 동일한 API를 호출하거나, 특정 API 응답이 다른 API에 영향을 미치는 경우 등에서 복잡한 사용자 시나리오 대응에 어려움을 겪는다.
위 두 가지의 문제가 있었기에, 전역 상태 관리 라이브러리인 Redux가 해당 용도로 많이 사용됐다.
하지만 Redux는 API 통신과 비동기 데이터 관리를 위한 라이브러리는 아니기에
실제로 관리를 위해서는 관련된 코드를 하나부터 열까지 개발자가 구현해야 했다. 덕분에 관리 방법도 각기각색.
API 통신 중 Loading 상태에 대해서도 boolean으로 나누기 vs 세분화하기로 나뉘는 일도 있었다는 카카오페이의 사례처럼 규격화된 방식이 없어 통일성이 없다는 단점이 있었다.
그 외에도 Redux 사용의 문제점들이 개발자들 눈에 띄었는데
이 부담을 덜어줄 수 있는 React-query가 등장! 했다고 한다.
TanStack Query? React Query?
'React Query에 대한 내용을 알아보고 싶은데 검색하면 왜 자꾸 TanStack Query가 나올까?'
'공식 홈페이지는 왜 또 TanStack Query일까? 그게 뭔데?'
굉장히 의문이 있었는데 다들 별 다른 언급이 없는 경우가 많아 직접 찾아봤다.
TanStack Query는 React Query의 업그레이드 버전이라고 말할 수 있는데
버전 업그레이드와 함께 React 외 다른 프레임워크에서도 사용 가능하도록 확장한 형태라고 한다.
- React : @tanstack/react-query (React Query v4 이상)
- Svelte : @tanstack/svelte-query
- Solid : @tanstack/solid-query
- Vue : @tanstack/vue-query
때문에 react-query를 설치하면 버전 3, @tanstack/react-query를 설치하면 현시점 기준 버전 5이다.
버전 3에서 4로 마이그레이션 하는 방법에 대해서도 공식 문서에 안내되어 있다.
https://tanstack.com/query/v4/docs/react/guides/migrating-to-react-query-4
프로젝트에 React Query 설치하기
프로젝트에 설치하는 명령어는 아래와 같다.
$ npm install react-query
# v4 이상 사용 원할 시 @tanstack/react-query 설치
$ npm install @tanstack/react-query
최신 환경에서 프로젝트를 진행하는 경우 사용 가능한 버전이 달라질 수 있으니 유의하자.
예를 들어 아래처럼 react v18 + typescript 환경에서는 버전 4 이상을 지원하기 때문에 TanStack Query로 설치해야만 한다.
아래부터는 react v18 + typescript + @tanstack/react-query v4 환경을 기준으로 설명을 이어가겠다!
# 포스팅 기준 최신 버전은 5. 버전 4를 원할 경우 아래처럼 버전을 지정해 설치해야 함
$ npm install @tanstack/react-query@4
React에서 사용하기 위한 준비 과정
App 전체에서 react-query를 사용할 수 있도록 하기 위해서
최상위인 index에서 QueryClientProvider로 내 App을 감싸주는 과정이 필요하다.
import React from "react";
import ReactDOM from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import App from "./App";
// client 생성
const queryClient = new QueryClient();
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<React.StrictMode>
{/* App에 client 전달 */}
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</React.StrictMode>
);
새로운 QueryClient 인스턴스를 생성하여 이를 이용해 각종 상태를 저장하고, 부가 기능을 제공한다.
그리고 이 새로운 QueryClinet 인스턴스를 내 App 전체에서 접근 가능하도록 해주는 도구가 QueryClientProvider이다.
QueryClientProvider를 통해 App에 전달된다.
React Query Devtools
React Query는 캐시로 저장되는 데이터에 대한 정보를 육안으로 보여주는 개발자 도구 Devtools를 제공한다.
v3에서는 react-query 설치와 함께 제공이 됐지만, v4부터는 Devtools를 별도로 설치해줘야 한다.
$ npm install @tanstack/react-query-devtools@4
devtools를 사용하기 위해서도 셋팅이 필요하다.
QueryClientProvider 아래에 Devtools를 적용해 주자.
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
/* ... */
root.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools initialIsOpen={true} /> {/* 기본적으로 펼쳐져 있도록 설정 */}
<App />
</QueryClientProvider>
</React.StrictMode>
);
React Query 일단 적용하면서 맛보기
코인 정보를 알려주는 프로젝트에서 직접 React Query를 적용해 보며 알아가 보자.
아래는 coinpaprika API를 활용하여 코인의 이름들을 리스트 형태로 나열하는 간단한 React Application이다.
fetch 함수를 통해서 비동기 통신을 진행하고 있고
해당 API에서 제공하는 JSON 데이터를 coins state에 저장한다.
비동기 통신을 진행하고 있는 중에는 loading state를 기본값인 true로 지정해 로딩 중임을 표시하고,
coins state에 데이터가 저장되면 false로 값을 변경해 화면에 렌더링 한다.
import React from "react";
import { useEffect, useState } from "react";
function App() {
const [loading, setLoading] = useState(true); // 로딩
const [coins, setCoins] = useState<{ name: string }[]>([]); // 코인 데이터
useEffect(() => {
(async () => {
const json = await (
await fetch(`https://api.coinpaprika.com/v1/coins`)
).json(); // 코인 데이터 비동기 통신
setCoins(json.slice(0, 100));
setLoading(false);
})();
}, []); // 새로고침 시 데이터를 가져오고 데이터가 준비되면 state에 저장하고 로딩은 false
return (
<>
<h1>리액트 쿼리 실습중</h1>
{loading ? (
<p>Loading...</p>
) : (
<ul>
{coins.map((coin) => (
<li>{coin.name}</li>
))}
</ul>
)}
</>
);
}
export default App;
위 코드에 React Query를 적용한다면 어떻게 될까?
우리가 만든, 데이터가 준비되면 데이터를 state에 집어넣어 수정해 주는 로직을 축약할 수 있고
한 번 불러왔던 데이터를 캐시로 저장해서 다시 불러오지 않도록 사용성 개선을 기대할 수 있다.
일단 한 번 적용해 보자!
useQuery라는 Hook을 활용해서
위에서 사용한 두 state loading과 coins를 각각 isLoading와 data로 바꾸고
useEffect와 setState 과정을 useQuery를 이용하는 한 줄로 단 번에 해결할 수 있다.
import React from "react";
import { useQuery } from "@tanstack/react-query"; // useQuery import
async function fetchCoins() { // fetcher 함수 생성
return fetch(`https://api.coinpaprika.com/v1/coins`).then((response) =>
response.json()
);
}
function App() {
const { isLoading, data } = useQuery<{ name: string }[]>(
["allCoins"], // 고유한 querykey
fetchCoins // fetcher 함수
);
return (
<>
<h1>리액트 쿼리 실습중</h1>
{isLoading ? (
<p>Loading...</p>
) : (
<ul>
{data?.slice(0, 100).map((coin) => ( // data 렌더링
<li>{coin.name}</li>
))}
</ul>
)}
</>
);
}
export default App;
실행해 보면 이전과 동일하게 데이터가 무사히 fetch 되어 렌더링 됨을 확인할 수 있다.
위에서 사용된 useQuery Hook에 대해서 더 알아보자.
useQuery
https://tanstack.com/query/v5/docs/react/reference/useQuery
useQuery는 React Query에서 Data fetching을 위해 제공하는 대표적인 Hook이다.
기본적으로 GET에는 useQuery,
PUT, UPDATE, DELETE에는 useMutation이 사용된다.
const { data } = useQuery({
queryKey: ["고유한 이름"],
queryFn: fetcher 함수
});
// v4에서는 축약 가능. v5에서는 위처럼 객체로 작성해주어야 한다
const { data } = useQuery(["고유한 이름"], fetcher 함수);
첫 번째 파라미터 queryKey는 data를 식별하도록 하는 고유한 key 값을 받는다.
데이터를 cache 할 때 개발자 도구에서 이 key값이 노출된다.
두 번째 파라미터 queryFn에는 실제 호출하고자 하는 비동기 함수(fetcher 함수)가 들어간다.
이 함수는 필히 Promise를 반환하는 형태여야 한다.
useQuery가 반환하는 객체는 여러 가지가 있는데 그중 자주 사용하는 것들은 아래와 같다.
- isLoading / isFetching / isSuccess 등 : 현재 Query의 상태
- data : 응답에 성공한 데이터
- error : 응답 중 에러가 발생했을 때 반환되는 객체
위에서 사용한 예시를 풀어서 작성해 보면 아래와 같다.
const { isLoading, data } = useQuery<{ name: string }[]>({
queryKey: ["allCoins"],
queryFn: () =>
fetch(`https://api.coinpaprika.com/v1/coins`).then((response) =>
response.json()
),
});
왜 isLoading과 data를 반환값으로 사용했는지,
fetcher 함수를 별도로 분리하고 축약하기 이전의 형태는 어떤지,
비교하면서 이해해 보면 동작을 이해할 수 있다.
가볍게만 React Query를 사용해 아직까지 useMutation을 이용해보지 못해 관련 내용 포스팅은 잠시 미루면서 마무리!
이 부분은 프로젝트에서 더 사용해 본 뒤 추가로 작성하기로...✏
useQuery만 이용해봤지만 왜 사람들이 React Query를 이용하는지에 대한 체감은 크게 됐다!
데이터 fetching의 상태와 데이터를 한 번에 관리할 수 있도록 도와준다는 점이
간결한 코드로 보여져 너무 와닿고 사용하기 편했다.
그리고 이렇게 블로그로 다시 한 번 정리해보면서 차이가 실감됐다.
좋은 도구는 손에 제대로 익혀서 사용해야지! 🤓
더 알아가보고 싶어지는 라이브러리인 것 같다
참고
카카오페이 프론트엔드 개발자들이 React Query를 선택한 이유
'REACT' 카테고리의 다른 글
[REACT] 리액트 앱 성능 개선! React.lazy를 이용한 코드 스플리팅 (0) | 2023.12.19 |
---|---|
[JAVASCRIPT][REACT] API 데이터 가져오기 (GET) (1) | 2023.11.26 |
[REACT] React Hooks 탐구하기 (0) | 2023.10.15 |
[REACT] createGlobalStyle 안에서 @import 사용 경고 해결하기 (0) | 2023.10.01 |
[REACT] TDD 와 리액트 테스트 도구 (0) | 2023.09.15 |