From f31ef95f3474b7db8df16094a609ad363d60cfe4 Mon Sep 17 00:00:00 2001 From: yeun38 Date: Thu, 8 Jan 2026 23:44:49 +0900 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20=EA=B2=B0=EC=A0=9C=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=EC=8B=9C=20=EC=BF=A0=ED=82=A4=20=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- QA_TEST_GUIDE.md | 165 ++++++++ src/api/client/cookie.ts | 2 +- src/app/(service)/payment/complete/page.tsx | 126 ------ src/components/card/mission-card.tsx | 17 +- .../my-page/ui/my-study-info-card.tsx | 6 +- src/middleware.ts | 3 +- src/mocks/homework-mock-data.ts | 385 ++++++++++++++++++ 7 files changed, 564 insertions(+), 140 deletions(-) create mode 100644 QA_TEST_GUIDE.md delete mode 100644 src/app/(service)/payment/complete/page.tsx create mode 100644 src/mocks/homework-mock-data.ts diff --git a/QA_TEST_GUIDE.md b/QA_TEST_GUIDE.md new file mode 100644 index 00000000..cb69d889 --- /dev/null +++ b/QA_TEST_GUIDE.md @@ -0,0 +1,165 @@ +# 미션/숙제 기능 QA 테스트 가이드 + +## 개요 +백엔드 API 오류로 인해 Mock 데이터를 사용하여 미션/숙제 관련 기능을 테스트할 수 있도록 설정했습니다. + +## Mock 모드 설정 + +현재 다음 파일들에서 `USE_MOCK = true` 로 설정되어 있습니다: + +1. `src/hooks/queries/group-study-homework-api.ts` +2. `src/hooks/queries/peer-review-api.ts` +3. `src/hooks/queries/evaluation-api.ts` +4. `src/hooks/queries/mission-api.ts` + +**백엔드 API를 다시 사용하려면**: 각 파일의 `USE_MOCK` 값을 `false`로 변경하고 주석 처리된 API 호출 코드의 주석을 해제하면 됩니다. + +## 구현된 기능 + +### 1. 미션 목록 및 상세 +- ✅ 미션 목록 조회 (진행중/완료 필터링) +- ✅ 미션 상세 정보 조회 +- ✅ 미션 생성 모달 (리더 전용) +- ✅ 미션 수정/삭제 모달 (리더 전용) + +### 2. 숙제 제출 +- ✅ 숙제 제출 모달 + - 텍스트 내용 (최소 100자) + - 첨부 링크 (선택사항) +- ✅ 숙제 수정 모달 (평가 전까지만 가능) +- ✅ 숙제 삭제 모달 (평가 전까지만 가능) + +### 3. 리더 평가 +- ✅ 평가 생성 모달 (리더 전용) + - 등급 선택 (A+, A-, B+, B-, C+, C-, F) + - 정성 코멘트 입력 +- ✅ 평가 후에는 숙제 수정/삭제 불가능 + +### 4. 피어 리뷰 +- ✅ 피어 리뷰 작성 (본인 과제와 리더는 작성 불가) +- ✅ 피어 리뷰 수정/삭제 (본인 리뷰만 가능) +- ✅ 리뷰 작성자 정보 표시 +- ✅ 수정 여부 표시 + +## 테스트 시나리오 + +### 시나리오 1: 미션 조회 및 숙제 제출 (일반 멤버) +1. 미션 목록 페이지 접근 +2. 진행 중인 미션 선택 (예: "React Hooks 학습") +3. 미션 상세 정보 확인 +4. "과제 제출하기" 버튼 클릭 +5. 과제 상세 내용 입력 (100자 이상) +6. 첨부 링크 입력 (선택사항) +7. "제출하기" 버튼 클릭 +8. 제출 완료 확인 + +### 시나리오 2: 숙제 수정/삭제 (본인 과제) +1. 제출한 과제 상세 페이지 이동 +2. "수정하기" 버튼으로 과제 내용 수정 +3. "삭제하기" 버튼으로 과제 삭제 (확인 모달) +4. ⚠️ 평가가 완료된 과제는 수정/삭제 불가 + +### 시나리오 3: 리더 평가 (리더 역할) +1. 멤버가 제출한 과제 상세 페이지 접근 +2. "리더 평가" 섹션에서 "과제 평가하기" 버튼 클릭 +3. 평가 등급 선택 (A+~F) +4. 정성 코멘트 작성 +5. "평가 완료" 버튼 클릭 +6. 평가 결과 확인 + +### 시나리오 4: 피어 리뷰 작성 (일반 멤버) +1. 다른 멤버의 과제 상세 페이지 접근 +2. "피어 리뷰" 섹션 하단의 입력창에 리뷰 작성 +3. "등록" 버튼 클릭 +4. 작성한 리뷰 확인 +5. 본인 리뷰의 "..." 메뉴에서 수정/삭제 가능 + +### 시나리오 5: 피어 리뷰 제한 확인 +1. ❌ 본인의 과제에서는 피어 리뷰 작성 불가 +2. ❌ 리더는 피어 리뷰 작성 불가 +3. ✅ 일반 멤버만 다른 멤버의 과제에 피어 리뷰 가능 + +## Mock 데이터 구조 + +### Mock 사용자 정보 +- **현재 사용자 ID**: 1 +- **리더 여부**: `useLeaderStore`와 `useUserStore`에서 확인 +- 테스트 시 리더/멤버 권한에 따라 다른 기능 접근 가능 + +### Mock 미션 데이터 +- 미션 ID: 1 - "React Hooks 학습" (진행중) +- 미션 ID: 2 - "TypeScript 기초" (시작 전) +- 미션 ID: 3 - "JavaScript ES6+ 복습" (완료) + +### Mock 숙제 데이터 +- Homework ID: 1 (제출 완료 상태) +- 피어 리뷰 2개 포함 +- 평가는 초기에 없음 (리더가 평가 가능) + +### Mock 평가 등급 +- A+ (95점) +- A- (90점) +- B+ (85점) +- B- (80점) +- C+ (75점) +- C- (70점) +- F (0점) + +## 데이터 저장 방식 + +Mock 데이터는 **localStorage**에 저장됩니다: +- `homework_{homeworkId}`: 숙제 상세 정보 +- `peerReviews_{homeworkId}`: 해당 숙제의 피어 리뷰 목록 + +### 데이터 초기화 +브라우저의 localStorage를 비우면 Mock 데이터가 초기화됩니다: +```javascript +localStorage.clear(); // 개발자 도구 콘솔에서 실행 +``` + +## 주요 파일 위치 + +### 컴포넌트 +- `src/components/section/mission-section.tsx` - 미션 목록/상세 페이지 +- `src/components/contents/mission-detail-content.tsx` - 미션 상세 컨텐츠 +- `src/components/contents/homework-detail-content.tsx` - 숙제 상세 컨텐츠 + +### 모달 +- `src/components/modals/submit-homework-modal.tsx` - 숙제 제출 +- `src/components/modals/edit-homework-modal.tsx` - 숙제 수정 +- `src/components/modals/delete-homework-modal.tsx` - 숙제 삭제 +- `src/components/modals/create-evaluation-modal.tsx` - 평가 생성 + +### API 훅 +- `src/hooks/queries/group-study-homework-api.ts` - 숙제 관련 API +- `src/hooks/queries/peer-review-api.ts` - 피어 리뷰 API +- `src/hooks/queries/evaluation-api.ts` - 평가 API +- `src/hooks/queries/mission-api.ts` - 미션 API + +### Mock 데이터 +- `src/mocks/homework-mock-data.ts` - 모든 Mock 데이터 정의 + +## 알려진 제한사항 + +1. **사용자 권한**: Mock 데이터에서는 현재 사용자 ID가 고정(1)되어 있습니다. +2. **미션 생성/수정/삭제**: Mock 모드에서 실제로 동작하지 않습니다 (실제 API 필요). +3. **이미지 업로드**: 프로필 이미지는 기본 이미지(`/profile-default.svg`)만 사용됩니다. +4. **페이지네이션**: Mock 데이터는 전체 목록만 반환합니다. + +## 백엔드 복구 후 작업 + +백엔드 API가 정상화되면: + +1. 각 API 훅 파일에서 `USE_MOCK = false` 로 변경 +2. 주석 처리된 API 호출 코드 활성화 +3. Mock import 문 제거 (선택사항) +4. localStorage 초기화 + +```typescript +// 예시: src/hooks/queries/group-study-homework-api.ts +const USE_MOCK = false; // true → false 로 변경 +``` + +## 문의사항 + +QA 테스트 중 문제가 발생하거나 질문이 있으시면 개발팀에 문의해 주세요. diff --git a/src/api/client/cookie.ts b/src/api/client/cookie.ts index a932eb70..f050c2c0 100644 --- a/src/api/client/cookie.ts +++ b/src/api/client/cookie.ts @@ -15,7 +15,7 @@ export const setCookie = ( const { path = '/', secure = true, - sameSite = 'Strict', + sameSite = 'Lax', // Strict에서 Lax로 변경: 외부 사이트에서 redirect 시 쿠키 전송 허용 maxAge = 86400, // 1일 httpOnly = false, } = options; diff --git a/src/app/(service)/payment/complete/page.tsx b/src/app/(service)/payment/complete/page.tsx deleted file mode 100644 index 2a6def22..00000000 --- a/src/app/(service)/payment/complete/page.tsx +++ /dev/null @@ -1,126 +0,0 @@ -'use client'; - -import Image from 'next/image'; -import { useRouter } from 'next/navigation'; -import PageContainer from '@/components/layout/page-container'; -import Button from '@/components/ui/button'; - -interface PaymentResult { - orderName: string; - productAmount: number; - paymentMethod: string; - totalAmount: number; -} - -// ✅ 가데이터 -const MOCK_PAYMENT_RESULT: PaymentResult = { - orderName: '1일코테문풀 인증 챌린지', - productAmount: 35000, - paymentMethod: '카드결제', - totalAmount: 35000, -}; - -export default function PaymentCompletePage() { - const router = useRouter(); - const data = MOCK_PAYMENT_RESULT; - - const formatKRW = (n: number) => `${n.toLocaleString('ko-KR')}원`; - - return ( - -
- {/* 아이콘 */} -
-
- success -
-
- - {/* 타이틀 */} -
-

