Hyeon_E 2024. 5. 8. 02:47

리액트에서 훅의 개념으로 상태관리를 시작한 최초의 라이브러리 중 하나이며, 최소 상태 개념인 Atom을 처음 사용한 Recoil!! 많은 회사에서나 프로젝트에서 사용하는 전역 상태 관리 라이브러리임 하지만 20년 처음 만들어졌지만 아직 정식으로 출시한 라이브러리가 아니며 Recoil 팀에서는 리액트 18에서 제공되는 동시성 렌더링, 서버 컴포넌트, Streaming SSR이 지원되기 전까지는 1.0.0을 릴리스 하지 않을것이라고 함 따라서 실제 프로덕션에 사용하기에는 안전성, 성능, 사용성이 떨어질수 있음 또한 가장 큰 핵심 포인트는 핵심개발자가 이미 퇴사한 상태라고 함 원래 Recoil을 사용하는 기업들이 많았는데 현재 Jotai와 Redux, Zustand로 변경하는 모습도 보여 이참에 Recoil과 비슷한 Jotai에 대해 알아보고자 함

 

Jotai

Jotai는 React State Management Libarary 중 하나이고 Recoil, Redux와 같이 상태 관리에 필요한 편의 기능을 제공하는 라이브러리

Jotai는 Recoil과 같은 Atomic 접근 방식으로 React에서 사용되는 state와 비슷하게 리액트 트리 안에서 상태를 저장하고 관리하는 방법임

 

Jotai 특징

https://jotai.org/

 

Jotai, primitive and flexible state management for React

Jotai takes a bottom-up approach to global React state management with an atomic model inspired by Recoil. One can build state by combining atoms and renders are optimized based on atom dependency. This solves the extra re-render issue of React context and

jotai.org

Jotai 공식문서에 따르며 Recoil에서 영감을 받아 React 상태 관리에 대한 원자적 접근 방식을 취하는 도구이며 원자 종속성에 따라 자동으로 최적화되고 매우 최소한의 API를 가지고 있으며 useState의 간단한 대체부터 복잡한 요구 사항이 있는 엔터프라이즈 TypeScript 애플리케이션까지 확장 가능하며, 다양한 유틸리티와 통합 기능을 제공한다고 나와있음

 

특징을 정리해보면

  • 작은 번들 크기
  • Recoil에서 영감을 받아 아토믹( Atomic) 모델과 함께 상향식 접근(Bottom-up) 방식으로 접근
  • 아톰과 함께 상태를 생성하고 렌더링 최적화
    • 리액트 context의 Re-rendering 이슈를 해결하고 메모이제이션(memorization)의 의존도를 줄일 수 있음
  • React의 Context(useState + useContext) 기반 상태관리 모델에서 발생한 주요 이슈들의 개선에 초점을 맞춤

 

Recoil보다 선호되는 이유

  • 경량화된 API
  • String key의 미사용
  • TypeScript 기반
  • utils 함수들의 제공

 

Jotai는 언제 사용할까?

  • 작은 규모의 프로젝트
    • Redux나 MobX와 같은 다른 상태 관리 라이브러리보다 상대적으로 작음
  • 간단한 상태 관리
    • Redux나 MobX와 같은 상태 관리 라이브러리에 비해 보다 단순하고 직관적인 API를 제공
  • 별도의 라이브러리를 사용하지 않아도 됨
    • Context API와 함께 작동하며, Context API의 간편한 사용법을 이용하여 상태관리를 할 수 있음

 

Jotai 사용

atom()

import { atom } from 'jotai';

const priceAtom = atom(10); // number
const messageAtom = atom('hello'); // string
const productAtom = atom({ id: 12, name: 'good stuff' }); // object
const citiesAtom = atom(['Tokyo', 'Kyoto', 'Osaka']); // array
  • jotail의 내장 API
  • 상태의 단위(조각)이자 state를 생성하는 함수
  • Recoil과 달리 key값(string)을 설정하지 않음

 

읽기 / 쓰기 전용 atom

import { atom } from 'jotai';

const priceAtom = atom(10);

const readOnlyAtom = atom((get) => get(priceAtom) * 2);
const writeOnlyAtom = atom(
  null, // 첫 번째 인자로 전달하는 초기값은 null
  (get, set, update) => {
    // update는 atom을 업데이트하기 위해 받아오는 값
    set(priceAtom, get(priceAtom) - update.discount)
  }
);

