본문 바로가기
MJU Session (22-23)

[Session] React Query란? / 김보현

by 월월월월 2023. 1. 9.

React Query란?

React Query는 데이터 패칭, 캐싱, 서버 state와의 동기화 및 업데이트를 해주는 라이브러리이다.

왜 React Query를 사용해야 할까?

리엑트는 컴포넌트에서 데이터 패칭이나 서버 state 업데이트하는 방법을 한가지로 강제하지 않는다. 그래서 개발자들은 각자만의 데이터 패칭을 하는 방법을 갖고 있다.

이 뜻은 개발자들이 그들 자신만의 로직으로 컴포넌트의 state와 effect를 React의 hooks를 통해 다루거나, 전역 상태 관리 라이브러리를 사용하여 비동기 데이터를 다루어야한다는 뜻이다.

Redux나 Recoil, 그리고 Mobs 같은 전통적인 전역 상태 관리 라이브러르는 클라이언트의 state를 관리하는데는 매우 좋지만, 비동기적인 state 또는 서버 state를 관리하는데는 그렇지 않다.

다음과 같은 이유로 서버 state는 클라이언트 state와 다르다.

  • 클라이언트가 통제하거나 소유할 수 없는 원격에서 유지된다.
  • 데이터 패칭과 업데이트를 하는데에 비동기 api가 필요하다.
  • 다른 사람과 같이 사용함으로서 나도 모르는 사이 바뀔 수 있다.
  • 내가 주의하지 않으면 데이터가 구식이 될 수 있다.

위에서 서버 state의 생태에 대해 알아봤다면, 더 도전적인 과제들이 남아있다.

  • 캐싱 (프로그래밍에서 가장 어려운 수 있는 것)
  • 하나의 데이터에 대한 중복요청처리
  • 구식 데이터를 백그라운드에서 업데이트
  • 데이터가 구식이 됬을 때 아는 것
  • 데이터에 대한 업데이트를 최대한 빨리 반영하기
  • 페이지네이션이나 lazy loading에 대한 성능 최적화
  • 서버 state에 대한 메모리 관리와 garbage collection
  • 구조적인 공유로 쿼리 결과 Memoizing

React Query는 위와 같은 서버 state를 관리하는데 최고의 라이브러리이다. 즉시 사용가능하고, 설정도 필요없고, customizable한 라이브러리다.

여기에 더해 React Query는 다음을 도와준다.

  • 복잡하고 이해할 수 없는 코드들을 React Query logic을 사용한 간단한 코드로 대체해준다.
  • 어플리케이션을 더 유지보수가 쉽게 만들고, 새로운 기능을 만들 때 새로운 서버 state에 대한 걱정을 없에준다.
  • 사용자의 경험을 개선하는데 직접적인 영향을 끼친다.
  • 잠재적으로 대역폭을 절약하고, 메모리 퍼포먼스를 증가시킨다.

그렇다면 React Query는 구체적으로 어떤 기능들을 제공할까?

  • 클라이언트에 있는 server data의 cache를 관리
  • Loading 및 Error state 관리
  • 페이지네이션 및 무한스크롤 쿼리 제공
  • 데이터 Prefetching
  • Mutation (서버 state 변경)
  • 중복된 요청 제거
  • 에러 발생시 Retry
  • ... 등이 있다.

기본적인 사용법

설정

queryClient 인스턴스를 생성하고 React query를 제공하고 싶은 최상단 컴포넌트를 QueryClientProvider로 감싼후 QueryClientProvider에 client로 queryClient를 넘겨준다.

import { QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import React from 'react'
import ReactDOM from 'react-dom/client'

const queryClient = new QueryClient()

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App/>
    </QueryClientProvider>
  </React.StrictMode>,
)

데이터 패칭

기본적인 데이터 페칭 useQuery를 사용한다.

useQuery는 다음과 같은 파라미터를 넘겨받을 수 있고 다음과 같은 property를 가진 object를 리턴한다.

const {
  data,
  dataUpdatedAt,
  error,
  errorUpdatedAt,
  failureCount,
  failureReason,
  isError,
  isFetched,
  isFetchedAfterMount,
  isFetching,
  isPaused,
  isLoading,
  isLoadingError,
  isPlaceholderData,
  isPreviousData,
  isRefetchError,
  isRefetching,
  isStale,
  isSuccess,
  refetch,
  remove,
  status,
  fetchStatus,
} = useQuery({
  queryKey,
  queryFn,
  cacheTime,
  enabled,
  networkMode,
  initialData,
  initialDataUpdatedAt,
  keepPreviousData,
  meta,
  notifyOnChangeProps,
  onError,
  onSettled,
  onSuccess,
  placeholderData,
  queryKeyHashFn,
  refetchInterval,
  refetchIntervalInBackground,
  refetchOnMount,
  refetchOnReconnect,
  refetchOnWindowFocus,
  retry,
  retryOnMount,
  retryDelay,
  select,
  staleTime,
  structuralSharing,
  suspense,
  useErrorBoundary,
})

기본적인 데이터 패칭을 하려면 useQuery를 호출하여 query key와 query 함수를 인자로 넣어준다.

import { useQuery } from '@tanstack/react-query'

function Example() {
  const { isLoading, error, data } = useQuery({
    queryKey: ['repoData'],
    queryFn: () =>
      fetch('https://api.github.com/repos/tannerlinsley/react-query').then(
        (res) => res.json(),
      ),
  })

  if (isLoading) return 'Loading...'

  if (error) return 'An error has occurred: ' + error.message

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.description}</p>
      <strong>👀 {data.subscribers_count}</strong>{' '}
      <strong>✨ {data.stargazers_count}</strong>{' '}
      <strong>🍴 {data.forks_count}</strong>
    </div>
  )
}

이렇게 작성된 query는 다음과 같은 경우에 자동으로 실행된다

  • 컴포넌트가 다시 마운트 되었을 때
  • 윈도우가 다시 포커스 되었을 때
  • 네트워크가 끊어졌다가 다시 연결 되었을 때
  • Refetch 함수를 실행할 때
  • mutation후에 query가 invalidate되었을 때

이렇게 만든 query는 추가적인 설정을 하지 않으면 데이터 패칭 실패시 3번까지 재시도를 하고 5분의 cacheTime을 갖는다.


참고

[React Query 공식 홈페이지]: https://tanstack.com/query/v4