들어가며



안녕하세요. Playce Dev team Frontend 개발자 노은지입니다. :blush:


난 글에서 저희는 State의 개념에 대해 이해하고, state를 슬기롭게 관리 하기 위한 방법에 어떤 것들이 있는지, 왜 Client state와 Server state를 나누어 관리해야 하는지 이해해보는 시간을 가졌습니다. 


지난 글을 읽지 못하셨다면 여기를 눌러 읽어 보시는 것을 추천드립니다 🙂


또한 server state 를 client state 와 분리하게 되면서, 기존에 사용하던 Redux 보다 가볍고, 쉽게 client state 를 관리할 수 있는 새로운 전역 상태 관리 라이브러리들에 대한 필요성이 생겨났을 것이라 추측하였습니다.


그럼 이번에는 다양한 전역 상태 관리 라이브러리에 대해서 조금 알아보는 시간을 가져볼까요?


피드백은 언제나 환영입니다 🙂









다양한 전역 상태 관리 라이브러리


앞서 정리한 내용을 토대로 저는 한 어플리케이션 내의 다수의 컴포넌트에서 쓰이고, 자주 변화하는 여러 개의 프로퍼티를 가진 객체 형태의 state 는 React Hooks 나 Context API 로 관리 하는 것이 한계가 있기 때문에 전역 상태관리 라이브러리를 쓰는 것이 좋겠구나, 라는 것을 깨달았습니다.


하지만 현존하는 매우 다양한 상태관리 도구들을 선택하는 데 있어서 우리는 또 고민에 빠지게 될 수밖에 없습니다. 따라서, 간단한 카운터 앱을 구현한 예제코드를 통해서 Redux 를 포함한 다른 전역 상태 관리 도구들을 서로 비교해 보려고 합니다.


Redux 에서 다른 tool 로의 핵심적인 migration 이유가 더 가볍고, 쉬운 state management 가 가능하기 때문이라고 생각하여서, 다른 기능들 보다 그 부분에 초점을 맞추어 비교했습니다. 



이미지 출처: npm trends 

 

 


Redux, React-redux


Redux는 현존하는 라이브러리들 중 독보적으로 많이 사용 되고 있는 전역 상태관리 라이브러리입니다. npm trends 차트에서 볼 수 있듯이, Redux는 월등히 높은 npm 다운로드 수와 Github star를 보유하고 있습니다.


Redux 공식문서는 Redux를 이용해 일관적으로 동작하고, 서로 다른 환경에서 작동하며, 테스트하기 쉬운 앱을 설계할 수 있다고 말합니다. Flux 패턴의 구현체로도 널리 알려진 Redux는 Model – View – Controller의 양방향 패턴이 가지는 복잡하고 예측이 불가능한 데이터 흐름 때문에 생기는 문제들을 해결

하기 위한 대안으로 떠올랐습니다.


Redux에 대한 보다 더 자세한 내용은 Playce Dev team 이정현 Front-end 개발자님의 “복잡하고 어려운 Redux 적응기”에서 찾아 보실 수 있습니다.


이미지 출처: https://haruair.github.io/flux/docs/overview.html


핵심개념


Flux 패턴은 크게 3개 부분인 dispatcher, store, view(React components)로 구성되어있습니다. 모든 데이터는 중앙 허브인 dispatcher를 통해 흐르며 화면을 보는 유저의 상호작용에 의해서 action creator 메서드를 통해 action들이 dispatcher로 전달되고 동작합니다. 어플리케이션의 데이터와 비즈니스로직을 가지고 있는 store는 action이 전파되면 이 action에 영향을 받는 모든 view를 갱신하게 됩니다.

어플리케이션의 state는 store에 의해서 관리되고 이것은 어플리케이션의 다른 부분과 완전히 분리된 상태로 유지하여 state로 하여금 어플리케이션의 다른 부분들과의 의존도를 낮추는 것을 가능하게 합니다.


