본문 바로가기

카테고리 없음

The Best React State Management Tools for Enterprise Applications

아래 글을 번역 했습니다.

 

React State Management Tools for Enterprise Applications | Toptal®

State management is important in any React project, but it is crucial in enterprise-level applications. Here’s how the most popular React state management tools compare in an enterprise setting.

www.toptal.com

 

엔터프라이즈 레벨 리액트 애플리케이션 개발자들은 일관된 사용자 경험을 위해 상태관리가 얼마나 중요한지 알고 있습니다. 하지만 상태 관리의 영향을 받는 것은 사용자만이 아닙니다. 리액트 개발자는 state를 생성하고 관리합니다. 개발자들은 상태 관리가 단순하며 확장 가능하고, 원자적이어야 한다고 생각합니다. 리액트 훅은 이를 도입함으로써 이러한 방향으로 나아가고 있습니다. 

 

여러 컴포넌트 간에 상태를 공유해야 하는 경우 문제가 발생할 수 있습니다. 엔지니어는 필요에 맞는 도구와 라이브러리를 찾아야 하며, 동시에 엔터프라이즈급 앱에 필요한 높은 표준을 충족해야 합니다. 

 

이 글에서는 가장 많이 사용되는 라이브러리를 분석하고 비교하여 엔터프라이즈급 애플리케이션에서 리액트 상태 관리에 가장 적합한 라이브러리를 선택해 보겠습니다. 

 

Built-in React State Management Capabilities

리액트에는 여러 컴포넌트에 걸쳐 데이터를 제공하기 위한 훌륭한 도구가 있습니다. 컨텍스트 API의 주요 목적은 prop drilling을 피하는 것입니다. 우리의 목표는 잦은 업데이트, 재설계, 새로운 기능 도입 등 엔터프라이즈 애플리케이션에서 발생할 수 있는 다양한 시나리오에서 상태를 관리할 수 있는 쉬운 도구를 만드는 것입니다.

 

The only advantage of Context is that it doesn't depend on a third-party library, but that can't outweigh the effort to maintain this approach.

 

이론적으로는 컨텍스트를 통해 이 모든 작업을 수행할 수 있지만, 설정, 지원 및 최적화에 시간이 필요한 맞춤형 솔류션이 필요합니다. 컨텍스트의 유일한 장점은 타사 라이브러리에 의존하지 않는다는 점이지만, 이 접근 방식을 유지하기 위한 노력을 능가할 수는 없습니다. 

 

React의 팀원 Sebastian Markbage가 언급했듯이, 컨텍스트 API는 빈도가 높은 업데이트를 위해 구축되고 최적화된 것이 아니라 테마 업데이트 및 인증 관리와 같은 빈도가 낮은 업데이트를 위해 개발되었습니다. 

 

Examining Existing React State management Libraries

Github에는 수십 가지의 상태 관리 라이브러리가 있습니다.하지만 모든 도구를 고려하면 끝없이 조사하고 비교해야 합니다. 그래서 저는 인기도, 사용량, 메인테이너를 기준으로  세가지 주요 경쟁 서비스로 선택 범위를 좁혔습니다.

 

비교를 정확하게 하기 위해 다음과 같은 품질 속성을 사용했습니다. 

 

- Usability

- Maintainability

- Performance

- Testability

- Scalability(works with the same performance on the bigger states)

- Modifiability

- Reusability

- Ecosystem(has a variety of helper tools to extend the functionality)

- Community(has a lot of users and their questions are answered on the web)

- Portability(can be used with libraries/frameworks other than React)

 

 

Redux

Redux는 2015년에 만들어진 상태 관리 라이브러리입니다. 이 라이브러리가 큰 인기를 끌게 된 이유는 다음과 같습니다.

 

- 출시 당시에는 마땅한 대안이 없었다.

- 상태와 액션을 분리할 수 있었다.

- 리액트 리덕스의 마법을 통해 state와 액션을 간단하게 연결할  수 있었다.

- 리덕스 라이브러리의 공동 제작자는 저명한 Facebook의 개발자이자 React의 핵심 팀원인 Dan abramov이다.

 

데이터가 있는 글로벌 스토어가 있고, 스토어를 업데이트해야 할 때마다 action을 dispatch한다.(reducer로 이동) 액션의 유형에 따라서 리듀서는 immutable한 방식으로 상태를 업데이트한다. 리액트와 함께 리덕스를 사용하려면 react-redux를 사용해 컴포넌트를 store 업데이트에 구독해야 한다.

 

 

Redux API Example

슬라이스는 다른 도구와 차별화되는 Redux 코드 베이스의 기본 부분입니다. 슬라이스에는 액션과 리듀서의 모든 로직이 포함되어 있습니다.

 

// slices/counter.js
import { createSlice } from "@reduxjs/toolkit";

export const slice = createSlice({
  name: "counter",
  initialState: {
    value: 0
  },
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    }
  }
});

export const actions = slice.actions;
export const reducer = slice.reducer;


// store.js
import { configureStore } from "@reduxjs/toolkit";
import { reducer as counterReducer } from "./slices/counter";

