리액트가 쉬워지는 상태 관리 라이브러리

Zustand for State Management


들어가며

“상태 관리” 하면 무엇이 가장 먼저 떠오르나요?
Redux MobX Context API Recoil 등등..
다양한 상태 관리 라이브러리가 있고 각각 장단점이 있지만 저는 그중에서 Redux를 사용해왔는데 Redux보다 편리하게 사용할 수 있을 것 같은 라이브러리를 소개합니다.

Zustand..?

간소화된 플럭스 원칙을 사용하는 작고 빠르고 확장 가능한 베어본 상태 관리 솔루션이라고 소개하고 있어요.
현재(22.05.20) 기준으로 github 17k stars를 기록하고 있는 인기 라이브러리에요.

아래와 같은 특징들을 갖고 있어요.

  • Redux와 사용 방법이 거의 유사한 듯.
  • 기존의 Store 구조(한 개의 중앙 집중된 형식)를 활용하는 것은 동일하지만 사용 방법이 비교적 단순하다.
  • Provider로 감싸주지 않아도 된다.
  • context 방식보다 리렌더링이 줄어든다.

흔하디흔한 예제, 카운터를 만들어보자.

프로젝트를 생성하고 시작하는 과정은 생략할게요.
CRA를 통해서 만들면 뚝딱 만들어질 테니까요.
어떻게 사용하는지 알아보기 위한 예제니까 별도의 컴포넌트 파일로 분리하지 않고 App.js 파일 하나에 코드를 모두 작성할 거예요.

1. 일단 설치부터 하고

# npm
npm install zustand

# yarn
yarn add zustand

2. App.js에 코드를 작성해 보자

export default App() {
  return(
    <div className="App">
      <Counter />
      <button>증가</button>
      <button>감소</button>
    </div>
  )
}

const Counter = () => {
  return <div>Counter</div>
}

3. store 생성

import create from 'zustand';

const useStore = create(() => ({
  count: 0,
}));

스토어에는 카운터를 만들 거니까 count라는 상태를 만들었어요. 스토어 생성 끝.

4. store에서 값 가져오기

import create from "zustand";

const useStore = create(() => ({
  count: 0,
}));

const useStore

export default App() {
  return(
    <div className="App">
      <Counter />
      <button>증가</button>
      <button>감소</button>
    </div>
  )
}

const Counter = () => {
  const { count } = useStore();

  return <div>Counter : { count }</div>
}

Counter 컴포넌트에서 그냥 const { count } = useStore();로 불러오면 돼요. <div>Counter : { count }</div> 그리고 그냥 바인드 해줘요. 무언가 더 해줄 것도 없어요.

5. store의 값을 변경하기

import create from "zustand";

const useStore = create(() => ({
  count: 0,
}));

const useStore

export default App() {
  const { count } = useStore();

  return(
    <div className="App">
      <Counter />
      <button onClick={() => {
        useStore.setState({
          count : count + 1
        });
      }}>증가</button>
      <button onClick={() => {
        useStore.setState({
          count : count - 1
        })
      }}>감소</button>
    </div>
  )
}

const Counter = () => {
  const { count } = useStore();

  return <div>Counter : { count }</div>
}

state를 변경해 주려고 buttononClick 이벤트를 만들고 setState를 시켜줬어요.
그런데 이렇게 state를 각각의 이벤트에서 변경해 주면 많은 컴포넌트에서 사용하게 될 경우 문제가 발생할 여지가 많아 보여요. 수정을 좀 해야겠네요.

6. store의 값을 변경하기 : 수정

import create from "zustand";

const useStore = create((set) => ({
  count: 0,
  increase: () => set((state) => ({ count: state.count + 1 })),
  decrease: () => set((state) => ({ count: state.count - 1 }))
}));

const useStore

export default App() {
  const { count } = useStore();

  return(
    <div className="App">
      <Counter />
      <button onClick={ increase }>증가</button>
      <button onClick={ decrease }>감소</button>
    </div>
  )
}

const Counter = () => {
  const { count } = useStore();

  return <div>Counter : { count }</div>
}

state의 값을 변경하는 함수를 store에 미리 만들어 놓고 필요한 컴포넌트에서 불러다 사용하는 것이 여러 컴포넌트에서 사용할 때에 버그를 유발하지 않고 더 좋을 거예요.

데모 코드는 다음 링크에서 다시 보실 수 있어요. [codesandbox 링크]

Debugging은 어떻게?

Redux를 사용할 때와 동일하게 미들웨어를 통해 redux-devtools를 그대로 사용할 수 있어요.

// store.js
import create from 'zustand';
import { devtools } from 'zustand/middleware';

const store = () => ({
  // 생략
});

const useStore = create(devtools(store));

export default useStore;

마치며

우와! 이거 엄청 쉽고 편리해요! 하려고 아주 간단한 예제로 보여드렸어요.
깃허브의 readme를 읽어보면 사용하기 쉽다는 것 외에도 많은 기능들을 제공하는 걸 알 수 있어요. 심지어 리액트 없이 vanila javascript에서도 사용할 수 있다고 하네요.
이만 zustand 라이브러리 소개 글을 마치겠습니다.



참고문서


추천 글