React Query

이찬영·2025년 1월 6일

React Query

리액트 쿼리란, React 애플리케이션에서 서버 상태를 효율적으로 관리하기 위한 라이브러리 이다. 클라이언트 상태와는 달리 서버 상태는 비동기 데이터, 캐싱, 동기화, 갱신 등이 필요하기 때문에 이를 단순화하고 최적화 한다.

주요기능
1. 데이터 가져오기(Fetching)
2. 데이터 캐싱(Caching)
3. 데이터 무효화(Invalidation)
4. 자동 갱신(Auto Refetching)
5. 병렬 요청 및 의존성 관리
6. 뮤테이션(Mutation)
7. 서버 상태 동기화

React Query 설치

리액트 쿼리를 사용하려면

npm install @tanstack/react-query

설치해준뒤,
최상위 컴포넌트로 가서

import {QueryClinet, QueryClientProvider} from 'tanstack/react-query';

const queryClient = new QueryClient;

const App() {
	...
    return (
    	<QueryClinetProvider client={queryClient}>
      			<HomePage />
      	</QueryClientProvider>
    )
    ...
}

와 같이 해주면 된다.
QueryClientProvider는 React Context의 Provider와 유사한 역할을 한다. 그리고, React Query Devtools는 개발 중 서버 상태를 디버깅 할 수 있는 도구인데,

npm install @tanstack/react-query-devtools

로 설치가 가능하며, 이것도 최상위 컴포넌트에 import해주고,
<ReactQueryDevtools />와 같이 아무데나 쑤셔 박으면 된다. 또, initialsOpen옵션으로 Devtools기본 열림 여부 설정이 가능하다.

쿼리란, 문의하다. 질문하다.라는 뜻으로, 데이터베이스나 백엔드에 필요한 데이터를 요청하는 작업이다.

useQuery()

  1. useQuery() 훅:
  • react 쿼리에서 데이터를 받아오는 주요 리액트 훅이다.
  • queryKey : 쿼리를 식별하는 고유 키
  • queryFn : 데이터를 가져오는 함수(예 : API 호출)
  1. API 함수 작성
  • 데이터를 받아오는 비동기 함수 작성이 필요 (axios등)

핵심 :
1. useQuery()리턴 값

  • data : 백엔드에서 받아온 실제 데이터
  • dataUpdateAt : 데이터를 마지막으로 받아온 시간
  • isError : 에러 상태인지 불린값으로 확인
  • isLoading : 로딩 상태인지 불린값으로 확인
  • isFetched : 데이터가 가져와 졌는지 확인
  • status : 쿼리의 현재 상태 (Success, Error)등
    ** tip : useQuery를 사용해서 서버데이터를 가져오면 useState로 상태관리를 할 필요없이 (필요에 따라 사용) 바로 데이터를 사용할 수 있다.
    const postData = data.result ?? [];와 같이

예시 :

import axios from 'axios';

const BASE_URL = 'https://example.co.kr';

export async function getPosts() {
	const response = await axios.get(`${BASE_URL}/posts`);
  	return response.data;
}


// HomePage
import {useQuery} from '@tanstack/react-query';
import {getPosts} from './api';

function HomePage() {
	const {data, isError} = useQuery({
    	queryKey : ['posts'] // 내가 지정
      	queryFn : getPosts // api함수 사용
    });
  
  	if(isError) {
    	return <LoadingPage />
    }
        
    return (
    	<div>홈페이지</div>
    )
}

정리된 흐름
1. API 함수 작성
2. useQuery훅 사용

  • 데이터를 가져올 쿼리를 설정하고, 리턴값에서 상태와 데이터를 관리
  1. 리턴값 활용
  • data로 데이터를 렌더링
  • isError, isLoading등으로 상태 관리

QueryStatus

  1. QueryStatus
    의미 : 데이터를 성공적으로 받아왔는지 여부를 나타낸다.
    상태값 : pending, success, error
  • pending : 데이터 로드 중
  • success : 데이터 로드 성공
  • error : 데이터 로드 실패
    관련 속성 : isPending, isSuccess, isError
  1. FetchStatus
    의미 : 쿼리함수(queryFn)의 실행 상태를 나타냄
    상태값 : fetching, paused, idle
  • fetching : 쿼리함수 실행중
  • paused : 네트워크 문제 발생
  • idle : 실행중이지 않은 상태
  1. 독립적 상태
  • query status와 fetch status는 서로 독립적이므로 다양한 조합이 가능하다.
  • pending & fetching : 데이터 로드 중
  • success & idle : 데이터 로드 완료 후 대기 상태
  • error & idle : 데이터 로드 실패 후 대기 상태
  • success & fetching : 성공 상태에서 데이터 다시 로드

