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 라이센스를 따릅니다.