
여러분은 상태 관리를 어떻게 하시나요? 사용하는 라이브러리가 있다면, 그 선택의 이유는 무엇인가요? 과거에 저는 '최신 트렌드'를 기준으로 했습니다. "이 라이브러리가 지금 인기가 많다," "이 라이브러리가 미래에 성공할 것이라고 해"와 같은 이야기를 듣고, 깊이 고민하지 않고 선택했습니다.
물론, 어떤 라이브러리를 사용하든 그 목적에 맞게 잘 사용하기만 한다면 큰 문제는 없습니다. 그러나 왜 그 라이브러리를 사용하는지, 선택한 이유가 무엇인지 고민해보고 선택한다면 더 의미 있는 선택이 될 것입니다. 오늘은 제가 좋아하는 상태 관리 라이브러리와 그 이유에 대해 이야기해보려고 합니다.
NPM Trend
2년동안 FE에서 많이 사용하는 라이브러리들을 기준으로 트렌드를 뽑아보았습니다.

많은 사람들이 아직까지 react-redux를 많이 사용하고 있고, 생각보다 recoil이 인기가 없다는것을 알 수 있었습니다. 분명 이전에 취업 준비했을때만해도 recoil이 정말 핫했던거로 기억하는데말이죠.. 그리고 엄청난 상승곡선을 그리고 있는 zustand도 보입니다. 어떻게 zustand는 저렇게 단기간에 많은 유저를 모았을까요?
Redux
Redux는 많은 사람들이 React 전역 상태 관리 라이브러리로 생각하는 가장 대표적인 선택입니다. Redux 커뮤니티는 매우 방대하며, 오랜 시간 동안 많은 검증을 거쳐왔습니다. 사용하기는 다소 어렵지만, 방대한 커뮤니티 덕분에 자료를 쉽게 찾을 수 있습니다.
Redux를 사용한 간단한 전역 상태 모달 관리 예제를 소개합니다. 이 예제는 모달 상태 관리를 위한 액션, 리듀서, 스토어 설정 및 사용 방법을 포함합니다.
1. 액션 정의하기
먼저, 모달을 열고 닫는 액션을 정의합니다.
// actions/modalActions.js export const OPEN_MODAL = 'OPEN_MODAL'; export const CLOSE_MODAL = 'CLOSE_MODAL'; export const openModal = () => ({ type: OPEN_MODAL, }); export const closeModal = () => ({ type: CLOSE_MODAL, });
2. 리듀서 정의하기
다음으로, 이러한 액션에 반응하여 상태를 변경하는 리듀서를 정의합니다.
// reducers/modalReducer.js import { OPEN_MODAL, CLOSE_MODAL } from '../actions/modalActions'; const initialState = { isOpen: false, }; const modalReducer = (state = initialState, action) => { switch (action.type) { case OPEN_MODAL: return { ...state, isOpen: true, }; case CLOSE_MODAL: return { ...state, isOpen: false, }; default: return state; } }; export default modalReducer;
3. 스토어 설정하기
Redux 스토어를 설정하고 리듀서를 스토어에 연결합니다.
// store.js import { createStore, combineReducers } from 'redux'; import modalReducer from './reducers/modalReducer'; const rootReducer = combineReducers({ modal: modalReducer, }); const store = createStore(rootReducer); export default store;
4. 모달 컴포넌트 및 연결
마지막으로, 모달 컴포넌트를 만들고 Redux 스토어와 연결하여 상태를 관리합니다.
// components/ModalComponent.js import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { closeModal } from '../actions/modalActions'; const ModalComponent = () => { const isOpen = useSelector(state => state.modal.isOpen); const dispatch = useDispatch(); return ( isOpen && ( <div style={{ background: 'rgba(0, 0, 0, 0.5)', position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, display: 'flex', justifyContent: 'center', alignItems: 'center' }}> <div style={{ background: 'white', padding: 20 }}> <p>Modal is open!</p> <button onClick={() => dispatch(closeModal())}>Close Modal</button> </div> </div> ) ); }; export default ModalComponent;
이 컴포넌트를 사용하려면,
Provider
를 사용하여 앱 최상위 컴포넌트에서 Redux 스토어를 제공해야 합니다.// App.js import React from 'react'; import { Provider } from 'react-redux'; import store from './store'; import ModalComponent from './components/ModalComponent'; const App = () => ( <Provider store={store}> <div> <ModalComponent /> {/* 여기에 나머지 앱 컴포넌트 */} </div> </Provider> ); export default App;
이 예제는 모달을 전역 상태로 관리하는 기본적인 방법을 보여줍니다. 전역으로 관리할 모달 하나를 만드는데 코드가 무슨.. 어마어마한게 보이시나요?
물론 견고하고, 세밀하고, 대규모 애플리케이션 관리에 유용하다해도 관리하는 측면에서 상당한 코드의 양과, 유지보수할 개발자의 러닝커브를 커버하긴 어려울 것 같다는 생각이 듭니다. (지극히 개인적인 생각입니다.)
Zustand
Zustand 개발 팀은 그들의 라이브러리에 대해 자신감을 보여줍니다. "작고 빠르며, 간결한 Flux 원칙을 사용하는 상태 관리 솔루션이며, Hook 기반의 편리한 API를 제공하고, 보일러플레이트가 없으며, 의견을 강요하지 않습니다."
Zustand는 스토어 설정이 매우 간결하고 직관적이며, 다른 라이브러리들에 비해 학습 곡선이 낮습니다. 이러한 점이 단기간에 많은 사용자를 확보할 수 있었던 이유일 것입니다. Redux와 비교했을 때, Zustand를 사용한 전역 상태 관리는 훨씬 간단하고 직관적이며, 함수형 컴포넌트와의 호환성도 뛰어납니다.

