728x90

React

React에서 전역 상태를 관리하기 위해서 Redux를 많이 사용합니다. 하지만, Redux의 사용법이 간단하지 않아서 대체하기 위한 라이브러리가 많이 나오고 있습니다.

이 게시글에서는 간단하고 사용하기 쉬운 sagen에 대해 알아보겠습니다.

sagen은 무엇인가?

먼저, sagen은 전역 상태 관리를 하기 위한 최선의 도구는 아닙니다. 아직 개발자 도구도 없으며, 개발이 지속적으로 이루어지고 있는 라이브러리입니다.

sagen에 대해 간단히 알아보기 전, sagen은 다른 라이브러리와 어떻게 다른지 알아보겠습니다. 현재 sagen@2.2.0의 MINIFIED + GZIPPED 사이즈는 1.1KB로, Redux@4.1.0(1.6KB)보다 가볍습니다.

sagen 시작하기

sagen을 이용해 상태 변경을 위한 스토어를 작성하기 위해서는 아래 두 줄의 코드가 필요합니다.

import { createStore } from 'sagen';

const Store = createStore({ num: 0 });

store를 만들기 위해서는 createStore 함수가 필요하며, 이 함수는 store가 갖고있는 값을 관리하기 위한 함수를 반환합니다.

리액트에서 store가 갖고 있는 값을 구독하기 위해서는 아래 코드처럼 작성하면 됩니다.

function NumberCounter() {
  const [store, setStore] = useGlobalStore(Store);

  return (
    <p>num: {store.num}</p>
  );
}

전체적인 사용법은 React.useState와 동일합니다.

차이점은 동기로 값이 변한다는 점과 구독하고 있는 모든 컴포넌트에 리렌더링을 발생시킨다는 점입니다.

sagen의 특징

여러 store 생성 가능

sagen은 기본적으로 여러 store를 만들 수 있습니다. Redux 등의 라이브러리는 보통 하나의 Store에 값이 모두 저장되고, 필요한 값을 selector로 불러 사용하지만, sagen은 원하는 단위로 store를 쪼개서 생성할 수 있습니다.

또한, Redux처럼 store내의 필요한 값만 selector를 이용해 가져올 수도 있습니다.

const infoStore = createStore({
  name: 'jungpaeng',
  age: 22,
});

const ageSelector = store => store.age;

function AgeComponent() {
  // Pass the ageSelector as the component uses only the age value.
  const [age, setInfo] = useGlobalStore(infoStore, ageSelector);

  const incrementAge = () => {
    setInfo(curr => ({ ...curr, age: curr.age + 1 }));
  };

  return (
    <div>
      <p>age: {age}</p>
      <button onClick={incrementAge}>
        Increment
      </button>
    </div>
  );
}

ContextAPI를 사용하지 않음

sagen은 ContextAPI가 사용된 Redux, Recoil 등과는 달리 ContextAPI가 사용되지 않았습니다. 이는 곧 React가 아닌 곳에서도 사용될 수 있음을 의미합니다.

sagen-core 라이브러리는 바닐라 자바스크립트를 위한 sagen store를 제공하며, sagencreateStoresagen-corecreateStore와 동일합니다.

즉, sagen-core에 React Hook을 제공한 것이 sagen 라이브러리입니다.

Redux의 middleware를 사용할 수 있음

Redux는 middleware를 제공함으로써 다양하게 기능을 확장할 수 있었습니다. sagen은 Redux의 middleware를 최대한 호환할 수 있는 구조를 갖고 있습니다.

const loggerMiddleware = store => next => action => {
  console.log('Current state', store.getState());
  console.log('Action', action);
  next(action);
  console.log('Next state', store.getState());
}

위 코드는 Redux의 간단한 logger 미들웨어이며, sagen에서 사용하는 방법은 다음과 같습니다.

const store = createStore(0, composeMiddleware(loggerMiddleware));

정해진 규칙이 없습니다.

Redux는 상태를 관리하기 위해 액션을 정의하고, dispatch를 사용하는 방식이 강제되었습니다. sagen은 Redux 스타일의 reducer를 이용해 관리하는 것과 React의 useState 스타일을 제공하고 있습니다. 즉, 사용자가 원하는 방식으로 간단히 작업을 할 수 있습니다.

useState 방식

import { createStore } from 'sagen';

const Store = createStore({ num: 0 });

function NumberCounter() {
  const [store, setStore] = useGlobalStore(Store);

  return (
    <p>num: {store.num}</p>
  );
}

Dispatch 방식

import { createStore } from 'sagen';

const Store = createStore({ num: 0 });
const storeDispatch = createDispatch(store);
const storeAction = Store.setAction((getter) => ({
  INCREMENT: () => getter() + 1,
  ADD: (num) => getter() + num,
}));

function NumberCounter() {
  // useSagenState Hook은 getter만을 반환하는 Hook 입니다.
  const store = useSagenState(Store);

  return (
    <div>
      <p>num: {store.num}</p>
      <button onClick={() => storeDispatch(storeAction.INCREMENT)}>
        Increment
      </button>
      <button onClick={() => storeDispatch(storeAction.ADD, 10)}>
        Add 10
      </button>
    </div>
  );
}

sagen의 단점

sagen은 상태 관리를 '간단히' 하고 싶은 사람에게 유용한 라이브러리입니다. 또한, 현재 개발이 활발하게 이뤄지고 있는 라이브러리입니다.

이는 다음과 같은 문제점이 있을 수 있습니다.

  1. 문서가 변경될 수 있습니다.
    현재 sagen의 문서는 ReadMe가 유일합니다. 라이브러리를 보다 쉽게 이해할 수 있도록 각 사용 예시를 설명하고 있지만 모든 사용 케이스가 나와있지는 않습니다. 때문에 어떻게 동작하는지 TypeScript에서 어떻게 사용하는지 확인하기 위해서는 코드를 열어봐야 할 수 있습니다.
  2. store를 디버깅할 수 있는 수단이 없습니다.
    이는 sagen store 내에서 문제가 발생했을 때 이를 확인해볼 수 있는 수단이 debugger 또는 console.log가 유일함을 의미합니다. 때문에 복잡한 상태 관리가 필요한 경우, sagen은 최선의 선택이 아닐 것입니다.

정리

sagen 라이브러리의 특징과 sagen을 사용하면서 생길 수 있는 문제에 대해 간단히 정리해보았습니다.

현재 sagen을 직접 사용하면서 문제가 생길 수 있는 부분은 수정하고, 보다 더 안정적인 라이브러리를 위한 준비를 하고 있습니다.

sagen에 대해 관심이 있다면 다음 문서를 확인해주세요. 한글 ReadMe도 있습니다.

728x90
728x90