서론
요즘 프론트엔드 개발은 비즈니스 로직이 복잡해짐에 따라 상태 관리가 점점 어려워지고 있다. 코드에서 사용하는 상태들이 많아지면서, 상태 관리는 더욱 중요해졌다. 상태들을 관리하고 어떤 상태 변화가 다른 상태 변화를 일으키는지 예측하는 게 어려워지고 있어, 이 글에서는 개인 프로젝트를 진행하며 상태 관리 라이브러리들을 선택할 때 영향을 미쳤던 요인들을 비교하고 정리해보겟다.
Flux vs Atom
상태 관리 라이브러리들은 프론트엔드 개발에서 필수적인 부분이 되었다. 이들 중 대부분은 Flux 패턴이나 Atomic 방식으로 구현되어 있다. Proxy 기반의 발티오도 있지만, 대체로 개발자들은 Flux와 Atomic 중 하나를 선택한다.
Flux 패턴은 top-down 방식을 따른다. 이는 데이터의 흐름이 단방향이라는 것을 의미한다. Flux에서는 모든 데이터 흐름이 순서를 따른다. 먼저, View가 Action을 발생시킨다. 이 Action은 Dispatcher를 거쳐 Store에 도달하고, 최종적으로 View를 업데이트한다. 이 과정에서 Store는 애플리케이션의 상태를 관리하며, 상태 변화가 있을 때마다 View를 새로고침하여 사용자에게 반영된 상태를 보여준다. Flux의 이러한 구조는 데이터 흐름을 예측 가능하게 만들어주며, 디버깅과 테스트를 용이하게 한다. Redux와 Zustand는 이 Flux 패턴을 따르는 대표적인 예시들이다.
Atomic 방식은 bottom-up 접근을 취한다. 이 방식에서는 애플리케이션의 각 부분이 독립적인 상태(Atom)를 가지며, 이러한 상태들이 서로 결합하여 전체 애플리케이션의 상태를 형성한다. 이 접근법은 개별 컴포넌트가 자신의 상태를 관리하도록 하여, 전역 상태 관리의 복잡성을 줄여준다. Recoil과 Jotai는 이 Atomic 접근법을 사용하는 대표적인 라이브러리다.
Flux
3.1. Redux
Redux는 action이라 불리는 event로 애플리케이션 상태를 관리하고 업데이트하는 패턴이다. JS를 위해 존재하지만 주로 React와 함께 많이 쓰인다. Redux는 세 가지 원칙에 기초해 구현됐는데, 이 원칙들은 다음과 같다:
1. 진실은 하나의 근원에서만: 전역 상태는 하나의 트리에 저장된다. 상태들이 직렬화되고 hydrated되기 때문에 디버깅이 쉬워진다.
2. State는 읽기 전용이다: State를 변경하는 유일한 방법은 action을 dispatch하는 것이다. 이를 통해 상태 변화를 추적하기 쉬워진다.
3. 변화는 순수 reducer 함수로 이루어진다: State를 변경시키는 action을 작성하기 위해 reducer 함수를 써야 한다. Reducer 함수는 순수 함수로, action과 이전 state를 입력으로 받아 다음 state를 출력한다. Redux는 Flux 패턴을 기초로 하기에 데이터는 한 방향으로만 흐른다.
장점:
- 예측 가능한 상태 업데이트: Redux의 단방향 데이터 흐름 덕분에 상태 업데이트가 예측 가능하고 관리하기 쉽다.
- 일관된 출력을 제공하는 순수 reducer 함수: 모든 상태 변경이 순수 함수인 reducer를 통해 이루어지기 때문에, 동일한 입력에 대해 항상 동일한 출력을 보장한다. 이로 인해 단위 테스트가 용이하다.
- 중앙화된 데이터 관리: 전역 상태 관리를 통해 애플리케이션 내 여러 부분에서 일관된 데이터 접근과 관리가 가능하다. 이는 복잡한 비즈니스 로직을 효율적으로 구현하는 데 도움이 된다.
단점:
- 보일러플레이트와 높은 러닝 커브: Redux는 많은 초기 설정과 보일러플레이트 코드가 필요하며, 이는 러닝 커브를 증가시킨다. Redux Toolkit과 같은 도구가 이를 다소 완화시키지만, 여전히 상당한 양의 보일러플레이트가 필요하다.
- 비동기 처리를 위한 추가 라이브러리 필요: Redux 자체는 비동기 로직을 처리하지 않아, Redux-thunk와 같은 추가 라이브러리가 필요하다. 이는 설정과 관리의 복잡성을 증가시킨다.
- 패키지 크기: Redux와 필수적인 미들웨어 또는 도구들을 함께 사용할 경우, 전체 패키지 크기가 커질 수 있다. 특히, Redux-thunk와 Redux Toolkit을 함께 사용하는 경우, 전체 용량이 6MB를 넘어갈 수 있다.
3.2. Zustand
Zustand도 Flux 패턴을 사용한다. 하지만 Redux와 달리, Zustand는 많은 보일러플레이트가 필요하지 않다. State와 action, 그리고 이를 통해 리턴된 hook을 어떤 컴포넌트에서든 import하여 사용하기 쉽다. Zustand도 바닐라 JS에서 사용 가능하다.
장점:
- 적은 보일러플레이트: 많은 설정이나 복잡한 코드 없이도 쓸 수 있다.
- 종속적이지 않은 상태 관리: Context api가 아닌 클로저로 상태 관리를 해서, 리액트뿐만 아니라 다양한 환경에서 사용 가능하다.
- 가벼운 라이브러리: 용량이 1.1kb로 앱의 성능에 거의 영향을 주지 않는다.
- 쉬운 리렌더링 제어: React에 종속되지 않아 상태 변화에 따른 컴포넌트의 리렌더링을 잘 제어할 수 있다.
- 단순하고 고정 관념이 없음: 상태 관리를 위한 복잡한 철학이나 규칙이 없다.
- 앱을 컨텍스트 프로바이더로 감싸지 않음: 앱 전체를 컨텍스트 프로바이더로 감쌀 필요가 없어 구조가 단순해진다.
- 컴포넌트에 일시적으로 정보 제공 가능: 재렌더링 없이도 컴포넌트에 정보를 전달할 수 있다.
- 중앙화된 데이터 관리: 전역 상태 관리를 통해 애플리케이션 내 여러 부분에서 일관된 데이터 접근과 관리가 가능하다. 이는 복잡한 비즈니스 로직을 효율적으로 구현하는 데 도움이 된다.
단점:
- 구조화된 패턴 부재: Zustand는 Redux만큼 엄격하거나 구조화된 패턴을 제공하지 않는다. 이는 프로젝트가 커짐에 따라 상태 관리가 복잡해질 수 있다.
- 비표준화: 표준화된 개발 방식이 정립되어 있지 않아, 개발 팀 간 일관된 개발 방식을 유지하기 어려울 수 있다.
- 대규모 프로젝트에서의 한계: Zustand는 구조화 되어 있지 않기에 작은 프로젝트나 간단한 상태 관리에 적합하지만, 매우 복잡한 상태 관리가 필요한 대규모 프로젝트에서는 제한적일 수 있다.
Atomic
4.1. Recoil
Recoil은 React 문법에 친화적이다. React의 hook을 이용해 개발되었으며 2020년 메타팀이 공개하였다. React의 상태처럼 간단한 get/set 인터페이스로 사용할 수 있는 보일러플레이트가 없는 API를 제공한다. 비동기 처리를 추가적인 라이브러리 없이(예: Redux-thunk, Redux-saga) Recoil 안에서 가능하다. 내부적으로 캐싱을 지원해, 동일한 atom 값에 대한 내부적으로 메모이제이션된 값을 반환하여 속도가 빠르다.
장점:
- React 문법과의 일치성: Recoil은 React를 개발한 메타에서 개발한 라이브러리로 React 문법에 친화적이다.
- 간단한 API: Recoil은 복잡한 보일러플레이트 없이 간단한 get/set 인터페이스를 제공한다. 이는 상태 관리를 더욱 간결하고 직관적으로 만들어 준다.
- 내장된 비동기 처리: Recoil은 Redux-thunk나 Redux-saga와 같은 추가 라이브러리 없이 내부적으로 비동기 처리를 지원한다.
- 캐싱과 성능 최적화: Recoil은 동일한 atom에 대해 메모이제이션을 내부적으로 지원하여, 성능을 최적화한다. 이는 특히 데이터 요청이 잦은 애플리케이션에서 유용하다.
단점:
- 잠재적인 사이드 이펙트: Recoil에서는 어떤 컴포넌트에서도 atom을 구독하고 업데이트를 받을 수 있다. 이 때, atom이 여러 곳에서 사용되면 예상치 못한 사이드 이펙트가 발생할 수 있다.
- 패키지 크기: Recoil의 패키지 용량은 약 2.21 MB로, 상대적으로 큰 편이다.
결론
마지막으로 고려한 점은 라이브러리의 다운로드 순위였다. npmtrendsd에서 확인한 결과 redux, zustand, recoil순으로 많이 사용함을 알 수 있었다. recoil의 다운로드수가 가장 적긴 하였으나 토스나 네이버 프론트엔드 개발자 채용공고에는 포함되어 있는 것을 볼 수 있었다. 나는 러닝 커브가 적고 보일러 플레이트가 쉬워 개인 프로젝트에 바로 적용 가능한 recoil을 선택하였다. 또한 atomic한 방식은 bottom up방식으로 데이터 흐름이 효율적이고 선택적 렌더링이 가능하다. 이런 근거들로 나는 recoil을 선택하였다.
참고자료
https://github.com/pmndrs/zustand?tab=readme-ov-file
https://npmtrends.com/recoil-vs-redux-vs-zustand
'Library, Tool' 카테고리의 다른 글
CSS Framework들의 특징과 장단점 비교 (tailwind, styled-componets) (0) | 2024.07.09 |
---|---|
Recoil 사용이유와 사용방법 (1) | 2024.01.08 |
ESLint와 Prettier로 컨벤션 준수하기 with Webstorm (0) | 2023.10.27 |
MSW로 실제 네트워크 환경에서 Mocking하기 (0) | 2023.10.26 |
CSS정리: flexbox와 grid (0) | 2023.05.29 |