포스트

Zustand 비동기 액션 처리

Zustand 비동기 액션 처리

Zustand 비동기 액션

정의

  • 외부 API 호출이나 타이머 등 시간이 소요되는 작업을 Zustand 스토어 내에서 비동기적으로 수행하는 방식
  • async/await 문법을 사용하여 비동기 로직을 동기적인 코드 흐름처럼 직관적으로 작성한다.

특징

  • 미들웨어 불필요
    • Redux Thunk나 Saga 같은 별도의 미들웨어 없이도 함수 자체를 비동기로 정의하여 사용할 수 있다.
  • 유연한 상태 업데이트
    • 비동기 작업의 시작, 성공, 실패 시점에 맞춰 set 함수를 호출하여 상태를 자유롭게 갱신한다.
  • 비즈니스 로직 집중
    • 데이터 페칭과 관련된 상태(Loading, Error, Data)를 스토어 한곳에서 집중 관리할 수 있다.

장점

  • 비동기 로직이 컴포넌트에서 분리되어 UI 코드의 복잡도가 낮아진다.
  • API 호출 시 필요한 파라미터나 상태들을 스토어에서 직접 참조할 수 있어 코드가 간결해진다.
  • 여러 컴포넌트에서 동일한 비동기 작업이 필요할 때 로직을 쉽게 공유할 수 있다.

단점

  • 비동기 액션이 많아지면 스토어의 규모가 커지고 복잡해질 수 있다.
  • 레이스 컨디션(Race Condition) 처리를 위한 추가적인 로직이 필요할 수 있다.

비동기 처리 패턴

1. 기본 비동기 액션 구조

가장 일반적인 async/await 기반의 처리 방식이다.

const useTodoStore = create((set) => ({
  todos: [],
  fetchTodos: async () => {
    const response = await fetch('https://jsonplaceholder.typicode.com/todos');
    const data = await response.json();
    set({ todos: data });
  },
}));

2. 로딩 및 에러 상태 관리

사용자 경험을 위해 비동기 작업의 진행 상태와 결과에 따른 에러 상태를 함께 관리한다.

const useUserStore = create((set) => ({
  user: null,
  loading: false,
  error: null,
  getUser: async (userId: string) => {
    set({ loading: true, error: null });
    try {
      const response = await fetch(`/api/users/${userId}`);
      if (!response.ok) throw new Error('User not found');
      const data = await response.json();
      set({ user: data, loading: false });
    } catch (err: any) {
      set({ error: err.message, loading: false });
    }
  },
}));

3. 스토어 상태 참조

액션 내부에서 다른 상태값이 필요한 경우 get 함수를 활용할 수 있다.

const useSearchStore = create((set, get) => ({
  query: '',
  results: [],
  search: async () => {
    const currentQuery = get().query; // 현재 쿼리 상태 참조
    if (!currentQuery) return;

    const response = await fetch(`/api/search?q=${currentQuery}`);
    const data = await response.json();
    set({ results: data });
  },
}));

결론

Zustand는 비동기 작업을 처리하는 데 있어 매우 낮은 진입 장벽과 높은 자유도를 제공한다. 단순히 API를 호출하는 것을 넘어 로딩, 에러, 캐싱 등 관련 상태를 체계적으로 설계하면 더욱 견고한 애플리케이션을 구축할 수 있다.

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