Skip to content

Comments

Create Week9 Mission0,1,2,3#120

Open
sueonnn wants to merge 6 commits intomainfrom
sueonnn/week09
Open

Create Week9 Mission0,1,2,3#120
sueonnn wants to merge 6 commits intomainfrom
sueonnn/week09

Conversation

@sueonnn
Copy link
Contributor

@sueonnn sueonnn commented Dec 14, 2025

📝 미션 번호

9주차 Misson 1,2,3

📋 구현 사항

-Redux Toolkit 활용
-Modal Slice 활용하여, 모달 기능 추가
-Redux Toolkit으로 만든 UMC Play List를 Zustand로 리팩토링

📎 스크린샷

2025-12-14.10.12.57.mov

✅ 체크리스트

  • [0] Merge 하려는 브랜치가 올바르게 설정되어 있나요?
  • [0] 로컬에서 실행했을 때 에러가 발생하지 않나요?
  • [0] 불필요한 주석이 제거되었나요?
  • [0] 코드 스타일이 일관적인가요?

🤔 질문 사항

@sueonnn sueonnn requested a review from woojo230 December 14, 2025 12:59
@sueonnn sueonnn self-assigned this Dec 14, 2025
@github-actions
Copy link

github-actions bot commented Dec 14, 2025

🤖 Gemini 코드리뷰 결과

코드 통합 리뷰: 장바구니 기능 구현 PR

안녕하세요! 시니어 프론트엔드 개발자로서 제출해주신 Pull Request 변경사항을 면밀히 검토했습니다.
이번 PR은 장바구니 기능을 Redux Toolkit과 Zustand를 활용하여 구현한 것으로 보입니다. 전반적으로 TypeScript를 적극적으로 활용하고 있으며, 컴포넌트 구조는 비교적 잘 분리되어 있습니다.

하지만 현재 코드에는 두 가지 상태 관리 라이브러리(Redux Toolkit과 Zustand) 코드가 모두 포함되어 있으며, 실제 UI 컴포넌트에서는 주로 Zustand를 사용하는 방향으로 구현이 진행된 점이 가장 큰 문제점으로 파악됩니다. 이 점을 포함하여 프로젝트 설정, src 디렉토리 내 .ts.tsx 파일들을 중심으로 상세한 피드백을 드립니다.


[종합 요약 및 가장 중요한 개선 제안]

  1. 상태 관리 라이브러리 일원화 (Zustand로 통일 권장)

    • 문제점: src/slices, src/store/store.ts, src/hooks/useCustomRedux.ts 등 Redux Toolkit 관련 파일들이 존재하지만, CartItem.tsx, CartList.tsx, Navbar.tsx, Modal.tsx 등 대부분의 UI 컴포넌트들은 src/store/useCartStore.ts (Zustand)를 통해 상태를 관리하고 있습니다. 이러한 혼용은 코드의 복잡성을 증가시키고, 불필요한 의존성을 유발하며, 개발자의 혼란을 초래합니다.
    • 개선 제안: Zustand로 상태 관리를 일원화하고, 사용되지 않는 Redux Toolkit 관련 파일들을 모두 제거하는 것을 강력히 권장합니다. 현재 코드 구조와 UI 컴포넌트의 사용 패턴을 볼 때 Zustand를 주력으로 사용하는 것이 의도된 것으로 판단됩니다.
      • 제거 대상 파일:
        • src/slices/cartSlice.ts
        • src/slices/modalSlice.ts
        • src/store/store.ts
        • src/hooks/useCustomRedux.ts (이 파일은 Redux 훅을 래핑하는 용도이므로, Redux 제거 시 함께 제거되어야 합니다.)
    • 기대 효과: 코드베이스 단순화, 유지보수성 향상, 불필요한 학습 곡선 제거.
  2. 타입 정의 중앙화 및 price 타입 개선

    • 문제점: CartItemType 인터페이스가 src/constants/cartItems.ts, src/store/useCartStore.ts, src/types/cart.ts 세 곳에 중복 정의되어 있습니다. 또한, CartItemTypeprice 필드가 string으로 되어 있어, 숫자 계산이 필요한 곳에서 parseInt()를 반복적으로 사용해야 합니다.
    • 개선 제안:
      1. src/types/cart.ts 파일에서 CartItemType 인터페이스를 유일하게 정의하고 export합니다.
      2. CartItemTypeprice: string;price: number; 로 변경합니다.
      3. src/constants/cartItems.ts, src/store/useCartStore.ts, src/components/CartItem.tsxCartItemType을 사용하는 모든 파일에서 src/types/cart.ts로부터 CartItemTypeimport해서 사용하도록 수정합니다.
      4. src/constants/cartItems.ts: cartItems 배열의 price 값을 string 대신 number로 직접 입력합니다 (예: "25000" -> 25000).
      5. src/components/CartItem.tsx: parseInt(price).toLocaleString()price.toLocaleString()으로 수정합니다.
    • 기대 효과: 타입 정의의 일관성 유지, 타입 안정성 강화, 불필요한 런타임 parseInt 호출 제거 (성능 소폭 개선), 코드 명확성 증대.

