1. React Query란
1) Client State vs. Server State
- Client State : 웹 브라우저 세션과 관련된 정보
- ex. 유저의 언어, 테마 등
- 서버에서 일어나는 일과는 아무 관련이 없는 state들
- 단순히 사용자의 상태를 tracking하는 것
- Server State : 서버에 저장,그러나 클라이언트에게 표시하는데 필요한 데이터들
- ex. 블로그 포스트 data from DB
2) React Query의 역할
- 서버 데이터 캐시를 관리한다.
- React 코드에 서버데이터가 필요할 때 fetch나 axios를 사용해 서버로 바로 이동하지 않고, React Query 캐시를 요청
- 즉, React Query 클라이언트를 어떻게 구성했느냐에 따라 해당 캐시의 데이터를 유지 관리 하는 것
- 서버로부터 새로운 데이터를 언제 캐시에 업데이트해야하는지 지시함
- 명령형(imperatively) : 데이터 무효화
- 선언형(declaratively) : 어떻게 (ex. window가 focus이 됐을 때) 및 언제 re-fetch를 트리거할지 설정
- 서버 상태 관리에 도움이 되는 많은 도구 제공
- Loading / Error states
- Pagination(페이지 매김)과 무한 스크롤이 필요한 경우 데이터를 조각으로 가져올 수 있는 도구도 제공
- Prefetch : 데이터를 미리 fetch해서 캐시에 넣으면, 사용자에게 데이터가 필요할 때 앱이 캐시에서 해당 데이터를 알아서 가져옴 → 사용자는 서버에 연결할 때까지 기다릴 필요가 없게 됨
- Mutations : 데이터의 변이(mutation)나 업데이트를 관리
- 중복요청 제거
- Query는 key로 식별되기 때문에 React Query는 요청을 관리할 수 있음
- 페이지를 로드하고 해당 페이지의 여러 구성요소가 동일한 데이터를 요청하는 경우, 중복 요청을 제거하여 쿼리를 한 번에 보내도록 처리
- Retry on error : 서버에서 오류가 발생하는 경우에 대한 retry 관리
- Callbacks : 쿼리가 성공하거나 오류 났을 때를 구별하여 조치하도록 callback을 전달
2. Getting Started
1) 기본 세팅 및 개념
yarn add react-query
// App.jsx
import React from "react";
import Router from "./shared/Router";
import { QueryClient, QueryClientProvider } from "react-query";
const queryClient = new QueryClient();
const App = () => {
return (
<QueryClientProvider client={queryClient}>
<Router />
</QueryClientProvider>
);
};
export default App;
(1) 최상위 컴포넌트에서 할 일 (index.js 또는 App.jsx)
- Query client 생성
- const queryClient = new QueryClient();
- 쿼리와 서버의 데이터 캐시를 관리하는 클라이언트
- 클라이언트가 있어야 provider를 추가할 수 있음
- 그러면 provider가 client를 prop으로 사용하게 되면서 클라이언트가 갖고 있는 캐시와 모든 기본 옵션을 provider의 자녀 컴포넌트들(=Router 이하 모든 컴포넌트들)도 사용할 수 있게 됨
- QueryProvider로 래핑
- 자녀 컴포넌트에 캐시와 클라이언트 구성을 적용
- 이에 대한 값으로 사용 될 쿼리 클라이언트 필요
(2) 데이터를 불러올 하위 컴포넌트에서 할 일
- useQuery
- 서버에서 데이터를 fetch할 때 사용하는 hook
3. isLoading과 isError 처리하기
1) isLoading
import { useState } from "react";
import { useQuery } from "react-query";
import { PostDetail } from "./PostDetail";
async function fetchPosts() {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts?_limit=10&_page=0"
);
return response.json();
}
export function Posts() {
const [currentPage, setCurrentPage] = useState(0);
const [selectedPost, setSelectedPost] = useState(null);
// replace with useQuery
// const data = [하드코딩하는 배열 형태는 naver...XXX];
const { data, isError, isLoading } = useQuery("posts", fetchPosts);
if (isLoading) return <h3>Loading...</h3>;
return (
<>
<ul>
{data.map((post) => (
...
</>
);
}
(1) isLoading vs. isFetching
- isFetching
- 비동기 쿼리 함수가 아직 해결되지 않았음을 의미
- 아직 fetching을 완료하지 않았다는 의미
- 쿼리가 Axios 호출 또는 GraphQL 호출일 수도 있음
- isLoading
- 아직 데이터를 가져오는 중이면 표시할 캐시 데이터조차 없다는 뜻
- isFetching의 하위 집합
- 쿼리 함수가 아직 해결되지 않은 것 + 캐시 된 데이터도 없음
- 이 쿼리를 만든 적이 없다는 뜻
- 나중에 Pagination을 진행할 때 캐시 데이터가 있을 때와 없을 때를 구분해야함!
💡 국밥으로 비유..!?
간단하게 생각하자면 isLoading은 어떤 데이터를 처음 가져올 때 사용하면 되고,
isFetching은 데이터를 다시 가져와야 할 때 사용하면 된다.
그 두개가 달라야 하나? 싶을텐데, 다시 한번 국밥을 생각해보자.
국밥을 처음 가져올때는 숟가락, 젓가락, 물도 가져다줘야 하지만, (isLoading)
다시 한번 주문하면 국밥만 가져다 준다. (isFetching)
이처럼 두 경우에 있어서 작업의 차이가 필요할 때 사용 한다고 보면 되겠다.
출처 : https://velog.io/@himprover/React-query-에서-isLoading이랑-isFetching은-뭐가-다르지
2) isError
import { useState } from "react";
import { useQuery } from "react-query";
import { PostDetail } from "./PostDetail";
async function fetchPosts() {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts?_limit=10&_page=0"
);
return response.json();
}
export function Posts() {
...
// replace with useQuery
const { data, isError, error, isLoading } = useQuery("posts", fetchPosts);
if (isLoading) return <h3>Loading...</h3>;
if (isError) return (
<>
<h3>Oops, something went</h3>
<p>{error.toString()}</p>
</>
)
return (
<>
<ul>
{data.map((post) => (
...
</>
);
}
4. staleTime vs. cacheTime
1) Stale Data
- 오래된 식빵🍞과 비슷
- data refetching은 만료된 데이터에서만 trigger된다
- 데이터 리페칭 실행에는 만료된 데이터 외에도 여러 트리거가 있음
- ex. 컴포넌트 리마운트, 윈도우 refocus
- staleTime은 데이터를 허용하는 최대 나이라고 생각하기
- 데이터가 만료됐다고 판단하기 전까지 허용하는 시간을 뜻함
- 설정 방법
// Posts.jsx
// 쿼리 데이터를 사용하는 앱에서 useQuery의 세 번째 인자에 설정해주면 된다
...
const { data, isError, error, isLoading } = useQuery(
"posts",
fetchPosts,
{ staleTime: 2000 },
);
❓ 왜 stale time의 기본 설정값은 0일까?
react query 개발자 says..
- ’업데이트가 왜 안되죠?’ 보다 ‘데이터를 어떻게 늘 최신 상태로 유지하나요?’가 훨씬 나은 질문이다.
- staletime이 0이면 데이터는 항상 만료상태이므로 서버에서 다시 가져와야 한다고 가정하게 됨
→ 그러면 실수로 클라이언트에게 만료된 데이터를 제공할 가능성이 훨씬 줄어듦!
2) cacheTime과 staleTime
- staleTime은 re-fetching 할 때의 고려사항이다.
- cache는 나중에 다시 필요할 수도 있는 데이터를 위한 것
- 쿼리에 대한 활성 useQuery가 없는 경우 해당 쿼리 데이터는 cold storage로 이동
- 설정한 cacheTime(default값은 5분)이 지나면 캐시 데이터는 만료됨
- cacheTime이 관찰하는 시간의 양은 쿼리에 대한 useQuery가 활성화된 후 경과한 시간 → 페이지에 표시되는 컴포넌트가 특정 쿼리에 대해 useQuery를 사용한 시간
- cache가 만료되면 만료된 cache data는 가비지 컬렉션이 실행되고, 클라이언트는 데이터를 더이상 사용할 수 없게 됨
- cache는 화면이 fetching 중일 때, 화면에 보여지는 backup data(최신 데이터X)의 역할
- 데이터 fetching을 중단하지 않으므로 서버의 최신 데이터로 새로고침 가능
- 하지만 fetching 중일 때 계속해서 아무 데이터도 없는 빈 데이터만 보는 경우가 생길 수 있음
- 새로운 데이터를 fetching하는 동안 약간 오래된 데이터(=캐시 데이터)를 보는 게 더 나은 선택
- 예외) 만료된 데이터가 위험할 수 있는 앱의 경우 cacheTime을 0으로 설정하면 된다.
'✍️ What I Learned > TIL' 카테고리의 다른 글
[TIL] TypeScript + React 개발환경 세팅 (0) | 2023.07.27 |
---|---|
[TIL] TypeScript 언어의 탄생 배경 기본적인 특징 (0) | 2023.07.25 |
[TIL] 클린 코드 - Custom Hook, Container - Presentational 패턴, 파일/폴더 구조(패턴) (0) | 2023.07.05 |
[WIL] 팀 프로젝트 회고 (React 뉴스피드 프로젝트) (0) | 2023.07.04 |
[TIL] Git / GitHub에 대해 정리 (0) | 2023.06.30 |