Zustand를 선택한 이유
이 글은 React 프로젝트를 하면서 Zustand와 Recoil을 사용하여 상태 관리를 구현했던 경험을 바탕으로 프로젝트에 Zustand를 선택한 이유를 되짚어 보기 위해 작성한 글이다. 과거 Zustand로 상태관리를 구현한 코드를 다시 보면서 해당 라이브러리를 선택한 이유와 장점에 대해 간단하게 글로 정리해 보았다.
Zustand를 선택한 이유
프로젝트에서 전역 상태관리를 사용한 이유는 사용자 정보와 같이 여러 컴포넌트에서 사용하는 정보를 관리할 때, 상태 변화에 따른 불필요한 렌더링 최소화 하기 위해 사용했다. 프로젝트에 적용한 부분은 사용자 아이디를 여러 컴포넌트에서 필요로 한 경우가 많았고, 로그인 및 로그아웃 상태를 전역으로 관리하는 것이 더 효율적이라고 판단하여 사용자 상태를 전역상태로 설정했다.
Zustand는 리액트에서 모듈 상태를 사용하는 작은 라이브러리로 Zustand의 create 함수를 사용해 사용자 정보(아이디) 및 상태(로그인, 로그아웃)를 저장하는 store를 생성했다. 또한 persist 메소드를 활용하여 사용자 아이디를 로컬스토리지에 저장하고 필요한 컴포넌트에서 스토어를 호출하여 사용하는 방식으로 구현했다. Devtools를 통해 상태를 쉽게 확인할 수 있는 방법도 제공한다.
interface UserStateType {
...
}
// 사용자 정보(id) 및 상태(로그인, 로그아웃)를 저장하는 store
const useUserStore = create<UserStateType>()(
devtools(
persist(
(set) => ({
user: initialValue,
updateUser: (user) =>
set((state) => ({
user: {
...state.user,
...user,
},
})),
logoutUser: () =>
set(() => ({
user: initialValue,
})),
}),
{
name: 'user-storage', // localStorage에 저장될 이름
}
),
{
name: 'user-store', // Devtools에서 사용될 스토어 이름
}
)
);
export const useUser = useUserStore;
zustand를 선택한 이유는 이처럼 create의 두 번째 인수로 미들웨어(persist, immer 등)를 추가하여 미들웨어를 쉽게 사용할 수 있도록 도와주며 타입스크립트 기반으로 작성되어 있기 때문에 별도의 @types를 설치하거나 타입에 대해 걱정할 필요가 없기 때문이다. 또한, 자체 라이브러리 크기가 다른 라이브러리에 비해 작고, 리덕스에 비해 많은 코드를 작성하지 않아도 빠르게 스토어를 만들 수 있고 사용할 수 있다는 장점이 있다.
Zustand와 Recoil 구현 방식의 차이
상태관리의 대표적인 라이브러리로 Redux, Jotai 등이 있는데 이 중 직접 사용해 본 것인 Recoil이었다. Zustand가 하나의 store를 생성해서 상태를 관리한다면 Recoil은 atom에 상태를 저장한다. atom은 상태를 나타내는 Recoil의 최소 상태 단위로 key값을 식별자로 가지며 defaultValue와 함께 atom을 선언한다. 그리고 컴포넌트는 Recoil에서 제공하는 훅을 통해 atom의 상태 변화를 구독하고 값이 변경되면 리렌더링을 실행해 최신 atom값을 가져오게 된다.
사용방법에서부터 차이가 나는 것으로 알 수 있듯이 Recoil은 Context와 Provider, hook을 기반으로 작은 상태를 효율적으로 관리하는 데 초점을 맞추고 있으며 Context API의 기반이 되는 점을 알 수 있다. 반면에 Zustand와 리덕스의 경우에는 하나의 큰 스토어를 기반으로 하여 Context가 아니라 스토어가 가지는 클로저를 기반으로 생성되며, 이 스토어의 상태가 변경되면 이 상태를 구독하고 있는 컴포넌트에 전파해서 리렌더링을 알리는 방식이다.
이후 Zustand는 어떻게 상태를 관리하고 전파하는지에 대해 포스팅할 예정이다.
참고
1. [책] 모던 리액트 딥다이브 5장
2. [책] 리액트 훅을 활용한 마이크로 상태 관리 7장