장점

    • 상태의 중앙화: Redux는 중앙화된 store를 제공하기 때문에 state를 관리하기 쉬우며 디버깅에 용이합니다.

    • 예측 가능한 상태: Redux는 순수 함수(리듀서)를 사용하기 때문에 state의 변화와 버그를 추적하기 쉽습니다.

    • 쉬운 테스트: Redux의 state가 순수 함수인 리듀서에 의해 update 되기 때문에 어플리케이션의 state와 행동을 테스트하기 쉽게 해 줍니다.

    • 높은 상호 운용성: Redux는 널리 쓰이고 있으며, 많은 라이브러리들과 통합 가능하기 때문에 이것을 활용한 부가적 기능의 원활한 구현이 가능합니다.

    • 규모가 큰 커뮤니티: 오래된 역사를 자랑하는 Redux는 그에 맞게 큰 규모의 커뮤니티를 보유하고 있기 때문에 개발자들에게 풍부한 리소스를 제공할 수 있습니다.

단점

    • 많은 양의 보일러 플레이트: 많은 개발자들이 많은 양의 보일러 플레이트를 Redux의 큰 단점으로 꼽았습니다. 특히 store, action, reducer들을 정의할 때가 그렇습니다. 아래의 코드는 Redux를 활용해 카운터 앱을 만든 예시 코드입니다.

// configStore.js

import { createStore } from "redux";
import { combineReducers } from "redux";
import counter from "../modules/counter";

const rootReducer = combineReducers({
    counter: counter,
});
const store = createStore(rootReducer);

export default store;

rootReducer와 store를 생성해 줍니다. 이곳에서 모든 리듀서들을 한 번에 관리할 수 있습니다.

