React Query?
React Query는 React 어플리케이션에서 데이터 패칭 및 캐싱 프로세스를 간소화 하는 라이브러리이다. React 컴포넌트에서 데이터를 더 쉽게 다룰 수 있는 다양한 훅과 유틸리티를 제공하며, 이는 일반적으로 많이 사용하는 Fetch(또는 Axios) + useState
+ useEffect
를 대체함과 동시에 서버에서 받아오는 데이터를 자동으로 캐싱하고 관리해주는 역할을 한다. 또한 API 요청의 로딩 및 오류 상태 역시 관리 가능하다.
React Query의 기본 개념
Queries(쿼리) : API 엔드포인트 또는 데이터베이스와 같은 원격 데이터 소스로부터 데이터를 요청하는 것을 의미하며,
useQuery
훅을 사용해 관리된다.Mutations(뮤테이션) : 서버에 새로운 데이터를 추가하거나 수정하는 요청을 의미하며,
useMutation
훅을 사용해 관리된다.Query Caching(쿼리 캐싱) : React Query의 내장 기능으로, 쿼리 결과를 메모리에 저장한다.
Query Invalidation(쿼리 무효화) : 쿼리를 무효하거나 오래된 상태로 표시하는 과정이다.
useQuery
훅으로 데이터 페칭하기
쿼리는 고유 키에 연결된 데이터 비동기 소스에 대한 선언적 종속성(declarative dependency)입니다.
- 공식 문서에서 발췌
여기서 선언적 종속성은 useQuery
훅을 사용한 코드에서 선언한 쿼리를 나타낸다. 이 쿼리는 API 데이터 포인트 또는 데이터베이스에서 비동기적으로 데이터를 가져오도록 서버에 요청하는 것이다. 쉽게 말해 REST API의 GET 요청을 위한 것으로, 1. 고유한 키(unique key)와 2. promise를 반환하는 함수를 필수 옵션으로 사용한다. 해당 함수에서 실질적인 데이터 요청을 진행하게 된다.
const queryObj = useQuery({
queryKey: ['repoData'], // unique key가 포함된 배열이 들어간다.
queryFn: () => // promise를 반환하는 함수가 들어가며, 실제로 호출할 비동기 함수이다.
fetch('<https://api.github.com/repos/tannerlinsley/react-query>').then(
(res) => res.json(),
),
})
useQuery
가 반환하는 것
useQuery
는 객체를 반환한다. 반환된 객체에는 쿼리의 상태에 대한 정보가 포함되어 있으며, 많이 사용하게 될 값과 메서드는 다음과 같다.
data
: 성공적으로 페칭된 경우 쿼리로부터 반환된 데이터error
: 에러가 발생했을 시 쿼리 도중 발생한 에러isLoading
: 쿼리가 현재 로딩 중인지 여부를 나타내는 값 (boolean)isError
: 쿼리 결과가 오류인지 여부를 나타내는 값 (boolean)refetch()
: 쿼리 데이터를 수동으로 리페치하도록 트리거하는 메서드remove()
: 캐시에서 특정 쿼리를 제거하는 메서드
이 외에도 많은 값과 메서드가 존재하며, 공식 문서에서 확인 할 수 있다.
useQuery
와 데이터 리페칭 API
기본적으로 useQuery
는 useEffect
처럼 컴포넌트가 처음 마운트 될 때 API에서 데이터를 자동으로 페칭한다. 하지만 이후 서버에서 데이터가 업데이트 되더라도 자동으로 가져오지 않는다. 기본적으로 useQuery
에서 데이터를 리페칭하는 이벤트가 별도로 정해져있으며, 이는 useQuery
의 옵션인 refetchOnMount
, refetchOnReconnect
, refetchOnWindowFocus
이다. 이 세 옵션의 default값은 모두 true이다.
refetchOnMount
: 데이터가 오래된 경우 브라우저 창이 포커스되면 리페치refetchOnReconnect
: 데이터가 오래된 경우 마운트 시 리페치refetchOnWindowFocus
: 데이터가 오래된 경우 재연결 시 리페치
단, 이러한 옵션 외에도 시간을 기준으로(refetchInterval
) 리페치를 실행할 수도 있다. 이 경우 브라우저 창이 유휴(idle) 상태일 때에도 데이터를 가져올 수 있어 유용하다.
useMutation
훅으로 데이터 생성/업데이트
useQuery
가 서버에서 데이터를 가져오기 위함이었다면, useMutation
은 서버로 데이터를 생성하거나 업데이트, 삭제 하기 위해 사용하는 훅이다. promise를 반환하는 함수를 필수 옵션으로 사용하며, 마찬가지로 해당 함수에서 실질적인 데이터 요청을 진행하게 된다.
const mutation = useMutation({
mutationFn: (newTodo) => { // promise를 반환하는 함수가 들어가며, 실제로 호출할 비동기 함수이다.
return axios.post('/todos', newTodo)
},
})
...
<button
onClick={() => {
mutation.mutate({ id: new Date(), title: 'Do Laundry' })
}}
>
Create Todo
</button>
useMutation
이 반환하는 것
useMutation
역시 객체를 반환한다. 반환된 객체에는 쿼리의 상태에 대한 정보가 포함되어 있으며, 많이 사용하게 될 값과 메서드는 다음과 같다.
data
: (데이터가 있다면) 뮤테이션으로부터 반환된 데이터error
: 에러가 발생했을 시 뮤테이션 도중 발생한 에러isLoading
: 뮤테이션이 현재 중인지 여부를 나타내는 값 (boolean)isError
: 뮤테이션 결과가 오류인지 여부를 나타내는 값 (boolean)mutate()
: 뮤테이션을 실행하기 위해 호출할 수 있는 메서드. 첫번째 인자로 필요한 변수를 가진 객체 전달 가능reset()
: 뮤테이션을 초기상태로 리셋하는 메서드onSuccess()
: 뮤테이션이 성공적으로 완료되었을 때 호출될 콜백을 정의할 수 있는 메서드onError()
: 뮤테이션 중 오류가 발생했을 때 호출될 콜백을 정의할 수 있는 메서드
이 외에도 많은 값과 메서드가 존재하며, 공식 문서%EC%97%90%EC%84%9C)에서 확인 할 수 있다.
React Query의 쿼리 캐싱
앞서 설명한 것 처럼 useQuery
는 고유한 키를 사용한다. 서버에서 반환된 데이터는 이 키 아래에 캐시되는데, 기본적으로 useQuery
는 이 캐시 데이터를 오래된(stale) 상태로 표시하며, 오래된 상태와 캐시를 하는 기준은 아래의 옵션을 통해 결정된다.
staleTime
: 쿼리 결과가 오래된 것으로 간주되는데 걸리는 시간(ms)cacheTime
: 쿼리 결과가 캐시에 유지되는 시간(ms)
const query = useQuery({
queryKey: [‘users’],
queryFn: getUsers,
staleTime: 5000,
cacheTime: 60000
})
예를 들어 staleTime
이 5000
이고, cacheTime
이 60000
이라면 데이터가 5초가 지난 후에 stale 상태가 되고 1분간 캐시에 유지되다 그 이후에는 데이터가 가비지 컬렉션 됨을 의미한다.
React Query의 쿼리 무효화(Invalidation)
위와 같이 캐시에 저장된 데이터는 staleTime
옵션에 따라 오래된 데이터로 처리 될 수 있지만, 특정 상황에서는 이 옵션을 무시하고 데이터를 무효화하거나 오래되었다고 표시해야 하는 상황이 발생할 수 있다. 예를 들어 API에 POST 요청을 보낸다면 정상적으로 응답이 돌아올 시 최신 데이터가 변경되었으므로 캐시에 있는 데이터는 유효하지 않은 것으로 표시해야 한다. 이 때 React Query의 QueryClient
객체는 invalidateQueries
메서드를 제공하여 모든 쿼리 또는 고유 키를 사용해 특정 쿼리를 오래된(stale) 상태로 표시할 수 있다.
import { useQueryClient } from '@tanstack/react-query'
// useQueryClient 훅을 사용하여 queryClient 객체 생성
const queryClient = useQueryClient()
// 캐시의 모든 쿼리 무효화
queryClient.invalidateQueries()
// 'users'로 시작하는 키가 있는 모든 쿼리 무효화
queryClient.invalidateQueries({ queryKey: ['users'] })
React Query가 가지는 특장점
DevTools
공식적으로 DevTools를 지원한다. 개발 모드에서만 사용하며, 좀 더 확실하게 데이터 흐름을 파악할 수 있다.
무한 스크롤 구현
React-Query에는
getPreviousPageParam
,fetchPreviousPage
,hasPreviousPage
와 같은 다양한 페이지 관련 기능이 존재하기 때문에 이를 이용하면 무한 스크롤은 물론 페이지네이션을 쉽게 구현할 수 있다.Selectors
select
키워드를 활용해 raw data로부터 원하는 데이터를 추출해 반환할 수 있다.import { useQuery } from 'react-query' function User() { const { data } = useQuery('user', fetchUser, { select: user => user.username, }) return <div>Username: {data}</div> }