PromleeBlog
sitemap
aboutMe

posting thumbnail
Zustand 소개 및 기본 사용법 - Zustand 기초부터 아키텍쳐 설계까지 1편
Introduction to Zustand and Basic Usage - From Basics to Architecture Part 1

📅

🚀

들어가기 전에 🔗

React 기반 프레임워크를 사용하여 개발을 할 때 규모가 조금씩 커지다 보면, '상태'라는 것을 어떻게 다뤄야 할지 막막해지는 순간이 찾아옵니다.

A 컴포넌트의 버튼을 눌렀을 때, 저 멀리 떨어진 B 컴포넌트의 내용이 바뀌어야 하는 상황을 상상해 보세요.
이처럼 여러 컴포넌트가 함께 공유하고 사용하는 데이터를 '전역 상태'라고 부릅니다.
그리고 이 전역 상태를 효과적으로 관리하는 것을 '상태 관리'라고 합니다.

이번 시간에는 수많은 상태 관리 라이브러리 중에서, 간결하고 쉬운 사용법으로 많은 사랑을 받고 있는 Zustand에 대해 알아보겠습니다.
상태 관리가 왜 필요한지부터 차근차근, 누구나 이해할 수 있도록 쉽게 설명해 드리겠습니다.

🚀

상태 관리는 왜 필요할까요? 🔗

리액트는 부모 컴포넌트에서 자식 컴포넌트로 데이터를 props를 통해 전달하는 하향식 데이터 흐름을 가지고 있습니다.

그런데 만약 최상위 컴포넌트의 상태를 아주 깊숙한 곳에 있는 하위 컴포넌트에서 사용해야 한다면 어떨까요?.
상태를 필요로 하지 않는 중간의 모든 컴포넌트들을 거쳐 props를 전달해야만 합니다.
이것을 바로
prop drilling
이라고 부릅니다.
Prop drilling
Prop drilling
prop drilling은 코드를 복잡하게 만들고, 유지보수를 어렵게 만듭니다.
상태 관리 라이브러리는 이러한 문제를 해결하기 위해 등장했습니다.
컴포넌트 트리 바깥에 '전역 스토어'라는 상태 저장 공간을 만들고, 어떤 컴포넌트든 필요할 때 이 스토어에 직접 접근하여 상태를 읽거나 업데이트할 수 있게 해줍니다.

🚀

기존 상태 관리 도구들과의 비교 🔗

Zustand가 등장하기 전에도 여러 상태 관리 도구들이 있었습니다.
대표적인 몇 가지를 간단히 비교해 보겠습니다.

React Context API 🔗

리액트에 내장된 기능으로, 별도의 라이브러리 설치 없이 사용할 수 있습니다.
하지만 Context API는 상태의 작은 일부만 변경되어도 해당 Context를 구독하는 모든 컴포넌트가 불필요하게 다시 렌더링되는 단점이 있습니다.
따라서 자주 변경되지 않는 상태(예: 테마 정보, 사용자 인증 정보)에 사용하는 것이 적합합니다.

Redux 🔗

가장 오래되고 유명한 상태 관리 라이브러리입니다.
Flux 아키텍처를 기반으로 하여 상태 변화를 예측 가능하게 만들고, 미들웨어를 통한 비동기 처리, 강력한 개발자 도구 등 생태계가 매우 발달해 있습니다.
하지만 '보일러플레이트'라고 불리는, 하나의 상태를 추가하기 위해 작성해야 하는 코드(Action, Reducer, Dispatch 등)가 너무 많다는 단점이 있습니다.

Recoil 🔗

페이스북에서 만든 상태 관리 라이브러리로, Redux의 복잡함을 개선하고자 등장했습니다.
상태를 'atom'이라는 작은 단위로 쪼개어 관리하고, 이 atom들을 조합하여 새로운 파생 상태(selector)를 만들 수 있습니다.
Context API와 비슷한 사용법을 가지면서도, 상태 변화에 따른 리렌더링 최적화 문제를 해결했습니다.

🚀

Zustand란 무엇일까요? 🔗

Zustand는 독일어로 '상태'를 의미합니다.

이름처럼 상태 관리에만 집중한, 아주 작고 빠른 라이브러리입니다.
Zustand는 Redux와 Recoil의 장점을 합치고, 단점은 개선하려는 목표를 가지고 만들어졌습니다.

🚀

Zustand 예제: 카운터 만들기 🔗

백문이 불여일견입니다.
간단한 카운터 예제를 통해 Zustand를 어떻게 사용하는지 직접 코드로 확인해 보겠습니다.
➡️

1. Zustand 설치하기 🔗

먼저 프로젝트에 Zustand를 설치합니다.
npm install zustand
➡️

2. 스토어(Store) 만들기 🔗

스토어는 상태와 그 상태를 변경하는 함수들을 담고 있는 공간입니다.
src 폴더 아래에 store.ts 파일을 하나 만들어 보겠습니다.
src/store.ts
import { create } from 'zustand'
 
const useCounterStore = create((set) => ({
  count: 0,
  increase: () => set((state) => ({ count: state.count + 1 })),
  decrease: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}))
 
export default useCounterStore
➡️

3. 컴포넌트에서 스토어 사용하기 🔗

이제 리액트 컴포넌트에서 방금 만든 스토어를 사용해 보겠습니다.
App.tsx
import React from 'react'
import useCounterStore from './store'
 
function Counter() {
  const { count, increase, decrease, reset } = useCounterStore()
 
  return (
    <div>
      <h1>카운터</h1>
      <p>{count}</p>
      <button onClick={increase}>+1</button>
      <button onClick={decrease}>-1</button>
      <button onClick={reset}>리셋</button>
    </div>
  )
}
 
export default Counter

정말 간단하지 않나요?.
별도의 Provider 설정 없이, create로 스토어를 만들고, 컴포넌트에서 훅을 호출하는 것만으로 전역 상태 관리가 가능해졌습니다.

🚀

전역 상태와 지역 상태 🔗

Zustand가 편리하다고 해서 모든 상태를 전역 스토어에 넣는 것은 좋지 않습니다.
상태는 사용 범위에 따라
전역 상태
지역 상태
로 구분하여 관리하는 것이 바람직합니다.
“이 상태가 다른 컴포넌트에서도 필요한가?”
라는 질문을 스스로에게 던져보세요.
'아니오'라면 지역 상태로, '예'라면 전역 상태로 관리하는 것을 고려하면 됩니다.

🚀

결론 🔗

오늘은 상태 관리가 왜 필요한지부터 시작해서, 기존의 여러 상태 관리 도구들과 Zustand를 비교해 보았습니다.

그리고 간단한 카운터 예제를 통해 Zustand의 기본적인 사용법을 익혔습니다.
Zustand는 복잡한 설정 없이도 강력한 상태 관리를 가능하게 해주는, 매우 직관적이고 효율적인 도구입니다.

이번 시간에는 Zustand의 가장 기본적인 모습만 살펴보았습니다.
다음 편에서는 Zustand의 핵심 개념인 selectorshallow, 그리고 스토어를 다루는 다양한 함수들에 대해 더 깊이 파고들어 보겠습니다.

참고 🔗