// index.js에서 Provider로 App을 감싸주고 import한 store를 넣어 줍니다.

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import store from "./redux/config/configStore";
import { Provider } from "react-redux";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(

  <Provider store={store}>
    <App />
  </Provider>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

Provider로 감싸 store를 최상단 컴포넌트에 보내줍니다. App 컴포넌트는 이제 store를 구독하게 되었습니다.

// module.js

// 초기 상태값
const initialState = {
  number: 0,
};

// 액션밸류
const PLUS_ONE = "PLUS_ONE";
const MINUS_ONE = "MINUS_ONE";

// 액션크리에이터
export const plusOne = () => {
  return {
    type: PLUS_ONE,
  };
};

export const minusOne = () => {
  return {
    type: MINUS_ONE,
  };
};

// 리듀서
const counter = (state = initialState, action) => {
  switch (action.type) {
    case PLUS_ONE:
      return {
        number: state.number + 1, 
      };

    case MINUS_ONE:
      return {
        number: state.number - 1,
      };
    default:
      return state;
  }
};

export default counter;

모듈에 initial state, action value, action creator, reducer를 정의합니다.

import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { plusOne, minusOne } from "./redux/modules/counter";

const App = () => {
  const dispatch = useDispatch();
  const number = 0;
  const globalNumber = useSelector((state) => state.counter.number);

  const onClickAddNumberHandler = () => {
    dispatch(plusOne(number));
  };

  const onClickMinusNumberHandler = () => {
    dispatch(minusOne(number));
  };
  return (
    <div>
      <div>{globalNumber}</div>
      <button onClick={onClickAddNumberHandler}>+1</button>

      <button onClick={onClickMinusNumberHandler}>-1</button>
    </div>
  );
};

export default App;

단순히 +1, -1을 하는 기능이 있는 어플리케이션의 모듈을 생성하는 과정에서 우리는 action value, action creator, reducer를 각각 정의해주어야 합니다.


이러한 불편으로 인해 Redux팀은 Redux-toolkit(RTK)을 공식적으로 사용하기를 권장하고 있습니다. RTK는 createSlice에 각 Action에 따른 reducer를 작성할 수 있도록 하여 보일러 플레이트를 줄였으며, 기존 Redux는 서버와의 비동기 통신 등 유용하게 활용하기 위해서 많은 라이브러리를 설치해야 했던 반면, RTK는 ‘toolkit’이란 이름답게 더 많은 기능을 제공하고 있습니다. 


이 글에서는 RTK에 대한 내용은 자세히 다루지 않고 있지만, 공식 문서를 통해 자세한 내용을 확인할 수 있습니다.

    • 복잡함: 작고 간단한 어플리케이션을 구현할 때, Redux를 사용하는 것은 오히려 프로젝트를 복잡하게 만들 수 있습니다.

    • 높은 러닝커브: Redux는 state 관리와 함수형 프로그래밍 개념에 익숙지 않은 주니어 개발자들이 배우기 쉽지 않습니다.

    • 퍼포먼스: Redux는 중앙화된 store를 통해 state의 업데이트가 이루어지고 state가 변화할 때마다 리렌더링 되기 때문에 퍼포먼스 측면에서 좋지 않을 수 있습니다.

왜 리듀서를 사용하는 것이 테스트와 추적을 쉽게 만들어 주는 걸까요?

순수 함수란 동일한 인자가 전달되면 항상 동일한 결과를 반환하는 함수(코드블록)입니다. 그리고 리듀서는 순수한 함수로 만들어졌기 때문에 그 결과를 쉽게 예측할 수 있습니다.





Zustand

이미지 출처: https://docs.pmnd.rs/zustand/getting-started/introduction

Zustand는 작고 빠르고 확장성이 좋은 상태 관리 툴이며 hooks를 기반으로 사용이 편리한 API를 제공합니다. 보일러 플레이트가 없고, 유연하지만 명확성을 가질 만큼 충분한 convention 또한 보유하고 있는 Flux-like 상태관리 툴입니다.


 Zustand의 창시자 Daishi Kato는 Zustand가 React에서의 Zombie child 문제, React concurrency, renderers 사이에서 일어나는 context loss와 같은 문제들을 해결할 수 있는 상태관리 매니저가 될 수 있다고 말합니다.   


Zustand 가 해결 하고자 하는 문제들

 

Zombie Child 

예를 들자면 이런 것입니다. First pass에서 여러 nested connected components들이 mount 되면서, child compent가 parent component 보다 먼저 store를 구독하는 경우가 발생합니다. 

이때, todo item과 같은 data를 store에서 지우는 action을 dispatch 하게 되면 결과적으로 parent 컴포넌트는 해당하는 child compent의 렌더링을 멈추어야 합니다. 

하지만 child compent가 parent compent 보다 먼저 store를 구독하고 있는 상태이기 때문에 extraction logic에 유의하지 않을 경우 action에 의해 지워진 데이터는 더 이상 존재하지 않음에도 불구하고 child compent가 렌더링이 되면서 에러를 초래할 수 있습니다.  


React concurrency

concurrent란 동시성을 뜻합니다. Concurrency issue란 Zombie child 문제와 비슷하게, 2개 이상의 컴포넌트들이 같은 가변성 데이터 소스로부터 데이터를 받아올 때, 오직 일부 components만이 같은 결과를 갖는 일이 발생할 수 있습니다.

한가지 예로, 가변적 데이터를 제공하는 소스로부터 data를 요청하는 경우, 밀리초의 간격을 두고 서로 다른 컴포넌트들이 같은 가변적 data를 제공 받더라도 동시에 서로 조금 다른 결과 값을 보여주게 될지도 모르죠. 따라서 동시성 문제를 야기시킵니다. Zustand는 state의 불변성을 지킴으로써 이 문제를 해결합니다.


Context loss

Context loss는 useContext를 사용하는 component가 unmount 되면서 state를 잃었을 때 발생하는 흔한 문제입니다. 

이것은 page간 이동이나 DOM에서 component가 사라지면서 발생하는데 이 때문에 component는 data를 refetch 하거나 state를 reset 해야 하는 상황이 연출됩니다. 

Zustand는 각각의 component 밖에서 전역으로 state를 저장하는 방식을 사용해 언제든지 component들이 state에 접근할 수 있게 하여 이 문제를 해결합니다. 



Zustand 예제: 카운터 앱

이번엔 카운터 앱을 Zustand를 이용해 구현하였습니다. 

// useStore.js

import { create } from "zustand";

const useStore = create((set) => ({
  number: 0,
  plusOne: () => set((state) => ({ number: state.number + 1 })),
  minusOne: () => set((state) => ({ number: state.number - 1 })),
}));

export default useStore;

useStore라는 hook을 store로 만듭니다. number의 초기 값은 0이며, plusOne은 number에 1을 더한 값, minusOne은 1을 뺀 값입니다. store에는 원시 값, 객체, 함수 등 모든 것을 저장할 수 있습니다. set 함수로 state를 병합해 줍니다.

// App.js

import "./App.css";
import useStore from "./useStore";

function App() {
  const number = useStore((state) => state.number);

  const addNumber = useStore((state) => state.plusOne);

  const minusNumber = useStore((state) => state.minusOne);
  return (
    <div>
      <div>{number}</div>
      <button onClick={addNumber}>+ 1</button>
      <button onClick={minusNumber}>- 1</button>
    </div>
  );
}

export default App;

그리고 컴포넌트에 바인딩하면 끝입니다! 보일러 플레이트 없이 가볍고, provider로 store를 제공해 줄 필요 없이 hook을 어디서나 사용하면 됩니다. 필요한 state와 필요한 component를 선택해 사용하고 state가 바뀌면 리렌더링 될 겁니다. 


그럼 Redux와 비교해 보았을 때, Zustand는 어떤 장점과 단점이 있을까요?


장점

    • 보일러 플레이트 코드: Redux와 비교했을 때 가장 돋보이는 장점이라고 할 수 있을 것 같습니다. 설정의 복잡도가 확연히 낮습니다.

    • 낮은 러닝커브: 직관적인 API를 제공하고, 사용하기와 배우기가 쉽기 때문에 러닝커브가 낮아 Redux에 비해 진입 장벽이 낮습니다.

    • 쉬운 디버깅: Redux와 Zustand 둘 다 debug tool이 존재하기 때문에, 특정 상황이나 개발자의 경험에 따라 디버깅의 난이도가 달라지겠지만, 대체적으로 Zustand는 심플한 디자인과 직관적인 API 때문에 Redux보다 쉽게 debug 할 수 있다고 여겨집니다. Zustand는 memoization과 불변성을 강조하는 함수형 프로그래밍을 추구하기 때문에 어플리케이션 내에서 state가 바뀌는 이유를 더욱 명확하게 만들어 줍니다.

단점

    • 작은 커뮤니티 규모: 4년 전에 공개된 라이브러리인 만큼 Redux에 비하면 라이브러리의 규모가 작습니다. 하지만 앞으로도 지속과 성장이 가능한 상태관리 라이브러리로서 여겨지며 많은 관심을 받고 있습니다.

    • plugin, extension: 아직은 발전 중인 Zustand 생태계는 Redux에 비하면 적은 plugin과 extenstion들을 보유하고 있습니다. 




MobX

많은 상태관리 라이브러리들은 state 변경을 불가하게 하거나, 수정하지 못하도록 제한하는 방법을 시도했습니다. 하지만 이런 방법은 새로운 문제를 유발합니다. 


데이터는 정규화되어야 하고 참조 무결성 또한 보장될 수 없고, 데이터가 필요한 경우 클래스와 같은 효과적인 개념을 사용하는 것이 불가능합니다.(Redux의 경우 state의 불변성을 지키기 위해 이전 상태의 객체의 값을 변경시키지 않고 새로운 객체를 반환합니다.)


MobX는 쉬운 state 관리를 위해서 일관되지 않는 state가 애초에 생성되지 않도록 합니다. 애플리케이션 내의 state에서 파생 가능한 모든 것이 자동적으로 파생되도록 합니다. 따라서 불변성에 신경 쓰지 않아도 알아서 불변성을 유지시켜 줍니다. 


핵심개념

개념적으로 MobX는 애플리케이션을 스프레드시트처럼 다룹니다. 

이미지 출처: MobX 공식문서

    • state(observable state): 애플리케이션의 모델을 구성하는 객체, 배열, 원시 값, 참조의 그래프입니다. 이 값들이 애플리케이션의 데이터 셀입니다.

    • derivation(파생): 애플리케이션 내의 observable state로부터 자동으로 계산될 수 있는 모든 값입니다. 값이 변경되면 자동으로 업데이트가 됩니다.

    • reaction: derivation과 굉장히 유사하지만 가장 큰 차이점은 reaction 함수는 값을 생성하지 않습니다. reaction은 DOM이 업데이트되었는지, 네트워크 요청이 제때 자동적으로 이루어졌는지 확인합니다.

    • action: action은 state를 변경하는 모든 것입니다. MonX는 action으로 인해 발생하는 모든 애플리케이션 state 변화가 derivation과 reaction에 기반해 자동적으로 이루어졌는지 확인해 줍니다. 


MobX 예제: 카운터 앱

아래 예시 코드는 리덕스와 마찬가지로 버튼을 통해 +1, -1을 할 수 있는 간단한 기능을 가진 어플리케이션을 구현한 코드입니다.

// counterStore.js

import { observable } from "mobx";

export const counterStore = observable({
  num: 0,
  addNumber() {
    this.num++;
  },
  minusNumber() {
    this.num = this.num - 1;
  },
  reset() {
    this.num = 0;
  },
});

export default counterStore;

Observable은 Observable state를 만들어 우리가 관찰하려는 state를 주시하며 state에 변화가 있을 때 MobX에 알려줍니다.

// useStore.js

import { counterStore } from "./counterStore";

const useStore = () => {
  return { counterStore };
  //여러개일 경우, { counterStore, others ... }
};

export default useStore;

useStore는 counterStore를 return 하는 hook입니다. 

// App.jsx

import "./App.css";
import useStore from "./useStore";
import { useObserver } from "mobx-react";

function App() {
  const { counterStore } = useStore();

  const addNumber = () => {
    counterStore.addNumber();
  };

  const minusNumber = () => {
    counterStore.minusNumber();
  };

  return useObserver(() => (
    <div>
      <div>{counterStore.num}</div>

      <button onClick={addNumber}>+ 1</button>
      <button onClick={minusNumber}>- 1</button>
    </div>
  ));
}

export default App;

App.js에서 state를 관찰하기 위해 useObserver를 사용해 return 해 줍니다.



장점

    • 적은 양의 코드와 보일러 플레이트: 간단하고 적은 코드와 보일러 플레이트로 자바스크립트 어플리케이션을 관리하는 것, 그것이 MobX의 가장 큰 장점이자 추구하는 바입니다. MobX는 observable data를 이용해 변화를 자동으로 감지하게 도와줍니다. 

    • 낮은 러닝커브: 배우기 쉽고 이해하기 쉽습니다. 객체 지향 프로그래밍에 익숙한 개발자에게는 더더욱.

    • 불변성 유지: Redux는 객체의 레벨이 깊을 경우 불변성을 쉽게 유지하기 위해서 종종 Immer.js 같은 라이브러리를 사용합니다. MobX는 그럴 필요가 없습니다.

단점

    • 디버깅: Redux가 제공하는 개발자 툴들에 비하면 MobX는 디버깅이 어렵습니다.

    • 작은 커뮤니티 규모: Redux에 비하면 작은 커뮤니티를 보유하고 있습니다. 확실히 Redux에 비해 참고 자료가 적다고 느껴졌습니다.

    • 높은 자율성: 자율성이 높으면 코드의 일관성을 지키기가 어려울 수 있습니다. 따라서 state에 잘못된 update가 일어나기 쉽습니다.




Recoil


Recoil은 Facebook에서 출시한 React 스러운 상태관리 라이브러리입니다. 2020년 5월에 출시 되었지만 현재 많은 관심을 받고 있습니다. 그럼 바로 Recoil이 가진 핵심 개념을 살펴보겠습니다.



핵심개념

이미지 출처: better programming

 Recoil을 사용하면 atoms(공유된 state)에서 selectors(순수 함수들)을 거치는 데이터 흐름을 만들 수 있습니다. Atom은 컴포넌트가 구독할 수 있는 단위입니다. Selectors는 이 상태를 동기, 비동기 적으로 변환해 줍니다.


Atoms


state의 단위로, 업데이트와 구독이 가능합니다. Atom이 업데이트되면 구독하고 있던 컴포넌트들은 새로운 값을 반영하며 리렌더링 됩니다. 하나의 Atom은 전역적으로 고유한 key 값을 가지기 때문에 같은 키를 공유하는 Atoms는 존재할 수 없습니다. React 컴포넌트의 state처럼 default 값도 가집니다. 컴포넌트에서 Atom을 읽고 쓰기 위해 useRecoilState hook을 사용합니다. 이를 통해 state가 컴포넌트 간에 공유될 수 있습니다.


Selectors

Selector는 Atoms나 다른 Selectors를 입력으로 받아들이는 순수한 함수입니다. 상위 Atoms, Selectors가 업데이트되었을 때  하위 selector 함수는 재평가 됩니다. Selectors는 state를 기반으로 하는 파생된 데이터들을 계산하는데 쓰입니다. selectors는 useRecoilValue()를 사용해 읽을 수 있으며 하나의 atom이나 selector를 인자로 받아 그에 맞는 값을 반환해줍니다.




Recoil 예제: 카운터 앱

같은 카운터 어플리케이션을 Recoil을 이용해 구현해 보겠습니다.

// index.js

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

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <RecoilRoot>
    <App />
  </RecoilRoot>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

최상단의 index.js에서 RecoilRoot로 공유하고 싶은 컴포넌트를 감싸줍니다.

// atom.js

import { atom } from "recoil";

export const countState = atom({
  key: "countState",
  default: 0,
});

countState라는 atom을 정의합니다. 고유한 key는 countState, 기본 값은 0으로 설정하겠습니다.

// App.js

import "./App.css";
import { useRecoilState } from "recoil";
import { countState } from "./atom";

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

  const addNumber = () => {
    setNumber((prev) => prev + 1);
  };

  const minusNumber = () => {
    setNumber((prev) => prev - 1);
  };

  return (
    <div>
      <div>{number}</div>
      <button onClick={addNumber}>+ 1</button>
      <button onClick={minusNumber}>- 1</button>
    </div>
  );
}

