일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- NestedFor
- 추상메서드
- 컬렉션 타입
- 집합_SET
- 생성자오버로드
- 참조형변수
- 정수형타입
- 예외처리
- abstract
- exception
- 객체 비교
- 어윈 사용법
- 예외미루기
- 메소드오버로딩
- 자동차수리시스템
- 다형성
- Java
- oracle
- 자바
- 환경설정
- 한국건설관리시스템
- GRANT VIEW
- 사용자예외클래스생성
- 인터페이스
- 오라클
- EnhancedFor
- 대덕인재개발원
- cursor문
- 컬렉션프레임워크
- 제네릭
- Today
- Total
거니의 velog
(50) 리액트 쿼리와 리코일 2 본문
2. 상품목록 페이지
* 상품목록에서 데이터를 조회하는 기능은 동일하지만 페이지의 번호나 검색 조건 등이 변경될 수 있으므로 useQuery를 이용할 때 주의할 점이 생긴다.
(1) 중복적인 쿼리 키(key)
* 기존 components/products/ListComponent.js 에 useQuery를 이용해서 page와 size까지 쿼리 키(key)가 되도록 수정한다.
import React from "react";
import { getList } from "../../api/productsApi";
import useCustomMove from "../../hooks/useCustomMove";
import FetchingModal from "../common/FetchingModal";
import PageComponent from "../common/PageComponent";
import { API_SERVER_HOST } from "../../api/todoApi";
import useCustomLogin from "../../hooks/useCustomLogin";
import { useQuery } from "@tanstack/react-query";
const host = API_SERVER_HOST;
const initState = {
(...)
};
const ListComponent = () => {
const { page, size, moveToList, moveToRead } = useCustomMove();
const { moveToLoginReturn } = useCustomLogin();
const { isFetching, data, error, isError } = useQuery(
["products/list", { page, size }],
() => getList({ page, size })
);
if (isError) {
console.log(error);
return moveToLoginReturn();
}
const serverData = data || initState;
return (
<div className="border-2 border-blue-100 mt-10 mr-2 ml-2">
{isFetching ? <FetchingModal /> : <></>}
(...)
<PageComponent
serverData={serverData}
movePage={moveToList}
></PageComponent>
</div>
);
};
export default ListComponent;
* 코드에서는 page와 size가 쿼리 키(key)로 지정되었다. 만일 2페이지를 호출한다면 아래와 같이 쿼리 키(key)가 보관되는 것을 볼 수 있다.
[동일 페이지 갱신 문제]
* 현재 코드에서는 staleTime을 지정하지 않았기 때문에 서버에서 가져온 데이터를 사용한 후에 바로 stale한 상태가 된다. 따라서 잠시 브라우저를 벗어나 다른 프로그램을 클릭하거나 이용한 후에 다시 브라우저를 활성화하면 서버를 재호출하는 것을 확인할 수 있다.
* 조회 페이지와 달리 상품 목록 페이지는 아래 쪽에 페이지 번호를 클릭할 수 있다. 이 때 페이지 번호가 다르다면 useQuery()가 이용하는 쿼리 키(key) 값이 달라지므로 문제가 없겠지만, 동일한 페이지를 클릭하는 경우에 문제가 된다. 아래의 결과는 2페이지를 조회한 상태에서 지속적으로 2페이지를 클릭했지만, API 서버에 호출이 한 번벆에 일어나지 않은 결과이다.
(2) invalidateQueries()
* 동일한 쿼리 키(key)가 반복적으로 호출할 때 문제를 해결하는 가장 간단한 방법은 리액트 쿼리가 보관하는 데이터를 무효화(invalidate) 시키는 방법이다. 리액트 쿼리는 해당 키(key) 값의 데이터가 무효화되면 다시 서버를 호출해서 데이터를 조회하게 된다.
* ListComponent에는 현재 리액트 쿼리의 QueryClient를 가져오는 useQueryClient()를 선언하고 페이지 번호를 클릭했을 때 동작하는 handleClickPage()를 추가한다.
import React from "react";
import { getList } from "../../api/productsApi";
import useCustomMove from "../../hooks/useCustomMove";
import FetchingModal from "../common/FetchingModal";
import PageComponent from "../common/PageComponent";
import { API_SERVER_HOST } from "../../api/todoApi";
import useCustomLogin from "../../hooks/useCustomLogin";
import { useQuery, useQueryClient } from "@tanstack/react-query";
const host = API_SERVER_HOST;
const initState = {
(...)
};
const ListComponent = () => {
const { page, size, moveToList, moveToRead } = useCustomMove();
const { moveToLoginReturn } = useCustomLogin();
const { isFetching, data, error, isError } = useQuery(
["products/list", { page, size }],
() => getList({ page, size })
);
const queryClient = useQueryClient(); // 리액트 쿼리 초기화를 위한 현재 객체
const handleClickPage = (pageParam) => {
if (pageParam.page === parseInt(page)) {
queryClient.invalidateQueries("products/list");
}
moveToList(pageParam);
};
if (isError) {
console.log(error);
return moveToLoginReturn();
}
const serverData = data || initState;
return (
<div className="border-2 border-blue-100 mt-10 mr-2 ml-2">
{isFetching ? <FetchingModal /> : <></>}
(...)
<PageComponent
serverData={serverData}
movePage={handleClickPage}
></PageComponent>
</div>
);
};
export default ListComponent;
* handleClickPage()는 동일한 페이지를 클릭한 경우에만 invalidateQueries()를 실행한다. 리액트 쿼리에서 invalidateQueries()는 해당 키로 시작하는 결과를 모두 무효화시킨다.
* 예를 들어 1페이지와 2페이지를 조회한다면 쿼리 키(key) 값들은 아래와 같이 보관된다.
* 동일한 페이지를 다시 클릭한다면 products/list 키로 시작하는 데이터들은 무효화되기 때문에 다시 API 서버를 호출하게 된다(임시로 API 서버가 몇 초 이후에 응답을 보내도록 조정하고 현재 페이지를 다시 클릭해 보자). 화면을 보면 freshing 처리가 되면서 매번 서버를 호출하는 것을 확인할 수 있다.
(3) refresh 활용
* invalidateQueries()를 이용하는 방식의 단점은 사용자가 짧은 시간 동안 동일한 페이지를 클릭하는 경우 매번 서버의 호출이 너무 많아진다는 점이다.
* 이런 상황을 피하고 싶다면 매번 변경되는 refresh 값과 staleTime을 같이 이용하면 일정 시간 동안은 서버를 반복적으로 호출하는 문제를 해결할 수 있다. staleTime을 이용해서 약간의 시간 동안 반복적으로 서버를 호출하는 것을 막고 refresh를 이용해서 동일한 페이지에 대한 쿼리 키(key) 값을 변경하는 방식이다.
const ListComponent = () => {
const { page, size, moveToList, moveToRead, refresh } = useCustomMove();
const { moveToLoginReturn } = useCustomLogin();
const { isFetching, data, error, isError } = useQuery(
["products/list", { page, size, refresh }], // refresh 추가
() => getList({ page, size }),
{ staleTime: 1000 * 5 } // staleTime 추가
);
// const queryClient = useQueryClient(); // 필요하지 않음
const handleClickPage = (pageParam) => {
// if (pageParam.page === parseInt(page)) {
// queryClient.invalidateQueries("products/list");
// }
moveToList(pageParam);
};
* 아래 화면은 짧은 시간 동안 동일한 페이지를 여러 번 클릭했을 경우의 결과로 5초가 지난 후에만 한 번 호출이 되는 상황을 캡쳐한 것이다.
* 동일한 페이지의 호출은 useQuery()의 결과 중 refetch를 이용할 수도 있긴 하지만, 이 경우 다른 쿼리 스트링을
변경하는 등의 처리는 어렵기 때문에 컴포넌트의 상태(state)를 이용하는 것이 좋다.
'SpringBoot_React 풀스택 프로젝트' 카테고리의 다른 글
(52) 리액트 쿼리와 리코일 4 (0) | 2024.03.11 |
---|---|
(51) 리액트 쿼리와 리코일 3 (0) | 2024.03.11 |
(49) 리액트 쿼리와 리코일 1 (0) | 2024.03.11 |
(48) 리액트 장바구니 구성 3 (0) | 2024.03.11 |
(47) 리액트 장바구니 구성 2 (0) | 2024.03.08 |