핵심
1. Query Status 흐름

  • 컴포넌트가 마운트 -> pending
  • 데이터 로드 성공 -> success
  • 쿼리 함수 실행 종료 -> idle
  1. Fetch Status 흐름
  • 쿼리 함수 실행 -> fetching
  • 네트워크 문제 -> paused
  • 쿼리 함수 실행 종료 -> idle
  1. 두 상태의 조합
    상태 조합을 활용하여 다양한 로직을 구현할 수 있다.
const {status, isPending, isError, isSuccess, error} = useQuery({
	queryKey : ['posts'],
  	queryFn : getPosts,
});

if(isPending) return <LoadingPage />
if(isError) return <ErrorPage />
if(isSuccess) return <p>Welcome HomePage</p>

정리 :
1. Query Status

  • 데이터의 상태를 나타낸다.
  • 상태값으로 UI 조건부 렌더링이 가능하다.
  1. Fetch Status
  • 쿼리 함수 실행 상태를 나타낸다.
  • 네트워크 상황 및 실행 상태 확인이 가능하다.

캐시(Cache)

  1. 캐시(Cache)란?
  • 데이터를 임시로 저장하여 빠르게 재사용할 수 있는 공간
  • 자주 사용하는 데이터를 캐시에 저장하여 네트워크 비용 절약 및 속도 향상
  1. React Query의 캐싱
  • queryKey를 기준으로 데이터를 캐시에 저장한다.
  • 데이터가 신선한 상태(fresh), 신선하지 않은 상태(stale), 사용되지 않는 상태(inactive)로 분류
  1. 데이터 상태와 동작
  • Fresh : 캐시에 저장된 데이터가 최신 상태. staleTime내에만 유지
  • Stale : staleTime이 지나 데이터가 신선하지 않음. 백그라운드에서 refetch진행
  • Inactive : 컴포넌트 언마운트 후 데이터가 사용되지 않음. gcTime이 지나면 캐시에서 삭제

캐시 동작 과정

  • useQuery() 실행 시 queryKey로 캐시에 데이터가 있는지 확인
  • 캐시에 없으면 queryFn을 실행하여 데이터를 받아오고 캐시에 저장
  • 캐시에 데이터가 있으면 상태에 따라
    : Fresh = 캐시 데이터 리턴
    : Stale = 캐시 데이터 리턴 + 백그라운드에서 refetch

Refetch가 발생하는 상황

  • 새로운 쿼리 인스턴스가 마운트
  • 브라우저 창에 다시 포커스
  • 네트워크 재연결
  • 설정된 refetchInterval 시간이 지남

타임 설정

  • staleTime : 데이터가 신선하게 유지되는 시간 / 기본값 : 0ms
  • gcTime : 데이터가 캐시에 유지되는 시간 / 기본값 : 5분

캐시 타임 설정 예제 :

const result = useQuery({
	queryKey : ['posts'],
  	queryFn : getPosts,
  	staleTime : 60 * 1000 // 1분동안 fresh상태 유지
  	gcTime : 10 * 60 * 1000 // 10분 후 inactive상태의 데이터 삭제
})

Refetch 옵션 변경 예제

const result = useQuery({
	queryKey : ['posts'],
  	queryFn : getPosts,
  	refetchOnMount : false, // 마운트 시 refetch하지 않음
  	refetchOnWindowFocus : true, // 창 포커스 시 refetch
  	refetchOnReconnect : true, // 네트워크 재연결 시 refetch
  	refetchInterval : 5000, // 5초마다 refetch
});

상태 변화 흐름

  • Fresh -> Stale(staleTime 이후)
  • Stale -> Fresh(refetch성공)
  • Inactive -> 캐시 삭제(gcTime이후)

정리
1. 캐시 사용의 장점

  • 동일 데이터를 반복적으로 요청하지 않음
  • 유저에게 빠른 응답 제공
  1. React Query에서 캐시 처리
  • staleTime으로 신선 상태 유지 시간 설정 (기본값 : 0. 즉, 바로 stale)
  • gcTime으로 사용되지 않는 데이터 삭제 시간 설정
  1. Refetch관리
  • 필요 시 refetch 조건 및 주기를 세부적으로 조정 가능