아마 이러한 부분이 단기간에 많은 점유율을 갖을 수 있었던 이유가 아닐까 싶습니다. redux의 예시처럼 zustand를 활용해 전역 상태의 모달관리 코드를 작성해보겠습니다.
1. 스토어 생성하기
먼저, 모달 상태를 관리할 Zustand 스토어를 생성합니다.
// store/modalStore.js import create from 'zustand'; const useModalStore = create(set => ({ isOpen: false, openModal: () => set({ isOpen: true }), closeModal: () => set({ isOpen: false }), })); export default useModalStore;
2. 모달 컴포넌트 생성하기
모달 컴포넌트를 생성하고, Zustand 스토어의 상태와 액션을 사용하여 모달을 열고 닫습니다.
// components/ModalComponent.js import React from 'react'; import useModalStore from '../store/modalStore'; const ModalComponent = () => { const { isOpen, closeModal } = useModalStore(); if (!isOpen) return null; return ( <div style={{ background: 'rgba(0, 0, 0, 0.5)', position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, display: 'flex', justifyContent: 'center', alignItems: 'center' }}> <div style={{ background: 'white', padding: 20 }}> <p>Modal Content Here</p> <button onClick={closeModal}>Close Modal</button> </div> </div> ); }; export default ModalComponent;
3. 모달 열기
어디서든지 모달을 열기 위해 Zustand 스토어의
openModal
액션을 호출할 수 있습니다. 예를 들어, 어떤 버튼 클릭 이벤트에서 모달을 열고 싶다면 다음과 같이 할 수 있습니다.// App.js 또는 다른 컴포넌트 import React from 'react'; import ModalComponent from './components/ModalComponent'; import useModalStore from './store/modalStore'; const App = () => { const openModal = useModalStore(state => state.openModal); return ( <div> <button onClick={openModal}>Open Modal</button> <ModalComponent /> </div> ); }; export default App;
이 예제 코드는 Zustand를 사용하여 애플리케이션 내 어디서든 모달의 상태를 쉽게 관리하고 제어할 수 있는 방법을 보여줍니다. Zustand의 간결함과 유연성 덕분에 상태 관리 로직을 더 직관적이고 선언적으로 작성할 수 있습니다.
이 방식은 확실히 redux보다 간단해보이고 직관적으로 보이고 hooks 와 같은 사용법으로 함수형 컴포넌트를 사용하는 곳에선 추가적인 러닝커브도 없을 것 같아 보입니다.
최근에 나오는 라이브러리들은 대부분 간결하고 직관적인 사용법을 강조하면서 zustand와 대립구조를 이루는게 대부분입니다. 하지만 별 다른 특이점이 없다면 이미 커뮤니티가 활성화된 zustand를 포기하지 않을 것입니다.
저는 그렇기때문에 앞으로도 redux와 zustand의 체계가 지속되지 않을까 라는 생각을 해봅니다. 대규모 애플리케이션 서비스에 redux를 도입해 처음부터 전역 상태관리 스토어를 견고하게 가져갈 것이냐, zustand의 간결하고 직관적인 코드를 사용하면서 이외 비즈니스 로직에 더 많은 시간을 투자할거냐의 싸움이지 않을까요.
내가 고르는 상태관리 라이브러리의 기준
Redux와 Zustand를 대표적으로 언급하며 각각의 장단점을 소개했습니다. 어떤 것이 더 올바른 선택인지는 개인의 기준에 따라 다르며, 각자의 코딩 스타일에 맞는 라이브러리를 선택하는 것이 중요합니다.
제가 상태 관리 라이브러리를 선택하기 전 중요하게 고려한 두 가지는 다음과 같습니다.
- 커뮤니티의 크기 및 트렌드: Redux와 Zustand 모두 널리 사용되며, 각각 활성화된 커뮤니티를 갖고 있습니다.
- 코드의 간결함과 직관적인 구현: 이 기준에서는 Zustand가 더욱 부합한다고 생각했습니다. 선언적인 코드 작성이 가능하고, 다른 커스텀 훅과도 잘 어우러진다고 느꼈습니다.
대규모 프로젝트를 처음부터 구성할 기회가 있었다면, Redux도 충분히 고려할 만한 선택이었을 것입니다.
본인의 의지를 반영한 라이브러리 선택
이 글을 통해 전하고자 하는 바는, 라이브러리를 선택할 때 개발자 스스로가 명확한 기준을 세우고, 그 기준에 맞는 적합한 라이브러리를 직접 찾아보자는 것입니다. 상태 관리 라이브러리를 예로 들었지만, 이는 모든 라이브러리 선택에 적용될 수 있는 원칙입니다.
많은 개발자들이 “왜 이 라이브러리를 사용하셨나요?”, “현재 버전 4가 나왔는데 왜 아직 버전 3를 사용하시나요?”와 같은 질문에 명확한 대답을 하지 못하는 경우가 많습니다. 저 역시 과거에는 “트렌디해서 사용했습니다.”, “그냥
npm install 패키지명
하니까 버전 3가 설치되더라구요.”와 같이 대답했던 적이 있습니다.그러나 실제로 라이브러리의 내부를 들여다보면, 이러한 무책임한 대답과는 달리 수많은 복잡한 결정과 설계가 포함되어 있음을 알 수 있습니다. 예를 들어
react-query
의 경우, npm install react-query
를 실행하면 기본적으로 버전 3이 설치됩니다. 하지만 현재는 버전 5까지 출시되었습니다. 버전 3와 4에서는 함수의 매개변수에 대한 명확한 정의가 부족했습니다. 반면, 버전 5에서는 매개변수를 객체 형식으로만 받도록 변경되었습니다.- useQuery(key, fn, options) + useQuery({ queryKey, queryFn, ...options })
왜
react-query
팀은 버전 5에서 매개변수의 형태를 변경했을까요? 바로 기존의 방식보다 객체 타입이 매개변수의 역할을 명확하게 정의할 수 있기 때문입니다. 이러한 변경은 유지보수 측면에서의 이점은 물론, 사용자 오류를 줄이는 데에도 도움을 줍니다. 이처럼 더 나은 코드와 더 나은 아키텍처를 위해서는 단순히 남의 의견을 따르기보다는 스스로 생각하고 판단하는 능력을 키우는 것이 중요합니다.저는 이번 포스팅을 통해 zustand를 사용한 이유에 대해 나열하였습니다. 그것은 저의 기준이고 저의 코드 스타일이 반영된 결과입니다. 더 좋은, 더 안좋은 라이브러리는 의미가 없다고 생각합니다. 그렇기때문에 이 글은 참고 정도로만 봐주시고, 상태관리 라이브러리들을 직접 비교해가며 본인의 기준에 맞는 라이브러리를 채택하면서 사고를 키워나갔으면 좋겠다는 생각입니다.