export default App;

useRecoilState()를 이용해 countStatse를 불러옵니다. 기본적으로 useState와 생김새가 흡사합니다. 왜 Recoil을 ‘React스러운’이라고 표현하였는지 알 것 같습니다. useState를 사용하던 문법과 이질감 없이 자연스러운 상태 관리가 가능합니다.


저는 간단한 예제를 통해 상태관리 라이브러리들을 비교하고 있기 때문에, Recoil이 가지고 있는 더 많은 기능들을 살펴보시려면  공식 문서를 통해 찾아볼 수 있습니다.


그럼 Redux와 비교하여 장점과 단점을 비교해 볼까요?




장점 

    • 쉬운 사용: React API들을 사용하듯이 쉽게 state 공유가 가능합니다.

    • 낮은 러닝커브: Redux에 비해 배우기 쉽습니다.

    • Redux처럼 중앙화된 reducer 구조를 가지고 있지 않기 때문에 state 변화에 따라서 전체 state 객체가 재 생성 될 일이 없습니다.

    • 확정성: Atomic state 모델은 어플리케이션의 규모가 커진다 해도 확장성이 좋고 refactoring 또한 쉽습니다.

    • 좋은 커뮤니티: Facebook 팀이 개발했기 때문에 얼마 되지 않았음에도 불구하고 좋은 커뮤니티를 형성하고 있습니다.

    • 필요 없는 리렌더링이 일어나지 않습니다.

    • 쉬운 디버깅: Recoil이 atom과 selector가 바뀔 때마다 생성한 스냅샷을 통해 time travelling과 debugging을 쉽게 할 수 있습니다.