다양한 쿼리키와 형태

  1. queryKey는 왜 배열 형태인가
    설명 :
  • React Query는 queryKey를 사용해 데이터를 고유하게 식별하고 캐싱한다.
  • 배열 형태를 사용하면 데이터를 계층적으로 구분하고 관리할 수 있다.
    예 : ['posts'] -> 전체 포스트를 식별
    ['posts', username] -> 특정 유저의 포스트만을 식별

예제 :

const {data : postsData} = useQuery({
	queryKey : ['posts'], // 전체 포스트 데이터
  	queryFn : getPosts,
})

const {data : userData} = useQuery({
	queryKey : ['posts', username], // 특정 유저의 포스트 데이터
  	queryFn : () => getPostsByUsername(username),
})
  1. 계층적 관리
  • 배열 형태로 queryKey를 설정하면 세부적인 데이터를 효율적으로 구분이 가능하다.
  • 예를 들어
    -- ['posts']는 전체 포스트를 저장
    -- ['posts', username] 은 특정 유저의 포스트를 저장
    -- ['posts', username, {status : 'private'}]은 특정 유저의 private포스트만 저장

예제 :

const {data : privatePostsData} = useQuery({
	queryKey : ['posts', username, {status : 'private'}],
  	queryFn : () => getPrivatePostsByUsername(username),
});
  1. queryFn에서 queryKey값 활용
    쿼리 키 값 전달
  • queryFn은 queryKey를 매개변수로 받을 수 있으며, 이를 통해 API함수에 필요한 데이터를 동적으로 전달 가능
const {data : postsDataByUserId} = useQuery({
	queryKey : ['posts', {username}] // username이 포함된 객체 형태
    queryFn : ({queryKey}) => {
		const [key, {username}] = queryKey;
  		return getPostsByUserId(username);
	}
})
  1. React Query 캐싱 동작
    캐싱 흐름
  2. queryKey를 기준으로 데이터를 캐싱한다.
  3. 동일한 queryKey를 사용하면 캐시에 저장된 데이터를 반환한다.
  4. 다른 queryKey를 사용하면 새로운 데이터를 요청한다.
  • 자주쓰는 옵션과 리턴값 *
    useQuery를 디스트럭쳐링할때 data, isPending, isError, error등을 자주 쓴다.
    data : 실제 받아온 데이터
    isPending : 로딩처리
    isError : 에러처리
    isError가 true라면, error.message등 사용가능

useMutation

1. useMutation이란?
정의

  • useMutation() : 데이터를 추가, 수정, 삭제 (즉, 사이드 이펙트를 발생)할때 사용하는 react query 훅이다.
  • 뮤테이션은 상태나 구조의 변경을 의미하며, 여기서는 백엔드 데이터를 변경하는 작업을 뜻한다.

특징
1. 수동 실행

  • useQuery()와 달리 컴포넌트가 마운트될 때 자동으로 실행되지 않는다.
  • mutate()를 통해 명시적으로 실행해야 백엔드 데이터가 변경된다.
  1. 캐시 상태 유지
  • mutate()실행 후에도 캐시에 저장된 기존 데이터는 그대로 유지된다.
  • 그래서 화면에 새로운 데이터를 반영하려면 refetch가 필요하다.

2. useMutation()기본 사용법
-- API함수 (POST, PUT, PATCH, DELETE) (예 : uploadPost [POST요청])

  • mutationFn 옵션에다가 작성해둔 API함수를 등록
const uploadPostMutation = useMutation({
	mutationFn : (newPost) => uploadPost(newPost)
});
  • mutate()를 호출하여 API요청 실행
const handleSubmit = (e) => {
	e.preventDefault();
  	const newPost = {username : 'username', content};
  	uploadPostMutation.mutate(newPost);
  	setContent('');
}

요약
1. useMutation()

  • mutationFn : 데이터를 백엔드에 전송할 함수를 정의
  • mutate : api함수를 실행
  1. 캐시와 상태
  • mutate()를 실행해도 캐시는 업데이트되지않음
  • 화면에서 새로운 데이터를 반영하려면 refetch필요
  1. UI업데이트
  • 데이터를 수정한 뒤, UI에 반영하려면 화면을 새로고침하거나 refetch를 수동으로 호출해야함
    -mutate()호출 후 setContent('')등으로 UI를 적절히 초기화 해야함

