Skip to content

[9주차/클레버] 워크북 제출합니다.#88

Open
kcleverp wants to merge 5 commits into
UMC-Inha:클레버/mainfrom
kcleverp:main
Open

[9주차/클레버] 워크북 제출합니다.#88
kcleverp wants to merge 5 commits into
UMC-Inha:클레버/mainfrom
kcleverp:main

Conversation

@kcleverp
Copy link
Copy Markdown

@kcleverp kcleverp commented May 27, 2026

✅ 워크북 체크리스트

  • 모든 핵심 키워드 정리를 마쳤나요?
  • 핵심 키워드에 대해 완벽히 이해하셨나요?
  • 이론 학습 이후 직접 실습을 해보는 시간을 가졌나요?
  • 미션을 수행하셨나요?
  • 미션을 기록하셨나요?

✅ 컨벤션 체크리스트

  • 디렉토리 구조 컨벤션을 잘 지켰나요?
  • pr 제목을 컨벤션에 맞게 작성하였나요?
  • pr에 해당되는 이슈를 연결하였나요?(중요)
  • 적절한 라벨을 설정하였나요?
  • 파트장에게 code review를 요청하기 위해 reviewer를 등록하였나요?
  • 닉네임/main 브랜치의 최신 상태를 반영하고 있는지 확인했나요?(매우 중요!)

📌 주안점

@kcleverp kcleverp self-assigned this May 27, 2026
@kcleverp kcleverp requested a review from qkrdmsthff as a code owner May 27, 2026 13:29
@kcleverp kcleverp linked an issue May 27, 2026 that may be closed by this pull request
@@ -0,0 +1,52 @@
import type { CartItem } from '../types/cart'
import { useAppDispatch } from '../store/hooks'
import { decrease, increase } from '../store/cartSlice'
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

전반적으로 Redux 구조가 깔끔하게 잡혀있고, Zustand 전환도 방향이 정확해서 좋았던것같아요!
그런데 removeItem 액션이 UI에 연결되어 있지 않은 것 같아 보입니다
cartSlice.ts에서 removeItem은 잘 정의되어 있는데, CartItemRow.tsx에 이를 호출하는 버튼이 없어서 실제로 dispatch되는 곳이 없는것같아요..! 미션2, 3번에도 마찬가지로 개별 아이템에 삭제 버튼을 다음과 같이 추가해주시면 좋을 것 같습니다
이번주도 수고하셨고, 기말고사 화이팅입니다~!


// 수량 조절 버튼 옆에 추가
<button
  type="button"
  onClick={() => dispatch(removeItem(item.id))}
  className="ml-4 text-red-500 hover:text-red-700"
  aria-label={`${item.title} 삭제`}
>
  ✕
</button>```

return { cartItems, ...recalculateTotals(cartItems) }
}),

decrease: (id) =>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 decrease 함수에서 target 아이템의 수량을 확인하기 위해 먼저 .find()로 배열을 한 번 순회하고 조건에 따라 .filter() 또는 .map()을 돌리며 배열을 두 번 순회하는 구조로 되어 있는 것 같습니다

지금은 데이터가 소수라 체감이 괜찮을 것 같습니다만 향후 대규모 실시간 데이터를 다룰 때 배열을 여러 번 반복 순회 하는 것은 성능 저하의 원인이 될 수 있다고 합니다.

한 번의 순회만으로 "수량 감소"와 "1 이하일 때 삭제" 조건을 동시에 처리할 수 있는 방향을 제안해 드려보았습니당

decrease: (id) =>
  set((state) => {
    // reduce를 사용하여 한 번의 순회로 map과 filter를 동시에 처리
    const cartItems = state.cartItems.reduce((acc, item) => {
      if (item.id === id) {
        // 수량이 1 이하이면 누적 배열 푸시 하지 않고 건너뜀
        if (item.amount <= 1) return acc;
        
        // 1보다 크면 수량을 감소시켜서 푸시
        acc.push({ ...item, amount: item.amount - 1 });
      } else {
        // 상관없는 아이템은 그대로 푸시
        acc.push(item);
      }
...

export const { increase, decrease, removeItem, clearCart, calculateTotals } =
cartSlice.actions

export default cartSlice.reducer
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

장바구니 전역 상태 관리 구조를 처음부터 아주 탄탄하고 안전하게 잘 잡아주셨네요! 대단하십니다.

특히 아이템 수량이 바뀔 때마다 내부에서 자동으로 총액을 동기화(recalculateTotals)해 주신 덕분에, 컴포넌트단에서 불필요하게 두 번씩 디스패치를 호출할 필요가 없어 유저 인터랙션 제어가 훨씬 깔끔해질 것 같습니다. initialState 선언 직후에 최초 계산을 돌려 초기 진입 시 데이터 싱크를 맞추신 디테일도 정말 좋네요.

현재 구조에서는 모든 액션 내부에서 자동 계산이 완벽하게 돌고 있으므로, 역할이 모호해진 calculateTotals 리듀서와 외부 export 액션만 마저 정리해 주시면 더할 나위 없이 완벽한 코드가 될 것 같습니다!

`import { createSlice, type PayloadAction } from '@reduxjs/toolkit'
import cartItems from '../constants/cartItems'
import type { CartState } from '../types/cart'

function recalculateTotals(state: CartState) {
state.amount = 0
state.total = 0
for (const item of state.cartItems) {
state.amount += item.amount
state.total += item.amount * Number(item.price)
}
}

const initialCartItems = cartItems.map((item) => ({ ...item }))

const initialState: CartState = {
cartItems: initialCartItems,
amount: 0,
total: 0,
}

recalculateTotals(initialState)

const cartSlice = createSlice({
name: 'cart',
initialState,
reducers: {
increase: (state, action: PayloadAction) => {
const item = state.cartItems.find((cartItem) => cartItem.id === action.payload)
if (item) {
item.amount += 1
recalculateTotals(state)
}
},
decrease: (state, action: PayloadAction) => {
const item = state.cartItems.find((cartItem) => cartItem.id === action.payload)
if (!item) return

  // 💡 어차피 수량이 1개일 때 마이너스를 누르면 삭제되므로 분기 처리 최적화
  if (item.amount <= 1) {
    state.cartItems = state.cartItems.filter((cartItem) => cartItem.id !== action.payload)
  } else {
    item.amount -= 1
  }
  recalculateTotals(state)
},
removeItem: (state, action: PayloadAction<string>) => {
  state.cartItems = state.cartItems.filter((cartItem) => cartItem.id !== action.payload)
  recalculateTotals(state)
},
clearCart: (state) => {
  state.cartItems = []
  state.amount = 0
  state.total = 0
},
// 💡 불필요해진 calculateTotals 리듀서 제거

},
})

// 💡 외부 export에서도 굳이 내보내지 않습니다.
export const { increase, decrease, removeItem, clearCart } = cartSlice.actions

export default cartSlice.reducer`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Chapter09_Redux Toolkit & Zustand

4 participants