Skip to content

[9주차/동동] 워크북 제출합니다.#90

Open
bukyung03 wants to merge 7 commits into
UMC-Inha:동동/mainfrom
bukyung03:main
Open

[9주차/동동] 워크북 제출합니다.#90
bukyung03 wants to merge 7 commits into
UMC-Inha:동동/mainfrom
bukyung03:main

Conversation

@bukyung03
Copy link
Copy Markdown

@bukyung03 bukyung03 commented May 27, 2026

✅ 워크북 체크리스트

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

✅ 컨벤션 체크리스트

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

📌 주안점

@bukyung03 bukyung03 self-assigned this May 27, 2026
@bukyung03 bukyung03 requested a review from qkrdmsthff as a code owner May 27, 2026 14:59
@bukyung03 bukyung03 linked an issue May 27, 2026 that may be closed by this pull request
payload?: string;
}

function reducer(state: IState, action: IAction){
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

useReducer 파트에서 전개 연산자 ...state를 활용해 불변성을 유지해야 하는 이유를 주석으로 정리해 두셔서 핵심이 잘 전달되는 좋은 예제 코드인것 같습니다.

한 가지 첨언 드릴 것이 있다면,
혹시 의도하신 기능인지는 모르겠지만 카드메이커 직무 변경 이후 타이핑한 텍스트가 남아있을 수 있을 것 같습니다 .
dispatch 성공 후 setDepartment('')를 호출해 입력창을 깔끔하게 비워주는 건 어떨까요?
입력된 데이터는 깔끔하게 정돈되어 보다 나은 UX를 제공할 수 있지 않을까요?

const handleApplyChange = () => {
  dispatch({ type: 'CHANGE_DEPARTMENT', payload: department });
  
  if (department === '카드메이커') {
    setDepartment(''); // 성공 시 입력창 비우기
  }
};

// 버튼의 onClick에 연결
<button onClick={handleApplyChange}> 직무 변경하기 </button>

@@ -0,0 +1,48 @@
import { useDispatch } from 'react-redux';
import { type ICartItem } from '../constants/cartItems';
import { increase, decrease } from '../features/cart/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 구조 이해가 잘 된 것 같고, useEffect로 calculateTotals를 연동한 방식도 깔끔하게 구현하신 것 같아요 !
그런데 현재 removeItem이 UI에 연결되어 있지 않아 보입니다..!
cartSlice.ts 28번째 줄에 removeItem이 잘 정의되어 있는데, CartItem.tsx에 이를 호출하는 버튼이 없어서 실제로 dispatch되는 곳이 없는 것 같아요ㅜㅜ!
아래처럼 개별 아이템에 삭제 버튼을 추가해주시면 좋을 것 같습니다!
이번주도 수고 많으셨고, 기말고사 기간 이후 10주차 스터디때 봬용~

// CartItem.tsx - import에 removeItem 추가
import { increase, decrease, removeItem } from '../features/cart/cartSlice';

// 수량 조절 버튼 옆에 추가
<button
  onClick={() => dispatch(removeItem(item.id))}
  className="ml-2 text-red-500 hover:text-red-700 text-sm font-bold"
>
  ✕
</button>

});

export const { clearCart, removeItem, increase, decrease, calculateTotals } = cartSlice.actions;
export default cartSlice.reducer; No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

수량 변경 로직들이 아주 깔끔하게 잘 구현되었네요!

다만 현재 구조를 보면 increase, decrease, removeItem이 일어난 후에 amount와 total 상태가 자동으로 변하지 않아서, 컴포넌트단에서 매번 calculateTotals 액션을 함께 디스패치해줘야 하는 번거로움이 생길 것 같습니다.

또한 초기 상태(initialState)에서 cartItems 데이터는 들어가 있지만 총액은 0으로 시작되어 첫 진입 시 데이터 불일치가 발생할 위험도 보이네요.

리듀서 내부에 공통으로 총액을 계산해 주는 함수를 선언해 두고, 각 액션이 실행될 때마다 자동으로 계산이 완료되도록 갱신 흐름을 묶어두면 컴포넌트단 코드가 훨씬 가벼워지고 안전해질 것 같습니다.

`import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
import cartItems, { type ICartItem } from '../../constants/cartItems';

interface ICartState {
cartItems: ICartItem[];
amount: number;
total: number;
}

// 💡 1. 상태를 받아 자동으로 총 수량과 금액을 계산해주는 순수 헬퍼 함수
const updateTotals = (state: ICartState) => {
let totalAmount = 0;
let totalPrice = 0;

state.cartItems.forEach((item) => {
totalAmount += item.amount;
totalPrice += item.amount * parseInt(item.price, 10);
});

state.amount = totalAmount;
state.total = totalPrice;
};

// 💡 2. 초기 상태 객체를 선언하고, 최초 1회 계산을 돌려 싱크를 맞춥니다.
const initialState: ICartState = {
cartItems: cartItems,
amount: 0,
total: 0,
};
updateTotals(initialState); // 앱 구동 시 초기 데이터 기준 갱신

const cartSlice = createSlice({
name: 'cart',
initialState,
reducers: {
clearCart: (state) => {
state.cartItems = [];
state.amount = 0;
state.total = 0;
},
removeItem: (state, action: PayloadAction) => {
const itemId = action.payload;
state.cartItems = state.cartItems.filter((item) => item.id !== itemId);
updateTotals(state); // 💡 삭제 시 자동 계산
},
increase: (state, action: PayloadAction) => {
const cartItem = state.cartItems.find((item) => item.id === action.payload);
if (cartItem) {
cartItem.amount += 1;
updateTotals(state); // 💡 수량 증가 시 자동 계산
}
},
decrease: (state, action: PayloadAction) => {
const cartItem = state.cartItems.find((item) => item.id === action.payload);
if (cartItem) {
cartItem.amount -= 1;
if (cartItem.amount < 1) {
state.cartItems = state.cartItems.filter((item) => item.id !== action.payload);
}
updateTotals(state); // 💡 수량 감소 시 자동 계산
}
},
// 💡 컴포넌트단에서 호출할 필요가 없어졌으므로 calculateTotals 리듀서는 완전히 제거합니다.
},
});

// 외부로 export하는 액션에서도 calculateTotals를 제외합니다.
export const { clearCart, removeItem, increase, decrease } = 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