본문 바로가기
react

[React 상태관리] react-query 입문 하기

by HomieKim 2022. 4. 13.

 

https://www.youtube.com/watch?v=MArE6Hy371c&t=2195s 

react-query를 공부하기 위해 해당 영상을 보고 정리, 요약한 게시글 입니다.


✏️ FE 상태관리에 대해

  • redux, mobx, recoil 같은 라이브러리를 떠올릴 수 있음
  • 상태란? 주어진 시간에 대해 시스템을 나타내는 것으로 언제든지 변경될 수 있음 즉, 문자열, 배열, 객체 등의 형태로 응용 프로그램에 저장된 데이터 => 개발자 입장에서는 관리해야하는 데이터들
  • 모던 웹프론트엔드 개발에서 UI/UX의 중요성과 함께 프로덕트 규모가 많이 커지고 FE에서 수행하는 역할이 늘어남 -> 관리하는 상태가 많아짐
  • 상태관리는?  시간의 흐름에 따라 변화하는 상태를 관리하는 것, React에선 단방향 바인딩이므로 Props Drilling 이슈도 존재 (redux, mobx같은 라이브러리를 사용하여 해결하기도 함)
  • store는 전역상태가 저장되고 관리되는 공간인데 redux, redux-saga방식으로 비동기 요청을 구현하게 되면 redux 구현하는 모듈에 API통신 관련 코드가 대부분이게 됨 
  • 비슷한 구조의 API 통신 코드 반복(loading, error, fetching  등)

✔ 서버에서 받아야 하는 상태들의 특성

  •  Client에서 제어하거나 소유되지 않은 원격의 공간에서 관리되고 유지됨
  • Fetching이나 Updating에 비동기 API가 필요함
  • 다른 사람들과 공유되는 것으로 사용자가 모르는 사이에 변경될 수 있음
  • 신경 쓰지 않는다면 잠재적으로 'out of dated' 가 될 가능성을 지님
  • **사실상 FE에서 이 값들이 저장되어있는 state들은 일종의 캐시 **

✔ Client Sate vs Server State

  • 상태를 크게 Client State 와 Server State로 구분 가능
  • key point는 Ownership이 있는 곳
Client State Server State
Client에서 소유하며 온전히 제어 가능 Client에서 제어하거나 소유되지 않는 원격의 공간에서 관리되고 유지 됨
초기값 설정이나 조작에 제약사항 없음 Fetching / Updating에 필요한 비동기 API가 필요함
다른 사람들과 공유되지 않으며 Client 내에서 UI/UX 흐름이나 사용자 인터렉션에 따라 변할 수 있음 다른 사람들과 공유되는 것으로 사용자가 모르는 사이에 변경될 수 있음
항상 Client 내에서 최신 상태로 관리됨 신경 쓰지 않는다면 잠재적으로 "out of date"가 될 가능성을 지님

- 현재 사용되고 있는 redux, mobX 같은 상태관리 라이브러리가 서버 상태를 관리하기 적합한지 생각해 볼 필요가 있다.

 


✏️ React Query 살펴보기

-공식 문서 정의

Fetch, cache and update data in your React and React Native applications all without touching any "global state"

- OverView

React Query is often described as the missing data-fetching library for React, but in more technical terms, it makes fetching, caching, synchornizing and updating server state in your React applications a breeze.

- 즉, 정리하면 서버 상태를 관리 하는 라이브러리로, 데이터 가져오기, 캐시, 동기화, 데이터 업데이트 등의 기능이 있다.

- 공식 문서 예제

import { QueryClient, QueryClientProvider, useQuery } from 'react-query'
 
 const queryClient = new QueryClient()
 
 export default function App() {
   return (
     <QueryClientProvider client={queryClient}>
       <Example />
     </QueryClientProvider>
   )
 }
 
 function Example() {
   const { isLoading, error, data } = useQuery('repoData', () =>
     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>
   )
 }

- React에서 쓰려면 QueryClientProvider 필수!

✔ react-query의 3가지 컨셉

1️⃣Queries

  •  Queries는 데이터 fetching용 (GET메서드)
  •  Query Key와 Query Function 필요 (key value 매핑 구조)
  •  Query Key는 string 형태, Array 형태 두가지로 관리 가능
  •  Query Function은 Promise를 반환하는 함수!
  •  useQuery()의 반환 값

🗹 data : 마지막으로 성공한 resolved된 데이터 (Response)
🗹 error : 에러가 발생했을 때 반환 되는 객체
🗹 isFetching : Request가 in-flight 중일 때 true
🗹 status, isLoading, isSuccess, isLoading 등등  --> 현재 query의 상태
🗹 refetch : 해당 query refetch 하는 함수 제공
🗹 remove : 해당 query cache에서 지우는 함수 제공 
  • 3번째 인자로 option을 넣을 수 있다. (config custom)=

