Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c47a587
chore: prettier 설정 및 절대경로 설정
angielxx Aug 4, 2025
206c5df
docs: basic/App.tsx 클린코드 분석 및 문서 작성
angielxx Aug 4, 2025
630f98c
docs: 과제 목표 작성
angielxx Aug 4, 2025
2022827
refactor: 매직넘버 상수화 및 도메인별 constants 분리
angielxx Aug 4, 2025
01a4e25
chore: import문 정렬 설정
angielxx Aug 4, 2025
3a8069e
refactor: 데이터 분리 및 useLocalStorage 훅 최적화
angielxx Aug 4, 2025
bb3f3a8
docs: README 기반 문서 업데이트
angielxx Aug 4, 2025
28ec0cd
docs: hooks, models, utils 폴더 차이에 대해 이해하기
angielxx Aug 4, 2025
e237140
refactor: getFormattedProductPrice를 순수 함수로 리팩토링
angielxx Aug 5, 2025
e1cd9de
refactor: getMaxApplicableDiscountRate, calculateItemTotal, calculate…
angielxx Aug 5, 2025
269c454
docs: 과제 개인 목표 다시 작성
angielxx Aug 6, 2025
ba426f0
refactor: 엔티티별 커스텀 훅으로 분리 및 문자열 타입 enum화
angielxx Aug 6, 2025
0f942bd
refactor: 폴더 구조 체계화 및 네임스페이스 정리
angielxx Aug 6, 2025
0a75120
refactor: Header 컴포넌트 분리 및 Icon 시스템 구현
angielxx Aug 6, 2025
93e03a4
chore: eslint 절대 경로 엄격한 설정 제거 및 설정 최소화
angielxx Aug 6, 2025
8465f5c
refactor: feature-based 도메인 기반 폴더 구조로 변경
angielxx Aug 6, 2025
947ecfe
refactor: useLocalStorage 훅에 실시간 동기화 기능 추가 및 상품 목록 관련 UI컴포넌트 분리 및 개선
angielxx Aug 7, 2025
a122dfc
refactor: 결제 요약 컴포넌트 분리 중
angielxx Aug 7, 2025
2628ad3
refactor: 주문 요약 정리 중
angielee0304 Aug 7, 2025
17ac62a
refactor: 홈, 어드민 페이지 분리 및 홈 페이지 내부 UI 분리, formatPrice 유틸리티 개선
angielxx Aug 7, 2025
68eec23
refactor: AdminPage 내부 UI 컴포넌트 분리, 합성 패턴 Tabs로 AdminTabs 구현
angielxx Aug 7, 2025
9e05f7b
refactor: 모든 svg 아이콘 컴포넌트 분리
angielxx Aug 7, 2025
a28f1e6
refactor: ProductAdmin 섹션 UI 컴포넌트 분리
angielxx Aug 7, 2025
8ebf015
refactor: 정규식 패턴 분리 및 ProductForm 리팩토링
angielxx Aug 7, 2025
c0a640f
refactor: NotificationBoundary 구현 및 테스트
angielxx Aug 7, 2025
9527a23
refactor: throwNotificationError 유틸 생성 및 적용, addNotification 의존성 제거
angielxx Aug 7, 2025
bd1a997
chore: 기본 과제 결과물 심화 폴더로 복제
angielxx Aug 7, 2025
b7120af
refactor: jotai 설치 및 products jotai 적용
angielxx Aug 7, 2025
17e343f
test: jotai 적용하기 위한 advanced 테스트코드 수정
angielxx Aug 7, 2025
cc7ddb9
refactor: products, cart, coupon jotai 적용하여 전역상태로 구현
angielxx Aug 7, 2025
2ccc9ff
refactor: 쿠폰 작성 폼 UI 컴포넌트 분리
angielxx Aug 7, 2025
346900d
chore: gh-pages 배포 설정
angielxx Aug 7, 2025
fe3704b
chore: pnpm install
angielxx Aug 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 57 additions & 10 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,18 +1,65 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
env: {
browser: true,
es2020: true,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
ignorePatterns: ["dist", ".eslintrc.cjs", "node_modules"],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: 2020,
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
},
plugins: ["react-refresh", "@typescript-eslint"],
settings: {
react: {
version: "detect",
},
},
rules: {
'react-refresh/only-export-components': [
'warn',
// React Refresh 관련
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],

// TypeScript 관련 규칙
"@typescript-eslint/no-unused-vars": [
"warn",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
"@typescript-eslint/no-explicit-any": "warn",

// React Hooks 관련
"react-hooks/exhaustive-deps": "warn",
"react-hooks/rules-of-hooks": "error",

// 기본적인 에러 방지
"no-console": "warn",
"no-debugger": "error",
"no-unused-expressions": "error",
"no-unreachable": "error",
},
}
overrides: [
{
files: ["*.test.ts", "*.test.tsx", "*.spec.ts", "*.spec.tsx"],
rules: {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/ban-ts-comment": "off",
"no-console": "off",
},
},
],
};
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?

