개발천재

[ReactJS] 상태 관리를 더 쉽게, Zustand 본문

개발 준비/ReactJS

[ReactJS] 상태 관리를 더 쉽게, Zustand

세리블리 2025. 4. 30. 13:03
728x90
반응형

Zustand란?

Zustand는 React에서 상태(state)를 쉽게 관리할 수 있게 해주는 도구(라이브러리)이다.

React에서는 일반적으로 useState나 useContext를 쓰는데, 컴포넌트가 많아지면 상태를 공유하거나 관리하는 게 점점 복잡해진다. Zustand는 공용 창고처럼 이걸 한 곳에 다 모아서 관리하게 해주는 역할을 한다. 컴포넌트 간 상태 공유가 필요할 때, 또는 상태를 한 곳에 깔끔히 모아두고 싶을 때 유용하게 쓰인다.

 

Zustand 사용 목적

React에서 버튼 컴포넌트에서 숫자를 올렸는데, 헤더, 사이드바, 푸터에도 그 숫자가 보여야 하는 경우가 있다. 이럴 땐 상태를 여러 컴포넌트끼리 공유해야 한다. Zustand는 이런 걸 아주 간단하게 만들어준다.

Zustand가 항상 필요한 건 아니다. 예를 들어 컴포넌트 하나 안에서만 쓰는 count, toggle, input 같은 단순 상태는 그냥 useState로 충분하다. 프로젝트 규모가 작고, 상태 공유가 거의 없을 땐 Zustand를 쓰지 않아도 된다.

 

1) 여러 컴포넌트에서 같은 상태를 써야 할 때

예: 로그인 사용자 정보 (user)
로그인하면 유저 이름, 프로필, 토큰 같은 정보를 저장해야한다. 이 정보는 헤더, 사이드바, 마이페이지, 설정 페이지 등 여기저기에서 사용된다. 만약 이런 상태를 매번 props로 넘기거나 useContext로 관리하면 번거롭고 복잡하다. 그러나 Zustand로 한 번에 공유하면 코드가 깔끔해진다.

// store.js

import { create } from 'zustand'

const useUserStore = create((set) => ({
  user: null,
  setUser: (user) => set({ user }),
  logout: () => set({ user: null }),
}))

export default useUserStore
// Header.jsx

import useUserStore from './store'

function Header() {
  const user = useUserStore((state) => state.user)
  return <div>{user ? `안녕하세요, ${user.name}` : '로그인 해주세요'}</div>
}
// Login.jsx

import useUserStore from './store'

function Login() {
  const setUser = useUserStore((state) => state.setUser)

  const handleLogin = () => {
    const fakeUser = { name: '홍길동', email: 'hong@test.com' }
    setUser(fakeUser)
  }

  return <button onClick={handleLogin}>로그인</button>
}

 

 

2) 페이지가 바뀌어도 상태를 유지하고 싶을 때

예: 장바구니
예를 들어 사용자가 쇼핑몰에서 물건을 담았다고 가정해보자. 페이지를 여기저기 옮겨 다녀도 장바구니에 담긴 정보는 유지되어야 한다. useState는 컴포넌트가 사라지면 날아가지만 Zustand는 전역에 저장하기 때문에 날아갈 문제가 없다.

// cartStore.js

import { create } from 'zustand'

const useCartStore = create((set) => ({
  cart: [],
  addToCart: (item) => set((state) => ({ cart: [...state.cart, item] })),
  removeFromCart: (id) => set((state) => ({
    cart: state.cart.filter(item => item.id !== id)
  })),
}))
// ProductPage.jsx

import useCartStore from './cartStore'

function Product({ product }) {
  const addToCart = useCartStore((state) => state.addToCart)
  return <button onClick={() => addToCart(product)}>장바구니 담기</button>
}
// CartPage.jsx

import useCartStore from './cartStore'

function CartPage() {
  const cart = useCartStore((state) => state.cart)
  return (
    <ul>
      {cart.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  )
}

 

 

 

3) 모달, 토스트 등 UI 상태를 전역에서 제어할 때

예: 토스트 메시지
에러 나면 어느 컴포넌트에서든 showToast("에러 발생") 를 나타나게 하고 싶다. 그런데 useState는 특정 컴포넌트 안에 있어야 한다. Zustand를 쓰면 showToast 함수를 전역으로 두고, 어디서든 실행할 수 있다.

// uiStore.js

import { create } from 'zustand'

