React Query 공식 사이트)
아래 블로그에 대한 내용을 아래에 정리)
React- query 를 사용하는 이유
1. 데이터 캐싱
데이터를 주고 받은 결과를 캐시로 저장한다. 동일한 요청시 새로운 요청을 보내지 않고, 저장되어 있는 데이터를 사용한다.(애플리케이션 성능 향상, 네트워크 사용량 감소)
캐싱: 특정 데이터의 복사본을 저장하여 이후 동일한 데이터의 재접근 속도를 높이는 것
2. 로딩 및 오류 상태 관리
데이터 로딩시, 오류 발생시 사용자에게 알려준다.
3. 프리패칭
프리패칭: 다음 페이지 데이터를 미리 가져와서, 다음 페이지로 넘어갈 때 데이터를 미리 가져왔기 때문에 매끄럽게 처리가 됨.
ex.게시판
4. 가비지 컬렉션을 이용하여 자동으로 메모리를 관리
가비지 컬렉션 : 메모리 관리 방법 중 하나로, 프로그래머가 동적으로 할당한 메모리 영역 중 더 이상 쓰이지 않는 영역을 자동으로 찾아내어 해제하는 기능
[ 사용하기 ]
설치
npm install react-query
세팅
1. <QueryClientProvider> </QueryClientProvider>로 감싼다.
- App.tsx 또는 Next 경우 _app.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; //추가(react-query말고 @tanstack/react-query로 불러와야함)
const queryClient = new QueryClient();//추가
function App() {
return (
<QueryClientProvider client={queryClient}> {/*추가*/}
<div className="App">
(...)
</div>
</QueryClientProvider>
);
}
export default App;
2. React-query 개발자 도구도 추가해보기
리액트쿼리 개발자 도구를 사용하는 이유
- 쿼리 키로 쿼리를 표시해준다.
- 개발 중인 모든 쿼리의 상태를 표시해준다.
이것에 대한 상태란 활성, 비활성, 만료(stale) 등 모든 쿼리의 상태를 알려준다.- 마지막으로 업데이트 된 타임 스탬프도 알려준다.
- 데이터 탐색기도 있다.
- 쿼리를 볼 수 있는 쿼리 탐색기도 있다.
버전 4에서 개발자 도구 설치할 때 명령어 (버전3은 설치할 필요X)
npm i @tanstack/react-query-devtools
or
yarn add @tanstack/react-query-devtools
- App.tsx 또는 Next 경우 _app.tsx
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; //추가
return (
<>
<QueryClientProvider client={queryClient}>
(...)
<ReactQueryDevtools /> {/*추가*/}
</QueryClientProvider>
</>
);
개발자 도구까지 세팅이 완료되면 브라우저 하단에 아이콘이 생기는데 클릭하면 아래와 같은 화면이 나온다. 그리고 devtools는 기본적으로 개발환경에서만 제공되므로 프로덕션 빌드 중에 제외하는 것에 대해 걱정할 필요 없다.
사용
react-query 사용 인터페이스
const { data } = useQuery("쿼리명", 쿼리함수 = 데이터를 가져오는 함수, 옵션);
- 첫 번째 인수 : 쿼리명을 선언 -> 고유한 키값이라고 생각 (필수)
- 두 번째 인수 : 데이터 함수를 선언 (필수)
- 세 번째 인수 : (옵션) 이 구간에는 staleTime, cacheTime를 사용 가능
staleTime 란?
const { data, isError, isLoading } = useQuery("posts", fetchPosts, {staleTime: 3000})
if(isLoading) return <h3>로딩중...</h3>
- 세번째 인수에 위와 같이 옵션을 주면 2초동안 fresh였다가 stale로 변한다.
- 데이터가 만료되지 않으면 리페칭은 실행되지 않는다. 데이터가 만료된 경우에만 실행된다.
- staleTime의 기본값은 왜 0ms이냐면, 데이터는 항상 만료 상태이므로 서버에서 다시 가져와야 한다고 가정한다는 뜻이다.
- staleTime 윈도우가 다시 포커스될 때 같은 특정 트리거에서 쿼리 데이터를 다시 가져올지 결정한다.
👉 트리거란? 데이터베이스가 미리 정해놓은 조건을 만족하거나 어떤 동작이 수행되면 자동적으로 수행되는 동작이다.
적용됐는지를 확인하기 위해서는 개발자도구에서 2초 뒤에 데이터가 패칭되는 것을 확인해볼 수 있다.
cacheTime ?
- 캐시는 나중에 다시 필요할 수도 있는 데이터용이다.
- cacheTime은 데이터가 비활성화된 이후 남아 있는 시간을 말한다.
특정 쿼리에 대한 활성 useQuery가 없는 경우, 해당 데이터는 콜드 스토리지로 이동한다.
👉 콜드 스토리지란? 남겨진 데이터를 말함 - 구성된 cacheTime이 지나면 캐시의 데이터가 만료되며 유효 시간의 기본값은 5분이다.
- 캐시가 만료되면 가비지 컬렉션이 실행되고 클라이언트는 데이터를 사용할 수 없다.
데이터가 캐시에 있는 동안에는 페칭할 때 사용될 수 있다. - 서버의 최신 데이터로 새로 고침이 가능하다.
1. fetching : 요청 상태인 쿼리. 대기중
2. fresh : 새롭게 추가된 쿼리 인스턴스이며, 만료되지 않은 쿼리. 컴포넌트의 mount, update 시에 데이터를 재요청하지 않음(항상 캐시된 데이터를 가져옴)
3. stale : 데이터 패칭이 완료되어 만료된 쿼리. stale 상태의 같은 쿼리를 useQuery로 재호출하여 컴포넌트 마운트를 한다면 캐싱된 데이터가 반환됨.
4. inactive : 비활성 쿼리로써 사용하지 않음. 5분 뒤에 가비지 콜렉터가 캐시에서 제거함.
5. delete : 가비지 콜렉터에 의하여 캐시에서 제거된 쿼리.
const { data, isError, isLoading, error } = useQuery("posts", fetchPosts, {staleTime: 2000})
if(isLoading) return <h3>로딩중...</h3>
if(isError) return <h3>잘못된 데이터입니다. {error.toString()}</h3>
useQuery에서 사용하고 싶은 기능들을 구조 분해 할당하여 사용하면 된다.
- data : 데이터를 가져옴
- isError : 데이터를 가져올 때 오류가 있는지 여부를 알려준다.
- isLoading : 데이터를 가져오는 중
- isFetching : 비동기 쿼리가 해결되지 않았음을 의미
- error : 에러 메시지를 보여준다.
이것 외에도 여러 기능들이 있으니 공식문서 확인하면 된다.
🔑 쿼리키
어떠한 트리거가 있어야만 데이터를 다시 가져오게 된다.
그 어떠한 트리거란 ? 👇
- 컴포넌트를 다시 마운트하거나 윈도우를 다시 포커스할 때
- useQuery에서 반환되어 수동으로 리페칭을 실행할 때
- 지정된 간격으로 리페칭을 자동 실행할 때
- 변이를 생성한 뒤 쿼리를 무효화 할 때
- 클라이언트의 데이터가 서버의 데이터와 불일치할 때 리페칭 할 경우
- 쿼리는 게시물 아이디를 포함하기 때문에 쿼리별로 캐시를 남길 수 있다.
- 각 쿼리에 해당하는 캐시를 가지게 된다.
- 각 게시물에 대한 쿼리에 라벨을 설정해주면 된다. 바로 쿼리 키에 문자열 대신 배열을 전달하면 가능하다.
const { data, isLoading, isError, error } = useQuery(
["comments", post.id], () => fetchComments(post.id)
);
이처럼 의존성 배열로 취급해서 사용하면 된다. comments를 쿼리키로 지정한 것이다.
그리고 post.id가 업데이트 되면 리액트쿼리가 새 쿼리를 만들고 staleTime과 cacheTime을 갖게 된다.
1. Prefetching
- 데이터를 미리 가져와 캐시에 넣는다.
- 기본값으로 만료 (stale) 상태를 나타낸다.
- useQueryClient 를 사용한다.
import { useQuery, 🌟useQueryClient } from "react-query";
const queryClient = useQueryClient();
useEffect(() => {
// 10페이지에 있다면 미리 데이터를 가져온 필요가 없다.
if (currentPage < maxPostPage) {
const nextPage = currentPage + 1;
queryClient.prefetchQuery(["posts", nextPage], () =>
fetchPosts(nextPage)
);
}
}, [currentPage, queryClient]);
const { data, isError, isLoading, error } = useQuery(
// 배열에 담긴 첫 번째 요소를 쿼리키라고 한다.
["posts", currentPage],
// 이 배열이 바뀌면 함수도 바뀌기 때문에 데이터가 바뀔 수밖에 없다.
() => fetchPosts(currentPage), // -> 함수의 파라미터값을 currentPage로 함
{
staleTime: 2000,
// 쿼리키가 바껴도 지난 데이터를 유지해서 이전 페이지로 돌아갔을 때 캐시에 해당 데이터가 있도록 해준다.
keepPreviousData: true,
}
);
Dev tools를 확인해보면 "posts", 9가 "posts", 8보다 더 미리 패칭 된 것을 보아 미리 데이터를 fetch 된 것을 확인할 수 있다. 이것이 바로 프리패칭이라고 한다.
2. Mutations (변이)
- 변이는 서버에 데이터를 업데이트 하도록 서버에 네트워크 호출을 실시한다.
- 데이터를 추가, 삭제, 업데이트에 해당된다.
- 서버를 실제로 업데이트 하지는 않는다 ! 변이를 생성하는 서버 호출을 전송하게 된다.
- 값이 false일 경우 롤백 진행
- 업데이트 된 해당 데이터로 리액트쿼리 캐시를 업데이트 하는 것이다.
- 관련 쿼리를 무효화 할 수 있다. -> 서버에서 리페치를 개시하여 클라이언트에 있는 데이터를 서버의 데이터와 최신 상태로 유지한다.
적용 예시
import { useQuery, 👉 useMutation, useQueryClient } from "react-query";
const deleteMutation = useMutation((postId) => deletePost(postId));
const updateMutation = useMutation((postId) => updatePost(postId));
<button onClick={() => deleteMutation.mutate(post.id)}>삭제</button>
<button onClick={() => updateMutation.mutate(post.id)}>업데이트</button>
또 다른 방법으로는 아래와 같은 방법으로 적용해볼 수 있다.
import { useMutation } from '@tanstack/react-query';
// 댓글 삭제
const { mutate } = useMutation((id: any) => deleteComment(id));
const handleCommentDelete = () => {
confirm('댓글을 삭제하시겠습니까?');
mutate(userId, {
// 데이터 요청에 성공했을 경우
onSuccess: () => {
alert('삭제 완료되었습니다.');
},
});
};
3. infinite scroll 무한스크롤
- 사용자가 스크롤 할 때마다 새로운 데이터를 가져온다. (모든 데이터를 한 번에 가져오는 것보다 훨씬 효율적)
ex) 인스타그램, 페이스북, 트위터같은 플랫폼을 예시로 들 수 있다. - useInfiniteQuery 라는 훅을 사용한다.
- 업데이트된 상태가 쿼리 키를 업데이트하고 쿼리 키가 데이터를 업데이트 한다.
적용 예시
- useInfiniteQuery 훅 사용 인터페이스
useInfiniteQuery(['쿼리명'], ({ pageParam = defaultUrl}) => 데이터함수(pageParam))
- useInfiniteQuery훅의 인터페이스에서 data 객체는 두 개의 프로퍼티를 가지고 있다.
const { ⭐️ data, fetchNextPage, hasNextPage, isLoading, isError } = useInfiniteQuery(
['page'],
({ pageParam = 0 }) => getFeedPost({ page: pageParam, content: searchText, view: 5 }),
{
getNextPageParam: (lastPage, allPosts) => {
return lastPage.page !== allPosts[0].totalPage ? lastPage.page + 1 : undefined;
},
},
);
(1) pages: 데이터에 해당
(2) pageParams: 이것은 각 페이지의 쿼리 함수에 전달되는 매개변수이다.
- 모든 쿼리는 페이지 배열에 고유한 요소를 가지고 있고 그 요소는 해당 쿼리에 대한 데이터에 해당한다.
ex) 피드 게시물 api 데이터(파라미터 값에 page랑 totalPage와 view가 있다.)
- page : 페이지 수를 의미
- totalPage : 총 페이지 수를 의미
- view : 한 페이지에 몇개의 게시물을 보여줄 지
그래서 해당 데이터를 불러오기 위한 패칭 함수를 생성하고 page 파라미터를 리액트 쿼리에 pageParams로 적용하고, 리액트 쿼리에서 제공해주는 함수들로 제어하여 스크롤 위치가 페이지 하단에 맞닿았을 때마다 page가 +1이 되면서 다음 페이지의 데이터들이 스크롤 할 때마다 새롭게 불러와져 보여지는 것을 확인할 수 있다.
그리고 스크롤 위치가 페이지 하단에 맞닿았을 때마다 직접 코드를 구현해도 좋지만 'react-infinite-scroller' 라이브러리를 설치하여 정말 간편하게 구현가능하다. (이 라이브러리에 쓰로틀링도 내재되어 있다.)
쓰로틀링이란? 마지막 함수가 호출된 후 일정 시간이 지나기 전에 다시 호출되지 않도록 하는 것.
쓰로틀링은 스크롤을 올리거나 내릴 때 보통 사용.
// 설치 명령어
npm install react-infinite-scroller
해당 라이브러리 사용 설명서 참고
https://www.npmjs.com/package/react-infinite-scroller
현재 react-query에서 tanstack/react-query로 변경됨에 따라 아래와 같이 코드 쓰는 방식이 변경되었다.
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
function Home() {
const { isLoading, error, data, isFetching } = useQuery({
queryKey: ['todos'],
queryFn: async () => {
/* fetch의 경우:
const response = await fetch(
'https://api.github.com/repos/tannerlinsley/react-query'
);
const data = await response.json();
*/
/*axios 사용*/
const response = await axios.get(
'https://api.github.com/repos/tannerlinsley/react-query'
);
const data: any = response;
console.log(data);
return data;
},
});
if (isLoading) return <div>로딩중</div>;
if (error) return <div>에러남</div>;
return (
<div>
<div>{data?.data.name}</div>
<div>{isFetching ? 'Updating...' : ''}</div>
</div>
);
}
export default Home;
아래 사이트로 참고하여 코드 작성)
참고사이트)
'React' 카테고리의 다른 글
[React] React Quill 에디터 라이브러리 사용 1(feat.react-quill-new) (0) | 2024.10.15 |
---|---|
[React] npm 대신 yarn 사용하기 (0) | 2024.10.14 |
[React] React의 렌더링 방식 (0) | 2023.12.13 |
[React] React의 state (우아한테크 youtube정리) (0) | 2023.12.13 |
[React] 쿠키와 웹 스토리지란? (2) | 2023.12.05 |