export default configureStore({
  reducer: {
    counter: counterReducer
  }
});


// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import App from './App'
import store from './store'

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)


// App.js
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { actions } from "./slices/counter";

const App = () => {
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();

  return (
    <div>
      <div>
        <button onClick={() => dispatch(actions.increment())}>Increment</button>
        <span>{count}</span>
        <button onClick={() => dispatch(actions.decrement())}>Decrement</button>
      </div>
    </div>
  );
};

export default App;

Quality Attributes

- Usability: 공식 툴킷 패키지의 도입으로 Redux는 매우 간단해졌습니다. 슬라이스(초기 상태, 리듀서, 액션)를 생성하여 스토어에 전달하고 훅을 통해 컴포넌트에서 엑세스하기만 하면 됩니다. RTK 쿼리가 추가되면서 표준 데이터 쿼리를 처리하여 Redux 툴킷의 사용성이 더욱 향상되었습니다. 

- Maintainability: 리덕스는 간단합니다. 무언가를 개선하거나 복구하는 방법을 이해하는데 깊은 지식이 필요하지 않습니다.

- Performance: 리덕스의 성능에 가장 큰 영향을 미치는 사람은 소프트웨어 엔지니어입니다. 리덕스는 많은 로직이 필요 없는 간단한 도구입니다. 상태 업데이트가 느리다고 생각되면 공식 가이드라인에 따라 더 빠르게 만들 수 있습니다.

- Testability: 리덕스는 순수한 함수(액션과 리듀서)로 구성되어 있어 단위 테스트에 적합합니다. 또한 스토어, 액션, 리듀서가 함께 작동하는 통합 테스트를 작성할 수 있는 메커니즘을 제공합니다. 

- Scalability: 기본적으로 Redux는 하나의 전역 상태를 가지므로 확장하기 어렵습니다. 하지만 redux-dynamic-modules 라이브러리를 사용하면 모듈식 리듀서와 미들웨어를 생성할 수 있습니다.

- Modifiability: 미들웨어를 지원하기 때문에 Redux를 쉽게 커스터마이징 할 수 있습니다. 

- Pulse: 리덕스는 여전히 정기적으로 업데이트 및 유지 관리되고 있지만, 프로젝트가 성숙해짐에 따라 전반적으로 개발 속도가 느려졌습니다.

 

MobX

Mobx는 비교적 오래된 또 다른 라이브러리로 Github에서 약 25800개의 별을 보유하고 있습니다. 리덕스와 다른 점은 OOP 패러다임을 따르고 옵저버블을 사용한다는 점입니다. Mobx는 Michel Weststrate가 만들었으며, 현재 보스턴에 본사를 둔 Mendix의 도움을 받아 오픈소스 애호가 그룹이 유지 관리하고 있습니다. 

 

MobX에서는 생성자 내부에 관찰 가능한 저장소인 makeObservable 호출이 있는 JavaScript 클래스를 생성합니다.(적절한 로더가 있는 경우 @observable 데코레이터를 사용할 수 있음). 그런 다음 클래스의 속성(상태)과 메서드(동작 및 계산된 값)을 선언합니다. 컴포넌트는 이 관찰 가능한 저장소를 구독하여 상태, 계산된 값 및 액션에 액세스 합니다. 

 

MobX의 또 다른 필수 기능은 가변성입니다. 부작용을 피하고 싶을 때 상태를 조용히 업데이트할 수 있습니다.

 

MobX API Example

MobX의 고유한 특징은 모든 마법이 숨겨져 있는 거의 순수한 ES6 클래스를 생성한다는 것입니다. 로직에 집중할 수 있도록 라이브러리별 코드가 덜 필요합니다.

 

// stores/counter.js
import { makeAutoObservable } from "mobx";

class CounterStore {
  value = 0;

  constructor() {
    makeAutoObservable(this);
  }

  increment() {
    this.value += 1;
  }

  decrement() {
    this.value -= 1;
  }
}

export default CounterStore;


// index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "mobx-react";
import App from "./App";
import CounterStore from "./stores/counter";

ReactDOM.render(
  <Provider counter={new CounterStore()}>
    <App />
  </Provider>,
  document.getElementById("root")
);


// App.js
import React from "react";
import { inject, observer } from "mobx-react";

const App = inject((stores) => ({ counter: stores.counter }))(
  observer(({ counter }) => {
    return (
      <div>
        <div>
          <button onClick={() => counter.increment()}>Increment</button>
          <span>{counter.value}</span>
          <button onClick={() => counter.decrement()}>Decrement</button>
        </div>
      </div>
    );
  })
);

export default App;

 

- Usability: 관찰 가능한 저장소는 상태 관리를 위한 단일 진입점입니다. 수정할 수 있는 유일한 장소가 있기 때문에 MobX를 간단하게 사용할 수 있습니다. 

- Maintainability: 상당한 단점입니다. RxJS API에 대한 지식이 없으면 원하는 결과를 얻을 수 없습니다. 자격이 부족한 팀에서 MobX를 사용하면  상태 불일치 문제가 발생할 수 있다.