invalidateQueries()

정의

  • 캐시에 저장된 특정 쿼리 또는 모든 쿼리를 무효화(stale상태로 변경)하여 강제 refetch를 시키는 함수이다.
  • 데이터를 추가, 삭제, 수정 한뒤 캐시 데이터를 강제로 최신상태로 업데이트하기 위해 사용한다.
  • 데이터를 stale상태로 만들고 백그라운드에서 데이터를 refetch한다.
  1. 사용방법
  • 최상위 컴포넌트에 선언했던 queryClient가져오기
  • invalidateQueries()호출
    queryClinet.invalidateQuries({queryKey : ['posts']})
  1. useMutation() 의 콜백 옵션
  • onMutate : 뮤테이션이 실행되기 직전에 실행
  • onSuccess : 뮤테이션이 성공적으로 완료된 후 실행
  • onError : 뮤테이션이 실패했을 때 실행
  • onSettled : 성공 여부와 관계없이 뮤테이션이 종료된 후 실행

예제 :

const uploadPostMutation = useMutation({
	mutationFn : (newPost) => uploadPost(newPost),
  	onSuccess : () => {
    	queryClient.invalidateQuries({queryKey : ['posts']}), // 쿼리무효화 및 refetch 
    },
    onSettled : () => {
    	console.log('mutation has Settled');
    }
});
  1. mutate()의 콜백옵션
    특징
  • mutate() 호출 시 개별적인 콜백함수를 추가할 수 있음
  • 컴포넌트 의존적인 작업(토스트메시지, 리다이렉트)에 적합
uploadPostMutation.mutate(newPost, {
	onSuccess : () => {
    	toast.info('업로드 성공');
    }
});

주의 사항
1. useMutation() 콜백 vs mutate() 콜백

  • useMutation()의 콜백은 컴포넌트 언마운트와 상관없이 실행
  • mutate()의 콜백은 컴포넌트가 언마운트되면 실행되지 않음
  1. 필수로직
  • 꼭 실행되어야 하는 로직(invalidateQueries)은 useMutation()콜백에 등록
  • 컴포넌트 종속적인 로직은 mutate()콜백에 등록
  1. 버튼 비활성화
  • 뮤테이션이 진행중일때는 버튼을 비활성화하여 중복 요청을 방지한다.
<button disabled={uploadPostMutation.isPending || !content}>업로드</button>

개념 요약
1. invalidateQueries()

  • 특정 쿼리를 무효화해서 refetch를 트리거한다.
  • 새로운 데이터를 반영하려면 onSuccess에서 호출
  1. isPending
  • 뮤테이션 진행 중인지 확인하여 버튼 등 UI상태를 조정할 수 있음

Dependant Query란

Dependant Query는 어떤 데이터가 준비된 후에만 실행해야 하는 쿼리를 말한다. 예를 들어, 첫번째 쿼리의 결과가 준비되어야 두번째 쿼리를 실행할 수 있을때 이를 Depentdant Query라고 한다.

Dependant Query 예제

  • 사용자의 이메일을 기반으로 유저 정보를 받아오는 가정이다.
  • 받아온 유저 정보에서 유저 ID 를 이용해 해당 유저의 프로젝트 정보를 가져온다.
const {data : user} = useQuery({
	queryKey : ['user', email],
  	queryFn : getUserEmail,
})

const userId = user?.id;

const {data : projects} = useQuery({
	queryKey : ['Projects', userId],
  	queryFn : getProjectEmail,
  	enabled : !!userId // userId가 있을떄 (true)일때만 실행
})

enabled 옵션의 역할

  • enabled 옵션은 특정 조건을 만족할 때만 쿼리가 실행되도록 설정하는 옵션이다.
  • 위 코드에선 ebnabled : !!userId (userId가 있을때만 실행)

예제 : 간단한 로그인 기능 만들기

// api.js
const BASE_URL = 'http://example.com';

export async function getUserInfo(username) {
	const response = await.axios.get(`${BASE_URL}/users/${username}`);
  	return response.data;
}
// HomePage.js
function HomePage() {
	const [currentUsername, setCurrentUsername] = useState('');
  
  	// 유저 정보 쿼리
  	const {data : userInfoData, isLoading, isError} = useQuery({
    	queryKey : ['userInfo', currentUsername],
      	queryFn : () => getUserInfo(currentUsername),
      	enabled : !!currentUsername, // currentUsername이 있을때만 실행
    });
  
  	// 로그인 버튼 클릭시 유저명 설정
   	const handleLoginButtonClick = () => {
    	setCurrentUsername('exampleUser');
    };
}

동작과정
1. 처음 화면

  • currentUsername이 빈 상태이므로 ['userInfo']쿼리가 실행되지 않음
  • 로그인 버튼이 표시됨
  1. 로그인 버튼 클릭
  • currentUsername이 'exampleUser'로 설정됨
  • enabled : !!currentUsername 조건이 참이 되어 ['userInfo']쿼리가 실행됨
  1. 데이터 로드 완료
  • userInfo 데이터가 로드되면 화면에 유저 이름이 표시됨

enabled가 왜 중요한가

  • 특정 데이터가 준비되지 않은 상태에서 쿼리가 실행되는것을 방지한다.
  • 쓸데없는 API호출을 방지하여 성능을 최적화 한다.

한줄요약 : Dependant Query는 특정 조건이 충족될때만 실행되는 쿼리이며, 이를 설정하기 위해 enabled옵션을 사용한다.

Paginated Query (페이지네이션)

useQuery에서 keepPreviousData로 로딩 중에도 이전 페이지 데이터를 유지할 수 있다.

구현흐름
1. API함수

  • 백엔드에 page와 limit값을 전달해 특정 페이지 데이터를 요청한다.
export async function getPosts(page = 0, limit = 3){
	const response = await axios.get(`${BASE_URL}/api/posts?page=${page}&limit=${limit}`);
  	return response.data;
}
  1. 페이지 상태 관리
  • useState로 page상태를 생성
  • useQuery의 queryKey에 page를 포함하여 페이지별로 캐싱
  • placeholderData로 부드러운 전환
const [page, setPage] = useState(0);

const INITIAL_LIMIT = 3;

const {
	data : postsData,
  	isPending, 
  	isError,
  	isPlaceholderData
} = useQuery({
	queryKey : ['posts', page],
  	queryFn : () => getPosts(page, INITIAL_LIMIT),
  	placeholderData : keepPreviousData, // 다음 페이지 로딩 중이라면, 이전 페이지를 계속 보여줌
});

isPlaceholderData는 placeholderData : keepPreviousData를 설정해줬을때 사용할 수 있는데,
isPlaceholder 가 true => API호출 진행중...
isPlaceholder 가 false => API호출 완료!
이를 활용하여, API호출이 진행중일때 버튼을 비활성화 시킬 수도 있다.

  1. prefetchQuery로 다음 데이터 미리 가져오기
  • 백그라운드로 데이터를 미리 로드하여 끊김없는 UI를 제공
  • 현재 페이지를 기준으로 다음 페이지 데이터를 미리 가져옴
...
useEffect(() => {
	if(!isPlaceholderData && postsData?.hasMore) {
       	queryClient.prefetchQuery({
       		queryKey : ['posts', page + 1],
      		queryFn : () => getPosts(page + 1, INITIAL_LIMIT),
       });
     }
}, [...]);
...

핵심요약
1. keepPreviousData로 부드러운 데이터 전환이 가능하다.
2. isPlaceholderData : 임시 데이터 사용 여부
3. queryClient의 prefetchQuery로 다음 페이지에 대한 데이터를 백그라운드에서 미리 요청할 수 있다.

React Query는 queryKey를 기반으로 캐싱한다. 이 말은 캐시에 이미 queryKey와 동일한 값이 캐싱되어 있다면, 요청을 더이상 보내지않는다. 즉, queryKey안의 값이 바뀔때마다 서버에 요청을 보내는것이다.

useInfiniteQuery (더 불러오기 구현)

  1. useInfiniteQuery란
  • React Query에서 무한 스크롤이나 "더 불러오기"버튼을 쉽게 구현하기 위한 훅
  • 데이터를 한 페이지씩 불러와 모든 페이지 데이터를 배열로 관리
  1. 사용이유
  • 무한 스크롤 및 더 불러오기 구현
  • 전체 데이터를 한번에 요청하지 않고 필요한 만큼 잘라서 가져옴으로 성능 최적화
  • 이전 데이터와 새로운 데이터를 자동으로 병합하여 관리한다.

