Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
925dd6c
빈 커밋
yuhyeon99 Aug 4, 2025
22562d4
GEMINI.md 파일 추가
yuhyeon99 Aug 4, 2025
0319095
chore: Prettier 및 ESLint 연동을 위한 eslint-config-prettier, eslint-plugin…
yuhyeon99 Aug 5, 2025
637593e
chore: ESLint 구성 환경을 eslintrc 형식에서 플랫 구성 형식으로 마이그레이션
yuhyeon99 Aug 5, 2025
57b818b
style: Prettier 규칙에 따라 전체 코드 포맷 적용
yuhyeon99 Aug 5, 2025
9b39977
chore: ESLint와 Prettier 통합
yuhyeon99 Aug 5, 2025
a5419ff
style: .prettierrc 파일 추가 및 적용
yuhyeon99 Aug 5, 2025
cbdd60f
refactor(basic): 계산 관련 로직 분리
yuhyeon99 Aug 5, 2025
687a085
refactor(basic): App.tsx에서 유틸리티 함수 분리
yuhyeon99 Aug 6, 2025
6ba4b4a
refactor(basic): useLocalStorage Hook 분리 및 적용
yuhyeon99 Aug 6, 2025
9246dc9
refactor(basic): App.tsx 비즈니스 로직을 커스텀 Hook으로 분리
yuhyeon99 Aug 6, 2025
ac53586
refactor(basic): useProducts Hook 적용 및 상품 로직 분리
yuhyeon99 Aug 6, 2025
5c16b6f
refactor(basic): useCart Hook 적용 및 장바구니 로직 분리
yuhyeon99 Aug 6, 2025
bacc0bb
refactor(basic): useCoupons Hook 적용 및 쿠폰 로직 분리
yuhyeon99 Aug 6, 2025
c8a31fc
refactor(basic): types.ts 파일에서 인터페이스 import
yuhyeon99 Aug 6, 2025
b12f56d
feat(basic): UI 컴포넌트 분리 (Button, Notification)
yuhyeon99 Aug 6, 2025
dc931af
feat(basic): 엔티티 컴포넌트 분리
yuhyeon99 Aug 6, 2025
cf0bdae
feat(basic): 페이지 컴포넌트 분리
yuhyeon99 Aug 6, 2025
8d1b92a
feat: 심화 과제 초기 설정
yuhyeon99 Aug 7, 2025
a3021f4
feat: Jotai 기반 전역 상태 관리 체계 구축
yuhyeon99 Aug 7, 2025
ba36d45
4: Jotai Atom 내 `calculators.ts` 참조 경로 수정 및 `clearCartAtom` 추가
yuhyeon99 Aug 7, 2025
f754977
refactor: Jotai 기반 도메인 커스텀 훅 구현
yuhyeon99 Aug 7, 2025
d688fbf
refactor: `addNotification` 호출 방식 Jotai atom setter로 변경
yuhyeon99 Aug 7, 2025
2cec42a
refactor: `App.tsx` 및 페이지 컴포넌트 Jotai 훅 적용
yuhyeon99 Aug 7, 2025
a074587
feat: `main.tsx`에 Jotai Provider 추가
yuhyeon99 Aug 7, 2025
1a96459
fix: `ProductCard.tsx` 타입 임포트 추가
yuhyeon99 Aug 7, 2025
db21212
refactor: 계산함수 인자에 기본값을 추가하여 안정성 강화
yuhyeon99 Aug 7, 2025
3e148b1
style: prettier 적용
yuhyeon99 Aug 7, 2025
a33f357
refactor: advanced 폴더 내 불필요한 import 및 변수 제거
yuhyeon99 Aug 7, 2025
b8b9bf4
fix: AdminPage.tsx의 formatPrice 호출 오류 수정
yuhyeon99 Aug 7, 2025
9b400be
feat: GitHub Pages 배포를 위한 advanced 프로젝트 설정 및 CI/CD 추가
yuhyeon99 Aug 7, 2025
67ff2bb
fix: pnpm-workspace.yaml 파일에 패키지 경로 추가
yuhyeon99 Aug 7, 2025
0a63763
fix: 사용되지 않는 `get` 인자 수정
yuhyeon99 Aug 7, 2025
1906a26
fix: 사용되지 않는 import 구문 제거
yuhyeon99 Aug 7, 2025
0aa36d0
fix: `CartItem` 컴포넌트 이름 충돌 해결
yuhyeon99 Aug 7, 2025
e9eb087
불필요 import 제거
yuhyeon99 Aug 7, 2025
4ca0a36
불필요 import 제거 및 타입 수정
yuhyeon99 Aug 7, 2025
5fff534
fix: Basic AdminPage useCoupons 호출 타입 오류 수정
yuhyeon99 Aug 7, 2025
35c14e6
refactor(basic): `formatPrice` 시그니처 및 `ShoppingPage` 로직 개선
yuhyeon99 Aug 7, 2025
92a91b8
refactor: `formatPrice` 시그니처 통일 및 `CartItem` 컴포넌트 참조 수정
yuhyeon99 Aug 7, 2025
05804cb
불필요 인자 제거
yuhyeon99 Aug 7, 2025
e8f6e92
fix: update tsconfig to include only the advanced directory
yuhyeon99 Aug 7, 2025
2ce8097
refactor: update import path for types and add missing types file
yuhyeon99 Aug 7, 2025
765887d
fix: update import path for CartItem type
yuhyeon99 Aug 7, 2025
6ebe45a
fix: 타입 관련 오류 수정
yuhyeon99 Aug 7, 2025
de06ce1
fix: adminPage 컴포넌트 타입 import 수정
yuhyeon99 Aug 7, 2025
a3f214b
fix: update rollup input key from 'advanced' to 'index' in vite.confi…
yuhyeon99 Aug 7, 2025
2c1b7ba
chore: 배포 환경 진입점 HTML 파일 변경
yuhyeon99 Aug 7, 2025
9159f3f
fix: GitHub Pages 404 오류 해결을 위한 Vite 빌드 설정 개선
yuhyeon99 Aug 7, 2025
9329ad5
feat: refactor project structure and update entry point for Vite build
yuhyeon99 Aug 7, 2025
9ccb7ff
refactor(advanced): 역할에 따라 formatPrice 함수 분리
yuhyeon99 Aug 8, 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
2 changes: 1 addition & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ module.exports = {
{ allowConstantExport: true },
],
},
}
};
38 changes: 38 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Deploy Advanced to GitHub Pages

on:
push:
branches:
- main

jobs:
build-and-deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 8

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'

- name: Install dependencies
run: pnpm install

- name: Build Advanced project
run: pnpm run build

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist/advanced
publish_branch: gh-pages
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}
119 changes: 119 additions & 0 deletions GEMINI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Gemini Code Assist Memos

이 파일은 Gemini Code Assist와의 상호작용을 통해 얻은 프로젝트 관련 정보, 결정 사항, 유용한 팁 등을 기록하는 공간입니다.

# 작업 절대 원칙

작업 시 다음 원칙을 반드시 준수해야 합니다.

- **기능 불변성 유지**: 기존 애플리케이션의 기능적 동작은 마이그레이션 전후로 완벽하게 동일해야 합니다. 기능 변경은 허용되지 않습니다.
- **동작 일관성 보장**: 사용자 인터페이스(UI) 및 사용자 경험(UX)은 마이그레이션 전과 동일하게 유지되어야 합니다.
- **TypeScript 우선**: 모든 마이그레이션된 코드 및 새로 작성되는 코드는 TypeScript로 작성되어야 하며, 명확하고 정확한 타입 정의를 포함해야 합니다. `any` 타입 사용은 최소화해야 합니다.
- **React 관용적 코드**: React의 컴포넌트 기반 아키텍처, Hooks, 상태 관리 패턴을 사용하여 코드를 작성해야 합니다. 직접적인 DOM 조작은 React의 라이프사이클 및 렌더링 원칙에 따라 재구성되어야 합니다.
- **기존 Clean Code 규칙 준수**: `MANDATORY CODE WRITING RULES` 섹션에 명시된 모든 Clean Code 원칙(DRY, KISS, YAGNI, 단일 책임, 코드 조직화, 명명 규칙, 추상화)을 React 및 TypeScript 환경에 맞게 적용해야 합니다.
- **점진적 마이그레이션**: 작업을 가능한 한 작은 단위로 나누어 진행하고, 각 단위 작업 완료 후 커밋을 통해 변경 사항을 명확히 기록해야 합니다.
- **불필요한 라이브러리 추가 금지**: 마이그레이션 과정에서 새로운 외부 라이브러리 추가는 사용자 승인 없이는 금지됩니다. 기존 프로젝트의 의존성을 최대한 활용합니다.
- **React.FC 사용 금지**: `React.FC` 타입은 `children`을 암묵적으로 포함하고, 기본 Props 타입 추론을 제한하는 등 타입 안정성을 저하시킬 수 있습니다.
- 대신 `React.FunctionComponent` 대신 명시적인 Props 타입 정의를 사용해야 합니다.

# 절대 반드시 지켜야만 하는 절대적 원칙 (안지키면 다시해야함)

- 코드의 동작이나 구현이 바뀌면 안되고 반드시 구조 변경(리팩토링)만 해야해야만해
- 공통으로 쓰이는 파일만 공통 폴더에 넣어두고, 비즈니스 로직이 담긴 경우, 관심사끼리 묶어 폴더로 관리해야해
- src/basic/tests/basic.test.js, src/advanced/tests/advanced.test.js 테스트 코드가 모두 하나도 빠짐없이 통과해야해 (테스트는 npx vitest run 으로 watch 가 발생하지 않도록 해)
- 테스트 코드 검증 여부는 basic 및 advanced 폴더를 기준으로 검사해야만해. origin 파일은 의미없어.

-> 작업 후 마지막으로 절대 원칙이 지켜졌는지 한번 더 컴토 후 올바르게 고치고 알려줘

# Clean Code Writing Rules

## MANDATORY CODE WRITING RULES