.cursor
56 changes: 56 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Dependencies
node_modules/
pnpm-lock.yaml

# Build outputs
dist/
build/
*.min.js
*.min.css

# Generated files
coverage/
.nyc_output/

# IDE and editor files
.vscode/
.idea/
*.swp
*.swo

# OS generated files
.DS_Store
Thumbs.db

# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# Environment files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Cache
.cache/
.parcel-cache/

# Temporary folders
tmp/
temp/

# Test files
__tests__/
*.test.js
*.test.ts
*.test.jsx
*.test.tsx
*.spec.js
*.spec.ts
*.spec.jsx
*.spec.tsx
15 changes: 15 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"semi": true,
"trailingComma": "es5",
"singleQuote": false,
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "lf",
"plugins": ["@trivago/prettier-plugin-sort-imports"],
"importOrder": ["^react", "^@?\\w", "^@/"],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true
}
62 changes: 62 additions & 0 deletions PR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
## 과제의 핵심취지

- React의 hook 이해하기
- 함수형 프로그래밍에 대한 이해
- 액션과 순수함수의 분리

## 과제에서 꼭 알아가길 바라는 점

- 엔티티를 다루는 상태와 그렇지 않은 상태 - cart, isCartFull vs isShowPopup
- 엔티티를 다루는 컴포넌트와 훅 - CartItemView, useCart(), useProduct()
- 엔티티를 다루지 않는 컴포넌트와 훅 - Button, useRoute, useEvent 등
- 엔티티를 다루는 함수와 그렇지 않은 함수 - calculateCartTotal(cart) vs capaitalize(str)

### 기본과제

- Component에서 비즈니스 로직을 분리하기
- 비즈니스 로직에서 특정 엔티티만 다루는 계산을 분리하기
- 뷰데이터와 엔티티데이터의 분리에 대한 이해
- entities -> features -> UI 계층에 대한 이해

- [ ] Component에서 사용되는 Data가 아닌 로직들은 hook으로 옮겨졌나요?
- [ ] 주어진 hook의 책임에 맞도록 코드가 분리가 되었나요?
- [ ] 계산함수는 순수함수로 작성이 되었나요?
- [ ] Component에서 사용되는 Data가 아닌 로직들은 hook으로 옮겨졌나요?
- [ ] 주어진 hook의 책임에 맞도록 코드가 분리가 되었나요?
- [ ] 계산함수는 순수함수로 작성이 되었나요?
- [ ] 특정 Entitiy만 다루는 함수는 분리되어 있나요?
- [ ] 특정 Entitiy만 다루는 Component와 UI를 다루는 Component는 분리되어 있나요?
- [ ] 데이터 흐름에 맞는 계층구조를 이루고 의존성이 맞게 작성이 되었나요?

### 심화과제

- 재사용 가능한 Custom UI 컴포넌트를 만들어 보기
- 재사용 가능한 Custom 라이브러리 Hook을 만들어 보기
- 재사용 가능한 Custom 유틸 함수를 만들어 보기
- 그래서 엔티티와는 어떤 다른 계층적 특징을 가지는지 이해하기

- [ ] UI 컴포넌트 계층과 엔티티 컴포넌트의 계층의 성격이 다르다는 것을 이해하고 적용했는가?
- [ ] 엔티티 Hook과 라이브러리 훅과의 계층의 성격이 다르다는 것을 이해하고 적용했는가?
- [ ] 엔티티 순수함수와 유틸리티 함수의 계층의 성격이 다르다는 것을 이해하고 적용했는가?

# 과제 셀프회고

<!-- > 1. 얼마나 해당 과제에 대해서 스스로의 생각을 통해 해결해보려는 시도가 있었는가? (시행착오, 도전적 코딩)
> 2. 솔직하고 진솔한 회고를 통해서 과제 전후의 성장측면에서의 메타인지가 잘 드러나는가?
> 3. 이러한 이유로 다른 사람이 보았을때 나만의 생각과 시도, 그리고 경험의 공유로 인해 다른 사람에게 도움이 될 만한가? -->

