https://unseojang.github.io/front_5th_chapter2-1/
이번 과제의 핵심은 기존 바닐라 JS 기반의 장바구니 로직을 React 구조로 점진적으로 전환하는 것이었습니다. 단순히 기능만 옮기는 것이 아니라 다음의 기준들을 충족하기 위해 세심하게 리팩토링을 진행했습니다.
main() 안으로 넣기개인 팀의 컨벤션입니다, 다 지키지는 못한 것 같기도하고...최대한 지키려고 노력하면서 마이그레이션하려고 노력했습니다.
1. 조건문은 Early return 방식을 사용을 권장한다.
I prefix를 붙인다.IUser, IProduct , Request, 응답 인터페이스는 Response 접미사를 사용한다.data, info 를 붙이는 것은 최대한 지양한다.get으로 시작하는 것을 권장한다.strict하게 정의하여 사용한다. type Role = “Admin” | “Guest"handle prefix를 사용하며 handle + 동사 + 명사 형태로 작성한다.List , 단수형은 Item 접미사 사용을 권장한다.기존 기능을 그대로 유지하면서 구조를 점진적으로 React 컴포넌트화했습니다.
main.js의 DOM 조작 기반 구조를 Cart.tsx로 옮기되, 처음에는 로직 중심 컴포넌트로 시작한 뒤 점차 CartItem, ProductSelect, CartSummary,ProductSelect 등 하위 컴포넌트로 쪼갰습니다.
전체 구조를 한 번에 새로 만드는 방식이 아니라, 기존 로직과 맞닿아 있던 계산 로직, 이벤트 핸들링 로직을 하나씩 추출하며 점진적으로 모듈화/컴포넌트화를 했습니다.
컴포넌트 단위로의 역할 분리
CartItem, CartSummary, ProductSelect 등 의미 있는 단위로 나누고 각각의 책임을 명확히 했습니다. 예를 들어 CartSummary에서는 단순한 출력이 아니라 계산 로직까지 내부에서 useMemo로 처리하도록 해 최적화와 관심사 분리를 동시에 고려했습니다.
상태 관리 일관성 유지
prodList, cartItems, selectedProductId를 각각의 역할에 맞게 분리하고, 상호작용 시 원본 데이터가 무너지지 않도록 불변성을 최대한 지키려하며 업데이트했습니다. 특히, 재고 감소/복원 추가 로직이 엇갈리지 않도록 updateProductStock, decreaseCartItemQuantity 등의 유틸 함수도 분리했습니다.
기능 단위 유틸 함수화 및 커스텀 훅 분리 실험
handleAddToCart, handleQuantityChange, useLuckySaleEffect, useSuggestionEffect 등을 직접 작성하며 리액트스러운 구조의 방향성을 고민했습니다. setTimeout 타이머와 관련된 비즈니스 로직도 App 외부가 아닌 커스텀 훅으로 이동시켜 테스트와 유지보수성을 높이기 위해 노력했습니다.
테스트 코드와 호환성 유지
기존에 존재하던 테스트 스펙과도 최대한 호환되도록 할인 적용, 포인트 계산, 재고 관리의 핵심 비즈니스 로직은 기존 JS 코드에서 React로 마이그레이션 시 정확히 동작하도록 검증했습니다.
이벤트 로직과 화면 구성의 분리
Cart.tsx에서는 오직 컴포넌트의 상태 흐름과 구조만 다루고, 로직은 handlers나 utils에서 처리함.handleAddCartClick 함수는 DOM 기반에서도, React 기반에서도 내부 동작만 조금 다르게 전달하면 동일한 비즈니스 로직을 재활용할 수 있도록 설계했습니다.테스트가 쉬운 구조
함수 단위의 유닛 테스트가 최대한 가능하게끔 로직을 구성했습니다.React Testing Library를 사용할 경우, 이벤트 결과만 검증하면 되고 내부 로직은 따로 테스트할 수 있어 테스트 코드가 단순하고 명확해졌습니다.관심사의 분리와 협업 효율성 향상
handleAddToCart, handleQuantityChange, handleRemoveToCart 같은 핵심 로직은 하나의 책임만 수행하도록 만들었습니다.updateProductStock, decreaseCartItemQuantity 등으로 추출해 재사용성을 높였습니다.CartSummary 컴포넌트는 단순 출력용이 아닌 실제 계산 로직을 내포하도록 구성했습니다. 할인률, 최종 결제 금액, 포인트 계산 로직을 useMemo로 감싸 연산 최적화와 명확한 책임 분리를 동시에 달성했습니다.CartSummary만 따로 테스트하거나 변경하기도 수월해졌습니다.30, 0.1 등)는 모두 constants/discount.ts로 추출하여 최대한 의미 있는 이름을 부여했습니다.useMemo 내부에서 가공된 데이터를 중심으로 표현해 가독성을 높였습니다.setState 비동기 동작에 따른 혼란을 방지하기 위해 prev => next 형태로 모두 수정했지만, 초기에 의도한 대로 동작하지 않아 디버깅 시간이 길어졌습니다.useLuckySaleEffect, useSuggestionEffect)으로 번개 세일/추천 상품 로직을 분리한 것은 좋았지만, 의존성 배열이나 타이머 중첩 관련해서 조금 더 최적화할 여지가 있었습니다.CartSummary에서의 계산 로직을 useMemo로 감싸는 방식이 성능 측면에서는 명확하지만, 이 정도 복잡도의 계산도 분리하는 것이 일반적인지, 혹은 더 좋은 추상화 방법이 있을지 궁금합니다.useState 기반으로만 하고 있는데, 상품 목록(prodList)와 장바구니(cartItems)의 상태 동기화가 필요한 경우에는 어떤 상태 관리 전략(Context, Zustand 등)이 적절할지 피드백 받고 싶습니다.재고 부족, 품절) 로직을 JSX에서 다루다보니 컴포넌트 내 조건이 점점 복잡해졌습니다. 이럴 때 보통 presenter 컴포넌트 등을 분리해서 다루는 게 일반적인지 궁금합니다.CartItem 내부의 수량 변경도 onChangeQuantity props로 처리해 SRP를 지켰습니다.Cart.tsx는 상태 중심 컴포넌트로, 비즈니스 계산은 CartSummary, UI 제어는 CartItem/ProductSelect로 위임하는 구조입니다.✨ 구조 개선, 재사용성, 테스트 가능성, 가독성 모든 관점에서 개선을 목표로 했으며, 그 과정에서 리액트의 패턴과 상태 관리에 대한 실전 감각을 익힐 수 있었던 유익한 과제였습니다.