You MUST follow these rules when writing any code:

### CORE DESIGN PRINCIPLES

- **DRY**: NEVER repeat the same code
- **KISS**: Write code as simply as possible
- **YAGNI**: Do NOT write unnecessary code
- **Single Responsibility**: Functions MUST be under 20 lines and have ONE clear responsibility

### CODE ORGANIZATION RULES

Apply these 4 organization principles:

- **Proximity**: Group related elements with blank lines
- **Commonality**: Group related functionality into functions
- **Similarity**: Use similar names and positions for similar roles
- **Continuity**: Arrange code in dependency order

### NAMING REQUIREMENTS

#### Naming Principles (ALL MUST BE FOLLOWED)

1. **Predictable**: Name must allow prediction of value, type, and return value
2. **Contextual**: Add descriptive adjectives or nouns for context
3. **Clear**: Remove unnecessary words while maintaining clear meaning
4. **Concise**: Brief yet clearly convey role and purpose
5. **Consistent**: Use identical terms for identical intentions across entire codebase

#### REQUIRED Naming Patterns

**Action Functions - USE THESE PATTERNS:**

```
// Creation: create~(), add~(), push~(), insert~(), new~(), append~(), spawn~(), make~(), build~(), generate~()
// Retrieval: get~(), fetch~(), query~()
// Transformation: parse~(), split~(), transform~(), serialize~()
// Modification: update~(), mutation~()
// Deletion: delete~(), remove~()
// Communication: put~(), send~(), dispatch~(), receive~()
// Validation: validate~(), check~()
// Calculation: calc~(), compute~()
// Control: init~(), configure~(), start~(), stop~()
// Storage: save~(), store~()
// Logging: log~(), record~()
```

**Data Variables - USE THESE PATTERNS:**

```
// Quantities: count~, sum~, num~, min~, max~, total
// State: is~, has~, current~, selected~
// Progressive/Past: ~ing, ~ed
// Information: ~name, ~title, ~desc, ~text, ~data
// Identifiers: ~ID, ~code, ~index, ~key
// Time: ~at, ~date
// Type: ~type
// Collections: ~s
// Others: item, temp, params, error
// Conversion: from(), of()
```

### ABSTRACTION RULES

- **Data Abstraction**: Simplify data structure and processing methods
- **Process Abstraction**: Encapsulate complex logic into simple interfaces
- **Appropriate Level**: Do NOT over-abstract or under-abstract

### MANDATORY CHECKLIST

Before finalizing ANY code, you MUST verify:

1. ✅ **Applied standard naming patterns** from above
2. ✅ **Organized code using 4 organization principles**
3. ✅ **Split complex logic into small functions**
4. ✅ **Code expresses intent without comments** (comments only when absolutely necessary)
5. ✅ **Maintained consistent formatting**

### FORBIDDEN PRACTICES

- ❌ Do NOT mix similar terms (`display` vs `show`)
- ❌ Do NOT write functions longer than 20 lines
- ❌ Do NOT repeat code patterns
- ❌ Do NOT use unclear or ambiguous names
- ❌ Do NOT violate naming consistency across codebase

## COMPLIANCE REQUIREMENT

ALL code output MUST comply with these rules. No exceptions.
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가 아닌 컴포넌트 내부에서 상태를 관리하는 것이 좋습니다.



55 changes: 55 additions & 0 deletions eslint.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const { defineConfig, globalIgnores } = require('eslint/config');

const globals = require('globals');

const { fixupConfigRules } = require('@eslint/compat');

const tsParser = require('@typescript-eslint/parser');
const reactRefresh = require('eslint-plugin-react-refresh');
const js = require('@eslint/js');

const { FlatCompat } = require('@eslint/eslintrc');

const prettier = require('eslint-plugin-prettier');

const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all,
});

module.exports = defineConfig([
{
languageOptions: {
globals: {
...globals.browser,
},

parser: tsParser,
},

extends: fixupConfigRules(
compat.extends(
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'plugin:prettier/recommended'
)
),

plugins: {
'react-refresh': reactRefresh,
},

rules: {
'react-refresh/only-export-components': [
'warn',
{
allowConstantExport: true,
},
],
'prettier/prettier': 'error',
},
},
globalIgnores(['**/dist', '**/.eslintrc.cjs']),
]);
22 changes: 11 additions & 11 deletions index.advanced.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>장바구니로 학습하는 디자인패턴</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100">
<div id="root"></div>
<script type="module" src="/src/advanced/main.tsx"></script>
</body>
</html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>장바구니로 학습하는 디자인패턴</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100">
<div id="root"></div>
<script type="module" src="/src/advanced/main.tsx"></script>
</body>
</html>
22 changes: 11 additions & 11 deletions index.basic.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>장바구니로 학습하는 디자인패턴</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100">
<div id="root"></div>
<script type="module" src="/src/basic/main.tsx"></script>
</body>
</html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>장바구니로 학습하는 디자인패턴</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100">
<div id="root"></div>
<script type="module" src="/src/basic/main.tsx"></script>
</body>
</html>
Loading