상태 관리 라이브러리 정리

dudgus5766·2022년 12월 27일
0

TimeToEat

목록 보기
1/1
post-thumbnail

최근 근황을 짧게 말씀드리자면..음식과 관련된 사이드 프로젝트를 진행 중입니다! 🍚🍔🍖
기존 회사에서 쓰던 Redux 라이브러리가 아닌 React-QueryRecoil을 쓰기로 결정하였습니다. 해당 라이브러리들을 쓰기 전 특징과 장단점을 정리해보았습니다.

React-Query 🌺

React Query는 데이터 Fetching, 캐싱, 동기화, 서버 쪽 데이터 업데이트 등을 쉽게 만들어 주는 React 라이브러리입니다.

React-Query의 장점

1️⃣ React 컴포넌트 내에서 데이터 패칭, 캐싱, 상태 업데이트를 쉽게 만들어 줌.

  • 캐싱을 효율적으로 관리 가능함
  • get을 한 데이터에 대해 update를 하면 자동으로 get을 다시 수행함 (예를 들면 게시판의 글을 가져왔을 때 게시판의 글을 생성하면 게시판 글을 get하는 api를 자동으로 실행 )

2️⃣ React-Query는 별도의 설정 없이 즉시 사용 가능함(크기 ⬇️).

3️⃣ 같은 데이터에 대한 여러번의 요청이 있을 시 중복을 제거함(옵션에 따라 중복 호출 허용 시간 조절 가능).

React-Query의 사용법

React-Query를 사용해 서버 상태를 가져오기 위해서는 useQuery를 사용하면 됩니다. 만약 서버의 data를 수정하고자 한다면 useMutation 을 사용합니다.

요약하면 R ( Read ) 은 useQuery , CUD ( Create, Update, Delete )는 useMutation을 사용하면 됩니다.

📍 useQurey

  • 데이터를 get 하기 위한 api입니다. post, update는 useMutation을 사용합니다.
  • useQuery는 비동기로 작동합니다. 한 컴포넌트에 여러개의 useQuery가 있다면 useQuery가 동시에 실행됩니다. ** 순착적인 비동기 query가 필요하다면 enabled 로 동기적 사용가능!

예시

[예시 1]