4주차 과제에서 AI를 적극적으로 활용하며 과제를 진행했고, AI가 척척 내 요구에 맞게 코드를 짜는 모습을 보면서 희열과 함께 뇌리 저편에서 불안함이 엄습했습니다. 이쯤되니 '본전' 생각이 뇌를 떠나지 않았어요!

> 내가 지금 일주일에 10만원 넘는 비용을 지불하며 성장하려고 이 곳에 왔는데...정작 과제는 AI가 다하고 있네?
> 챕터 1에서는 기술적인 이해에 대한 얻어가는 게 확실했는데 챕터 2에서는 무엇을 어떻게 얻어갈 수 있을까?
> 어떻게 과제 속에서 내 경험과 성장을 최대치로 이끌어낼 수 있을까?

이러한 고민 속에서 5주차 과제를 시작하며

## 과제를 하면서 내가 제일 신경 쓴 부분은 무엇인가요?

## 과제를 다시 해보면 더 잘 할 수 있었겠다 아쉬운 점이 있다면 무엇인가요?

## 리뷰 받고 싶은 내용이나 궁금한 것에 대한 질문 편하게 남겨주세요 :)

# 리뷰 받고 싶은 내용이나 궁금한 것에 대한 질문
57 changes: 26 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
이번 과제는 단일책임원칙을 위반한 거대한 컴포넌트를 리팩토링 하는 것입니다. React의 컴포넌트는 단일 책임 원칙(Single Responsibility Principle, SRP)을 따르는 것이 좋습니다. 즉, 각 컴포넌트는 하나의 책임만을 가져야 합니다. 하지만 실제로는 여러 가지 기능을 가진 거대한 컴포넌트를 작성하는 경우가 많습니다.

[목표]

## 1. 취지

- React의 추구미(!)를 이해해보아요!
- 단일 책임 원칙(SRP)을 위반한 거대한 컴포넌트가 얼마나 안 좋은지 경험해보아요!
- 단일 책임이라는 개념을 이해하기 상태, 순수함수, 컴포넌트, 훅 등 다양한 계층을 이해해합니다.
- 엔티티와 UI를 구분하고 데이터, 상태, 비즈니스 로직 등의 특징이 다르다는 것을 이해해보세요.
- 이를 통해 적절한 Custom Hook과 유틸리티 함수를 분리하고, 컴포넌트 계층 구조를 정리하는 능력을 갖춥니다!


## 2. 목표

모든 소프트웨어에는 적절한 책임과 계층이 존재합니다. 하나의 계층(Component)만으로 소프트웨어를 구성하게 되면 나중에는 정리정돈이 되지 않은 코드를 만나게 됩니다. 예전에는 이러한 BestPractice에 대해서 혼돈의 시대였지만 FE가 진화를 거듭하는 과정에서 적절한 계측에 대한 합의가 이루어지고 있는 상태입니다.
Expand All @@ -22,7 +23,7 @@ React의 주요 책임 계층은 Component, hook, function 등이 있습니다.
- 엔티티를 다루는 상태와 그렇지 않은 상태 - cart, isCartFull vs isShowPopup
- 엔티티를 다루는 컴포넌트와 훅 - CartItemView, useCart(), useProduct()
- 엔티티를 다루지 않는 컴포넌트와 훅 - Button, useRoute, useEvent 등
- 엔티티를 다루는 함수와 그렇지 않은 함수 - calculateCartTotal(cart) vs capaitalize(str)
- 엔티티를 다루는 함수와 그렇지 않은 함수 - calculateCartTotal(cart) vs capaitalize(str)

이번 과제의 목표는 이러한 계층을 이해하고 분리하여 정리정돈을 하는 기준이나 방법등을 습득하는데 있습니다.

Expand All @@ -35,34 +36,34 @@ basic의 경우 상태관리를 쓰지 않고 작업을 해주세요.
#### 1) 장바구니 페이지 요구사항

