From 19b28fd5d671ce4fdea597e0608b21fa6ccbb213 Mon Sep 17 00:00:00 2001 From: LeeJaein Date: Sun, 3 May 2026 21:16:46 +0900 Subject: [PATCH] =?UTF-8?q?feat(#254):=20MSW=20=EB=AA=A9=20=ED=95=B8?= =?UTF-8?q?=EB=93=A4=EB=9F=AC=20=EC=B6=94=EA=B0=80=20-=20user(=EB=A7=88?= =?UTF-8?q?=EC=9D=B4=ED=8E=98=EC=9D=B4=EC=A7=80)=20=EB=B0=8F=20trash(?= =?UTF-8?q?=ED=9C=B4=EC=A7=80=ED=86=B5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/dev/login/route.ts | 20 ++++++ src/features/trash/mock/trash.ts | 120 +++++++++++++++++++++++++++++++ src/features/user/mock/user.ts | 54 ++++++++++++++ src/shared/mock/handlers.ts | 4 ++ 4 files changed, 198 insertions(+) create mode 100644 src/app/api/dev/login/route.ts create mode 100644 src/features/trash/mock/trash.ts create mode 100644 src/features/user/mock/user.ts diff --git a/src/app/api/dev/login/route.ts b/src/app/api/dev/login/route.ts new file mode 100644 index 00000000..b6192963 --- /dev/null +++ b/src/app/api/dev/login/route.ts @@ -0,0 +1,20 @@ +import { NextResponse } from "next/server"; + +import { setTokenCookies } from "@/shared/lib/auth/cookies"; + +export function GET() { + if (process.env.NODE_ENV === "production") { + return NextResponse.json({ error: "Not allowed" }, { status: 403 }); + } + + const response = NextResponse.redirect( + new URL( + "/taskmate", + process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:3000", + ), + ); + + setTokenCookies(response, "dev-access-token", "dev-refresh-token"); + + return response; +} diff --git a/src/features/trash/mock/trash.ts b/src/features/trash/mock/trash.ts new file mode 100644 index 00000000..27b7ada5 --- /dev/null +++ b/src/features/trash/mock/trash.ts @@ -0,0 +1,120 @@ +import { HttpResponse } from "msw"; + +import { apiMock } from "@/shared/mock/apiMock"; + +const mockPersonalTrashItems = [ + { + itemType: "GOAL", + id: 1, + deletedAt: new Date().toISOString(), + goalName: "이번 주 운동", + todoTitle: null, + }, + { + itemType: "TODO", + id: 10, + deletedAt: new Date().toISOString(), + goalName: "이번 주 운동", + todoTitle: "러닝 30분", + }, + { + itemType: "TODO", + id: 11, + deletedAt: new Date().toISOString(), + goalName: "독서", + todoTitle: "클린코드 1장", + }, +]; + +const mockTeamTrashItems = [ + { + itemType: "GOAL", + id: 2, + deletedAt: new Date().toISOString(), + teamName: "디자인팀", + goalName: "스프린트 목표", + todoTitle: null, + }, + { + itemType: "TODO", + id: 20, + deletedAt: new Date().toISOString(), + teamName: "디자인팀", + goalName: "스프린트 목표", + todoTitle: "와이어프레임", + }, +]; + +export const trashHandlers = [ + // 개인 휴지통 조회 + apiMock.get("/api/trash/personal", ({ request }: { request: Request }) => { + const url = new URL(request.url); + const page = Number(url.searchParams.get("page") ?? 0); + const size = Number(url.searchParams.get("size") ?? 20); + const content = mockPersonalTrashItems.slice( + page * size, + (page + 1) * size, + ); + + return HttpResponse.json({ + success: true, + code: "SUCCESS", + message: "개인 휴지통 조회에 성공했습니다.", + data: { + content, + page, + size, + totalElements: mockPersonalTrashItems.length, + totalPages: Math.ceil(mockPersonalTrashItems.length / size), + }, + timestamp: new Date().toISOString(), + }); + }), + + // 팀 휴지통 조회 + apiMock.get( + "/api/trash/teams/:teamId", + ({ request }: { request: Request }) => { + const url = new URL(request.url); + const page = Number(url.searchParams.get("page") ?? 0); + const size = Number(url.searchParams.get("size") ?? 20); + const content = mockTeamTrashItems.slice(page * size, (page + 1) * size); + + return HttpResponse.json({ + success: true, + code: "SUCCESS", + message: "팀 휴지통 조회에 성공했습니다.", + data: { + content, + page, + size, + totalElements: mockTeamTrashItems.length, + totalPages: Math.ceil(mockTeamTrashItems.length / size), + }, + timestamp: new Date().toISOString(), + }); + }, + ), + + // 휴지통 복구 + apiMock.post("/api/trash/restore", async () => { + return HttpResponse.json({ + success: true, + code: "SUCCESS", + message: "휴지통 복구에 성공했습니다.", + data: null, + timestamp: new Date().toISOString(), + }); + }), + + // 휴지통 영구 삭제 + apiMock.delete("/api/trash", async () => { + return HttpResponse.json({ + success: true, + code: "SUCCESS", + message: "휴지통 영구 삭제에 성공했습니다.", + data: null, + timestamp: new Date().toISOString(), + }); + }), +]; diff --git a/src/features/user/mock/user.ts b/src/features/user/mock/user.ts new file mode 100644 index 00000000..30ef00b3 --- /dev/null +++ b/src/features/user/mock/user.ts @@ -0,0 +1,54 @@ +import { HttpResponse } from "msw"; + +import { apiMock } from "@/shared/mock/apiMock"; + +const mockUserInfo = { + id: 101, + email: "leader@example.com", + nickname: "팀장", + profileImageUrl: null, + provider: "LOCAL", + createdAt: new Date().toISOString(), +}; + +export const userHandlers = [ + // 내 정보 수정 + apiMock.put("/api/users", async ({ request }: { request: Request }) => { + const body = (await request.json()) as { nickname?: string }; + return HttpResponse.json({ + success: true, + code: "SUCCESS", + message: "내 정보 수정에 성공했습니다.", + data: { + ...mockUserInfo, + nickname: body.nickname ?? mockUserInfo.nickname, + }, + timestamp: new Date().toISOString(), + }); + }), + + // 프로필 이미지 업로드 + apiMock.put("/api/users/image", async () => { + return HttpResponse.json({ + success: true, + code: "SUCCESS", + message: "프로필 이미지 업로드에 성공했습니다.", + data: { + ...mockUserInfo, + profileImageUrl: "https://placehold.co/100x100", + }, + timestamp: new Date().toISOString(), + }); + }), + + // 회원 탈퇴 + apiMock.delete("/api/users/me", () => { + return HttpResponse.json({ + success: true, + code: "SUCCESS", + message: "회원 탈퇴에 성공했습니다.", + data: null, + timestamp: new Date().toISOString(), + }); + }), +]; diff --git a/src/shared/mock/handlers.ts b/src/shared/mock/handlers.ts index 103c67c2..77ed98e9 100644 --- a/src/shared/mock/handlers.ts +++ b/src/shared/mock/handlers.ts @@ -4,6 +4,8 @@ import { managementHandler } from "@/features/management/mock/management"; import { notificationHandler } from "@/features/notification/mock/notification"; import { invitationsHandlers, teamsHandlers } from "@/features/team/mock"; import { todosHandlers } from "@/features/todo/mock/todos"; +import { trashHandlers } from "@/features/trash/mock/trash"; +import { userHandlers } from "@/features/user/mock/user"; import { homeHandler } from "@/widgets/home/mock/home"; export const handlers = [ @@ -15,4 +17,6 @@ export const handlers = [ ...todosHandlers, ...managementHandler, ...notificationHandler, + ...trashHandlers, + ...userHandlers, ];