- Performance: MobX는 독립적인 스토어들로 구성되어 있으며 필요한 스토어만 구독할 수 있습니다.

- Testability: 옵저버블 스토어는 반응형 기능이 숨겨진 일반 자바스크립트 객체입니다. 테스트는 다른 JavaScript 클래스와 동일합니다. 

- Scalability: 옵저버블 스토어는 논리적으로 분할되므로 MobX를 확장하는 데 어려움이 없습니다. 

- Modifiability: MobX를 사용하면 동작을 수정한 사용자 정의 옵저버블을 생성할 수 있습니다. 또한 리액션이라는 개념이 있습니다. 리액션은 자동 부작용을 모델링합니다. 이러한 것들은 MobX를 매우 커스터마이징할 수 있게 해줍니다. 

- Reusability: MobX는 프레임워크에 구애받지 않으므로 재사용성이 뛰어납니다. 

- Pulse: MobX는 정기적으로 업데이트 및 유지 관리되고 있으며, Redux와 마찬가지로 성숙 단계에 도달했습니다. 

Recoil

리코일은 비교적 최근에 출시된 것으로 리액트 팀의 최신 아이디어입니다. 기본 아이디어는 공유 상태 및 파생 데이터와 같은 누락된 리액트 기능을 간단하게 구현하는 것입니다. 실험적인 라이브러리가 왜 엔터프라이즈급 프로젝트에 검토되었는지 궁금하실 것입니다. 리코일은 페이스북의 지원을 받아 일부 애플리케이션에서 사용되고 있으며 리액트의 상태 공유에 완전히 새로운 접근 방식을 가져왔습니다 .리코일이 더 이상 사용되지 않더라도 Jotai와 같이 같은 경로를 따르는 다른 도구가 주목을 받을 것이라고 확신합니다.

 

리코일은 atom과 selector는 두가지 용어를 기반으로 합니다. 아톰은 공유 상태 조각입니다. 컴포넌트는 아톰을 구독하여 그 값을 가져오거나 설정할 수 있습니다. 

이미지에서 볼 수 있듯이 값이 변경되면 구독된 컴포넌트만 다시 렌더링됩니다. 이를 통해 리코일의 성능이 매우 향상됩니다. 리코일의 또다른 장점은 셀렉터입니다. 셀렉터는 원자 또는 다른 셀렉터에서 집계된 값입니다. 소비자 입장에서는 원자 또는 다른 셀렉터에서 집계된 값입니다. 소비자 입장에서는 아톰과 셀렉터가 차이가 없으며, 반응형 부분을 구독하고 사용하기만 하면 됩니다. 

 

원자/선택자가 변경될 때마다 해당 원자/선택자를 사용하는(즉, 구독 중인) 선택자가 재평가됩니다. 

 

Recoil API Example

리코일의 코드는 경쟁사들과는 상당히 다릅니다. 리액트 훅을 기반으로 하며 상태 변경보다는 상태 구조에 더 중점을 둡니다.

 

// atoms/counter.js
import { atom } from "recoil";

const counterAtom = atom({
  key: "counter",
  default: 0
});

export default counterAtom;


// index.js
import React from "react";
import ReactDOM from "react-dom";
import { RecoilRoot } from "recoil";
import App from "./App";

ReactDOM.render(
  <RecoilRoot>
    <App />
  </RecoilRoot>,
  document.getElementById("root")
);


// App.js
import React from "react";
import { useRecoilState } from "recoil";
import counterAtom from "./atoms/counter";

const App = () => {
  const [count, setCount] = useRecoilState(counterAtom);

  return (
    <div>
      <div>
        <button onClick={() => setCount(count + 1)}>Increment</button>
        <span>{count}</span>
        <button onClick={() => setCount(count - 1)}>Decrement</button>
      </div>
    </div>
  );
};

export default App;

 

- Performance: 리코일은 리액트 외부에 상태 트리를 구축합니다. 상태 트리를 사용하면 전체 트리의 변경 사항이 아닌 필요한 것만 가져와서 들을 수 있습니다. 또한 내부적으로 잘 최적화되어있습니다. 

- Reusability: 리코일은 리액트에 의존합니다. 다른 곳에서 재사용할 수 없습니다. 

 

Which Tool Results in the Best State Management for React? 

이러한 리액트 글로벌 상태 관리 라이브러리는 엔터프라이즈급 앱과 관련하여 다양한 장단점을 제공합니다.

MobX는 리액티브 프로그래밍에 대한 기본적인 이해가 필요하다. 프로젝트에 참여하는 엔지니어의 숙련도가 충분하지 않으면 애플리케이션 코드 불일치, 성능 문제, 개발 시간 증가 등의 문제가 발생할 수 있다. 팀에서 반응형 프로그래밍에 대한 이해가 있으면 MobX를 사용할 수 있으며 요구 사항을 충족할 수 있다.

 

리덕스에도 몇 가지 문제가 있는데, 주로 확장성과 성능과 관련된 문제이다. 하지만 MobX와 달리 이러한 문제에 대한 검증된 솔루션이 있습니다.