핵심 옵션
1. queryKey

  • 데이터를 캐싱할 키.
  1. queryFn
  • 데이터를 가져오는 함수. pageParam을 받아 페이지 단위 요청
  1. initialPageParam
  • 첫번째 페이지를 나타내는 초기값. 보통 0이나 1로 설정
  1. getNextPageParam
  • 다음 페이지를 가져올때 사용할 페이지값(pageParam)을 반환하는 함수
getNextPageParam : (lastPage, allPages, lastPageParam, allPageParams) =>
			lastPage.hasMore ? lastPage + 1 : undefined
  • getNextPageParam 함수는 queryFn에 전달될 pageParam값을 계산해서 반환한다. 그리고 그 값이 queryFn의 pageParam으로 다시 전달되는 것이다.
  • pageParam getNextPageParams에서 계산된 값을 기준으로 queryFn에 전달된다.
  • lastPage : 마지막으로 가져온 페이지의 데이터 (queryFn의 리턴값. 즉 페이지 데이터)
  • lastPageParam : 마지막으로 가져온 페이지 번호 (최초에는 initialPageParam이 지정한 초기값)
  • getNextPageParam함수는 undefined혹은 null을 반환하면 (flasy한 값), queryFn을 더이상 호출하지 않음
  1. fetchNextPage
  • 다음 페이지 데이터를 요청하는 함수. "더 불러오기"와 같은 버튼에 연결
  1. data.pages
  • 모든 페이지의 데이터를 배열 형태로 관리. 각 인덱스가 하나의 페이지 데이터를 나타낸다.

리턴값
1. data.pages

  • 지금까지 불러온 페이지 데이터들의 배열
  1. fetchNextPage
  • 다음 페이지 데이터를 요청하는 함수
  1. hasNextPage
  • 다음 페이지가 있는지 여부. true면 다음 페이지 요청 가능
  1. isFetchingNextPage
  • 다음 페이지를 요청 중인지 여부

실제 코드 흐름
1. 데이터 가져오기 (useInfiniteQuery 설정)

const {
	data : postsData,
  	fetchNextPage,
  	hasNextPage,
  	isFetchingNextPage,
} = useInfiniteQuery({
	queryKey : ['posts'],
  	queryFn : ({pageParam}) => getPosts(pageParam, INITIAL_LIMIT),
  	initialPageParam : 0,
  	getNextPageParam : (lastPage, allPages, lastPageParam, allPageParams) =>
    lastPage.hasMore ? lastPageParam + 1 : undefined,
});
  1. 데이터 구조 정리
  • 모든 페이지 데이터를 data.pages로 관리한다.
  • pages배열에서 각 페이지의 데이터를 꺼내 화면에 표시한다.
const postsPages = data?.pages ?? [];
  1. "더 불러오기 버튼"
  • 클릭 시 fetchNextPage함수 호출로 다음 페이지 데이터를 요청
  • 버튼 비활성화 조건
    !hasNextPage : 다음 페이지가 없을때
    isFetchingNextPage : 데이터를 요청중일 때
<button onClick={fetchNextPage} disabled={!hasNextPage || isFetchingNextPage}>더 불러오기</button>
  1. UI 렌더링
  • 모든 페이지의 데이터를 화면에 표시
<ul>
  {postsPages.map((postPage) => postPage.results.map((post) => <Post key={post.postId} post={post} />))}
</ul>

요약
1. useInfiniteQuery

  • 무한 스크롤 및 더 불러오기 버튼 구현을 위한 react query 훅
  1. 핵심옵션
  • queryFn : 데이터를 가져오는 함수 queryFn : ({pageParam}) => ...
  • getNextPageParam : 다음 페이지의 값을 결정하는 속성
  • fetchNextPage : 다음 페이지를 요청하는 함수
  • hasNextPage : 다음 페이지가 있는지 확인
  1. 더 불러오기 동작
  • fetchNextPage로 데이터를 요청하고, data.pages에 저장된 데이터를 화면에 표시
  1. 버튼 비활성화 조건
    useInfiniteQuery의 리턴값인
  • hasNextPage (true => 다음 페이지 데이터가 존재, false => 다음 페이지 데이터 X)
  • isFetchingNextPage (true => 다음 페이지를 가져오는 중, false => 요청이 완료)
    를 활용

0개의 댓글