[파일별 코드 리뷰 및 개선 제안]

1. src/App.tsx

  • 특별한 문제 없이 컴포넌트 조합이 잘 되어 있습니다.

2. src/components/CartItem.tsx

  • 불필요한 import 제거 및 아이콘 컴포넌트 사용: ChevronUp, ChevronDown 아이콘이 import 되어 있지만 실제로는 인라인 SVG가 사용되고 있습니다. src/icons.tsx에서 정의된 컴포넌트를 사용하여 불필요한 import를 제거하고 코드의 재사용성을 높여주세요.
  • decrease 로직 중복 제거: 수량 감소 버튼(decrease) 클릭 시 amount === 1일 때 removeItem을 호출하는 로직이 있습니다. 이 로직은 Zustand 스토어의 decrease 액션 내부에서도 처리될 수 있도록 설계되어야 합니다. UI 컴포넌트에서는 단순히 dispatch(decrease(id)) (또는 useCartStoredecrease(id))만 호출하도록 수정하여 관심사를 분리합니다.

3. src/components/CartList.tsx

  • 빈 장바구니 확인 로직 개선: amount < 1 대신 cartItems.length === 0을 사용하여 장바구니가 비었는지 확인하는 것이 더 명확하고 원본 데이터에 기반한 일관성 있는 방법입니다.
  • calculateTotals 호출 빈도: useEffect에서 cartItems가 변경될 때마다 dispatch(calculateTotals())를 호출하는 것은 cartItems의 양이 많아질 경우 미미한 오버헤드를 유발할 수 있습니다. Zustand 스토어 내에서 amounttotal을 파생 상태(selector 또는 getter)로 관리하는 방법을 고려하면 이 useEffect를 제거하고 코드를 더 간결하게 만들 수 있습니다. (아래 useCartStore.ts 개선 제안 참고)

4. src/components/Modal.tsx

  • 버튼 텍스트 색상 수정: "네" 버튼 (clearCart를 호출하는 버튼)의 텍스트 색상 (text-gray-700)이 배경색 (bg-red-500)과 어울리지 않아 가독성이 떨어집니다. text-white로 변경하여 가독성을 높이는 것이 좋습니다.
  • 모달 상태 분리: Modal 컴포넌트는 useCartStore에서 isOpen, openModal, closeModal 상태/액션을 가져오고 있습니다. 모달 관련 상태는 장바구니 상태와 직접적인 관련이 없으므로, 별도의 useModalStore.ts로 분리하여 관리하는 것이 좋습니다.

5. src/components/Navbar.tsx

  • 불필요한 import 제거 및 아이콘 컴포넌트 사용: CartIcon이 import 되어 있지만 실제로는 인라인 SVG가 사용되고 있습니다. src/icons.tsx에서 정의된 CartIcon 컴포넌트를 사용하여 불필요한 import를 제거하고 코드의 재사용성을 높여주세요.

