Introduction

김동현·2026년 3월 4일

소개 (Introduction)

Zustand 사용법

Logo Zustand

안녕하세요! 여러분의 프론트엔드 학습을 도와줄 강사입니다. 오늘은 최근 React 생태계에서 가장 뜨거운 사랑을 받고 있는 상태 관리 라이브러리인 Zustand(주스탠드)의 공식 문서를 함께 살펴보겠습니다. 자, 시작해볼까요?

Zustand는 작고, 빠르며, 확장 가능한 아주 기본적인(bearbones - 원문에서는 뼈대라는 뜻의 barebones를 곰을 뜻하는 bear와 합쳐 언어유희를 했네요! 🐻) 상태 관리 솔루션이에요. Zustand는 우리에게 아주 익숙한 React의 Hooks를 기반으로 한 편안한 API를 제공합니다. 다른 상태 관리 도구들처럼 초기 설정 코드(보일러플레이트)가 장황하거나 특정한 방식을 강요(opinionated)하지 않아요. 하지만 상태의 흐름을 명확하게 하고 Flux 패턴과 유사하게 동작할 수 있는 충분한 규칙은 잘 갖추고 있답니다.

💡 강사의 팁! > Flux 패턴이란 데이터가 한 방향으로만 흐르도록 하는 아키텍처를 말해요. Zustand도 상태(State)를 변경하려면 정해진 함수(Action)를 통해서만 가능하게 만들어, 애플리케이션의 데이터 흐름을 예측하기 쉽게 도와준답니다.

로고가 귀엽다고 무시하시면 안 돼요. 아주 날카로운 발톱을 숨기고 있거든요! Zustand 개발팀은 React 개발자들이 상태 관리를 하면서 흔히 겪는 골치 아픈 문제들을 해결하는 데 정말 많은 시간을 투자했어요. 예를 들면 끔찍한 좀비 자식(zombie child) 문제, React의 동시성(concurrency) 문제, 그리고 여러 렌더러가 섞여 있을 때 발생하는 컨텍스트 유실(context loss) 문제 같은 것들이죠. 아마도 React 생태계에서 이 모든 예외적인 문제들을 제대로 처리해 낸 유일한 상태 관리 도구일지도 모릅니다.

💡 강사의 팁! > "좀비 자식 문제"나 "컨텍스트 유실"은 Redux나 Context API를 깊게 사용할 때 가끔 마주치는 아주 까다로운 버그 현상이에요. Zustand를 쓰면 이런 복잡하고 어려운 엣지 케이스들을 라이브러리 내부에서 알아서 다 처리해주기 때문에, 우리는 화면을 그리고 비즈니스 로직을 짜는 데에만 온전히 집중할 수 있습니다. 정말 든든하죠?

여기에서 직접 코드를 만져보며 라이브 데모를 확인해 보실 수 있어요.


설치하기 (Installation)

Zustand는 NPM 패키지로 제공되므로 여러분의 프로젝트에 쉽게 설치해서 사용할 수 있습니다. 터미널을 열고 아래 명령어를 입력해 보세요.

# NPM
npm install zustand
# Or, use any package manager of your choice.

먼저, 스토어(Store)를 만들어보세요 (First create a store)

Zustand의 가장 큰 특징은 여러분의 스토어 자체가 바로 하나의 Hook이 된다는 점이에요! 이 스토어 안에는 정말 뭐든 다 넣을 수 있습니다. 숫자나 문자열 같은 원시 타입(primitives)부터 객체(objects), 심지어 상태를 변경하는 함수(functions)까지도요.

상태를 업데이트할 때 사용하는 set 함수는 기존 상태와 새로운 상태를 알아서 병합(merge) 해준답니다.

import { create } from 'zustand'

const useBear = create((set) => ({
  bears: 0,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
  updateBears: (newBears) => set({ bears: newBears }),
}))

💡 강사의 팁! > 여기서 Redux와 비교해보면 Zustand의 직관적인 매력이 확 드러납니다. Redux는 Action 타입 정의, Action Creator 함수, Reducer, Store 설정 등 셋업할 게 참 많죠?
하지만 Zustand는 create 함수 하나로 상태(bears)와 그 상태를 변경하는 액션 함수들(increasePopulation, removeAllBears 등)을 한 번에 깔끔하게 정의합니다.
게다가 set 함수가 1단계 깊이에 대해 자동 병합(shallow merge)을 지원하기 때문에, 상태 객체의 일부만 업데이트할 때 매번 ...state 처럼 스프레드 연산자를 쓰며 기존 상태를 복사해 올 필요가 없어서 코드 짜기가 무척 수월해집니다!


그다음, 컴포넌트에 연결하면 끝입니다! (Then bind your components, and that's it!)

이제 위에서 만든 Hook을 애플리케이션 어디서든 마음껏 사용할 수 있어요. Context API를 쓸 때처럼 복잡한 Provider 컴포넌트로 최상단 앱을 감쌀(wrapping) 필요도 전혀 없습니다.

우리의 컴포넌트에서 필요한 상태만 쏙쏙 골라내면(Select), 그 상태가 변경될 때만 해당 컴포넌트가 알아서 스마트하게 리렌더링(re-render) 된답니다.

function BearCounter() {
  const bears = useBear((state) => state.bears)
  return <h1>{bears} bears around here...</h1>
}

function Controls() {
  const increasePopulation = useBear((state) => state.increasePopulation)
  return <button onClick={increasePopulation}>one up</button>
}

💡 강사의 팁! > 이 부분에서 가장 중요한 핵심은 바로 "필요한 상태만 선택(Select)해서 가져온다"는 것입니다.
위의 BearCounter 컴포넌트를 보면 (state) => state.bears 처럼 내가 구독할 특정 상태만 딱 지정해주었죠? 이렇게 하면 스토어 내의 다른 상태(예: 호랑이 수, 사자 수 등)가 변하더라도 BearCounter 컴포넌트는 자신이 구독하지 않은 값이므로 불필요하게 리렌더링되지 않습니다. 즉, 코드를 이렇게 작성하는 것만으로도 자연스럽게 성능 최적화가 이루어진다는 뜻입니다! 정말 훌륭하죠?


profile
프론트에_가까운_풀스택_개발자

0개의 댓글