포스트

Zustand 액션 설계 가이드

Zustand 액션 설계 가이드

Zustand 액션 설계

정의

  • 스토어의 상태를 변경하는 함수(Action)를 효율적으로 정의하고 관리하기 위한 설계 방식
  • 상태 변경 로직을 컴포넌트와 분리하여 코드의 재사용성과 테스트 용이성을 높이는 것이 목적이다.

특징

  • 상태와 액션의 공존
    • Zustand는 상태(State)와 이를 변경하는 액션(Action)을 하나의 스토어 객체 내에 정의한다.
  • 불변성 유지
    • set 함수를 통해 상태를 변경하며, 내부적으로 불변성을 유지하도록 처리한다.
  • 비동기 처리 지원
    • 액션 내에서 async/await를 사용하여 비동기 로직(API 호출 등)을 직관적으로 작성할 수 있다.

장점

  • 컴포넌트 내부에 비즈니스 로직이 섞이는 것을 방지할 수 있다.
  • 액션을 모듈화하여 여러 컴포넌트에서 동일한 로직을 재사용하기 쉽다.
  • 디버깅 시 상태가 ‘어떻게’ 변하는지 액션 단위로 추적 가능하다.

단점

  • 액션 로직이 너무 커지면 스토어 파일이 비대해질 수 있다.
  • 상태 변경이 여러 액션에 분산되어 있으면 전체적인 흐름을 파악하기 어려울 수 있다.

액션 설계 패턴 및 가이드

1. 액션을 스토어 내부에 정의하기

Zustand의 기본 권장 사항으로, 상태와 액션을 한곳에 모아 관리한다.

const useUserStore = create((set) => ({
  name: 'Sangwon',
  // 액션을 상태와 함께 정의
  updateName: (newName) => set({ name: newName }),
}));

2. 비동기 액션 처리

API 호출과 같은 비동기 로직도 액션 내부에서 직접 처리한다.

const usePostStore = create((set) => ({
  posts: [],
  isLoading: false,
  fetchPosts: async () => {
    set({ isLoading: true });
    try {
      const response = await fetch('https://api.example.com/posts');
      const data = await response.json();
      set({ posts: data, isLoading: false });
    } catch (error) {
      set({ isLoading: false });
      console.error('Failed to fetch posts', error);
    }
  },
}));

3. 복잡한 로직의 캡슐화

여러 상태를 동시에 변경하거나 복잡한 계산이 필요한 경우, 컴포넌트가 아닌 액션 내부에서 모든 로직을 수행한다.

const useCartStore = create((set) => ({
  items: [],
  totalPrice: 0,
  addItem: (item) => set((state) => {
    const nextItems = [...state.items, item];
    const nextTotal = nextItems.reduce((acc, curr) => acc + curr.price, 0);
    return { items: nextItems, totalPrice: nextTotal };
  }),
}));

4. 커스텀 훅을 통한 액션 분리 (선택 사항)

스토어 파일이 너무 커질 경우, 액션만 따로 추출하여 관리하거나 커스텀 훅으로 감싸서 제공할 수 있다.

// 컴포넌트에서 필요한 액션만 따로 가져오기 쉽게 구성
export const useUserActions = () => useUserStore((state) => ({
  updateName: state.updateName,
  reset: state.reset,
}));

결론

Zustand의 액션은 상태와 밀접하게 결합되어 있을 때 가장 큰 효율을 발휘한다. 복잡한 비즈니스 로직은 최대한 액션 내부로 밀어 넣어 컴포넌트의 부담을 줄이고, 일관된 상태 변경 흐름을 유지하는 것이 중요하다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.