Zustand를 사용해보자!
프로젝트에서 사용할 기술 스택을 정하는 과정에서 프론트엔드 영역의 전역 상태 관리를 위해 Zustand를 사용하기로 하였다.
프로젝트에서 사용하게 된 배경은 우선 최근 트렌드에 맞는 기술이라고 생각했으며, 금방 배울 수 있기에 같이 프로젝트를 진행하는 팀원들(이제 막 리액트를 배우는 팀원도 있다.) 입장에서 사용하기도 편리할 것이라고 생각했다.
우선 필자는 React에선 Redux, Recoil를, Vue3에선 Pinia를 사용해 본 경험이 있다.
Redux, 특히 미들웨어 부분에서 어려움을 많이 느껴 한동안 Recoil을 주로 사용했는데 마침 최근 떠오르는 Zustand에 대해 궁금하기도 했고 이번 기회를 빌어 Zustand를 한 번 사용해보고자 한다.
1년 간의 주 단위 NPM 다운로드 수를 보면 Zustand의 인기가 많이 오른 것으로 보인다.(링크)
Zustand의 특징
본격적인 설치에 앞서, Zustand만이 가지는 특징에 대해서 먼저 알아보자.
가벼운 용량
Zustand는 1.16kb라는 번들 사이즈를 가졌다. 이는 상태 관리 라이브러리 중에서도 매우 가벼운 편에 속하며, 23kb가 넘는 Recoil과 비교하면 20배가 넘는 용량 차이를 확인할 수 있다.
단순 용량 비교로만 따지면 Zustand를 20개 정도 깔면 Recoil과 비슷한 번들 크기를 가지는 것이다.
라이브러리와 각종 툴들을 많이 사용하는 최근 프론트엔드 개발에서 적은 용량은 분명한 강점으로 작용한다.
쉬운 사용법
Zustand의 가장 큰 장점은 쉬운 사용법이다. 불필요한 Provider가 없고, 설치와 동시에 즉시 사용이 가능하며, 문법도 어렵지 않기에 초보자도 공식문서만 읽어보면 바로 사용이 가능하다.
React의 기본적인 useState 훅만 사용해 본 유저의 입장에선 Recoil이 조금 더 쉽게 느껴질 수 있지만, 개인적으론 Redux와 비교하면 정말 금상첨화가 따로없다.
TypeScript 친화
Zustand는 TypeScript로 개발되었다. 따라서 TypeScript를 사용해 개발할 때 타입 안정성과 타입 정의를 통해 TypeScript를 사용하는 개발 환경에서 주도적으로 사용이 가능하다.
MiddleWare 지원
‘persist’ 를 통해 페이지를 새로고침해도 상태가 유지되도록 할 수 있다. LocalStorage와 SessionStorage에 저장이 가능하며, zustand와 별개로 zustand/middleware 패키지를 설치해 사용할 수 있다.
또한, ‘Immer’ 를 통한 미들웨어 역시 지원한다.
이외에도 Zustand가 가지는 다양한 장점이 있는데, 이 정도만 해도 Zustand를 사용하는 이유로는 충분할 것 같다.
개인적인 입장으론 미들웨어에 대한 두려움(?)이라고 해야할까… Redux때문에 보이지 않는 벽에 막힌 듯한 느낌을 많이 받았는데, 그동안 너무 어렵고 막연하게만 생각했었나… 하는 생각도 들게 한다.
Zustand 설치
이제 아래 명령어를 이용해서 Zustand를 설치해보자.
npm install zustand
Yarn 등을 사용한다면 해당하는 명령어로 바꿔서 설치할 수 있다.
yarn add zustand
특징에서 언급했듯이 TypeScript로 작성된 라이브러리이므로, @types 설치 과정이 따로 필요없다.
Zustand 사용법
Zustand 공식 문서에서 안내하는 사용법은 매우 짧다. 공식 문서에서도 굉장히 간단하다는 걸 어필하는 듯 하다.
더 자세한 설명을 원한다면 Zustand Github에 있다.
import { create } from 'zustand'
const useStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
updateBears: (newBears) => set({ bears: newBears }),
}))
기본적으로 Zustand에서 상태는 create 로 생성한 store 에 저장된다.
useXXXStore 와 같이 선언해서 마치 React 커스텀 훅과 비슷하게 사용하는데, 실제로 Zustand의 스토어 자체가 React의 커스텀 훅의 일종이다. 다시 말해서, Zustand 스토어는 React 컴포넌트에서 훅과 같이 사용된다.
useXXXStore 내에는 내가 저장하고 싶은 데이터(상태)와 그 상태를 변화시킬 함수(액션)를 정의한다. useState() 에서 사용하는 상태변화 함수(setXXX())와 동일한 개념이며, 이전 값을 prev 또는 state 로 받아와 state를 변경하는 방식으로 사용한다.
선언한 Store 의 상태를 이제 실제 컴포넌트에서 사용하는 예제를 보자.
function BearCounter() {
const bears = useStore((state) => state.bears)
return <h1>{bears} around here...</h1>
}
function Controls() {
const increasePopulation = useStore((state) => state.increasePopulation)
return <button onClick={increasePopulation}>one up</button>
}
위 코드는 함수 선언문으로 작성된 컴포넌트 BearCounter 와 Controls 이다
BearCounter 의 경우 단순히 Store에서 bears 의 값을 가져다 사용하고 있다.
Controls 컴포넌트에선 버튼의 클릭 이벤트를 통해 increasePopulation 을 호출하고, 이는 곧 store 에서 가져온 increasePopulation 함수(액션)이 호출해 bear 의 값이 1 증가하게 된다.
이렇게 되면 BearCounter 의 값은 bear 값을 렌더링하고 있으므로, 이 값 또한 1 증가한 값이 보여질 것이다.
마치며
여기까지 기본적인 Zustand의 사용법을 알아봤다. 사실 전역 상태를 관리하는 목적이라면 이 정도까지만 학습하면 충분하지만, 비동기적 API 호출이나 미들웨어를 통한 효율적인 개발 등을 위해 Zustand에 대한 추가적인 학습 후 다음 글을 작성해야겠다.