const readWriteAtom = atom(
  (get) => get(priceAtom) * 2,
  (get, set, newPrice) => {
    set(proceAtom, newPrice / 2)
    // set 로직은 원하는 만큼 지정할 수 있음
  }
);

// 읽기(read)
import { useAtomValue } from 'jotai';
const count = useAtomValue(countAtom);

// 쓰기(write)
import { useSetValue } from 'jotai';
const count = useSetValue(countAtom);
  • 읽기 전용(Read-only)
  • 쓰기 전용(Write-only)
  • 읽기,쓰기 전용(Read-Write)

 

useAtom()

useAtom 훅은 atom을 인자로 받아, [atom, setAtom] 값과 세터함수를 튜플로 반환

상태를 적용하고자 하는 컴포넌트 내에서 useAtom을 import해서 상태 & 세터함수를 선언해주면 됨

import { useAtom } from 'jotai';
import { countAtom } from '../store';

function Counter() {
  const [count, setCount] = useAtom(countAtom)
  return (
    <div>
      {count}
      <button onClick={() => setCount(c => c + 1)}>+1</button>
    </div>
  )
}

 

 

Async

Jotai는 atom이 동기/비동기를 모두 담당

초기 fetch를 위해 write 함수인자를 활용하면 됨

const fetchUrlAtom = atom(async (get) => {
  const response = await fetch(get('https://my-api.com'));
  return await response.json();
})

 

비동기 상태 fetch간 노출할 목적으로, <Suspense> 컴포넌트로 감싸서 fallback을 설정해줘야 함

const App = () => (
  <Proveider>
    <Suspense fallback="Loading...">
      <Layout />
    </Suspense>
  </Provider>
)

 

Utils

 

Jotai에서 유용한 유틸을 제공해줌

리셋이나 스토리지 저장 등 다양한 메서드가 들어있음

import { atom, useAtom } from 'jotai';
import { useAtomValue, useUpdateAtom } from 'jotai/utils';

const exampleAtom = atom(0);

const Example = (0 => {
  // 기존 useAtom
  const [myAtom, setMyAtom] = useAtom(exampleAtom);
  
  // useAtomValue, useUpdateAtom 각각 적용
  connst myAyom = useAtomValue(exampleAtom);
  const setMyAtom = useUpdateAtom(exampleAtom);
  
  return <div>atom: {myAtom}</div>
}

 

AtomWithStorage

atom 상태값을 스토리지에 저장하는 유틸리티 함수. 토큰 등 스토리지와 연관되는 전역상태에 유용

인자는 키네임, 값, 옵션을 받으며, 옵션의 기본값은 localStorage

const anAtom = atomWithStorage('Is_key', [], {
  ...createJSONStorage(() => localStorage),
  delayInit: true,
})

 

😉🤔

기존에 나는 Recoil을 자주 사용했는데 위에 말했다 싶이 회사의 기술 스킬 동향과 핵심개발자가 퇴사로 인해 새로운 전역관리 라이브러리를 공부할 필요성이 느껴졌음. 나는 전역상태 라이브러리 중 Redux와 Recoil을 사용했는데 Recoil을 자주 사용한 이유는 내가 하는 프로젝트 규모가 크지 않고 Redux가 Recoil에 비해 보일러 플레이트가 굉장히 복잡하기 때문이었음 또한 Recoil은 API, 의미, 동작을 최대한 리액트스럽게 구현했기 때문에 React 프로젝트에서 자연스럽게 사용할 수 있어 매우 쉽고 편리했음. 따라서 잘 사용했던 Recoil과 비슷한 다른 라이브러리를 찾는것은 자연스런 수순이었던듯. Jotai에 대해 공부하게 되면서 각 라이브러리의 특징과 장단점을 비교하게 되었음. Jotai는 Recoil과 마찬가지로 간단하고 직관적인 API를 제공하여 프로젝트에 쉽게 통합할 수 있었음. 또한, Jotai는 최소한의 보일러플레이트 코드로 전역 상태 관리를 할 수 있어 초기 설정과 유지 보수가 용이했음. 특히 유틸함수가 내장되어있어 편리하다고 생각이 듬 다크모드 같은 경우 localStorage를 사용하게 되는데 유틸함수로 있기 때문에 여러모로 사용하기 좋은거 같음