목차

  • 상태란?
  • 상태 관리가 필요한 이유
  • 주문 팀의 고민 및 해결
  • React Query 살펴보기
  • 3가지 core 컨셉
  • 캐싱 및 동기화
  • 마무리

Intro

  • 상태관리하면 떠오르는 것 = 라이브러리 → 추상적
  • 라이브러리 = Redux, MobX, Recoil

상태란?


  • 주어진 시간에 대해 시스템을 나타내는 것 → 언제든지 변경이 가능
  • 즉, 문자열, 배열, 객체 등의 형태로 응용 프로그램에 저장된 데이터 → 개발자 입장에서 관리해야 할 데이터

모던 웹 프론트엔드 개발


  • UI/UX의 중요성의 향상
  • FE에서 수행하는 역할의 증가

=> 즉, 관리하는 상태가 많아지게 됨

상태관리?


  • 상태를 관리하는 방법을 모두 통틀어서 정의

상태 관리가 필요한 이유


  • state의 증가 + state의 시간에 따라 변화하는 성질
    • 다양한 상태 들의 조합(변형) → 관리가 필요
  • React의 Props Drilling 이슈

정리


  • 모던 웹 프론트엔드 환경 → 다양한 상태들의 등장
  • 그런 상태들을 관리하기 위한 상태 관리 라이브러리들의 등장

주문팀의 고민 및 해결


  • 리팩토링 계획 → 부가적인 아키텍처 들을 통합

그 중 상태관리에 관한 고민


  • Typescript, redux로 짜여진 이전 버전 코드
  • store 에서 저장하는 것들이 대부분 API 통신 코드

의문점 3가지

  1. Store에 API 통신 관련 코드가 대부분 → server state가 대부분
  2. 반복되는 isFetching과 isError
  3. 반복되는 비슷한 구조의 API 통신 코드

Server State 특성


  1. 즉, 서버에서 관리되고 유지되는 데이터들(DB에 저장된 데이터)
  2. 이 데이터들을 받아오기 위해 비동기 API가 필요
  3. ex) 내가 모르는 사이에 배민의 주문 상태가 변경 될 경우
  4. 신경쓰지 않을 경우 out of date(쓸모 없는 데이터)가 될 수 있음

  • client와 다른 상태들의 특성을 가짐 → 다른 관리 방법이 필요
  • client와 server state를 분리하는 것이 어떨까?

상태를 두 가지로 나누기


  • client stateserver state로 분리

Client State와 Server State의 특징


  • Client State( ex) 컴포넌트에서 관리하는 각각의 input value )
  • Server State ( ex) database에 저장되어있는 데이터 )
  • client state와 server state는 다음과 같은 특징을 지님
  • 가장 큰 차이는 Ownership이 client와 server 라는 것의 차이

다시 상태 관리 라이브러리


  • 이 라이브러리 들이 2022년 현재에 Server State를 위해서 활용할 수 있는가 ?
  • 즉, Server State를 관리 하기 적합한가? → React Query를 사용하자

React Query 살펴보기


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

  • 공식 문서 - React나 React Native 어플리케이션에서 어떠한 전역 상태의 터치 없이 데이터를 패칭, 캐싱, 업데이트 하도록 한다고 소개

Overview


  • 서버 데이터를 가져오고, 캐싱, 동기화, 업데이트 하는 기능을 수행

  • zero-config로 즉시 사용가능, 원하면 config로 커스텀도 가능

React-query State


  • fresh - 새롭게 추가된 쿼리 & 만료되지 않은 쿼리
    • 컴포넌트가 마운트, 업데이트 되더라도 데이터 재 요청 X
  • fetching - 요청 중인 쿼리
  • stale - 만료된 쿼리
    • 컴포넌트가 마운트, 업데이트데이터 재 요청 O
  • inactive - 비 활성화된 쿼리 → 특정 시간이 지나면 가비지 컬렉터(GC)에 의해 제거

install & setting


1. npm
$ npm i @tanstack/react-query

2. yarn
$ yarn add @tanstack/react-query

3. cdn
<script src="https://unpkg.com/@tanstack/react-query@4/build/umd/index.production.js"></script>

App.tsx

import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query'
const queryClient = new QueryClient()
export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Example />
    </QueryClientProvider>
  )
}
  • 반드시 최상위 컴포넌트 위에 QueryClientProvider로 감싸 줘야 함
  • QueryClientProvider의 prop으로 client(QueryClient)가 필요

세 가지 core 컨셉


  • Queries
  • Mutations
  • Query Invalidation

Queries (useQuery)


  • 보통 GET으로 받아올 대부분의 API에서 사용
  • Queries는 데이터 Fetching을 위해 사용

useQuery

  • 첫 번째 인자 - Query Key
  • 두 번째 인자 - Query Function

1. Query Key

  • Query를 관리하기 위한 key(맵핑 구조)
  • String 형태와 Array 형태로 나뉘며 주로 String 형태

2. Query Function

  • Promise를 반환하는 함수 → 데이터를 resolve 하거나 reject
    • ex) fetch, axios
  • API 패칭 용 함수

useQuery의 return


  • data : 서버 요청에 대한 데이터
  • isLoading : 캐시가 없는 상태에서 데이터 요청 중인 상태
  • isFetching : 캐시의 유무 상관없이 데이터 요청 중인 상태
  • isError : 서버 요청 실패에 대한 상태
  • error : 서버 요청 실패

