Programming/React

[React-Query] kakao tech, 우아한테크세미나를 보고나서..

hodo- 2023. 8. 18. 21:46

프로젝트를 하면서 데이터 캐싱이 필요하여 상태관리의 필요성을 느끼게 되었고 그중 react query에 대해 알아보고자 유튜브에서 kakao  tech와 우아한 테크세미나에서 좋은 내용을 보게 되어 이를 정리하고자 게시글을 작성하게 되었다.


눈에 보이지 않는 개선: My구독의 Redux에서 React-Query 전환 경험 공유 / if(kakao)2022

1. React-Query 조금 알아보기

  • React-Query는 주로 데이터 패칭, 캐싱, 동기화 작업을 한다.
  • React에서도 위의 작업은 가능하나 많은 테스트와 시간을 필요로 함 > 자체적으로 hook 만들어야함
    ContextAPI, useState, useEffect, useCallback, 캐싱, 에러핸들링 등 많은 고려사항을 가져가야함
  • 하지만 React-Query는 QueryClient, useQuery, useMutation

2. Redux에서 React-Query로 전환 이유

  • Redux는 비동기 처리 상태 관리 라이브러리가 아니기 때문에 Redux-Saga와 같은 미들웨어를 사용해야함
  • Redux-Saga에서 Redux로 데이터를 보내는데 isLoading, isError와 같은 상태를 지원해주지 않기 때문에 loadStatus로 관리하는데 별도의 상태관리가 필요함.(리액트쿼리는 자동으로 제공)
  • 새로운 API가 추가될 때마다 Actions, Reducers, Sagas를 구현해야함
  • Redux-Saga는 의존성이 깊은 구조를 만들어 낼 수도 있음
  • 간단한 API 추가에도 장황한 보일러플레이트가 필요하며 API 에러 핸들링 과정에서 다소 불필요한 작업이 발생할 수 있음

3. React-Query 개선

  • API가 늘어나면서 관리하기가 어려워지므로 useQueriesLoading() 커스텀훅을 만들어서 해결함

등록된 쿼리를 가져오고 전체 쿼리 개수를 state에 저장한다.
렌더링될 때마다 로딩하는 쿼리가 있는지 확인함 

그 결과, 상태 지옥에서 벗어남

  • Query 작성 방식, QueryMutation 네이밍, 폴더 구조 개선, 프로젝트 기본 옵션, Query Key 작성 규칙을 정하는 것이 중요

폴더 구조 개선

4. React-Query의 장점

  • Hook기반을 제공하고 캐싱을 지원
  • 비동기 처리를 위한 유용한 도구 제공
  • 백그라운드 패칭 지원

5. React-Query의 아쉬운 점

  • Mutation은 한 번만 호출하기 위해 Wrapper 함수 필요

await func(...args) : 비동기 작업 대기

  • UI테스트를 진행하게 될 시 Mook API 필요

  • 전체적인 데이터 흐름을 파악하기 어렵다


1. FE 상태관리에 대해

  • 상태란?
    주어진 시간에 대해 시스템을 나타내는 것으로 언제든 변경될 수 있음. > 관리해야하는 데이터들
  • UIUX의 중요성과 함께 프로덕트 규모가 커지면서 FE에서 수행하는 역할이 늘어남 > 관리하는 상태가 많아짐
  • 상태관리란?
    상태들은 시간에 따라 변화함
    React는 단방향 바인딩이므로 Props Drilling이슈 존재

2. 상태

  • 서버에서 받아야하는 상태들의 특성
    - 클라이언트에서 제어하거나 소유되지 않은 원격의 공간에서 관리되고 유지됨
    - 비동기 API가 필요
    - 신경 쓰지 않는다면 잠재적으로 옛날 데이터가 될 수도 있음
    - 다른 사람들과 공유되어 변경될 가능성이 있음