const useUIStore = create((set) => ({
  toastMessage: '',
  showToast: (msg) => set({ toastMessage: msg }),
  clearToast: () => set({ toastMessage: '' }),
}))

export default useUIStore
// Anywhere.jsx(에러 발생 시)

import useUIStore from './uiStore'

function SomeComponent() {
  const showToast = useUIStore((state) => state.showToast)

  return (
    <button onClick={() => showToast('에러가 발생했어요!')}>
      오류 발생 버튼
    </button>
  )
}
// Toast.jsx

import useUIStore from './uiStore'

function Toast() {
  const { toastMessage, clearToast } = useUIStore()

  if (!toastMessage) return null

  return (
    <div onClick={clearToast} className="toast">
      {toastMessage}
    </div>
  )
}

 

 

4) 게임, 시뮬레이션, 실시간 앱 등 상태가 자주 바뀌는 앱

예: 클릭 게임 점수, 물리 시뮬레이션 등
상태가 0.1초마다 막 바뀌고, 복잡한 구조를 갖고 있는 앱에서는 복잡한 상태 관리보단 간단하고 빠르게 쓸 수 있는 도구가 필요하다.  Zustand는 가볍고 빠르기 때문에 이런 앱에서도 잘 사용할 수 있다.

// gameStore.js

import { create } from 'zustand'

const useGameStore = create((set) => ({
  score: 0,
  increase: () => set((state) => ({ score: state.score + 1 })),
  reset: () => set({ score: 0 }),
}))

export default useGameStore
// Game.jsx

import useGameStore from './gameStore'

function Game() {
  const { score, increase, reset } = useGameStore()
  return (
    <div>
      <h1>점수: {score}</h1>
      <button onClick={increase}>+1</button>
      <button onClick={reset}>초기화</button>
    </div>
  )
}

 

 

5) 폼 데이터를 여러 페이지에 걸쳐서 저장할 때 (멀티스텝 폼)

예: 회원가입 단계

예를 들어 회원가입을 하는데 먼저 이름을 입력하는 페이지가 나오고, 다음에 주소를 입력하는 페이지가 나오고, 여러 페이지에 걸쳐서 폼이 나온다고 가정해보자. 이렇게 하면 각 단계마다 입력한 내용을 다음 페이지에서도 기억하고 있어야 한다. 이런 경우 상태를 하나로 묶어 Zustand에 저장하면 깔끔하게 처리할 수 있다.

// formStore.js

import { create } from 'zustand'

const useFormStore = create((set) => ({
  form: {
    name: '',
    email: '',
    password: ''
  },
  updateForm: (newValues) =>
    set((state) => ({ form: { ...state.form, ...newValues } })),
}))

export default useFormStore
// Step1.jsx

import useFormStore from './formStore'

function Step1() {
  const { form, updateForm } = useFormStore()

  return (
    <input
      value={form.name}
      onChange={(e) => updateForm({ name: e.target.value })}
      placeholder="이름 입력"
    />
  )
}
// Step2.jsx

import useFormStore from './formStore'

function Step2() {
  const { form, updateForm } = useFormStore()

  return (
    <input
      value={form.email}
      onChange={(e) => updateForm({ email: e.target.value })}
      placeholder="이메일 입력"
    />
  )
}

 

 

 

사용방법

Zustand 설치하기

터미널에 아래의 코드를 입력하여 Zustand 설치를 진행한다.

npm install zustand

 

 

상태 만들기
store.js 파일에 상태(state)를 만든다.

아래의 코드는 상태 count와 증가/감소 함수 increase, decrease를 만든 예제이다.

// store.js
import { create } from 'zustand'

const useStore = create((set) => ({
  count: 0,
  increase: () => set((state) => ({ count: state.count + 1 })),
  decrease: () => set((state) => ({ count: state.count - 1 })),
}))

 

 

컴포넌트에서 사용하기

이 Counter 컴포넌트에서는 어디서든 count 값을 가져다 쓸 수 있다. 다른 컴포넌트에서도 마찬가지이다.

// Counter.js
import React from 'react'
import useStore from './store'

function Counter() {
  const count = useStore((state) => state.count)
  const increase = useStore((state) => state.increase)
  const decrease = useStore((state) => state.decrease)

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={decrease}>-</button>
      <button onClick={increase}>+</button>
    </div>
  )
}



요약 정리

  • create() : 상태를 만드는 함수
  • useStore : 만든 상태를 가져오는 Hook
  • set() : 상태를 변경하는 함수
  • state : 현재 상태 값
반응형