FrontEnd/React

[React] React-Query의 개념 및 예제

검정색필통 2023. 3. 2. 11:27

React Query는 API 통신과 비동기 데이터 관리에 사용되는 React 라이브러리 입니다.

 

1. React-Query 이전

  • state에 비동기 데이터를 보관할 경우
    • 다수의 component의 lifecycle에 따라 비동기 데이터가 관리되기 때문에 캐싱 등 최적화를 하기 어려움
    • 다수의 component에서 동일한 API를 호출하거나, 특정 API응답이 다른 API에 영향을 미치는 경우 등에 대응하기 어려움

 

  • Redux를 사용하여 비동기 데이터를 관리할 경우
    • componet의 lifecycle과 관계없이 비동기 데이터를 관리할 수 있어 최적화가 쉬우며 복잡한 사용자 시나리오에도 대응 가능
    • 장황한 Boilerplate 코드로 인해 비동기 Action 처리에 복잡성 증가
    • Redux는 API 통신 및 비동기 상태 관리를 위한 라이브러리가 아니라 Global State Management Library, 즉 전역 상태 관리 라이브러리이기 때문에 API 요청 수행을 위한 규격화된 방식 부재
    • 사용자 경험 향상을 위한 복잡한 시나리오를 수행하는데 많은 개발 리소스 소모

 

 

2. React-Query란?

  • React Application에서 서버의 상태를 불러오고, 캐싱하며, 지속적으로 동기화하고 업데이트 하는 작업을 도와주는 라이브러리
  • 우리에게 친숙한 Hook을 사용하여 React Component 내부에서 자연스럽게 서버(또는 비동기적인 요청이 필요한 Source)의 데이터를 사용할 수 있는 방법을 제안
  • API 요청을 Query 그리고 Mutation 이라는 두 가지 유형으로 나누어 생각

 

 

3. Query 요청 개요

const { status, data, error } = useQuery(
  queryKey, // 이 Query 요청에 대한 응답 데이터를 캐시할 때 사용할 Unique Key (required)
  fetchFn, // 이 Query 요청을 수행하기 위한 Promise를 Return 하는 함수 (required)
  options, // useQuery에서 사용되는 Option 객체 (optional)
);
  • status
    • isLoading or status === 'loading' - 데이터가 없거나 아직 fetch 중일 때
    • isError or status === 'error' - 데이터를 불러오는 과정에서 error를 호출할 때
    • isSuccess or status === 'success' - 성공적으로 데이터를 불러왔을 때
    • isIdle or status === 'idle' - The query is currently disabled (you'll learn more about this in a bit)... 잘 모르겠음..?

 

  • error
    • isError state에 도달 하면 반환하는 객체

 

  • data
    • isSuccess state에 도달하면 반환하는 객체(데이터)

 

  • isFetching
    • 아직 query가 진행 중이면 isFetchingtrue를 반환

 

 

4. Query Key

  • Query Key는 string이거나 string이나 nested object로 이루어진 배열로, Query Key를 통해 React Query는 데이터를 캐싱하고 관리한다.
// string query keys
 useQuery('todos', ...) // queryKey === ['todos']
 
// array query keys
useQuery(['todo', 5], ...) // queryKey === ['todo', 5]
useQuery(['todo', 5, { preview: true }], ...) // queryKey === ['todo', 5, { preview: true }]
useQuery(['todos', { type: 'done' }], ...) // queryKey === ['todos', { type: 'done' }]

// array key에서 순서가 바뀌면 다른 키
// ex) 같은 키
useQuery(['todos', { status, page }], ...)
useQuery(['todos', { page, status }], ...)
// ex) 다른 키
useQuery(['todos', status, page], ...)
useQuery(['todos', page, status], ...)

// variable이 바뀔 경우에 query를 호출 하고 싶으면 query key에 variable을 담는다. (변수 의존)
 function Todos({ todoId }) {
   const result = useQuery(['todos', todoId], () => fetchTodoById(todoId))
 }

 

 

5. Query Function

promise를 반환하는 fuction으로 비동기 요청으로 받은 데이터나 에러를 반환한다.

// 다음과 같은 방법 모두 사용 가능하다.
useQuery(['todos'], fetchAllTodos)
useQuery(['todos', todoId], () => fetchTodoById(todoId))
useQuery(['todos', todoId], async () => {
   const data = await fetchTodoById(todoId)
   return data
})
useQuery(['todos', todoId], ({ queryKey }) => fetchTodoById(queryKey[1]))

 

 

6. useQueries

useQuery의 호출 수가 render마다 바뀌는 경우, hook rule을 위반하기 때문에 수동으로 useQuery를 사용할 수 없다. 이때 useQueries를 사용하여 동적으로 query를 호출한다.

 function App({ users }) {
   const userQueries = useQueries(
     users.map(user => {
       return {
         queryKey: ['user', user.id],
         queryFn: () => fetchUserById(user.id),
       }
     })
   )
 }

React.Suspense모드를 사용할 때도 suspense = true를 적용하여 로딩 상태 처리를 useQuery에서 처리하지 않도록 할 수 있다.