3. React Query 살펴보기

  • Server State
    - 데이터 가져오기
    - 캐시
    - 동기화
    - 데이터 업데이트

  • Queries (Query Key, Query Function)
    - get으로 받아옴, 데이터 Fetching용
    - Query Key
    Key, Value 맵핑구조로 key에 따라 관리됨
    - Query Function
    promise를 반환하는 함수 > 데이터 resolve하거나 error를 throw
  • useQuery가 반환하는 것
    - data : 마지막으로 성공한 resolved된 데이터
    - error : 에러가 발생했을 때 반환되는 객체
    - isFetching : Request가 in-flight 중일 때 true
    - status, isLoading 등 현재 query의 상태
    - refetch : 해당 쿼리 리패치하는 함수 제공
    - remove : 해당 쿼리 쿼리 캐쉬에서 지우는 함수 제공 
    등등등...
    유용한 여러 인터페이스 제공해줌!
  • uerQuery Option
    - onSuccess, onError, onSettled : query fetching 성공/실패/완료 시 실행할 side effect 정의
    - enabled : 자동으로 query를 실행시킬지 말지 여부
    - retry : query 동작 실패 시 자동으로 retry 할지 결정하는 옵션
    - select : 성공 시 가져온 data를 가공해서 전달
    - keepPreviousData : 새롭게 fetching시 이전 데이터 유지 여부
    - refetchInterval : 주기적으로 refetch할지 결정하는 옵션
    등등등...
  • 마운트 시 데이터가 패치되지 않고 특정한 상황에 패치되도록 하는 법
    우선 enabled를 false로 두고 이벤트 핸들러에서 refetch()로 매뉴얼하게 패치를 하고 그게 힘들다면 아래와 같이 진행(복잡한 상황이라면)
    enabled 옵션에 해당하는 상태를 useState로 컴포넌트 내에 두고 이벤트 핸들러에서 해당 상태 값을 변경하여 enabled를 조건부로 만족시켜 패치시키는 방법
  • Mutations
    - 데이터 생성/수정/삭제용
    - promise반환 함수만 있어도 됨
    - 자동으로 실행되지 않기 때문에 mutate : mutation을 실행하는 함수 
    - mutateAsync : mutate와 비슷하지만 promise반환
    - reset : mutation 내부 상태 clean

  • useMutation Option
    - onMutate : 본격적인 Mutation 동작 전에 먼저 동작하는 함수로 Optimistic update 적용할 때 유용
    요청이 성공할거라고 보고 UI우선 나옴. 실패시 롤백
  • Query Invalidation
    - 간단히 queryClient를 통해 invalidate 메소드 호출하면 끝.
    - queryClient.invalidateQueries()
    - 해당 key를 가진 query는 stale취급됨 > 신선하지 않은 데이터

3. Caching, Synchronization ? 

  • stale-while-revalidate
    -> 백그라운드에서 stale response를 revalidate하는 동안 캐시가 가진 stale response(낡은 데이터)를 반환
    낡은데이터를 보여주는 동안 다시 리패치하겠다.(새로운 데이터를 가져오는 동안에 낡은 데이터 보여주고 있어!)
  • 이 컨셉을 메모리 캐시에도 적용해보자 -> 이렇게 나온 것이 react-query
    - cacheTime : 메모리에 얼마만큼 있을건지 (해당시간 이후 처리됨, default 5분)
    - staleTime : 얼마의 시간이 흐른 후에 데이터를 stale 취급할 것인지(default 0)

4. 어디에서 값들을 관리할까?

  • server state는 어떻게 전역상태처럼 관리되는가?
    queryclient 내부적으로 context를 사용하고 있음.

5. 좋은 점

  • 서버상태 고나리 용이하며 직관적은 API 호출 코드
  • API 처리에 관한 각종 인터페이스 및 옵션제공
  • client store가 fe에서 정말로 필요한 전역상태만 남아 store답게 사용됨

6. 고민이 필요

  • 컴포넌트가 상대적으로 비대해짐 (설계/분리에 대한 고민)
  • 단순히 API 통신 이상의 가능성. 활용 방법 고민
  • 좀 더 난이도 높아진 프로젝트 설계