data life

[React] React-Query 사용해보기(1) 본문

Front-end/React

[React] React-Query 사용해보기(1)

주술회전목마 2023. 8. 19. 22:37

이번 팀프로젝트를 진행하면서 React-query를 도입해보고자 해당 기술에 대해 알아보고자 합니다.

 

React-Query란?

공식문서에 따르면 서버 상태 가져오기, 캐싱, 동기화 및 업데이트를 쉽게 만들어주는 라이브러리로 소개되어있으며 서버 상태를 관리하기 위한 최고의 라이브러리로 이해하면 되겠습니다.

 

기술적인 측면에서,

- 복잡하고 많은 양의 코드를 줄여 줄 수 있습니다.

- 유지 관리가 쉽고 새 기능을 쉽게 구축할 수 있습니다.

 

캐싱이란?

특정 데이터의 복사본을 저장하여 이후 동일한 데이터의 재접근 속도를 향상시켜준다.

React-Query는 캐싱을 통해 반복적인 비동기 데이터 호출을 방지하고 서버에 대한 부하를 줄여주는 장점이 있다.

(fresh Data : 최신 데이터 <-> stale Data : 오래된 데이터)

 

그렇다면 언제 호출하는데?

앞서 캐싱을 통해 불필요한 데이터 호출을 방지한다고 했지만 언제 어떤 상황에서 호출할 건지는 의문이다.

해당 React-Query에서는 적절하게 데이터를 갱신해줄 수 있는 옵션들을 제공하여준다.

refetchOnWindowFocus, //default: true
refetchOnMount, //default: true
refetchOnReconnect, //default: true
staleTime, //default: 0
cacheTime, //default: 5분 (60 * 5 * 1000) 밀리초 단위
retry //number 실패한 쿼리 다시 시도

1. 브라우저에 포커스될 경우

2. 새로운 컴포넌트 마운트가 발생한 경우

3. 네트워크 재연결

 

staleTime ⏰

- fresh -> stale 상태로 변경되는데 걸리는 시간을 뜻한다.

- 기본값이 0이므로 따로 설정해주지 않으면 무조건 refetch가 발생한다.

 

cacheTime

- 특정 컴포넌트가 unmount 상태일 때, 사용된 데이터는 cacheTime만큼 유지된다.

- 데이터가 비활성 상태일 때 캐싱된 상태로 남아있는 시간을 뜻한다.

- cacheTime이 지나면 가비지 콜렉터로 수집되어 메모리에서 삭제된다.

- 즉, fetch되는 동안 임시로 보여주는 시간이라고 생각하면 됨!

 

설치하기

$ npm i react-query
# or
$ yarn add react-query

* v4 부터는 아래와 같음

$ npm i @tanstack/react-query
# or
$ pnpm add @tanstack/react-query
# or
$ yarn add @tanstack/react-query

 

대표 기능

 

useQuery

- GET 요청과 같이 서버에서 데이터를 가져올 때 사용

const result = useQuery('todos', fetchTodoList)

첫번째 파라미터에 고유키가 들어가야 한다. 동일한 쿼리를 불러올 때 유용하게 쓰인다.

두번째 파라미터에는 실제 호출하고자 하는 비동기 함수가 들어간다.

return 값

- 데이터를 불러오거나

- 오류

 

* v4부터는 무조건 배열의 형태로 입력해야한다.

const result = useQuery(['todos'], fetchTodos)

 

해당 result에는 다음과 같은 상태가 포함되어있다. (기본)

- isLoading : 쿼리에 데이터가 없고 가져오는 상태

- isError : 쿼리에 오류가 발생한 상태

- isSuccess : 쿼리가 성공했고 데이터를 사용할 수 있는 상태

function Todos() {
  const { isLoading, isError, data, error } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodoList,
  })

  if (isLoading) {
    return <span>Loading...</span>
  }

  if (isError) {
    return <span>Error: {error.message}</span>
  }

  // We can assume by this point that `isSuccess === true`
  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}

 

 

useQueries

여러 개의 useQuery를 한 번에 실행할 때 사용한다.

const results = useQueries({
  queries: [
    { queryKey: ['post', 1], queryFn: fetchPost, staleTime: Infinity},
    { queryKey: ['post', 2], queryFn: fetchPost, staleTime: Infinity}
  ]
})

 

 

useMutation

PUT, UPDATE, DELETE와 같은 데이터를 변경 시 사용

function App() {
  const mutation = useMutation(newTodo => {
    return axios.post('/todos', newTodo)
  })

  return (
    <div>
      {mutation.isLoading ? (
        'Adding todo...'
      ) : (
        <>
          {mutation.isError ? (
            <div>An error occurred: {mutation.error.message}</div>
          ) : null}

          {mutation.isSuccess ? <div>Todo added!</div> : null}

          <button
            onClick={() => {
              mutation.mutate({ id: new Date(), title: 'Do Laundry' })
            }}
          >
            Create Todo
          </button>
        </>
      )}
    </div>
  )
}

 

 

useInfiniteQuery

무한 쿼리 이용 시

const {
  fetchNextPage,
  fetchPreviousPage,
  hasNextPage,
  hasPreviousPage,
  isFetchingNextPage,
  isFetchingPreviousPage,
  ...result
} = useInfiniteQuery(queryKey, ({ pageParam = 1 }) => fetchPage(pageParam), {
  ...options,
  getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
  getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,
})