6. src/components/PriceBox.tsx

  • 현재 비어 있는 파일입니다. 프로젝트에서 사용하지 않는다면 삭제하여 코드 베이스를 깔끔하게 유지하는 것이 좋습니다. 만약 추후 사용될 예정이라면, 파일 내부에 주석으로 해당 컴포넌트의 목적을 명시해주세요.

7. src/constants/cartItems.ts

  • CartItemType 제거 및 price 타입 변경: 위 "가장 중요한 개선 제안 2번"에 따라 CartItemType 인터페이스 정의를 제거하고 src/types/cart.ts에서 import하여 사용합니다. price 필드 값도 string에서 number로 변경합니다.

8. src/icons.tsx

  • 아이콘 컴포넌트의 중복 코드 제거: 각 SVG 컴포넌트가 동일한 SVG 속성(xmlns, fill, viewBox, strokeWidth, stroke)을 반복해서 정의하고 있습니다. 공통 속성을 감싸는 헬퍼 함수나 HOC(Higher-Order Component)를 만들어 중복을 제거하고 더 간결하게 관리할 수 있습니다.
    • 예시:
      // src/icons.tsx (개선 예시)
      import React from 'react';
      interface IconProps extends React.SVGProps<SVGSVGElement> { className?: string; }
      const createIcon = (pathData: string, defaultStrokeWidth: number = 1.5) => {
        return ({ className = "", ...props }: IconProps) => (
          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
               strokeWidth={defaultStrokeWidth} stroke="currentColor" className={className} {...props}>
            <path strokeLinecap="round" strokeLinejoin="round" d={pathData} />
          </svg>
        );
      };
      export const CartIcon = createIcon(
        "M2.25 3h1.386c.51 0 .955.343 1.087.835l.383 1.437M7.5 14.25a3 3 0 00-3 3h15.75m-12.75-3h11.218c1.121-2.3 2.1-4.684 2.924-7.138a60.114 60.114 0 00-16.536-1.84M7.5 14.25L5.106 5.272M6 20.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm12.75 0a.75.75 0 11-1.5 0 .75.75 0 011.5 0z",
        1.5 // 카트 아이콘의 strokeWidth
      );
      export const ChevronUp = createIcon("M4.5 15.75l7.5-7.5 7.5 7.5", 2);
      export const ChevronDown = createIcon("M19.5 8.25l-7.5 7.5-7.5-7.5", 2);

9. src/store/useCartStore.ts (Zustand Store)

  • CartItemType 제거 및 price 타입 변경: 위 "가장 중요한 개선 제안 2번"에 따라 CartItemType 인터페이스 정의를 제거하고 src/types/cart.ts에서 import하여 사용합니다.
  • 스토어 책임 분리: CartState 인터페이스에 장바구니와 직접적인 관련이 없는 isOpen (모달 상태)이 포함되어 있습니다. 이는 스토어의 관심사 분리를 약화시키므로, 모달 관련 상태(isOpen)와 액션(openModal, closeModal)을 별도의 src/store/useModalStore.ts 파일로 분리합니다.
  • 파생 상태 (amount, total) 관리 개선: amounttotalcartItems 배열에서 파생되는 값입니다. calculateTotals 액션을 통해 명시적으로 계산하고 set으로 업데이트하는 대신, Zustand의 selectors를 활용하거나 스토어 자체에 getter 로직을 추가하여 컴포넌트에서 직접 계산하도록 하는 것이 더 효율적이고 간결합니다. 이 방식을 적용하면 calculateTotals 액션과 CartList.tsxuseEffect를 제거할 수 있습니다.
    • 예시:
      // src/store/useCartStore.ts (개선 예시)
      import { create } from "zustand";
      import { CartItemType } from "../types/cart"; // 중앙화된 타입에서 import
      
      interface CartState {
        cartItems: CartItemType[];
        // amount, total은 getter 또는 selector로 처리하므로 직접 상태에 포함시키지 않을 수 있음
      }
      
      interface CartActions {
        // ... (기존 액션들) ...
        get total(): number; // Getter로 추가
        get amount(): number; // Getter로 추가
      }
      
      export const useCartStore = create<CartState & CartActions>((set, get) => ({
        cartItems: [/* 초기 데이터 */],
        // ... (기존 액션들) ...
      
        // Getter 구현
        get total() {
          return get().cartItems.reduce((acc, item) => acc + item.amount * item.price, 0);
        },
        get amount() {
          return get().cartItems.reduce((acc, item) => acc + item.amount, 0);
        },
      
        // decrease 액션에서 removeItem 재활용
        decrease: (id) =>
          set((state) => {
            const itemToCheck = state.cartItems.find((item) => item.id === id);
            if (itemToCheck && itemToCheck.amount <= 1) { // 0이하로 떨어지는 경우도 포함
              // 수량이 1이하가 되면 해당 아이템 제거
              return {
                cartItems: state.cartItems.filter((item) => item.id !== id),
              };
            }
            // 그 외엔 감소
            const newCartItems = state.cartItems.map((item) =>
              item.id === id ? { ...item, amount: item.amount - 1 } : item
            );
            return { cartItems: newCartItems };
          }),
      }));
  • decrease 로직 리팩토링: decrease 액션 내에서 아이템 수량이 1 미만일 때 filter를 통해 아이템을 제거하는 로직은 removeItem 액션과 유사합니다. decrease 액션 내에서 수량이 1 미만으로 떨어질 경우, 해당 아이템을 filter로 직접 제거하거나 removeItem 액션을 호출하도록 로직을 수정하여 중복을 피하고 의도를 명확히 합니다.