config 커스텀


  • useQuery의 3번째 인자로 사용이 가능

  • cacheTime - 언마운트된 후 어느 시점까지 메모리에 데이터를 저장하여 캐싱할 것인지 결정 (default - 5분)
  • staleTime - 쿼리가 fresh 상태에서 stale 상태로 전환되는 시간
    • default - 0
    • fresh 상태에선 컴포넌트가 마운트, 업데이트가 되어도 재요청을 보내지 않으므로 API 요청 횟수를 줄일 수 있음
    • 보통 쉽게 변하지 않는 컴포넌트에 한해 staleTime을 지정
  • refetchOnMount - 컴포넌트 마운트 시 새로운 데이터 패칭
    • default - true
    • false일 경우 마운트 시 새로운 데이터를 가지고 오지 않음
  • refetchOnWindowFocus - 브라우저 클릭 시 새로운 데이터 패칭
    • default - true
    • false 일 경우 브라우저가 포커스 되어도 데이터를 가지고 오지 않음
  • refetchInterval - 지정한 시간 간격 만큼 데이터 패칭
    • default - 0
    • 브라우저에 포커스가 없을 때 실행되지 않음
  • refetchIntervalInBackground - 브라우저에 포커스가 없어도 refetchInterval에서 지정한 시간 간격만큼 데이터 패칭
    • default - false
  • enabled - 컴포넌트가 마운트 되어도 데이터 패칭 x
    • default - true
    • useQuery의 반환 값 중 refetch를 활용하여 데이터 패칭이 가능 (enabled : false)
  • onSuccess - 데이터 패칭 성공
  • onError - 데이터 패칭 실패
  • select - 데이터 패칭 성공 시 원하는 데이터 형식으로 변환

queries 파일 분리


  • useQuery를 컴포넌트에 직접 쓰기 보단 queries를 따로 파일로 분리하여 사용

  • 쿼리가 여러개일 때도 잘 동작 → 병렬 처리

Mutations (useMutation)


  • 데이터 updating (POST, PUT, DELETE) 시 사용
  • 데이터 생성 및 수정, 삭제 모두에 사용

  • 첫 번째 인자 - API 호출 함수 (Promise 반환 함수)
  • 두 번째 인자 - 콜백 (라이프사이클에 따라 로직 작성)
import { useMutation } from '@tanstack/react-query';
const registerUserComponent = () => {
  const registerUser = (user) => {
    return axios.post('http://localhost:4000/user', user);
  };
  
  const { mutate: addUser, isLoading, isError, error } = useMutation(registerUser);

  const handleCreateUser = () => {
    const user = { 이름, 이메일, 비밀번호 };
    addUser(user);
  };

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

  if (isError) {
    return <h2>{error.message}</h2>;
  }
}

return


  • Mutation은 자동으로 실행 되지 않음

config


  • Optimistic update
    • ex) 페이스북 중 좋아요 기능
    • 성공할 것이라고 생각하고 UI를 업데이트 → 성공하면 그대로 실패하면 예전 값으로 롤백

Query Invalidation


  • ex
    import { useMutation } from '@tanstack/react-query';
    const registerUserComponent = () => {
    	const queryClient = useQueryClient();
    
      const registerUser = (user) => {
        return axios.post('http://localhost:4000/user', user);
      };
      
      const { mutate: addUser, isLoading, isError, error } = useMutation(registerUser, {
    		onSuccess : () => {
    			// 캐시가 있는 모든 쿼리 무효화
    			queryClient.invalidateQueries();
    			// queryKey가 'user'로 시작하는 모든 쿼리 무효화
    			queryClient.invalidateQueries('user');
    		}
    	});
    
      const handleCreateUser = () => {
        const user = { 이름, 이메일, 비밀번호 };
        addUser(user);
      };
    
      if (isLoading) {
        return <h2>Loading...</h2>;
      }
    
      if (isError) {
        return <h2>{error.message}</h2>;
      }
    }

캐싱 및 동기화

RFC 5861(HTTP Cache-Control Extensions for Stale Content)


  • stale response - 옛날 데이터
  • 리액트 쿼리의 컨셉

이 컨셉을 메모리 캐시에 적용


  • GC - 가비지 컬렉터
  • refetchOnWindowFocus - 다른 탭에 있다가 다시 포커싱 될 경우 refetch

Query 상태흐름(화면에 있다 사라지는 Query)


<과정>

  1. data fetching
  2. stale Time > 0이면 fresh state
  3. stale Time 만료 시 stale state (스크린에서 사용되는 동안 유지)
  4. 스크린에서 사용 x → inactive state(cache time 만료 전까지)
  5. cache Time 만료 시 deleted(GC가 처리)

Query 상태흐름(화면에 있다가 없다가 좀 더 복잡한 query)


zero-config에서도 이런 역할을(알아서 하는 것들이 있어서 좋지만 주의 해야함)


캐싱 관점에서 공식예제(예제 생략)


  • useQuery
    • stale Time = 0
    • cache Time = 5분
    • refetchOnMount, refetchOnWindowFocus, refetchOnReconnect → true
    • err → 3 relay try

전역 상태 처럼 관리되는 데이터들(어떻게 Server State들을 전역상태 처럼 관리?)


  • 중복 호출 문제?
  • 해답은 Context API

QueryClient 내부적으로 Context 사용


  • QueryClient는 내부적으로 Context를 사용 → QueryClientProvider가 필요한 이유

마무리

React Query 이후 주문 FE 프로덕트


React Query의 장점


React Query의 고민


React Query 사용 추천


레퍼런스

우아한 세미나

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

jkl1545 - react-query

https://velog.io/@jkl1545/React-Query#%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%9A%94%EC%B2%AD

0개의 댓글