최근 인기있는 상태 관리 도구인 Zustand의 코드를 분석하는 글을 보고, 이를 참고해서 직접 상태 관리 도구를 개발해 봤습니다. 바퀴를 재발명할 필요는 없지만 바퀴를 분해, 재조립하고 직접 만들어 보면서 배우는 것도 있다고 생각하기 때문에... 그래서 개발한 상태관리 도구에 대해 포스팅을 작성하려고 하다가, 상태관리 자체에 대해 한 번 정리해보고 넘어가면 좋겠다는 생각이 들었습니다.
제가 경험한 (리액트 위주) 프론트엔드 개발에서의 상태관리와 그 역사, 그리고 현 주소에 대해 생각을 정리해 봤습니다.
클라이언트 개발에서 이야기하는 상태는 UI를 표시하기 위한 데이터입니다. 프론트엔드 개발 도구들은 일반적으로 이 데이터를 관리하기 위한 기능을 제공합니다. 대표적으로는 리액트의 useState()
API가 있습니다.
최근 프론트엔드 개발의 대부분을 책임지고 있는 컴포넌트 기반-선언형 UI 라이브러리들은 필연적으로 컴포넌트 트리 내에서 여러 컴포넌트 간에 상태를 공유해야 하는 상황이 오게 됩니다.
단순한 서비스를 구현한다면 기본적인 기능만으로도 충분할 수 있지만, 1) 여러 컴포넌트가 같은 상태를 참조해야 하거나 2) 전달해야 하는 깊이가 깊어지면 골치아픈 상황이 발생합니다. 코드가 복잡해지면서 가독성이 떨어지고, 상태와 관련 없는 컴포넌트들이 상태에 결합(coupling) 되어버리게 됩니다. 구현하는 데는 큰 문제가 없을 수 있지만, 코드는 끊임없이 수정되어야 하고 그때마다 복잡한 의존성을 파악하는 것은 비용이 큰 작업이 됩니다.
그래서 여기서 상태 관리 도구가 필요해지게 됩니다. 그리고 이 상태 관리 도구는 위의 문제를 해결하기 위해, 1) 컴포넌트 트리 외부에서 필요한 컴포넌트에 상태를 주입하게 됩니다. 그리고 대부분 상태 관리의 안정성과 편의성을 위해 single-source-of-truth의 방법론을 따라 2) 중앙 집중형의 특징을 갖게 됩니다.
이런 상태 관리 도구 중에서 Flux 패턴을 기반으로 개발된 Redux는 가장 성공적인 라이브러리가 되었습니다. Vue는 같은 패턴을 기반으로 Vuex라는 공식 라이브러리를 제공하기도 했습니다.
하지만 앱의 스케일이 커질수록 외부 단일 상태 저장소(store)는 비대해지고, 비동기 처리가 복잡하다는 문제가 있었습니다.
상태 관리에서의 비동기 처리란, 대개 API 요청과 관련되어 있습니다. 요청 관련 동작, 요청 상태와 요청 결과에 따른 UI 변경, 캐싱, 캐시 무효화 등 API 요청은 부수적으로 많은 작업을 요구합니다. 이에 필요한 상태의 수와 관리의 복잡성도 급격히 증가하게 됩니다.
이 시점에서 서버 상태
의 개념과 이를 관리하기 위한 도구가 등장하게 됩니다. SWR과 React Query가 대표적이며, 국내에서는 이 중에서 React Query의 인기가 높은 것 같습니다.
npm-trends를 통해 봤을 때 실제로는 react-query와 swr은 각각 9년, 6년 전에 생성된 프로젝트이지만, 2020년부터 활발하게 개발되고 사용된 것을 볼 수 있습니다.
단일 상태 저장소가 비대해진다는 문제점을 해결하기 위해서는 여러가지 시도가 있었습니다. Redux는 Redux-toolkit으로 발전하면서 저장소를 slice라는 개념으로 분리하는 방안을 제시했습니다. 한편으로는 각 상태를 atomic하게 관리하는 아이디어도 제안되었고, Recoil과 Jotai는 이러한 아이디어를 기반으로 하고 있습니다.
그 중간 어디쯤 Zustand가 있는 것 같습니다. Redux-Toolkit이 중앙 집중적인 특징을 유지하는 반면 Zustand는 여러 스토어를 생성할 수 있고, atomic하다고 하기에는 각 atom들이 bottom-up으로 합쳐지면서 상태가 만들어지는 Recoil, Jotai와는 개념적으로 상이하기 때문입니다.
Jotai와 Zustand의 차이점에 대해서는 두 라이브러리를 만든 Kato Daishi의 설명을 읽어보는 것을 추천합니다.
기존의 중앙 집중형 상태 관리 도구와 대비되는 측면에서 분산형 상태 관리 도구라고 하면 어떨까 싶기도 한데.. 아직 그런 용어는 따로 없는 것 같습니다.
평소 컨퍼런스나 기술 블로그, 기술 뉴스레터, 채용 공고 등을 통해 기술 트렌드를 파악하는 편입니다. 이를 통해 파악한 최근 프론트엔드 기술 스택의 사실상의 표준(de facto standard)은 아래와 같습니다.
React + Next.js + React-Query + (Zustand | Jotai | Recoil)
우선 복잡한 서버 상태 관리를 React-Query에 위임하고, 이로 인해 무거운 단일 상태 저장소의 필요성이 줄어들면서 가벼운 상태 관리 도구들로 클라이언트 상태를 관리하는 것이 트렌드인 것 같습니다. 저도 업무에서 위와 같은 기술 스택을 사용하고 있습니다.
세상에 은탄환은 없다는 걸 알지만, 위와 같은 스택을 사용하며서 이전의 Redux 중심의 개발보다는 코드가 훨씬 간결하고 개발 속도가 빨라진 것은 사실인 것 같습니다. 이후에는 어떤 상태 관리의 패러다임이 트렌드가 될 지는 모르겠지만, 당분간은 이 트렌드가 유지되지 않을까 싶네요.