- 스터디 수강 신청이 완료되었습니다. -

-

- 수강/학습 내역과 결제 내역은 마이페이지에서 확인하실 수 있습니다. -

-
- - {/* 정보 카드 */} -
-
- - -
결제 정보
- - - -
- - - - -
- - {/* 버튼 */} -
- - - -
-
-
- - ); -} - -function Row({ - label, - value, - bold, - strong, -}: { - label: string; - value: string; - bold?: boolean; - strong?: boolean; -}) { - return ( -
-
- {label} -
-
- {value} -
-
- ); -} diff --git a/src/components/card/mission-card.tsx b/src/components/card/mission-card.tsx index 17ebb103..50a23763 100644 --- a/src/components/card/mission-card.tsx +++ b/src/components/card/mission-card.tsx @@ -1,6 +1,8 @@ 'use client'; +import dayjs from 'dayjs'; import { ComponentProps } from 'react'; + import { MissionListResponse } from '@/api/openapi/models'; import Badge from '@/components/ui/badge'; import Button from '@/components/ui/button'; @@ -42,9 +44,8 @@ const STATUS_CONFIG = { function formatDate(dateString?: string) { if (!dateString) return ''; - const date = new Date(dateString); - return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; + return dayjs(dateString).format('YYYY-MM-DD'); } function getDeadlineInfo(endDate?: string): { @@ -53,15 +54,13 @@ function getDeadlineInfo(endDate?: string): { } | null { if (!endDate) return null; - const now = new Date(); - const end = new Date(endDate); - end.setHours(23, 59, 59, 999); + const now = dayjs(); + const end = dayjs(endDate).endOf('day'); - const diffMs = end.getTime() - now.getTime(); - if (diffMs < 0) return null; + if (end.isBefore(now)) return null; - const diffHours = diffMs / (1000 * 60 * 60); - const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24)); + const diffHours = end.diff(now, 'hour'); + const diffDays = Math.ceil(end.diff(now, 'day', true)); if (diffHours <= 24) { return { text: '오늘 제출 마감', isUrgent: true }; diff --git a/src/features/my-page/ui/my-study-info-card.tsx b/src/features/my-page/ui/my-study-info-card.tsx index 8ece3fe2..6ccd6258 100644 --- a/src/features/my-page/ui/my-study-info-card.tsx +++ b/src/features/my-page/ui/my-study-info-card.tsx @@ -1,3 +1,4 @@ +import dayjs from 'dayjs'; import Image from 'next/image'; import Link from 'next/link'; import { IoMdPeople } from 'react-icons/io'; @@ -5,7 +6,6 @@ import { LuDot } from 'react-icons/lu'; import Badge from '@/components/ui/badge'; import Button from '@/components/ui/button'; import { MemberStudyItem } from '@/features/study/group/api/group-study-types'; -import { formatYYYYMMDD } from '@/utils/time'; interface MyStudyInfoCardProps extends MemberStudyItem { type: 'GROUP_STUDY'; @@ -22,8 +22,8 @@ export default function MyStudyInfoCard({ studyRole, title, }: MyStudyInfoCardProps) { - const startDate = formatYYYYMMDD(startTime, 'dot'); - const endDate = endTime ? formatYYYYMMDD(endTime, 'dot') : null; + const startDate = dayjs(startTime).format('YYYY.MM.DD'); + const endDate = endTime ? dayjs(endTime).format('YYYY.MM.DD') : null; return (
  • diff --git a/src/middleware.ts b/src/middleware.ts index e9f9a8cf..b55d6617 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -118,7 +118,7 @@ export async function middleware(request: NextRequest) { // 갱신 성공 response.cookies.set('accessToken', newAccessToken, { secure: true, - sameSite: 'strict', + sameSite: 'lax', // strict에서 lax로 변경: 외부 사이트에서 redirect 시 쿠키 전송 허용 path: '/', }); } else { @@ -156,6 +156,7 @@ export const config = { '/my-study', '/my-study-review', '/sign-up', + '/payment/:path*', // 결제 관련 모든 경로 '/admin/:path*', ], }; diff --git a/src/mocks/homework-mock-data.ts b/src/mocks/homework-mock-data.ts new file mode 100644 index 00000000..c485d3cb --- /dev/null +++ b/src/mocks/homework-mock-data.ts @@ -0,0 +1,385 @@ +import type { + HomeworkResponseDto, + EvaluationResponse, + PeerReviewResponse, + MissionResponseDto, + MissionListResponse, + GradeDto, +} from '@/api/openapi/models'; + +// Mock 평가 등급 데이터 +export const mockEvaluationGrades: GradeDto[] = [ + { code: 'A_PLUS', label: 'A+', score: 95, orderNum: 1 }, + { code: 'A_MINUS', label: 'A-', score: 90, orderNum: 2 }, + { code: 'B_PLUS', label: 'B+', score: 85, orderNum: 3 }, + { code: 'B_MINUS', label: 'B-', score: 80, orderNum: 4 }, + { code: 'C_PLUS', label: 'C+', score: 75, orderNum: 5 }, + { code: 'C_MINUS', label: 'C-', score: 70, orderNum: 6 }, + { code: 'F', label: 'F', score: 0, orderNum: 7 }, +]; + +// Mock 피어 리뷰 데이터 +export const mockPeerReviews: PeerReviewResponse[] = [ + { + peerReviewId: 1, + homeworkId: 1, + reviewerId: 2, + reviewerNickname: '김철수', + reviewerProfileImage: { + resizedImages: [ + { + resizedImageUrl: '/profile-default.svg', + }, + ], + }, + comment: '과제 내용이 정말 잘 정리되어 있네요! 특히 핵심 개념을 명확하게 설명한 부분이 좋았습니다.', + createdAt: '2026-01-07T10:30:00', + updatedAt: '2026-01-07T10:30:00', + updated: false, + }, + { + peerReviewId: 2, + homeworkId: 1, + reviewerId: 3, + reviewerNickname: '박영희', + reviewerProfileImage: { + resizedImages: [ + { + resizedImageUrl: '/profile-default.svg', + }, + ], + }, + comment: '코드 예제가 이해하기 쉬웠어요. 다음에는 더 복잡한 케이스도 다뤄보면 좋을 것 같습니다!', + createdAt: '2026-01-07T14:20:00', + updatedAt: '2026-01-07T14:20:00', + updated: false, + }, +]; + +// Mock 평가 데이터 +export const mockEvaluation: EvaluationResponse = { + evaluationId: 1, + homeworkId: 1, + grade: { + gradeCode: 'A_PLUS', + gradeLabel: 'A+', + gradeScore: 95, + }, + comment: '과제를 매우 성실하게 수행했습니다. 개념 이해도가 높고 코드 품질도 우수합니다. 계속 이런 식으로 학습하면 좋은 결과가 있을 것입니다!', + createdAt: '2026-01-07T09:00:00', + updatedAt: '2026-01-07T09:00:00', + updated: false, +}; + +// Mock 숙제 상세 데이터 +export const mockHomeworkDetail: HomeworkResponseDto = { + homeworkId: 1, + submitterId: 1, + submitterNickname: '홍길동', + submitterProfileImage: { + resizedImages: [ + { + resizedImageUrl: '/profile-default.svg', + }, + ], + }, + submitted: true, + submissionTime: '2026-01-06T23:45:00', + homeworkContent: { + textContent: `이번 주 학습 내용을 정리했습니다. + +1. React Hooks의 기본 개념 + - useState: 상태 관리를 위한 기본 훅 + - useEffect: 사이드 이펙트 처리 + - useContext: 전역 상태 관리 + +2. Custom Hooks 작성 + - 재사용 가능한 로직 분리 + - 컴포넌트 간 로직 공유 + +3. 실습 내용 + - Todo 앱 만들기 + - API 호출 및 데이터 페칭 + - 에러 핸들링 구현 + +배운 내용을 바탕으로 간단한 프로젝트를 만들어보았고, 많은 것을 배울 수 있었습니다.`, + optionalContent: { + link: 'https://github.com/example/react-hooks-practice', + }, + }, + evaluation: undefined, // 초기에는 평가 없음 + peerReviews: mockPeerReviews, +}; + +// 평가 완료된 숙제 데이터 +export const mockHomeworkWithEvaluation: HomeworkResponseDto = { + ...mockHomeworkDetail, + evaluation: mockEvaluation, +}; + +// Mock 미션 목록 데이터 +export const mockMissions: MissionListResponse[] = [ + { + missionId: 1, + weekNum: 1, + title: 'React Hooks 학습', + startDate: '2026-01-01', + endDate: '2026-01-07', + status: 'IN_PROGRESS', + maxEvaluationCount: 10, + evaluatedCount: 2, + }, + { + missionId: 2, + weekNum: 2, + title: 'TypeScript 기초', + startDate: '2026-01-08', + endDate: '2026-01-14', + status: 'NOT_STARTED', + maxEvaluationCount: 10, + evaluatedCount: 0, + }, + { + missionId: 3, + weekNum: 0, + title: 'JavaScript ES6+ 복습', + startDate: '2025-12-25', + endDate: '2025-12-31', + status: 'ENDED', + maxEvaluationCount: 10, + evaluatedCount: 10, + }, +]; + +// Mock 미션 상세 데이터 +export const mockMissionDetail: MissionResponseDto = { + missionId: 1, + weekNum: 1, + missionTitle: 'React Hooks 학습', + missionGuide: `이번 주는 React Hooks에 대해 학습합니다. + +[학습 목표] +1. useState, useEffect, useContext 이해하기 +2. Custom Hooks 만들어보기 +3. 실전 프로젝트에 적용하기 + +[제출 방법] +- 학습한 내용을 정리하여 제출해주세요 +- 실습한 코드를 GitHub에 올리고 링크를 첨부해주세요 +- 최소 100자 이상 작성해주세요`, + missionStartDate: '2026-01-01', + missionEndDate: '2026-01-07', + status: 'IN_PROGRESS', + currentHomeworkSubmissionCount: 8, + maxHomeworkSubmissionCount: 10, + homeworks: [ + { + homeworkId: 1, + submitterId: 1, + submitterNickname: '홍길동', + submitterProfileImage: { + resizedImages: [{ resizedImageUrl: '/profile-default.svg' }], + }, + submissionTime: '2026-01-06T23:45:00', + homeworkStatus: 'EVALUATION_COMPLETED', + evaluation: { + evaluationGradeLabel: 'A+', + }, + }, + { + homeworkId: 2, + submitterId: 2, + submitterNickname: '김철수', + submitterProfileImage: { + resizedImages: [{ resizedImageUrl: '/profile-default.svg' }], + }, + submissionTime: '2026-01-06T20:30:00', + homeworkStatus: 'SUBMITTED', + }, + { + homeworkId: 3, + submitterId: 3, + submitterNickname: '박영희', + submitterProfileImage: { + resizedImages: [{ resizedImageUrl: '/profile-default.svg' }], + }, + submissionTime: '2026-01-06T18:15:00', + homeworkStatus: 'SUBMITTED', + }, + { + homeworkId: 4, + submitterId: 4, + submitterNickname: '이민수', + submitterProfileImage: { + resizedImages: [{ resizedImageUrl: '/profile-default.svg' }], + }, + submissionTime: '2026-01-06T22:00:00', + homeworkStatus: 'EVALUATION_COMPLETED', + evaluation: { + evaluationGradeLabel: 'B+', + }, + }, + { + homeworkId: 5, + submitterId: 5, + submitterNickname: '정수현', + submitterProfileImage: { + resizedImages: [{ resizedImageUrl: '/profile-default.svg' }], + }, + submissionTime: '2026-01-07T01:20:00', + homeworkStatus: 'SUBMITTED', + }, + { + homeworkId: undefined, + submitterId: 6, + submitterNickname: '최지원', + submitterProfileImage: { + resizedImages: [{ resizedImageUrl: '/profile-default.svg' }], + }, + submissionTime: undefined, + homeworkStatus: 'NOT_SUBMITTED', + }, + { + homeworkId: 7, + submitterId: 7, + submitterNickname: '강태영', + submitterProfileImage: { + resizedImages: [{ resizedImageUrl: '/profile-default.svg' }], + }, + submissionTime: '2026-01-06T16:45:00', + homeworkStatus: 'SUBMITTED', + }, + { + homeworkId: 8, + submitterId: 8, + submitterNickname: '윤서진', + submitterProfileImage: { + resizedImages: [{ resizedImageUrl: '/profile-default.svg' }], + }, + submissionTime: '2026-01-07T02:10:00', + homeworkStatus: 'SUBMITTED', + }, + { + homeworkId: undefined, + submitterId: 9, + submitterNickname: '임하늘', + submitterProfileImage: { + resizedImages: [{ resizedImageUrl: '/profile-default.svg' }], + }, + submissionTime: undefined, + homeworkStatus: 'NOT_SUBMITTED', + }, + { + homeworkId: 10, + submitterId: 10, + submitterNickname: '송민재', + submitterProfileImage: { + resizedImages: [{ resizedImageUrl: '/profile-default.svg' }], + }, + submissionTime: '2026-01-06T21:30:00', + homeworkStatus: 'SUBMITTED', + }, + ], +}; + +// Mock 데이터를 localStorage에 저장하고 관리하는 헬퍼 함수들 +export const getMockHomework = (homeworkId: number): HomeworkResponseDto => { + const stored = localStorage.getItem(`homework_${homeworkId}`); + if (stored) { + return JSON.parse(stored); + } + + // 미션 목록 데이터와 동기화: homeworkId 1과 4는 평가 완료 상태 + if (homeworkId === 1 || homeworkId === 4) { + return { + ...mockHomeworkDetail, + homeworkId, + submitterId: homeworkId, + submitterNickname: homeworkId === 1 ? '홍길동' : '이민수', + evaluation: homeworkId === 1 ? mockEvaluation : { + evaluationId: 2, + homeworkId: 4, + grade: { + gradeCode: 'B_PLUS', + gradeLabel: 'B+', + gradeScore: 85, + }, + comment: '과제를 잘 수행했습니다. 기본 개념은 잘 이해하고 있으나, 조금 더 깊이 있는 학습이 필요합니다.', + createdAt: '2026-01-07T10:00:00', + updatedAt: '2026-01-07T10:00:00', + updated: false, + }, + }; + } + + // 기본 mock 데이터 반환 (평가 없음) + return { + ...mockHomeworkDetail, + homeworkId, + submitterId: homeworkId, + submitterNickname: `테스트유저${homeworkId}`, + }; +}; + +export const saveMockHomework = (homework: HomeworkResponseDto) => { + localStorage.setItem(`homework_${homework.homeworkId}`, JSON.stringify(homework)); +}; + +export const getMockPeerReviews = (homeworkId: number): PeerReviewResponse[] => { + const stored = localStorage.getItem(`peerReviews_${homeworkId}`); + if (stored) { + return JSON.parse(stored); + } + return mockPeerReviews; +}; + +export const saveMockPeerReviews = (homeworkId: number, peerReviews: PeerReviewResponse[]) => { + localStorage.setItem(`peerReviews_${homeworkId}`, JSON.stringify(peerReviews)); +}; + +export const addMockPeerReview = (homeworkId: number, newReview: Omit): PeerReviewResponse => { + const existing = getMockPeerReviews(homeworkId); + const newId = Math.max(...existing.map(r => r.peerReviewId || 0), 0) + 1; + const review: PeerReviewResponse = { + ...newReview, + peerReviewId: newId, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + updated: false, + }; + saveMockPeerReviews(homeworkId, [...existing, review]); + return review; +}; + +export const updateMockPeerReview = (peerReviewId: number, comment: string): PeerReviewResponse | null => { + // 모든 숙제의 피어 리뷰를 순회하며 찾기 + const keys = Object.keys(localStorage).filter(k => k.startsWith('peerReviews_')); + for (const key of keys) { + const reviews: PeerReviewResponse[] = JSON.parse(localStorage.getItem(key) || '[]'); + const index = reviews.findIndex(r => r.peerReviewId === peerReviewId); + if (index !== -1) { + reviews[index] = { + ...reviews[index], + comment, + updatedAt: new Date().toISOString(), + updated: true, + }; + localStorage.setItem(key, JSON.stringify(reviews)); + return reviews[index]; + } + } + return null; +}; + +export const deleteMockPeerReview = (peerReviewId: number): boolean => { + const keys = Object.keys(localStorage).filter(k => k.startsWith('peerReviews_')); + for (const key of keys) { + const reviews: PeerReviewResponse[] = JSON.parse(localStorage.getItem(key) || '[]'); + const filtered = reviews.filter(r => r.peerReviewId !== peerReviewId); + if (filtered.length !== reviews.length) { + localStorage.setItem(key, JSON.stringify(filtered)); + return true; + } + } + return false; +}; From b42b16618467da558daa2a77828347cf372d7089 Mon Sep 17 00:00:00 2001 From: yeun38 Date: Thu, 8 Jan 2026 23:52:56 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- QA_TEST_GUIDE.md | 165 -------------------------------- src/mocks/homework-mock-data.ts | 93 ++++++++++++------ 2 files changed, 64 insertions(+), 194 deletions(-) delete mode 100644 QA_TEST_GUIDE.md diff --git a/QA_TEST_GUIDE.md b/QA_TEST_GUIDE.md deleted file mode 100644 index cb69d889..00000000 --- a/QA_TEST_GUIDE.md +++ /dev/null @@ -1,165 +0,0 @@ -# 미션/숙제 기능 QA 테스트 가이드 - -## 개요 -백엔드 API 오류로 인해 Mock 데이터를 사용하여 미션/숙제 관련 기능을 테스트할 수 있도록 설정했습니다. - -## Mock 모드 설정 - -현재 다음 파일들에서 `USE_MOCK = true` 로 설정되어 있습니다: - -1. `src/hooks/queries/group-study-homework-api.ts` -2. `src/hooks/queries/peer-review-api.ts` -3. `src/hooks/queries/evaluation-api.ts` -4. `src/hooks/queries/mission-api.ts` - -**백엔드 API를 다시 사용하려면**: 각 파일의 `USE_MOCK` 값을 `false`로 변경하고 주석 처리된 API 호출 코드의 주석을 해제하면 됩니다. - -## 구현된 기능 - -### 1. 미션 목록 및 상세 -- ✅ 미션 목록 조회 (진행중/완료 필터링) -- ✅ 미션 상세 정보 조회 -- ✅ 미션 생성 모달 (리더 전용) -- ✅ 미션 수정/삭제 모달 (리더 전용) - -### 2. 숙제 제출 -- ✅ 숙제 제출 모달 - - 텍스트 내용 (최소 100자) - - 첨부 링크 (선택사항) -- ✅ 숙제 수정 모달 (평가 전까지만 가능) -- ✅ 숙제 삭제 모달 (평가 전까지만 가능) - -### 3. 리더 평가 -- ✅ 평가 생성 모달 (리더 전용) - - 등급 선택 (A+, A-, B+, B-, C+, C-, F) - - 정성 코멘트 입력 -- ✅ 평가 후에는 숙제 수정/삭제 불가능 - -### 4. 피어 리뷰 -- ✅ 피어 리뷰 작성 (본인 과제와 리더는 작성 불가) -- ✅ 피어 리뷰 수정/삭제 (본인 리뷰만 가능) -- ✅ 리뷰 작성자 정보 표시 -- ✅ 수정 여부 표시 - -## 테스트 시나리오 - -### 시나리오 1: 미션 조회 및 숙제 제출 (일반 멤버) -1. 미션 목록 페이지 접근 -2. 진행 중인 미션 선택 (예: "React Hooks 학습") -3. 미션 상세 정보 확인 -4. "과제 제출하기" 버튼 클릭 -5. 과제 상세 내용 입력 (100자 이상) -6. 첨부 링크 입력 (선택사항) -7. "제출하기" 버튼 클릭 -8. 제출 완료 확인 - -### 시나리오 2: 숙제 수정/삭제 (본인 과제) -1. 제출한 과제 상세 페이지 이동 -2. "수정하기" 버튼으로 과제 내용 수정 -3. "삭제하기" 버튼으로 과제 삭제 (확인 모달) -4. ⚠️ 평가가 완료된 과제는 수정/삭제 불가 - -### 시나리오 3: 리더 평가 (리더 역할) -1. 멤버가 제출한 과제 상세 페이지 접근 -2. "리더 평가" 섹션에서 "과제 평가하기" 버튼 클릭 -3. 평가 등급 선택 (A+~F) -4. 정성 코멘트 작성 -5. "평가 완료" 버튼 클릭 -6. 평가 결과 확인 - -### 시나리오 4: 피어 리뷰 작성 (일반 멤버) -1. 다른 멤버의 과제 상세 페이지 접근 -2. "피어 리뷰" 섹션 하단의 입력창에 리뷰 작성 -3. "등록" 버튼 클릭 -4. 작성한 리뷰 확인 -5. 본인 리뷰의 "..." 메뉴에서 수정/삭제 가능 - -### 시나리오 5: 피어 리뷰 제한 확인 -1. ❌ 본인의 과제에서는 피어 리뷰 작성 불가 -2. ❌ 리더는 피어 리뷰 작성 불가 -3. ✅ 일반 멤버만 다른 멤버의 과제에 피어 리뷰 가능 - -## Mock 데이터 구조 - -### Mock 사용자 정보 -- **현재 사용자 ID**: 1 -- **리더 여부**: `useLeaderStore`와 `useUserStore`에서 확인 -- 테스트 시 리더/멤버 권한에 따라 다른 기능 접근 가능 - -### Mock 미션 데이터 -- 미션 ID: 1 - "React Hooks 학습" (진행중) -- 미션 ID: 2 - "TypeScript 기초" (시작 전) -- 미션 ID: 3 - "JavaScript ES6+ 복습" (완료) - -### Mock 숙제 데이터 -- Homework ID: 1 (제출 완료 상태) -- 피어 리뷰 2개 포함 -- 평가는 초기에 없음 (리더가 평가 가능) - -### Mock 평가 등급 -- A+ (95점) -- A- (90점) -- B+ (85점) -- B- (80점) -- C+ (75점) -- C- (70점) -- F (0점) - -## 데이터 저장 방식 - -Mock 데이터는 **localStorage**에 저장됩니다: -- `homework_{homeworkId}`: 숙제 상세 정보 -- `peerReviews_{homeworkId}`: 해당 숙제의 피어 리뷰 목록 - -### 데이터 초기화 -브라우저의 localStorage를 비우면 Mock 데이터가 초기화됩니다: -```javascript -localStorage.clear(); // 개발자 도구 콘솔에서 실행 -``` - -## 주요 파일 위치 - -### 컴포넌트 -- `src/components/section/mission-section.tsx` - 미션 목록/상세 페이지 -- `src/components/contents/mission-detail-content.tsx` - 미션 상세 컨텐츠 -- `src/components/contents/homework-detail-content.tsx` - 숙제 상세 컨텐츠 - -### 모달 -- `src/components/modals/submit-homework-modal.tsx` - 숙제 제출 -- `src/components/modals/edit-homework-modal.tsx` - 숙제 수정 -- `src/components/modals/delete-homework-modal.tsx` - 숙제 삭제 -- `src/components/modals/create-evaluation-modal.tsx` - 평가 생성 - -### API 훅 -- `src/hooks/queries/group-study-homework-api.ts` - 숙제 관련 API -- `src/hooks/queries/peer-review-api.ts` - 피어 리뷰 API -- `src/hooks/queries/evaluation-api.ts` - 평가 API -- `src/hooks/queries/mission-api.ts` - 미션 API - -### Mock 데이터 -- `src/mocks/homework-mock-data.ts` - 모든 Mock 데이터 정의 - -## 알려진 제한사항 - -1. **사용자 권한**: Mock 데이터에서는 현재 사용자 ID가 고정(1)되어 있습니다. -2. **미션 생성/수정/삭제**: Mock 모드에서 실제로 동작하지 않습니다 (실제 API 필요). -3. **이미지 업로드**: 프로필 이미지는 기본 이미지(`/profile-default.svg`)만 사용됩니다. -4. **페이지네이션**: Mock 데이터는 전체 목록만 반환합니다. - -## 백엔드 복구 후 작업 - -백엔드 API가 정상화되면: - -1. 각 API 훅 파일에서 `USE_MOCK = false` 로 변경 -2. 주석 처리된 API 호출 코드 활성화 -3. Mock import 문 제거 (선택사항) -4. localStorage 초기화 - -```typescript -// 예시: src/hooks/queries/group-study-homework-api.ts -const USE_MOCK = false; // true → false 로 변경 -``` - -## 문의사항 - -QA 테스트 중 문제가 발생하거나 질문이 있으시면 개발팀에 문의해 주세요. diff --git a/src/mocks/homework-mock-data.ts b/src/mocks/homework-mock-data.ts index c485d3cb..1ca41df6 100644 --- a/src/mocks/homework-mock-data.ts +++ b/src/mocks/homework-mock-data.ts @@ -32,7 +32,8 @@ export const mockPeerReviews: PeerReviewResponse[] = [ }, ], }, - comment: '과제 내용이 정말 잘 정리되어 있네요! 특히 핵심 개념을 명확하게 설명한 부분이 좋았습니다.', + comment: + '과제 내용이 정말 잘 정리되어 있네요! 특히 핵심 개념을 명확하게 설명한 부분이 좋았습니다.', createdAt: '2026-01-07T10:30:00', updatedAt: '2026-01-07T10:30:00', updated: false, @@ -49,7 +50,8 @@ export const mockPeerReviews: PeerReviewResponse[] = [ }, ], }, - comment: '코드 예제가 이해하기 쉬웠어요. 다음에는 더 복잡한 케이스도 다뤄보면 좋을 것 같습니다!', + comment: + '코드 예제가 이해하기 쉬웠어요. 다음에는 더 복잡한 케이스도 다뤄보면 좋을 것 같습니다!', createdAt: '2026-01-07T14:20:00', updatedAt: '2026-01-07T14:20:00', updated: false, @@ -65,7 +67,8 @@ export const mockEvaluation: EvaluationResponse = { gradeLabel: 'A+', gradeScore: 95, }, - comment: '과제를 매우 성실하게 수행했습니다. 개념 이해도가 높고 코드 품질도 우수합니다. 계속 이런 식으로 학습하면 좋은 결과가 있을 것입니다!', + comment: + '과제를 매우 성실하게 수행했습니다. 개념 이해도가 높고 코드 품질도 우수합니다. 계속 이런 식으로 학습하면 좋은 결과가 있을 것입니다!', createdAt: '2026-01-07T09:00:00', updatedAt: '2026-01-07T09:00:00', updated: false, @@ -296,19 +299,23 @@ export const getMockHomework = (homeworkId: number): HomeworkResponseDto => { homeworkId, submitterId: homeworkId, submitterNickname: homeworkId === 1 ? '홍길동' : '이민수', - evaluation: homeworkId === 1 ? mockEvaluation : { - evaluationId: 2, - homeworkId: 4, - grade: { - gradeCode: 'B_PLUS', - gradeLabel: 'B+', - gradeScore: 85, - }, - comment: '과제를 잘 수행했습니다. 기본 개념은 잘 이해하고 있으나, 조금 더 깊이 있는 학습이 필요합니다.', - createdAt: '2026-01-07T10:00:00', - updatedAt: '2026-01-07T10:00:00', - updated: false, - }, + evaluation: + homeworkId === 1 + ? mockEvaluation + : { + evaluationId: 2, + homeworkId: 4, + grade: { + gradeCode: 'B_PLUS', + gradeLabel: 'B+', + gradeScore: 85, + }, + comment: + '과제를 잘 수행했습니다. 기본 개념은 잘 이해하고 있으나, 조금 더 깊이 있는 학습이 필요합니다.', + createdAt: '2026-01-07T10:00:00', + updatedAt: '2026-01-07T10:00:00', + updated: false, + }, }; } @@ -322,10 +329,15 @@ export const getMockHomework = (homeworkId: number): HomeworkResponseDto => { }; export const saveMockHomework = (homework: HomeworkResponseDto) => { - localStorage.setItem(`homework_${homework.homeworkId}`, JSON.stringify(homework)); + localStorage.setItem( + `homework_${homework.homeworkId}`, + JSON.stringify(homework), + ); }; -export const getMockPeerReviews = (homeworkId: number): PeerReviewResponse[] => { +export const getMockPeerReviews = ( + homeworkId: number, +): PeerReviewResponse[] => { const stored = localStorage.getItem(`peerReviews_${homeworkId}`); if (stored) { return JSON.parse(stored); @@ -333,13 +345,25 @@ export const getMockPeerReviews = (homeworkId: number): PeerReviewResponse[] => return mockPeerReviews; }; -export const saveMockPeerReviews = (homeworkId: number, peerReviews: PeerReviewResponse[]) => { - localStorage.setItem(`peerReviews_${homeworkId}`, JSON.stringify(peerReviews)); +export const saveMockPeerReviews = ( + homeworkId: number, + peerReviews: PeerReviewResponse[], +) => { + localStorage.setItem( + `peerReviews_${homeworkId}`, + JSON.stringify(peerReviews), + ); }; -export const addMockPeerReview = (homeworkId: number, newReview: Omit): PeerReviewResponse => { +export const addMockPeerReview = ( + homeworkId: number, + newReview: Omit< + PeerReviewResponse, + 'peerReviewId' | 'createdAt' | 'updatedAt' + >, +): PeerReviewResponse => { const existing = getMockPeerReviews(homeworkId); - const newId = Math.max(...existing.map(r => r.peerReviewId || 0), 0) + 1; + const newId = Math.max(...existing.map((r) => r.peerReviewId || 0), 0) + 1; const review: PeerReviewResponse = { ...newReview, peerReviewId: newId, @@ -351,12 +375,19 @@ export const addMockPeerReview = (homeworkId: number, newReview: Omit { +export const updateMockPeerReview = ( + peerReviewId: number, + comment: string, +): PeerReviewResponse | null => { // 모든 숙제의 피어 리뷰를 순회하며 찾기 - const keys = Object.keys(localStorage).filter(k => k.startsWith('peerReviews_')); + const keys = Object.keys(localStorage).filter((k) => + k.startsWith('peerReviews_'), + ); for (const key of keys) { - const reviews: PeerReviewResponse[] = JSON.parse(localStorage.getItem(key) || '[]'); - const index = reviews.findIndex(r => r.peerReviewId === peerReviewId); + const reviews: PeerReviewResponse[] = JSON.parse( + localStorage.getItem(key) || '[]', + ); + const index = reviews.findIndex((r) => r.peerReviewId === peerReviewId); if (index !== -1) { reviews[index] = { ...reviews[index], @@ -372,10 +403,14 @@ export const updateMockPeerReview = (peerReviewId: number, comment: string): Pee }; export const deleteMockPeerReview = (peerReviewId: number): boolean => { - const keys = Object.keys(localStorage).filter(k => k.startsWith('peerReviews_')); + const keys = Object.keys(localStorage).filter((k) => + k.startsWith('peerReviews_'), + ); for (const key of keys) { - const reviews: PeerReviewResponse[] = JSON.parse(localStorage.getItem(key) || '[]'); - const filtered = reviews.filter(r => r.peerReviewId !== peerReviewId); + const reviews: PeerReviewResponse[] = JSON.parse( + localStorage.getItem(key) || '[]', + ); + const filtered = reviews.filter((r) => r.peerReviewId !== peerReviewId); if (filtered.length !== reviews.length) { localStorage.setItem(key, JSON.stringify(filtered)); return true; From f3d19e127db230f801982e5d4c1cca564261164a Mon Sep 17 00:00:00 2001 From: yeun38 Date: Sat, 10 Jan 2026 00:55:39 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix:=EC=B6=94=EA=B0=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(admin)/admin/sales-management/payment-refund/page.tsx | 1 + src/app/(service)/(my)/payment-management/page.tsx | 1 + src/components/summary/study-info-summary.tsx | 1 - src/widgets/home/header.tsx | 2 +- 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/(admin)/admin/sales-management/payment-refund/page.tsx b/src/app/(admin)/admin/sales-management/payment-refund/page.tsx index 14a7adb8..c13efa10 100644 --- a/src/app/(admin)/admin/sales-management/payment-refund/page.tsx +++ b/src/app/(admin)/admin/sales-management/payment-refund/page.tsx @@ -26,6 +26,7 @@ const PAYMENT_HISTORY_TYPE_MAP: Record< } > = { PAYMENT_REQUESTED: { label: '결제대기', color: 'blue' }, + PAYMENT_WAITING_FOR_DEPOSIT: { label: '입금대기', color: 'blue' }, PAYMENT_SUCCESS: { label: '결제완료', color: 'green' }, PAYMENT_FAILED: { label: '결제실패', color: 'red' }, PAYMENT_CANCELED: { label: '결제취소', color: 'red' }, diff --git a/src/app/(service)/(my)/payment-management/page.tsx b/src/app/(service)/(my)/payment-management/page.tsx index cb067c97..76164e3b 100644 --- a/src/app/(service)/(my)/payment-management/page.tsx +++ b/src/app/(service)/(my)/payment-management/page.tsx @@ -33,6 +33,7 @@ const TRANSACTION_TYPE_MAP: Record< } > = { PAYMENT_REQUESTED: { label: '결제대기', color: 'blue' }, + PAYMENT_WAITING_FOR_DEPOSIT: { label: '입금대기', color: 'blue' }, PAYMENT_SUCCESS: { label: '결제완료', color: 'green' }, PAYMENT_FAILED: { label: '결제실패', color: 'red' }, PAYMENT_CANCELED: { label: '결제취소', color: 'red' }, diff --git a/src/components/summary/study-info-summary.tsx b/src/components/summary/study-info-summary.tsx index 854c0f89..0283c116 100644 --- a/src/components/summary/study-info-summary.tsx +++ b/src/components/summary/study-info-summary.tsx @@ -132,7 +132,6 @@ export default function SummaryStudyInfo({ data }: Props) { }; const isApplyDisabled = - !isLoggedIn || isLeader || myApplicationStatus?.status !== 'NONE' || groupStudyStatus !== 'RECRUITING' || diff --git a/src/widgets/home/header.tsx b/src/widgets/home/header.tsx index 9d3e215d..5c5da095 100644 --- a/src/widgets/home/header.tsx +++ b/src/widgets/home/header.tsx @@ -1,5 +1,6 @@ import Image from 'next/image'; import Link from 'next/link'; +import HeaderNav from '@/components/layout/header-nav'; import NotificationDropdown from '@/components/modals/notification-dropdown'; import Button from '@/components/ui/button'; import { getUserProfileInServer } from '@/entities/user/api/get-user-profile.server'; @@ -7,7 +8,6 @@ import HeaderUserDropdown from '@/features/auth/ui/header-user-dropdown'; import LoginModal from '@/features/auth/ui/login-modal'; import { getServerCookie } from '@/utils/server-cookie'; import { isNumeric } from '@/utils/validation'; -import HeaderNav from '@/components/layout/header-nav'; export default async function Header() { const memberIdStr = await getServerCookie('memberId'); From 9e40417cedd19d01296dc738e5883b0b1e75764a Mon Sep 17 00:00:00 2001 From: yeun38 Date: Sat, 10 Jan 2026 01:01:47 +0900 Subject: [PATCH 4/4] fix : lint --- src/components/contents/homework-detail-content.tsx | 2 +- src/components/pages/group-study-list-page.tsx | 2 +- src/components/pages/premium-study-list-page.tsx | 2 +- src/features/auth/model/use-auth-mutation.ts | 2 +- src/mocks/homework-mock-data.ts | 6 ++++++ 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/contents/homework-detail-content.tsx b/src/components/contents/homework-detail-content.tsx index 67df9c41..2f3a103c 100644 --- a/src/components/contents/homework-detail-content.tsx +++ b/src/components/contents/homework-detail-content.tsx @@ -10,7 +10,6 @@ import type { import Avatar from '@/components/ui/avatar'; import Button from '@/components/ui/button'; import MoreMenu from '@/components/ui/dropdown/more-menu'; -import { useUserStore } from '@/stores/useUserStore'; import ConfirmDeleteModal from '@/features/study/group/ui/confirm-delete-modal'; import { useDeleteHomework, @@ -22,6 +21,7 @@ import { useUpdatePeerReview, } from '@/hooks/queries/peer-review-api'; import { useIsLeader } from '@/stores/useLeaderStore'; +import { useUserStore } from '@/stores/useUserStore'; import DeleteHomeworkModal from '../modals/delete-homework-modal'; import EditHomeworkModal from '../modals/edit-homework-modal'; diff --git a/src/components/pages/group-study-list-page.tsx b/src/components/pages/group-study-list-page.tsx index 69ea9aaf..717a09f4 100644 --- a/src/components/pages/group-study-list-page.tsx +++ b/src/components/pages/group-study-list-page.tsx @@ -14,11 +14,11 @@ import StudyFilter, { import StudySearch from '@/components/filtering/study-search'; import PageContainer from '@/components/layout/page-container'; import Button from '@/components/ui/button'; +import { useAuth } from '@/hooks/common/use-auth'; import { useGetStudies } from '@/hooks/queries/study-query'; import GroupStudyFormModal from '../../features/study/group/ui/group-study-form-modal'; import GroupStudyPagination from '../../features/study/group/ui/group-study-pagination'; import GroupStudyList from '../lists/group-study-list'; -import { useAuth } from '@/hooks/common/use-auth'; const PAGE_SIZE = 15; diff --git a/src/components/pages/premium-study-list-page.tsx b/src/components/pages/premium-study-list-page.tsx index d6f61a9f..0d88fb46 100644 --- a/src/components/pages/premium-study-list-page.tsx +++ b/src/components/pages/premium-study-list-page.tsx @@ -17,8 +17,8 @@ import PremiumStudyList from '@/components/premium/premium-study-list'; import PremiumStudyPagination from '@/components/premium/premium-study-pagination'; import Button from '@/components/ui/button'; import GroupStudyFormModal from '@/features/study/group/ui/group-study-form-modal'; -import { useGetStudies } from '@/hooks/queries/study-query'; import { useAuth } from '@/hooks/common/use-auth'; +import { useGetStudies } from '@/hooks/queries/study-query'; const PAGE_SIZE = 15; diff --git a/src/features/auth/model/use-auth-mutation.ts b/src/features/auth/model/use-auth-mutation.ts index 13c37587..4506540b 100644 --- a/src/features/auth/model/use-auth-mutation.ts +++ b/src/features/auth/model/use-auth-mutation.ts @@ -6,8 +6,8 @@ import { useRouter } from 'next/navigation'; import { deleteCookie, getCookie } from '@/api/client/cookie'; import { logout, signUp, uploadProfileImage } from '@/features/auth/api/auth'; import { hashValue } from '@/utils/hash'; -import { useUserStore } from '../../../stores/useUserStore'; import { SignUpRequest, SignUpResponse } from './types'; +import { useUserStore } from '../../../stores/useUserStore'; // 회원가입 요청 커스텀 훅 export const useSignUpMutation = () => { diff --git a/src/mocks/homework-mock-data.ts b/src/mocks/homework-mock-data.ts index 1ca41df6..e89391be 100644 --- a/src/mocks/homework-mock-data.ts +++ b/src/mocks/homework-mock-data.ts @@ -342,6 +342,7 @@ export const getMockPeerReviews = ( if (stored) { return JSON.parse(stored); } + return mockPeerReviews; }; @@ -372,6 +373,7 @@ export const addMockPeerReview = ( updated: false, }; saveMockPeerReviews(homeworkId, [...existing, review]); + return review; }; @@ -396,9 +398,11 @@ export const updateMockPeerReview = ( updated: true, }; localStorage.setItem(key, JSON.stringify(reviews)); + return reviews[index]; } } + return null; }; @@ -413,8 +417,10 @@ export const deleteMockPeerReview = (peerReviewId: number): boolean => { const filtered = reviews.filter((r) => r.peerReviewId !== peerReviewId); if (filtered.length !== reviews.length) { localStorage.setItem(key, JSON.stringify(filtered)); + return true; } } + return false; };