const Todos = () => {
  const { isLoading, isError, data, error } = useQuery("todos", fetchTodoList, {
    refetchOnWindowFocus: false, // react-query는 사용자가 사용하는 윈도우가 다른 곳을 갔다가 다시 화면으로 돌아오면 이 함수를 재실행합니다. 그 재실행 여부 옵션 입니다.
    retry: 0, // 실패시 재호출 몇번 할지
    onSuccess: data => {
      // 성공시 호출
      console.log(data);
    },
    onError: e => {
      // 실패시 호출 (401, 404 같은 error가 아니라 정말 api 호출이 실패한 경우만 호출됩니다.)
      // 강제로 에러 발생시키려면 api단에서 throw Error 날립니다. (참조: https://react-query.tanstack.com/guides/query-functions#usage-with-fetch-and-other-clients-that-do-not-throw-by-default)
      console.log(e.message);
    }
  });

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

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

  return (
    <ul>
      {data.map(todo => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
};

// 또는 isLoading, isSuccess 를 status 로 한번에 처리가 가능하다.

[예시 2]

 const Todos = () => {
  const { status, data, error } = useQuery("todos", fetchTodoList);

  if (status === "loading") {
    return <span>Loading...</span>;
  }

  if (status === "error") {
    return <span>Error: {error.message}</span>;
  }

  return (
    <ul>
      {data.map(todo => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}

✅ 'todos': 첫번째 인자로, unique key를 명시해줍니다.

  • 해당 key는 내부적으로 데이터 재요청, 캐싱, 쿼리를 공유하기 위해 사용됩니다.
  • 다른 컴포넌트에서도 해당 키를 사용하면 호출 가능합니다.

✅ fetchTodoList: 두번째 인자에는 우리가 요청할 비동기 함수를 넣어주는데, 데이터와 error를 return해줍니다.

주요 states

  • isLoading or status === 'loading' : 현재 데이터를 요청 중이나 아직 데이터가 없을 경우
  • isError or status === 'error': 쿼리에서 에러가 났을 경우
  • isSuccess or status === 'success' : 쿼리 요청 성공
  • isIdle or status === 'idle': 이 쿼리는 현재 사용할 수 없을 때 나옴
  • isFetching : 데이터 요청 중일 때는 (내부적으로 리패칭 중 일때도 포함) 항상 True를 리턴한다

📍 useMutation

  • useQuery 와 다르게 useMutation은 데이터를 post, update, delete 할 때 사용됩니다.
  • 미리 UI부터 변화시켜주는 optimistic update 기능도 사용자에게 정말 좋은 경험을 제공할 수 있습니다.

useMutation 매개변수

useMutation은 3가지 매개변수를 받습니다.

useMutation(key, function, options) 요렇게 인자 3개를 받는다
				      ↑      ↑        ↑
            쿼리키  api호출함수  쿼리옵션

✅ mutationKey: 첫번째 인자로, unique key를 명시해줍니다.

✅  mutationFn: 서버와 통신할 promise 처리가 이루어지는 api 함수를 의미합니다.

  • promise 기반의 axios 방식으로 통신을 하는 경우, try catch 문을 사용해 에러핸들링을 하는데,
    React-Query 는 옵션을 사용해 에러핸들링.
    ```jsx
    const saveItem1 = useMutation((item) => axios.post('/saveItem', item));
    
    const saveItem2 = useMutation({
        mutationFn: (item) => axios.post('/saveItem', item)
    })
    ```

✅  options: 서버와 통신할 promise 처리가 이루어지는 api 함수를 의미합니다.

  • try catch 문을 대신해 onSuccess, onError, onSettled 옵션을 사용가능!
    {
      onSuccess: () => {		// 요청 성공시 실행
        console.log('onSuccess');
      },
      onError: err => {		// 요청 실패시 실행
        console.log('onError')
      },
      onSettled: () => {   // 성공실패 관계없이 실행
        console.log('onSettled') 
      },
    }

📍 invalidateQueries

invalidateQueries는 useQuery에서 사용되는 queryKey의 유효성을 제거해주는 목적으로 사용됩니다.
(서버로부터 다시 데이터를 조회해오기 위함 + 데이터를 캐싱하고 싶지 않을 때)

const queryClient = useQueryClient();  // 등록된 quieryClient 가져오기

const saveItem = useMutation((item) => axios.post('/saveItem', item), {
    onSuccess: () => { // 요청이 성공한 경우
        console.log('onSuccess');
        queryClient.invalidateQueries('item'); // queryKey 유효성 제거
    },
    onError: (error) => { // 요청에 에러가 발생된 경우
        console.log('onError');
    },
    onSettled: () => { // 요청이 성공하든, 에러가 발생되든 실행하고 싶은 경우
        console.log('onSettled');
    }
});

Recoil 🔀

Recoil은 Facebook에서 출시된 React 전용 상태관리 라이브러리입니다. (React에 최적화)

Recoil을 사용하면 atoms (공유 상태)에서 selectors (순수 함수)를 거쳐 React 컴포넌트로 내려가는 data-flow graph를 만들 수 있습니다.

Recoil의 장점 👍

1️⃣ React 문법 친화적

  • React를 위해 만들어진 상태 관리 라이브러리이기 때문에 React처럼 작동을 하며 기존의 React를 사용하였던 개발자라면 쉽게 접근할 수 있음.

2️⃣ 간단하고, 강력함.

  • store, action, reducer 등 여러 가지를 신경 쓸 일 없이 초기 세팅이 직관적이고 간단!
  • 비동기 데이터 흐름을 위한 내장 솔루션까지 제공
  • 앱 전체의 모든 상태 변경을 관찰하여 지속성, 라우팅, 디버깅을 구현하는데 용이

3️⃣ 내부적으로 자동적으로 캐싱되어 빠름(side-effect 존재)

Recoil의 단점 👎

1️⃣ 안정성

  • 22.12 현재 최신 버전이 **0.7.6**

2️⃣ 부족한 레퍼런스

Recoil의 사용법

📍 RecoilRoot

  • Recoil을 사용하기 위해서는 사용하고자하는 부모 컴포넌트에다 <RecoilRoot>를 사용해야합니다.
    React Native에서는 루트 컴포넌트가 RecoilRoot를 넣기에 가장 적합하여, App.js에 적용합니다.
// App.js
import React from 'react';
import App from './App';
import { RecoilRoot } from 'recoil';

function App() {
  return (
    <RecoilRoot>
    	<Navigator />
    </RecoilRoot>
  );
}

📍 Atom

  • Atom은 redux의 store와 같은 개념입니다.
  • 데이터 state의 단위이며, 업데이트와 구독이 가능합니다.
    atom의 값이 바뀌면 구독하고있는 장소는 모두 새로운 값으로 리렌더링됩니다.
import { atom } from "recoil";

export default atom({
    key: 'countState',
    default: 0,
});

📍 Selector

  • atom에서는 불가능한 비동기 처리와 복잡한 로직을 구현할 수 있습니다.

    공식문서 설명
    Selector는 파생된 상태(derived state)의 일부를 나타낸다.
    파생된 상태를 어떤 방법으로든 주어진 상태를 수정하는 순수 함수에 전달된 상태의 결과물로 생각할 수 있다.

import { DefaultValue, selector } from "recoil";
import countState from "../atom/countState";

export default selector({
    key: "countSelector",
    get: ({get}): number => {
        const count = get(countState);
        return count + 1;
    },
    set: ({set, get}, newCount)=>{
        return set(countState, newCount + 10)
    }
})

출처📚


https://kyounghwan01.github.io/blog/React/react-query/basic/#usequery

https://recoiljs.org/ko/docs/introduction/installation

profile
RN App Developer

0개의 댓글