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/(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/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 (
-
-
- {/* 아이콘 */}
-
-
- {/* 타이틀 */}
-
-
- 스터디 수강 신청이 완료되었습니다.
-
-
- 수강/학습 내역과 결제 내역은 마이페이지에서 확인하실 수 있습니다.
-
-
-
- {/* 정보 카드 */}
-
-
-
|
-
-
결제 정보
-
-
|
-
-
-
-
|
-
-
|
-
-
- {/* 버튼 */}
-
-
-
-
-
-
-
-
- );
-}
-
-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/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/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/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/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..e89391be
--- /dev/null
+++ b/src/mocks/homework-mock-data.ts
@@ -0,0 +1,426 @@
+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,
+ 'peerReviewId' | 'createdAt' | 'updatedAt'
+ >,
+): 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;
+};
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');