Use Cases

Zustand: Claude Code 활용 가이드

zustand: Claude Code 활용. 실용적인 팁과 코드 예시를 포함합니다.

Zustandでシンプルかつ強力な状態관리を実現する

Zustandは最小限のAPIで強力な状態관리を提供するReact向け라이브러리です。Redux와 같은複雑な보일러플레이트が不要で、TypeScriptとの相性も抜群です。Claude Code는 ストア설계から최적화まで的確にサポートしてくれます。

기본적인ストア설계

Claude Code에ストアの설계を依頼합시다。

> Zustandでショッピングカートのストア를 생성해줘。
> 상품추가、삭제、数量변경、合計計算を含めて。TypeScript대응で。
import { create } from "zustand";

interface CartItem {
  id: string;
  name: string;
  price: number;
  quantity: number;
}

interface CartStore {
  items: CartItem[];
  addItem: (item: Omit<CartItem, "quantity">) => void;
  removeItem: (id: string) => void;
  updateQuantity: (id: string, quantity: number) => void;
  clearCart: () => void;
  totalItems: () => number;
  totalPrice: () => number;
}

export const useCartStore = create<CartStore>((set, get) => ({
  items: [],

  addItem: (item) =>
    set((state) => {
      const existing = state.items.find((i) => i.id === item.id);
      if (existing) {
        return {
          items: state.items.map((i) =>
            i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
          ),
        };
      }
      return { items: [...state.items, { ...item, quantity: 1 }] };
    }),

  removeItem: (id) =>
    set((state) => ({
      items: state.items.filter((i) => i.id !== id),
    })),

  updateQuantity: (id, quantity) =>
    set((state) => ({
      items: state.items.map((i) =>
        i.id === id ? { ...i, quantity: Math.max(0, quantity) } : i
      ).filter((i) => i.quantity > 0),
    })),

  clearCart: () => set({ items: [] }),

  totalItems: () => get().items.reduce((sum, i) => sum + i.quantity, 0),

  totalPrice: () =>
    get().items.reduce((sum, i) => sum + i.price * i.quantity, 0),
}));

미들웨어の활용

Zustandの미들웨어で永続化や로그출력を추가할 수 있습니다。

import { create } from "zustand";
import { persist, devtools, subscribeWithSelector } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";

interface AppStore {
  theme: "light" | "dark";
  locale: string;
  notifications: Notification[];
  setTheme: (theme: "light" | "dark") => void;
  setLocale: (locale: string) => void;
  addNotification: (notification: Notification) => void;
  removeNotification: (id: string) => void;
}

export const useAppStore = create<AppStore>()(
  devtools(
    persist(
      immer(
        subscribeWithSelector((set) => ({
          theme: "light",
          locale: "ja",
          notifications: [],

          setTheme: (theme) =>
            set((state) => {
              state.theme = theme;
            }),

          setLocale: (locale) =>
            set((state) => {
              state.locale = locale;
            }),

          addNotification: (notification) =>
            set((state) => {
              state.notifications.push(notification);
            }),

          removeNotification: (id) =>
            set((state) => {
              state.notifications = state.notifications.filter(
                (n) => n.id !== id
              );
            }),
        }))
      ),
      {
        name: "app-store",
        partialize: (state) => ({
          theme: state.theme,
          locale: state.locale,
        }),
      }
    ),
    { name: "AppStore" }
  )
);

スライスパターンで大規模ストアを分割

大規模애플리케이션では、ストアをスライスに分割して관리します。

// userSlice.ts
interface UserSlice {
  user: User | null;
  isAuthenticated: boolean;
  login: (credentials: LoginCredentials) => Promise<void>;
  logout: () => void;
}

const createUserSlice: StateCreator<StoreState, [], [], UserSlice> = (set) => ({
  user: null,
  isAuthenticated: false,
  login: async (credentials) => {
    const user = await authApi.login(credentials);
    set({ user, isAuthenticated: true });
  },
  logout: () => set({ user: null, isAuthenticated: false }),
});

// uiSlice.ts
interface UISlice {
  sidebarOpen: boolean;
  modalStack: string[];
  toggleSidebar: () => void;
  openModal: (id: string) => void;
  closeModal: () => void;
}

const createUISlice: StateCreator<StoreState, [], [], UISlice> = (set) => ({
  sidebarOpen: true,
  modalStack: [],
  toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
  openModal: (id) => set((s) => ({ modalStack: [...s.modalStack, id] })),
  closeModal: () => set((s) => ({ modalStack: s.modalStack.slice(0, -1) })),
});

// store.ts - スライスを통합
type StoreState = UserSlice & UISlice;

export const useStore = create<StoreState>()((...a) => ({
  ...createUserSlice(...a),
  ...createUISlice(...a),
}));

セレクター에 의한성능최적화

不要な再렌더링を防ぐ위해、セレクターを使い分けます。

// 個別の値を취득(推奨)
function CartIcon() {
  const totalItems = useCartStore((state) => state.totalItems());
  return <span>カート ({totalItems})</span>;
}

// shallow比較で객체を취득
import { useShallow } from "zustand/react/shallow";

function CartSummary() {
  const { totalItems, totalPrice } = useCartStore(
    useShallow((state) => ({
      totalItems: state.totalItems(),
      totalPrice: state.totalPrice(),
    }))
  );

  return (
    <div>
      <p>{totalItems}点の商品</p>
      <p>合計: ¥{totalPrice.toLocaleString()}</p>
    </div>
  );
}

정리

Zustandは그シンプルさと柔軟性で、多くのReact프로젝트の状態관리に適しています。Claude Code를 활용하면 ストア설계から미들웨어구성、성능최적화まで一貫して빠르게구현할 수 있습니다。

アトミックな状態관리のアプローチはJotaiアトミック状態관리を、데이터취득との연동はTanStack Query활용가이드를 참고하세요.Zustand공식 문서도 확인해 두세요.

#Claude Code #Zustand #React #state management #TypeScript