10. src/types/modal.ts

  • useModalStore를 분리할 경우, 이 파일에서 모달 관련 타입을 정의하도록 구조를 개선합니다.

11. src/main.tsx

  • React.StrictMode를 사용하여 개발 단계에서 잠재적 문제를 감지하는 것은 좋은 접근입니다. Zustand를 사용하는 경우 Provider 컴포넌트는 필요하지 않습니다.

12. src/vite-env.d.ts

  • 특별한 문제 없습니다.

[프로젝트 설정 및 의존성 리뷰]

1. ESLint 설정 (.eslintrc.cjs)

  • ESLint 타입 기반 규칙 활성화 (필수): 현재 @typescript-eslint/recommended를 사용하지만, 더 강력한 타입 기반 린팅 규칙(recommended-type-checked 또는 strict-type-checked)을 활성화하도록 권장합니다. parserOptions.project 설정 및 extends 배열에 plugin:@typescript-eslint/recommended-type-checked를 추가하여 TypeScript 코드에 대한 정적 분석을 강화해야 합니다.
  • ESLint 버전 업데이트 (최신화): package-lock.jsoneslint@8.57.1 버전이 "This version is no longer supported."라고 명시되어 있습니다. ESLint를 최신 LTS 버전인 ^9.0.0 (또는 최신 안정 버전)으로 업데이트하여 최신 기능 및 버그 수정을 활용하고 지원이 중단된 버전을 사용하지 않도록 해주세요. 마이그레이션 가이드를 참고하여 설정 파일 변경이 필요할 수 있습니다.
  • ESLint React 플러그인 추가 (React 권장사항): eslint-plugin-react를 설치하고 .eslintrc.cjsextendsplugin:react/recommendedplugin:react/jsx-runtime을 추가하여 React 관련 코드에 대한 린팅 규칙을 강화해야 합니다.
  • react-refresh/only-export-components 규칙 검토: allowConstantExport: true 옵션은 Hot Module Replacement (HMR)에서 잠재적 문제를 일으킬 수 있는 edge case를 허용할 수 있습니다. Fast Refresh 동작에 문제가 발생하거나 더 엄격한 HMR 규칙을 적용하고 싶다면 이 옵션을 false로 설정하고 컴포넌트 내보내기 방식을 재검토하는 것을 고려해볼 수 있습니다.
  • ignorePatterns 정리: .eslintrc.cjs 파일이 ignorePatterns 목록에 자기 자신을 포함하고 있습니다. 보통 린터는 설정 파일을 린팅하지 않으므로, 이 패턴은 불필요할 수 있습니다.