- 상품 목록
- 상품명, 가격, 재고 수량 등을 표시
- 각 상품의 할인 정보 표시
- 재고가 없는 경우 품절 표시가 되며 장바구니 추가가 불가능
- 상품명, 가격, 재고 수량 등을 표시
- 각 상품의 할인 정보 표시
- 재고가 없는 경우 품절 표시가 되며 장바구니 추가가 불가능
- 장바구니
- 장바구니 내 상품 수량 조절 가능
- 각 상품의 이름, 가격, 수량과 적용된 할인율을 표시
- 적용된 할인율 표시 (예: "10% 할인 적용")
- 장바구니 내 모든 상품의 총액을 계산해야
- 장바구니 내 상품 수량 조절 가능
- 각 상품의 이름, 가격, 수량과 적용된 할인율을 표시
- 적용된 할인율 표시 (예: "10% 할인 적용")
- 장바구니 내 모든 상품의 총액을 계산해야
- 쿠폰 할인
- 할인 쿠폰을 선택하면 적용하면 최종 결제 금액에 할인정보가 반영
- 할인 쿠폰을 선택하면 적용하면 최종 결제 금액에 할인정보가 반영
- 주문요약
- 할인 전 총 금액
- 총 할인 금액
- 최종 결제 금액
- 할인 전 총 금액
- 총 할인 금액
- 최종 결제 금액

#### 2) 관리자 페이지 요구사항

- 상품 관리
- 상품 정보 (상품명, 가격, 재고, 할인율) 수정 가능
- 새로운 상품 추가 가능
- 상품 제거 가능
- 상품 정보 (상품명, 가격, 재고, 할인율) 수정 가능
- 새로운 상품 추가 가능
- 상품 제거 가능
- 할인 관리
- 상품별 할인 정보 추가/수정/삭제 가능
- 할인 조건 설정 (구매 수량에 따른 할인율)
- 상품별 할인 정보 추가/수정/삭제 가능
- 할인 조건 설정 (구매 수량에 따른 할인율)
- 쿠폰 관리
- 전체 상품에 적용 가능한 쿠폰 생성
- 쿠폰 정보 입력 (이름, 코드, 할인 유형, 할인 값)
- 할인 유형은 금액 또는 비율로 설정 가능
- 전체 상품에 적용 가능한 쿠폰 생성
- 쿠폰 정보 입력 (이름, 코드, 할인 유형, 할인 값)
- 할인 유형은 금액 또는 비율로 설정 가능

### (2) 코드 개선 요구사항

Expand All @@ -88,9 +89,8 @@ basic의 경우 상태관리를 쓰지 않고 작업을 해주세요.

### (3) 테스트 코드 통과하기



## 심화과제: Props drilling

- 이번 심화과제는 Context나 Jotai를 사용해서 Props drilling을 없애는 것입니다.
- 어떤 props는 남겨야 하는지, 어떤 props는 제거해야 하는지에 대한 기준을 세워보세요.
- Context나 Jotai를 사용하여 상태를 관리하는 방법을 익히고, 이를 통해 컴포넌트 간의 데이터 전달을 효율적으로 처리할 수 있습니다.
Expand All @@ -102,25 +102,20 @@ basic의 경우 상태관리를 쓰지 않고 작업을 해주세요.
- basic에서 열심히 컴포넌트를 분리해주었겠죠?
- 아마 그 과정에서 container - presenter 패턴으로 만들어졌기에 props drilling이 상당히 불편했을거에요.
- 그래서 심화과제에서는 props drilling을 제거하는 작업을 할거에요.
- 전역상태관리가 아직 낯설다 - jotai를 선택해주세요 (참고자료 참고)
- 나는 깊이를 공부해보고 싶아. - context를 선택해서 상태관리를 해보세요.

- 전역상태관리가 아직 낯설다 - jotai를 선택해주세요 (참고자료 참고)
- 나는 깊이를 공부해보고 싶아. - context를 선택해서 상태관리를 해보세요.

### (1) 요구사항

- 불필요한 props를 제거하고, 필요한 props만을 전달하도록 개선합니다.
- Context나 Jotai를 사용하여 상태를 관리합니다.
- 테스트 코드를 통과합니다.


### (2) 힌트

- UI 컴포넌트와 엔티티 컴포넌트는 각각 props를 다르게 받는게 좋습니다.
- UI 컴포넌트는 재사용과 독립성을 위해 상태를 최소화하고,
- UI 컴포넌트는 재사용과 독립성을 위해 상태를 최소화하고,
- 엔티티 컴포넌트는 가급적 엔티티를 중심으로 전달받는 것이 좋습니다.
- 특히 콜백의 경우,
- UI 컴포넌트는 이벤트 핸들러를 props로 받아서 처리하도록 해서 재사용성을 높이지만,
- 엔티티 컴포넌트는 props가 아닌 컴포넌트 내부에서 상태를 관리하는 것이 좋습니다.



Loading