[9주차/오스카] 워크북 제출합니다.#94
Hidden character warning
Conversation
|
|
||
| clearCart: () => set({ cartItems: [], amount: 0, total: 0 }), | ||
|
|
||
| calculateTotals: () => |
There was a problem hiding this comment.
zustand의 사용법을 잘 알고 계시고 사용하신 것 같아 좋습니다.
하지만 기존의 calculateTotals를 외부에서 매번 수동으로 호출하게 유도하는 구조는 잠재적인 UI 동기화 버그가 있을 수 있습니다.
현재 increase, decrease, removeItem 액션들이 실행될 때, cartItems만 갱신할 뿐 amount와 total을 업데이트하지 않고 있습니다.
다른 컴포넌트에서 액션을 호출한 후 calculateTotals()를 호출해 주어야만 UI에 값이 동기화 될 것으로 보입니다.
Zustand에는 셀렉터 기능을 사용하여 상태가 변할 때 총액과 총 수량을 계산하게 하는 로직을 구축하면 다른 컴포넌트에서는 해당 훅 하나만 호출하면 스토어에 있던 구독하는 상태가 변할 때 알아서 변하는 것을 보실 수 있을 것 입니다.
초안으로 작성해본 코드를 첨부했습니다. 참고하시어 리팩토링 하면 더 좋은 코드가 될 것 같습니다
import { create } from 'zustand';
import type { CartItem } from '../types/cart';
import cartItems from '../constants/cartItems';
type CartStore = {
cartItems: CartItem[];
increase: (id: string) => void;
decrease: (id: string) => void;
removeItem: (id: string) => void;
clearCart: () => void;
};
// 1. 스토어는 cartItems만 관리합니다.
export const useCartStore = create<CartStore>((set) => ({
cartItems,
increase: (id) =>
set((state) => ({
cartItems: state.cartItems.map((item) =>
item.id === id ? { ...item, amount: item.amount + 1 } : item
),
})),
decrease: (id) =>
set((state) => ({
cartItems: state.cartItems
.map((item) => (item.id === id ? { ...item, amount: item.amount - 1 } : item))
.filter((item) => item.amount >= 1),
})),
removeItem: (id) =>
set((state) => ({
cartItems: state.cartItems.filter((item) => item.id !== id),
})),
clearCart: () => set({ cartItems: [] }),
}));
// 계산 전용 Selector 훅을 분리합니다.
export const useCartTotals = () => {
// 스토어에서 cartItems의 변경만 구독합니다.
const cartItems = useCartStore((state) => state.cartItems);
// 데이터가 바뀔 때만 총수량과 총액을 계산합니다.
const amount = cartItems.reduce((sum, item) => sum + item.amount, 0);
const total = cartItems.reduce((sum, item) => sum + Number(item.price) * item.amount, 0);
return { amount, total };
};|
|
||
| const initialState: CartState = { | ||
| cartItems, | ||
| amount: cartItems.length, |
There was a problem hiding this comment.
코드 잘 읽었습니다! 전체적으로 구조가 촘촘히 잘 짜여 있는 좋은 코드인 것 같아요!
다음 한가지 사항만 개선되면 좋을 것 같아 리뷰 남깁니다!
현재 미션1,2,3에서 공통적으로 amount: cartItems.length로 초기화하고 있는데, amount는 아이템 개수가 아니라 각 아이템의 수량 합계여야 의미가 적합할 것 같습니다!
지금은 초기 데이터의 모든 amount가 1이라서 우연히 같은 값이 나올 수 있지만, 수량이 다른 데이터가 들어오면 틀린 값이 되어버릴 수 있어요!
다음과 같이 수정하면 좋을 것 같습니다!
amount: cartItems.reduce((sum, item) => sum + item.amount, 0),
이번 주차도 너무 수고 많으셨습니다! 기말고사 화이팅 하세용~!
| const handleConfirm = () => { | ||
| clearCart(); | ||
| calculateTotals(); | ||
| closeModal(); | ||
| }; | ||
|
|
There was a problem hiding this comment.
현재 handleConfirm 내부를 보면 장바구니를 비운 직후 수동으로 calculateTotals()를 연속 호출되고 있습니다
스토어의 increase, decrease 같은 액션들이 일어날 때 스토어 내부에서 cartItems 변경과 동시에 합산 연산까지 한 번에 처리하도록 스토어가 리팩토링된다면 컴포넌트단에서 수동으로 calculateTotals()를 연달아 호출해 줄 필요가 사라질 것 같습니다
장바구니를 비워버리는 clearCart 액션의 경우, 스토어 내부에서 배열을 지움과 동시에 { cartItems: [], amount: 0, total: 0 } 형태로 토탈 값까지 초기화해 버리는 것이 안전합니다
스토어 리팩토링과 함께 컴포넌트단 코드를 한 줄 줄이면 깔끔하고 완성도 높은 코드가 될거같아요
✅ 워크북 체크리스트
✅ 컨벤션 체크리스트
📌 주안점