Skip to content

Zustand 상태관리 컴포넌트 - ShoppingList 구현

Zustand로 간결한 상태관리 스토어를 구현하고 Redux와의 차이를 비교 분석한다

학습 목표

  1. Zustand의 핵심 API와 스토어 생성 패턴을 익힌다
  2. Zustand와 Redux Toolkit의 구조적 차이를 이해한다
  3. Tailwind CSS 환경에서 ShoppingList 컴포넌트를 구현할 수 있다
  4. 마이크로 프론트엔드에서 상태관리 라이브러리 선택 기준을 세울 수 있다

본문

1. Zustand와 Redux 비교 개요

web 프로젝트는 docs 프로젝트(Redux + ANTD)와 다른 기술 스택을 선택했다. 상태관리에 Zustand를, UI에 Tailwind CSS를 사용한다. 이를 통해 마이크로 프론트엔드에서 각 프로젝트가 독립적인 기술 선택을 할 수 있음을 보여준다.

2. Zustand 스토어 구현

데이터 모델

typescript
// store.ts
import { create } from "zustand";

interface Item {
  id: number;
  name: string;
  price: number;
  count: number;
}

스토어 생성 - Redux와의 코드량 비교

typescript
// Zustand 스토어 - 단 몇 줄로 완성
interface ShoppingStore {
  items: Item[];
  deleteItem: (id: number) => void;
}

const useShoppingStore = create<ShoppingStore>((set) => ({
  items: [
    { id: 1, name: "MacBook Pro", price: 2500000, count: 1 },
    { id: 2, name: "iPad Mini 6", price: 650000, count: 2 },
    { id: 3, name: "AirPods Pro", price: 350000, count: 3 },
    { id: 4, name: "USB-C Hub", price: 89000, count: 1 },
  ],
  deleteItem: (id: number) =>
    set((state) => ({
      items: state.items.filter((item) => item.id !== id),
    })),
}));

export default useShoppingStore;

Redux Toolkit 동일 기능 대비

typescript
// Redux Toolkit - 상대적으로 긴 설정
const itemSlice = createSlice({
  name: "items",
  initialState: [...],
  reducers: {
    deleteItem: (state, action: PayloadAction<number>) => {
      return state.filter(item => item.id !== action.payload);
    }
  }
});
export const { deleteItem } = itemSlice.actions;
export const selectItems = (state: RootState) => state.items;
const store = configureStore({ reducer: { items: itemSlice.reducer } });
// + Provider로 감싸야 함

3. 상세 비교 분석

비교 항목Redux ToolkitZustand
보일러플레이트많음 (slice, store, provider)적음 (create 한 줄)
Provider필수불필요
불변성 관리Immer 내장수동 (또는 Immer 미들웨어)
DevToolsRedux DevTools 내장미들웨어로 지원
미들웨어풍부한 에코시스템persist, devtools 등
TypeScript타입 설정 다소 복잡간단한 제네릭
번들 크기~12KB (gzipped)~1KB (gzipped)
학습 곡선높음낮음

4. ShoppingList 컴포넌트 구현

tsx
import useShoppingStore from "./store";

function ShoppingList() {
  const { items, deleteItem } = useShoppingStore();

  const columns = [
    { dataIndex: "id", title: "ID" },
    { dataIndex: "name", title: "상품명" },
    {
      dataIndex: "price",
      title: "가격",
      render: (value: number) => `${value.toLocaleString()}원`,
    },
    { dataIndex: "count", title: "수량" },
    {
      dataIndex: "id",
      title: "처리",
      render: (id: number) => (
        <Button buttonType="cancel" onClick={() => deleteItem(id)}>
          삭제
        </Button>
      ),
    },
  ];

  return (
    <div>
      <Table columns={columns} datas={items} rowKey="id" />
    </div>
  );
}

5. Zustand의 고급 패턴

셀렉터로 리렌더링 최적화

typescript
// 전체 스토어 구독 - items 외의 변경에도 리렌더링
const store = useShoppingStore();

// 셀렉터 사용 - items 변경 시에만 리렌더링
const items = useShoppingStore((state) => state.items);

// 다중 값 셀렉터
const { items, deleteItem } = useShoppingStore(
  (state) => ({ items: state.items, deleteItem: state.deleteItem }),
  shallow  // 얕은 비교로 불필요한 리렌더링 방지
);

미들웨어 적용

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

const useShoppingStore = create<ShoppingStore>()(
  devtools(
    persist(
      (set) => ({
        items: [...],
        deleteItem: (id) => set((state) => ({
          items: state.items.filter(item => item.id !== id)
        })),
      }),
      { name: "shopping-store" }  // localStorage 키
    )
  )
);

6. 마이크로 프론트엔드에서의 상태관리 선택 기준

7. 핵심 포인트: 기술 선택의 독립성

마이크로 프론트엔드의 가장 큰 장점 중 하나는 각 프로젝트가 독립적으로 기술을 선택할 수 있다는 점이다.

  • docs 프로젝트: Redux Toolkit + ANTD
  • web 프로젝트: Zustand + Tailwind CSS
  • legacy 프로젝트: 클래스 컴포넌트 + 순수 CSS

이 세 프로젝트의 컴포넌트가 하나의 페이지에서 동시에 렌더링되더라도, Shadow DOM 격리 덕분에 기술 스택의 차이가 문제가 되지 않는다.

핵심 정리

  1. Zustand의 간결함: create() 한 번의 호출로 상태와 액션을 모두 정의하고, Provider 없이 훅으로 바로 사용할 수 있다
  2. Redux 대비 장점: 보일러플레이트가 적고, 번들 크기가 작으며, Provider 없이 동작하므로 Shadow DOM 환경에 더 적합하다
  3. 선택 기준: 프로젝트 규모, Shadow DOM 사용 여부, 팀 경험에 따라 적절한 라이브러리를 선택한다
  4. 기술 독립성: 마이크로 프론트엔드에서는 각 프로젝트가 서로 다른 상태관리 라이브러리를 사용해도 문제가 없다

다음 단계

Tailwind 공유 컴포넌트 ->