2. 의존성 관리 (package-lock.json)

  • 의존성 메이저 버전 업데이트 확인 및 테스트 강화: @reduxjs/toolkit, esbuild, @tailwindcss/* 등 여러 라이브러리들이 업데이트되었습니다. 메이저 버전 업데이트는 잠재적인 breaking change를 포함할 수 있으므로, 릴리스 노트를 면밀히 검토하고 충분한 테스트를 수행해야 합니다.
  • Deprecated 의존성 처리: eslint (8.57.1)와 @humanwhocodes/config-array (0.13.0) 패키지에 deprecated 경고가 붙어 있습니다. Deprecated 된 의존성은 보안 취약점, 버그 수정 미반영, 호환성 문제의 원인이 될 수 있으므로, 가능한 한 빨리 지원되는 최신 버전으로 업데이트하거나 대체 라이브러리를 검토해야 합니다.
  • 번들 사이즈 변화 모니터링: 빌드 및 스타일링 관련 의존성들이 대거 추가되거나 업데이트되었습니다. 번들 분석 도구를 사용하여 최종 빌드 번들 크기에 어떤 변화가 있었는지 분석하고 최적화 포인트를 찾아볼 수 있습니다.
  • Node.js 엔진 호환성 확인: 프로젝트가 사용하는 Node.js 버전이 모든 새로 업데이트되거나 추가된 의존성들의 engines.node 요구사항을 만족하는지 확인해야 합니다.
  • dev 의존성 명확화 및 검증: dev: true로 표시된 의존성들이 실제로 개발 및 빌드 과정에서만 사용되고 런타임 번들에 포함되지 않도록 빌드 설정을 검증해야 합니다.
  • 보안 취약점 스캔 자동화: npm audit 또는 yarn audit 명령어를 CI/CD 파이프라인에 포함시켜 Pull Request가 머지되기 전에 자동으로 보안 취약점을 검사하도록 설정하는 것을 권장합니다.

3. 문서화 (README.md)

  • 초기 내용 보강: 현재 README.md는 Vite 프로젝트 생성 시의 기본 설명만을 포함하고 있습니다. 프로젝트의 목적, 주요 기능, 개발 환경 설정 방법, 빌드 및 배포 방법, 그리고 스크립트(package.jsonscripts 섹션)에 대한 간략한 설명을 추가하여 프로젝트 이해도를 높여주세요.

[전반적인 코드 품질 개선점]

  • 변수명, 함수명, 주석 품질: 전반적으로 변수명, 함수명은 의미가 명확하고 좋습니다. 주석은 현재 코드를 이해하는 데 도움이 되지만, 특히 커스텀 훅이나 복잡한 로직, 핵심 비즈니스 로직이 포함된 파일에는 JSDoc 형태의 주석을 추가하여 코드 문서화를 강화하는 것을 권장합니다.
  • React 컴포넌트 타입 정의 일관성: const Component = ({ prop }: Type) => {...} 형태 대신 const Component: React.FC<PropsType> = ({ prop }) => {...} 와 같이 React.FC를 명시적으로 사용하는 것이 React 컴포넌트임을 더 명확하게 보여주고, 추가적인 타입 안전성 이점을 제공합니다.

마지막으로, 전반적으로 기능 구현은 잘 되어 있으나, 상태 관리 라이브러리 선택의 모호성과 타입 정의의 중복이 주요 개선점으로 보입니다. 위의 제안들을 통해 코드의 일관성, 가독성, 유지보수성, 그리고 타입 안전성을 크게 높일 수 있을 것입니다.

궁금한 점이 있으시면 언제든지 질문해주세요. 검토해 주셔서 감사합니다!

@sueonnn sueonnn changed the title Create Week9 Mission1,2,3 Create Week9 Mission0,1,2,3 Dec 14, 2025
Copy link
Collaborator

@woojo230 woojo230 left a comment

Choose a reason for hiding this comment

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

중복되는 로직은 유틸 함수나 컴포넌트로 분리하면 좋을 것 같아요

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants