지금 일하게 된 회사에서 상태관리를 할 때 Zustand를 쓴다는 것을 알게되었다. 사실 동료분이 "저희 회사는 Zustand 써서 상태관리 하고 있는데 혹시 뭔지 아시나요?" 라고 물어보셨을 때 진심 난생 처음 들어본다는 말 밖에 못했었다 😅
아직 본격적인 개발 업무가 주어지지 않았기 때문에 기본 개념부터 간단하게 정리하려고 한다!
리액트에서 상태관리를 하기 위해 사용하고 있는 Redux, Recoil, MobX, Jotai... 기타 등등의 상태관리 라이브러리들과 동일한 역할 수행하는 도구이다.
물론 각 라이브러리들만의 장단점이 있고, 프로젝트의 성격이나 규모에 따라 상황에 맞게 골라서 사용하면 된다고 한다. 어쨌든 본질은 복잡한 컴포넌트로 이뤄져있는 프로젝트에서 상태관리를 쉽게 하기 위함이고, 그 중에Zustand는 작성하는 코드의 양이 적고 가벼운 편으로 보인다.
Zustand는 Jotai를 개발한 회사에서 만들었다고 하고, React의 zombie children
, React Concurrency
, context loss
에 대한 해결책을 제시하기 위해서 제작되었다고 한다.
concurrent mode
라는 기능이 있는데, 리액트에서 렌더링되는 요소에서 일어나는 작업들을 "chunk"로 나눠서 더 수월하고 효과적인 방법으로 렌더링 관리를 할 수 있게 한다고 한다.E > D > C > B > A
형식으로 너무 깊숙하게 얽혀있다면 context에 접근이 제대로 되지 않을 수 있다는 의미다.기본적으로 Zustand는 설치 후 특정 파일에 스토어를 생성해서 관리하고 싶은 요소들을 넣어두고, 스토어와 컴포넌트를 바인딩해서 사용한다.
npm install zustand
yarn add zustand
Zustand는 store를 생성해서 관리할 상태들과, 그 상태들의 값을 변경할 수 있는 setState()
함수를 저장한다. 그리고 상태를 사용하고 싶은 컴포넌트엔 상태를 import하고, setState()
함수를 사용하고 싶다면 함수를 컴포넌트에 import하여 사용한다.
Zustand의 동작원리와 사용법은 더 무궁무진하지만, 일단 나는 여기서 기본적인 개념과 Flat update만 적어두려고 한다.
import { create } from 'zustand'
// 1. `create()` 함수를 import하여 스토어를 만들 수 있도록 한다
const useBearStore = create((set) => ({
// 2. 스토어의 이름을 지정하고 "set"을 매개변수로 받도록 지정한다.
bears: 0,
// 3. 관리하고 싶은 상태를 스토어에 저장하고,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
// 4. 특정 상태를 변경하는 함수를 생성한다.
// 4-1. 해당 함수는 setState() 함수로 상태를 변경하기 때문에 set을 스토어가 매개변수로 받는다.
removeAllBears: () => set({ bears: 0 }),
// 5. 관리할 상태는 1개 이상일 수 있고, 해당 상태를 변경하는 함수도 1개 이상일 수 있다.
}))
위의 예시를 보면 bears
라는 변수는 기본값이 0으로 되어있다.
그리고 해당 상태를 1씩 증가시키는 increasePopulation
함수와, 초기값으로 상태를 되돌리는 removeAllBears
함수가 있다. 이제 각각 함수를 사용해서 원하는 컴포넌트에서 상태를 변경할 수 있도록 import를 해야한다.
function BearCounter() {
const bears = useBearStore((state) => state.bears)
return <h1>{bears} around here ...</h1>
}
function Controls() {
const increasePopulation = useBearStore((state) => state.increasePopulation)
return <button onClick={increasePopulation}>one up</button>
}
스토어에 있는 값을 사용하기 위해선 컴포넌트에 변수/상수를 선언하고, 해당 변수/상수에 원하는 값을 store에서 꺼내와서 할당한다. 함수에 경우 이벤트를 발생하는 요소에 심어서 상태를 변경할 수 있다.
만약 스토어에서 1개 이상의 값을 가져오고싶다면, 간단하게 구조분해할당 문법을 사용해서 값을 손쉽게 가져올 수 있다.
const useStore = create((set) => ({
count: 0,
message: 'Hello, Zustand!',
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
updateMessage: (newMessage) => set({ message: newMessage }),
}))
function MyComponent() {
const { count, message, increment, decrement, updateMessage } = useStore()
return (
<div>
<p>Count: {count}</p>
<p>Message: {message}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={() => updateMessage('Zustand is awesome!')}>
Update Message
</button>
</div>
)
}
정말 기본적인 개념만 알아보았는데, 일단 보일러플레이트 코드가 굉장히 적어서 놀라울 정도였다.
state를 변경하는 법도 꽤나 직관적이라 러닝커브도 적어보이기 때문에 회사의 소스코드를 열심히 보면서 사용법과 응용방식을 익혀야겠다.
이전에 프로젝트를 진행할 땐 Redux만 사용해서 보일러플레이트 코드가 있다고해도 불편한점을 직관적으로 느끼지 못 했었는데, 이렇게 간결한 코드만으로 상태관리를 할 수 있는 라이브러리를 보니 "보일러플레이트 코드"에 대한 느낌이 어떤것인지 더 명확하게 알 수 있었다.
물론 프로젝트의 성격에 따라 라이브러리를 선택해야한다고는 하지만, 본질이 상태관리인만큼 점점 Redux를 사용하는 프로젝트가 줄어들지 않을까... 라는 생각이 든다.
Zustand는 꽤나 좋은 라이브러리인 것 같다!
출처: