Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

강원대 FE_허윤수 5주차 과제 STEP2-login #39

Open
wants to merge 45 commits into
base: sugoring
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
7142c9b
docs: 전체 기능요구사항 정리
Jul 22, 2024
4f5ac5d
docs: step0 기능요구사항 구현
Jul 22, 2024
98f0be9
docs: step1 구현 요구사항 정리
Jul 25, 2024
e13b0c0
feat: MSW를 사용한 상세 및 옵션 API 모킹 핸들러 추가
Jul 25, 2024
c1bc343
feat: 제품 옵션 API 모킹 핸들러 추가
Jul 25, 2024
799ed8a
feat: 제품 상세 정보 API 모킹 핸들러 추가
Jul 25, 2024
cb54939
chore: ESLint 룰 적용 및 설정 추가
Jul 25, 2024
a75ce66
refactor: useGetCategorys 대신 categories.mock.ts를 사용하여 카테고리 데이터 모킹
Jul 25, 2024
8d4c1e4
refactor: useGetProducts에서 pageParam 사용 제거
Jul 25, 2024
0c390c8
fix: 제품 상세 정보 API 호출에서 핸들러 경로와 요청 경로 일치 문제 해결
Jul 25, 2024
394ca8e
feat: 상품 상세 정보 및 옵션 조회 API 모킹 데이터 개선
Jul 25, 2024
8468844
fix: 상품 상세 헤더 컴포넌트 null 체크 추가
Jul 25, 2024
68c6c14
fix: handle undefined price and add error handling in OptionSection c…
Jul 25, 2024
f8c5bdf
fix: handle null detail and remove unused error variable in GoodsInfo…
Jul 25, 2024
ea21d7a
fix: handle 'detail' possibly being null
Jul 25, 2024
b73df39
feat: Implement product options data fetching and mocking
Jul 25, 2024
d879506
ix: 상품 옵션 계산 로직 수정 및 타입 오류 해결
Jul 25, 2024
2b1263b
feat: 제품 상세 정보 조회 기능 구현 및 MSW 핸들러 추가
Jul 25, 2024
40e33f7
feat: 제품 상세 정보 API 모킹 및 useSuspenseQuery 훅 추가
Jul 25, 2024
d4845d6
docs: step1 msw에 대한 브랜치 전환
Jul 25, 2024
c71e5d9
Merge pull request #4 from sugoring/step0
sugoring Jul 25, 2024
2ac29c5
test: useGetProducts 테스트 추가
Jul 25, 2024
a02b848
test: getProductDetail 함수에 대한 단위 테스트 추가
Jul 25, 2024
b5c2014
feat: 현금영수증 입력 컴포넌트(CashReceiptFields) 단위 테스트 추가
Jul 25, 2024
3960572
feat(components/order): 메시지 카드 입력 컴포넌트(MessageCardFields) 단위 테스트 추가
Jul 25, 2024
b278fd5
docs: 단위 테스트 구현 목록
Jul 25, 2024
33d8b9a
test: useGetCategories 훅에 대한 통합 테스트 추가
Jul 25, 2024
4842d82
test: useGetProducts 훅에 대한 통합 테스트 추가
Jul 25, 2024
91ed3a4
test: useGetProductDetail 훅 통합 테스트 구현
Jul 25, 2024
8c30ec4
test: useGetProductOptions 훅 통합 테스트 구현
Jul 25, 2024
c288458
docs: mock 통합 테스트 구현
Jul 25, 2024
1ae0bd3
feat: 현금영수증 입력 컴포넌트 통합 테스트 추가
Jul 25, 2024
9826bdb
test: 메시지 카드 입력 컴포넌트 통합 테스트 추가
Jul 25, 2024
ec2703b
docs: step2 구현 목록 정리
Jul 25, 2024
da347d0
fix: Remove req
Jul 26, 2024
18366d0
feat: 로그인 기능 추가
Jul 26, 2024
689ef92
feat(login): 회원가입 버튼 UI 구현
Jul 26, 2024
526e094
feat(router): 회원가입 페이지 라우팅 설정 및 컴포넌트 추가
Jul 26, 2024
22925be
feat(signup): 회원가입 UI 구현
Jul 26, 2024
90cbb38
feat(api): 회원가입 요청을 처리하는 useRegister 훅 생성
Jul 26, 2024
786b62d
fest: 타입스크립트 환경에서 MSW 초기화 코드 리팩터링
Jul 26, 2024
6764475
feat(auth): implement login functionality and always return success r…
Jul 26, 2024
eec996c
feat(mock): 상품 옵션 API 모의 응답 개선
Jul 26, 2024
e11b867
fix: lint 룰 적용
Jul 26, 2024
7fd36c6
Merge branch 'sugoring' into step2-login
sugoring Jul 29, 2024
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
79 changes: 78 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,78 @@
# 카카오 테크 캠퍼스 - 프론트엔드 카카오 선물하기 편
# React Product msw

## 개요

본 저장소는 5주차 과제 (2024-07-22 ~ 2024-07-16)를 위한 로그인 및 관심목록 구현을 담고 있습니다. 상세한 학습 내용은 [Notion 노트](https://www.notion.so/TIL-FE-25dbeb894e884b889eca0fa3e4e13904)에서 확인할 수 있습니다.

---

## 0단계 - 기본 코드 준비

- [x] 기본 코드 준비

---

## 1단계 - Form 부분 테스트 코드 작성하기

### 테스트 기반 환경 구축

- [x] Jest 테스트 환경 설정
- [x] React Testing Library 테스트 환경 설정

### MSW를 사용하여 Mock API 설정

- [x] 상세 API 엔드포인트 추가
- [x] 옵션 API 엔드포인트 추가

### 단위 테스트 작성

- CashReceiptFields 컴포넌트
- [x] 렌더링 테스트: 현금영수증 관련 입력 컴포넌트(체크박스, 셀렉트, input)가 화면에 정상적으로 표시되는지 확인
- [x] 사용자 상호작용 테스트: 체크박스 클릭, 셀렉트 옵션 선택, input 값 입력 등 사용자 입력에 대한 테스트
- MessageCardFields 컴포넌트
- [x] 렌더링 테스트: 메시지 카드 입력 textarea가 화면에 정상적으로 표시되는지 확인
- [x] 사용자 입력 테스트: textarea에 메시지 입력 후 값이 제대로 반영되는지 확인

### 통합 테스트 작성

- 상품 상세 페이지
- [x] useGetCategorys.test
- [x] useGetProducts.test
- [x] useGetProductDetail.test
- [x] useGetProductOptions.test

- 결제하기 페이지
- 현금영수증
- [x] Checkbox 상태에 따른 필드 활성화/비활성화 테스트
- [x] Checkbox가 `true`인 경우 필드 값 입력 테스트
- Form
- [x] 필수 입력 필드 검사
- [x] 입력 값 형식 검사

---

## 2단계 - 로그인, 관심 상품 등록 / 삭제, 관심 목록 구현

- 계정 관리
- [x] 로그인 기능 구현
- [x] 회원가입 버튼 UI 구현: 로그인 화면 하단에 회원가입 버튼 배치
- [x] 회원가입 버튼 로직 구현: 버튼 클릭 시 회원가입 페이지로 이동
- [x] 회원가입 UI 구현: 로그인 UI 참고 및 사용
- [x] 회원가입 로직 구현: 회원가입 성공 시 로그인 페이지로 이동 및 성공 메시지 표시

- 상품 상세 페이지
- [ ] 관심 등록 버튼 UI 구현
- [ ] 관심 등록 버튼 로직 구현: 관심 등록 성공 시 "관심 등록 완료" Alert 메시지 표시

- 마이 페이지
- [ ] 관심 목록 리스트 UI 구현: Chakra UI 컴포넌트 활용
- [ ] 관심 목록 API 활용: 선물하기 API 노션의 response 데이터 활용
- [ ] 관심 목록 리스트 로직 구현: 관심 삭제 성공 시 해당 항목 리스트에서 제거

---

## 3단계 - 질문의 답변을 README에 작성

- **질문 1**: Test code를 작성해보면서 좋았던 점과 아쉬웠던 점에 대해 말해주세요.
- **질문 2**: 스스로 생각했을 때 좋은 컴포넌트란 무엇인지 본인만의 기준을 세우고 설명해 주세요.
- **질문 3**: 스스로 생각했을 때 공통 컴포넌트를 만들 때 가장 중요한 요소 2개를 선택하고 이유와 함께 설명해주세요.
92 changes: 75 additions & 17 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
"build": "craco build",
"test": "craco test",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
"build-storybook": "storybook build",
"lint": "eslint \"./src/**/*.{ts,tsx,js,jsx}\"",
"lint:fix": "eslint --fix \"./src/**/*.{ts,tsx,js,jsx}\"",
"prettier": "prettier --write \"./src/**/*.{ts,tsx,js,jsx}\""
},
"browserslist": {
"production": [
Expand All @@ -27,15 +30,15 @@
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@tanstack/react-query": "^5.24.1",
"axios": "^1.6.7",
"axios": "^1.7.2",
"framer-motion": "^11.0.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.12",
"react-hook-form": "^7.50.1",
"react-intersection-observer": "^9.8.1",
"react-router-dom": "^6.22.1"
"react-router-dom": "^6.22.1",
"zod": "^3.23.8"
},
"devDependencies": {
"@craco/craco": "^7.1.0",
Expand All @@ -49,8 +52,10 @@
"@storybook/react": "^7.6.17",
"@storybook/react-webpack5": "^7.6.17",
"@storybook/test": "^7.6.17",
"@tanstack/react-query": "^5.51.11",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.82",
Expand Down
15 changes: 15 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,26 @@ import { queryClient } from './api/instance';
import { AuthProvider } from './provider/Auth';
import { Routes } from './routes';

const initializeMocks = async () => {
if (process.env.NODE_ENV === 'development') {
const { worker } = await import('./mocks/browser');
worker.start().then(() => {
console.log('Mock Service Worker is running');
});
}
};

initializeMocks();

const App = () => {
return (
// ChakraProvider wraps the app to provide Chakra UI components and theme
<ChakraProvider>
{/* QueryClientProvider provides React Query context for managing server state */}
<QueryClientProvider client={queryClient}>
{/* AuthProvider provides authentication context to the app */}
<AuthProvider>
{/* Routes component handles the routing of the application */}
sugoring marked this conversation as resolved.
Show resolved Hide resolved
<Routes />
</AuthProvider>
</QueryClientProvider>
Expand Down
21 changes: 12 additions & 9 deletions src/api/hooks/categories.mock.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import { rest } from 'msw';

import { getCategoriesPath } from './useGetCategorys';

export const categoriesMockHandler = [
rest.get(getCategoriesPath(), (_, res, ctx) => {
return res(ctx.json(CATEGORIES_RESPONSE_DATA));
}),
];

const CATEGORIES_RESPONSE_DATA = [
// 기존에 정의된 카테고리 데이터
export const CATEGORIES_RESPONSE_DATA = [
{
id: 2920,
name: '생일',
Expand All @@ -26,3 +19,13 @@ const CATEGORIES_RESPONSE_DATA = [
'https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240131153049_5a22b137a8d346e9beb020a7a7f4254a.jpg',
},
];

// 카테고리 API 경로
export const getCategoriesPath = () => '/api/categories';

// MSW 핸들러 정의
export const categoriesMockHandler = [
rest.get(getCategoriesPath(), (_, res, ctx) => {
return res(ctx.json(CATEGORIES_RESPONSE_DATA));
}),
];
26 changes: 26 additions & 0 deletions src/api/hooks/login.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { rest } from 'msw';

const BASE_URL = 'http://localhost:3000';

type LoginRequestBody = {
email: string;
password: string;
};

type LoginSuccessResponse = {
email: string;
token: string;
};

export const loginMockHandler = [
rest.post<LoginRequestBody>(`${BASE_URL}/api/members/login`, async (req, res, ctx) => {
const { email } = await req.json();

// 항상 성공 응답 반환
const response: LoginSuccessResponse = {
email,
token: 'mocked-jwt-token',
};
return res(ctx.status(200), ctx.json(response));
}),
];
34 changes: 34 additions & 0 deletions src/api/hooks/productDetail.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { rest } from 'msw';

const BASE_URL = 'http://localhost:3000';

interface ProductDetail {
id: number;
name: string;
price: number;
imageUrl: string;
categoryId: number;
}

const mockProductDetail: ProductDetail = {
id: 1,
name: 'Sample Product',
price: 100,
imageUrl:
'https://i.namu.wiki/i/lTIwu3NCJk-m5VOdugukoiVGzyZAVauahUc2qnrOX-j8XFCA7PXv95cioeTRqrixnTUYDdfZnapP2Fo-jz3OBl5VYyd5SJpft-ZcMedgg4QmJGEkeol2W-do5U3mL6_vqQYTPAr7QBwp7VTts7kmfiYUgQ_Hosv7gwcBxnFagmo.webp',
categoryId: 1,
};

export const productDetailMockHandler = [
rest.get(`${BASE_URL}/api/products/:productId`, (req, res, ctx) => {
const { productId } = req.params;

// 실제 환경에서는 여기서 productId를 사용하여 다양한 상품을 반환할 수 있습니다.
// 이 예제에서는 항상 같은 mockProductDetail을 반환합니다.
if (productId) {
return res(ctx.status(200), ctx.json(mockProductDetail));
} else {
return res(ctx.status(404), ctx.json({ message: 'Product not found' }));
}
}),
];
Loading