React

React 상태관리를 파헤쳐보자 2탄

안루피 2024. 4. 23. 10:23
728x90

저번 게시글에 이어서 상태관리 라이브러리를 파헤쳐보자.

Redux까지 저번에 얘기했는데

 

 

Redux를 보완한 MobX

2. MobX

1. Action이 실행되면,

2. 상태값이 업데이트되고,

3. 해당 값을 구독하는 곳에 Notify가 되고,

4. 렌더링이 트리거된다.

 

class Counter {
  number = 0;

  constructor() {
    makeAutoObservable(this);
  }

  add() {
    this.number += 1;
  }
}

const counter = new Counter()

function App() {
  return (
    <div>
      <span>{count}</span>
      <button onClick={counter.add}>+</button>
    </div>
  )
}

 

리덕스에 비해 코드량이 훨씬 적고, 상당히 객체지향적인 것을 확인할 수 있다.

리덕스에서는 Reducer를 정의하고, Reducer를 위한 액션을 따로 만들어야했지만,

MobX에서는 Action이 직접 상태를 업데이트할 수 있다.

 

이렇게 쉽고 간결한 MobX의 최대 단점은 커뮤니티가 작다는 것이다.

 

 

그리고 나타난 신인, Recoil

 

3. Recoil

 

Facebook의 Recoil은 Redux의  Store와 유사하면서도 다른 atom이라는 개념을 내놓았다. 

Recoil은 Context API 기반으로 구현된 라이브러리지만, 위에서 말한 Context API의 문제점들을 해결해준다.

또한 거대한 보일러 플레이트를 작성해야하는 Redux와 달리,

Recoil은 React의 useState와 유사하게 사용할 수 있으며, 굉장히 간결하게 작성할 수 있다.

 

export const numberAtom = atom({
  key: 'number',
  default: 0,
})

function App() {
  const [number, setNumber] = useRecoilState(numberAtom)

  const add = () => {
    setNumber(number + 1)
  }

  return (
    <RecoilRoot>
      <span>{count}</span>
      <button onClick={add}>+</button>
    </RecoilRoot>
  )
}

 

이때까지 상태관리를 트리 형태의 2차원적인 관점에서 바라봤다면,

아톰이라는 개념은 3차원적인 관점이라고 볼 수 있다.

 

 

결론적으로, store라는 외부 요인이 아닌 context API 기반으로 되어있다는 것이

Redux와 MobX와의 가장 큰 차이점이라고 할 수 있다.

 

 

 

그럼 Zustand랑 Jotai는 뭔데?

Zustand는 Redux처럼 Flux를 사용하는 친구다.

차이점이라면,

 

1. SSR을 공식적으로 지원한다.

2. Redux보다 더 간단하고 직관적인 훅 기반의 API를 제공한다.

3. Provider로 감싸지 않아도 된다.

 

export const useCounter = create((set) => ({
  number: 1,
  add: () => set((state) => ({ number: state.count + 1 })),
}))

function App() {
  const { number, add } = useCounter();

  return (
    <div>
      <span>{count}</span>
      <button onClick={add}>+</button>
    </div>
  )
}

 

 

Jotai는 Recoil처럼 atomic 개념을 따르는 친구다,

차이점이라면,

 

1. SSR을 공식적으로 지원한다.

2. key를 정의할 필요가 없다.

3. 여러 Provider를 정의할 수 있다. (Provider를 사용하지 않으면 글로벌에 존재하는 atom에 저장된다.)

4. store 기능으로 리액트 컴포넌트 밖에서 훅 없이 상태값을 바꿀 수 있다. 

(하지만 권장하지 않는다..)

 

export const numberAtom = atom(0)

function App() {
  const [number, setNumber] = useAtom(numberAtom)

  const add = () => {
    setNumber(number + 1)
  }

  return (
    <RecoilRoot>
      <span>{count}</span>
      <button onClick={add}>+</button>
    </RecoilRoot>
  )

 

 

그래서 뭘 써야 하나요...?

 

 

 

 

atomic 패턴을 사용하고 싶다면 jotai를,

flux 패턴을 사용하고 싶다면 redux-toolkit이나 zustand를 사용하면 된다는 것이 결론!

 

어떤 패턴, 어떤 라이브러리가 더 좋냐는 것에 대해서는 정답이 없다고 생각한다.

프로젝트마다 규모도 다르고 성격도 다르기 때문에, 프로젝트에 알맞는 라이브러리를 잘 선택하는 것이 정답!

 

 

 

 

굳이 redux-toolkit과 zustand를 비교해야겠다면, 서버 상태 관리 라이브러리가 큰 갈림길이 될 것 같다.

redux-toolkit은 redux-toolkit Query와 함께 사용되고, Zustand는 React-query와 상당히 조합이 괜찮다.

 

recoil, zustand, jotai 모두 비동기 처리를 지원하긴 하지만,

현재로서는 infinite query, mutation, optimistic updates 등 react-query에서 제공하는 기능이 더 압도적으로 보인다

 

 

 

jotai + react-query

 

클라이언트 상태관리에 비해 서버 상태 관리가 더 큰 비중을 차지 한다면

클라이언트 내부의 상태를 관리하는 것보다는 서버에서 받아온 데이터들에 대한 상태관리가 더 중요한 경우라면

 

클라이언트 상태 관리를 할 만한 것들은 전역적인 alert 상태와 같은 자잘한 것들이 대부분이고,

해당 상태들만 atom으로 간결하게 선언하고 대부분의 상태는 props drilling이 심하지 않다면 

props로 넘겨도 좋을 듯

 

서버 상태 관리로는 react-query가 다양한 기능을 제공해서 주로 사용하는 편이고,

클라이언트 상태 관리와 서버 상태 관리를 분리하는 것이 좋을 것 같다.

 

그리고 훅 기반의 API는 사용성도 좋을 뿐만 아니라, 리액트의 생명주기와 동일하게 상태를 가져갈 수 있는

장점도 가진다고 생각한다. Redux와 MobX의 경우, 상태가 리액트의 범위 밖에서 정의되고 핸들링된다.

더욱 복잡한 상태관리가 가능하다는 장점이 될 수도 있지만, 디버깅 및 추적이 어려워질 수잒에 없다는 단점이 된다고 생각한다.

 

Zustand와 Jotai에서도 store라는 기능을 사용하면 리액트없이 사용할 수 있지만, 어떤 사이드 이펙트가

발생할지 모른다는 점에서 사용을 지양하는 편이 좋겠다.

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90