단점

    • 출시한 지 얼마 되지 않은 실험적인 라이브러리이기 때문에, 업데이트 시마다 잦은 변화를 경험할 수 있습니다.

    • 많은 수의 Atom들과 상호작용 하게 되면 그 상호작용들을 디자인하는데 더 많은 노력을 요구할 수 있습니다.

    • Redux와 같은 오래된 라이브러리만큼 큰 커뮤니티를 보유하지는 않습니다.

    • 아직은 새로운 라이브러리이기 때문에, 실제 규모가 큰 프로젝트에서 실제로 사용되고 있는 예제를 찾기는 힘듭니다.

    • 캐싱과 같은 기능은 현재 공식 문서에 명확히 명시되어 있지 않습니다. 



간단한 예시코드를 통해 비교했음에도 불구하고 Redux를 제외한 다른 라이브러리들이 보였던 공통적인 특징은 코드의 작성이 쉽고, 간단하다는 점이었습니다.


아직까지 대부분의 페이스북 같은 큰 프로젝트들은 Redux를 이용한 상태 관리를 하고 있기 때문에 이 솔루션들이 얼마나 확장성이 좋은지는 확인할 수 없는 것이 사실입니다. 


하지만 오픈 소스 커뮤니티의 성장과 함께 한다면 프로젝트 규모도 자연스럽게 커질 수 있지 않을까 조심스럽게 생각해 보았습니다. 