🗹 onSuccess, onError, onSettled : query fetching 성공/ 실패/ 완료 시 실행할 Side Effect 정의
🗹 enabled : 자동으로 query를 실행시킬지 말지 여부
🗹 retry : query 동작 실패 시, 자동으로 retry 할지 결정하는 옵션
🗹 select : 성공 시 가져온 data를 가공해서 전달
🗹 keepPreviousData : 새로게 fectching 시 이전 데이터 유지 여부
🗹 refetchInterval : 주기적으로 refetch 할지 결정하는 옵션

 

2️⃣Mutations

  • get 요청이외에 서버 상태를 update 할 때 사용( POST, DELETE, PATCH)
  • useMutation()의 경우 Query key 없이 프로미스 반환 함수만 인자로 전달해 주면 됨
  • 단, Query key 넣어주면 dev tools에서 볼 수 있음
  • 반환 값

mutate : mutation을 실행하는 함수
mutateAsync : mutate와 비슷하나 Promise를 반환함
reset : mutation 내부 상태 clean
나머지는 useQuery와 유사 합니다.
  • 옵션 값

onMutate : 본격적인 Mutation 동작 전에 먼저 동작하는 함수, Optimistic update 적용할 때 유용
나머지는 useQuery와 유사
Optimistic update 란?
낙관적 업데이트 즉, API요청이 성공할 것이라 보고 UI를 먼저 업데이트, 성공 시 그대로 가고 실패시 롤백

3️⃣Query Invalidation

  • 간단히 queryClient를 통해 invalidate 메소드를 호출하면 끝
// Invalidate every query in the cache
queryClient.invalidateQueryies()
// Invalidate every query with a key that starts with 'todos'
queryClient.invalidateQueryies('todos')
  • 해당 key를 가진 query는 stale 취급 되고, 현재 rendering 되고 있는 qeury 들을 백그라운드 에서 refetch 됩니다.

✏️ Caching 과 Synchronization

  • Query 옵션으로 cacheTime, staleTime, refetchOnWindowFocus, refetchOnMount 등이 있음

✔ RFC 5861

- HTTP Cache-Control Extensions for Stale Content

  • 리액트 쿼리에서 캐시를 관리하는 컨셉이라고 이해할 수 있습니다.
  • stale-while-revalidate : 백그라운드에서 stale response를 revalidate하는 동안 캐시가 가진 stale response를 반환
  • 정리 하자면 stale response (=최신이 아닌 낡은 데이터) 캐시의 유효시간이 끝나면 revalidate하는 동안 stale한 response 즉, 기존 데이터를 보내 준다.
  • 이러한 컨셉을 메모리 캐시에 적용해보자! -> react-query , swr ...
react-query에서 Cache
🗹 cacheTime : 메모리에 얼마만큼 있을 건지(해당 시간 이후 GC에 의해 처리, default 5분)
🗹 staleTime : 얼마의 시간이 흐른 후에 데이터를 stale 취급 할 것인지(default 0)
🗹 refetchOnMount / refetchOnWindowFocus / refetchOnReconnect : true 이면 Mount / window focus / reconnect 시점에 data가 stale이라고 판단되면 모두 refetch (모두 default true)

✔ 리액트 쿼리 상태 흐름

  • staleTime의 default 값이 0이므로 custom 하지 안효는 다면 fetching 될 때 바로 stale상태로 넘어감
  • staleTime을 custom해서 0보다 크게 만들어 주면 fresh 상태를 유지
  • stale상태는 스크린에서 사용되는 동안 active 상태로 유지 됨
  • 스크린에서 사용 하지 않는 다면 inactive 상태로 바뀌고 cacheTime이 만료되기 전까지 메모리에서 유지됨
  • cacheTime이 만료되면 GC에 의해 처리 됨
  • 즉, 상태가 fresh 하다면 refetch가 일어나지 않음!

정리
🗹 fresh : 새롭게 추가된 쿼리 & 만료되지 않은 쿼리 ➜ 컴포넌트가 마운트, 업데이트되어도 데이터 재요청 ❌
🗹 fetching : 요청 중인 쿼리
🗹 stale : 만료된 쿼리 ➜ 컴포넌트가 마운트, 업데이트되면 데이터 재요청 ⭕️

🗹 inactive : 비활성화된 쿼리 ➜ 특정 시간이 지나면 가비지 컬렉터에 의해 제거

zero-config (기본 설정 default 값)

  • Queries에서 cached data는 언제나 stale 취급
  • 각 시점에서 data가 stale이라면 항상 refetch 발생
  • inactive Query들은 5분 뒤 GC에 의해 처리
  • Query 실패 시 3번까지 retry 발생

✏️ React-query에서 데이터 관리

- 마치 전역상태처럼 관리 되는 데이터 => ContextAPI 사용

  •  QueryClient 내부적으로 Context 사용

 

 

✔ 참조

https://velog.io/@jkl1545/React-Query

https://www.youtube.com/watch?v=MArE6Hy371c&t=2195s 

 

댓글