글을 마무리하며,



Migration이란 복잡하고도 힘든 여정을 지나온 많은 국내외 개발자들의 사례들을 많이 보면서, 계속해서 바뀌는 트렌드를 따라가야 하는 Frontend 개발이지만 그들이 그것을 결정하고 추진하기까지 수많은 고민을 했을 것이라는 것을 체감할 수 있었습니다.


많은 포스트들은 불편함의 원인을 찾아 그것을 팀에게 공유하고, 모두가 납득할 만한 결론을 이끌어 냈고, 프로젝트를 마치고 회고하기까지의 과정을 설명하고 있었습니다. 진행한 방식이나 모양은 모두 조금씩 달랐을지 몰라도 모두가 결국 ‘왜 이 migration을 진행하게 되었는가?’라는 질문에 대답하고 있었습니다. 


나 자신이 ‘왜?’라고 자주 생각하는 사람인가?를 생각해 보았을 때, 호기심은 많지만 때로는 사람들이 그렇다면 그런 거겠지, 하고 깊은 고민 없이 받아들인 적도 많았던 것 같습니다. 항상 공부 하면서 드는 생각이지만, 오늘도 누군가 나의 의견에 ‘왜’냐고 묻는다면 누구나 그 뜻을 이해할 수 있도록 항상 나의 의도를 분명히 전달해야겠다고 생각했습니다. 



긴 글을 읽어주셔서 감사합니다. 다음 글에서 뵙겠습니다 🙂








참고

React Docs : State: A Component’s Memory

freeCodeCamp: How to manage state in your react apps

Geeksforgeeks: what is prop drilling and how to avoid it?

Jannik Wempe: When to not use react context api for state

GitNation: React Query: It’s time to break up with your “Global State”!

Nathan Sebhastian: How and Why you should use React Query

Redux: Flux

MobX: 10분만에 알아보는 MobX와 React

Recoil Docs: Recoil official

Reaktor: is Recoil what will kill Redux?

Fernando Doglio: Let’s talk about Zustand (A converstation with Daishi Kato)

다양한 경험을 하고 그 가치를 함께 나누는 것을 좋아합니다. 오픈소스컨설팅 Playce-Dev Team 프론트엔드 개발자 노은지입니다.

Leave a Reply

Your email address will not be published. Required fields are marked *