diff --git a/src/app/(auth)/components/LoginForm.test.tsx b/src/app/(auth)/components/LoginForm.test.tsx
new file mode 100644
index 0000000..2b292f2
--- /dev/null
+++ b/src/app/(auth)/components/LoginForm.test.tsx
@@ -0,0 +1,80 @@
+import { render, screen, fireEvent, waitFor } from "@testing-library/react";
+import { vi, Mock } from "vitest";
+
+import { useAuth } from "@/hooks/useAuth";
+
+import LoginForm from "./LoginForm";
+
+// Mock the useAuth hook
+vi.mock("@/hooks/useAuth");
+
+describe("LoginForm", () => {
+ const mockLoginMutation = {
+ mutate: vi.fn(),
+ isLoading: false,
+ };
+
+ beforeEach(() => {
+ (useAuth as Mock).mockReturnValue({
+ loginMutation: mockLoginMutation,
+ });
+ });
+
+ it("모든 입력 필드와 로그인 버튼이 렌더링되어야 한다.", () => {
+ render();
+
+ expect(screen.getByPlaceholderText("이메일")).toBeInTheDocument();
+ expect(screen.getByPlaceholderText("비밀번호")).toBeInTheDocument();
+ expect(screen.getByRole("button", { name: "로그인" })).toBeInTheDocument();
+ });
+
+ describe("유효성 검사", () => {
+ it("이메일 및 비밀번호 필드 유효성 검사", async () => {
+ render();
+ fireEvent.blur(screen.getByPlaceholderText("이메일"));
+ fireEvent.blur(screen.getByPlaceholderText("비밀번호"));
+
+ await waitFor(() => {
+ expect(screen.getByText("유효한 이메일 주소를 입력해주세요.")).toBeInTheDocument();
+ expect(screen.getByText("비밀번호는 최소 8자 이상이어야 합니다.")).toBeInTheDocument();
+ });
+ });
+
+ it("이메일과 비밀번호로 로그인합니다.", async () => {
+ render();
+
+ fireEvent.change(screen.getByPlaceholderText("이메일"), {
+ target: { value: "test@example.com" },
+ });
+ fireEvent.change(screen.getByPlaceholderText("비밀번호"), {
+ target: { value: "password123" },
+ });
+ fireEvent.click(screen.getByRole("button", { name: "로그인" }));
+
+ await waitFor(() => {
+ expect(mockLoginMutation.mutate).toHaveBeenCalledWith({
+ email: "test@example.com",
+ password: "password123",
+ });
+ });
+ });
+
+ it("제출 시 로딩 중인 스피너를 표시합니다.", async () => {
+ mockLoginMutation.isLoading = true;
+ render();
+
+ fireEvent.change(screen.getByPlaceholderText("이메일"), {
+ target: { value: "test@example.com" },
+ });
+ fireEvent.change(screen.getByPlaceholderText("비밀번호"), {
+ target: { value: "password123" },
+ });
+
+ fireEvent.click(screen.getByRole("button", { name: "로그인" }));
+
+ await waitFor(() => {
+ expect(screen.getByTestId("spinner")).toBeInTheDocument();
+ });
+ });
+ });
+});
diff --git a/src/app/(auth)/components/SignupForm.test.tsx b/src/app/(auth)/components/SignupForm.test.tsx
index 1a83c5c..272f84d 100644
--- a/src/app/(auth)/components/SignupForm.test.tsx
+++ b/src/app/(auth)/components/SignupForm.test.tsx
@@ -2,11 +2,14 @@ import { render, screen, waitFor } from "@testing-library/react";
import { vi, Mock } from "vitest";
import userEvent from "@testing-library/user-event";
-import { useSignup } from "@/hook/useSignup";
+import { useAuth } from "@/hooks/useAuth";
import SignupForm from "./SignupForm";
// Mock modules
+
+vi.mock("@/hooks/useAuth");
+
vi.mock("next/navigation", () => ({
useRouter: () => ({
push: vi.fn(),
@@ -25,17 +28,17 @@ vi.mock("react-toastify", () => ({
}));
describe("SignupForm", () => {
- const mockMutate = vi.fn();
+ const mockSignupMutation = { mutate: vi.fn() };
beforeEach(() => {
vi.clearAllMocks();
- (useSignup as Mock).mockReturnValue({
- mutate: mockMutate,
+ (useAuth as Mock).mockReturnValue({
+ signupMutation: mockSignupMutation,
isLoading: false,
});
});
-
- describe("렌더링 테스트", () => {
+
+ describe("SignupForm 렌더링 테스트", () => {
it("모든 입력 필드와 제출 버튼이 렌더링되어야 한다", () => {
render();
@@ -117,7 +120,7 @@ describe("SignupForm", () => {
await userEvent.click(submitButton);
await waitFor(() => {
- expect(mockMutate).toHaveBeenCalledWith(
+ expect(mockSignupMutation.mutate).toHaveBeenCalledWith(
expect.objectContaining({
nickname: validFormData.nickname,
email: validFormData.email,
@@ -137,7 +140,7 @@ describe("SignupForm", () => {
},
};
- mockMutate.mockImplementation((_, options) => {
+ mockSignupMutation.mutate.mockImplementation((_, options) => {
options.onError(mockError);
});
diff --git a/src/app/dashboard/goal/[goalId]/page.tsx b/src/app/dashboard/goal/[goalId]/page.tsx
index be19ca2..07306c1 100644
--- a/src/app/dashboard/goal/[goalId]/page.tsx
+++ b/src/app/dashboard/goal/[goalId]/page.tsx
@@ -7,7 +7,7 @@ import { AxiosError } from "axios";
import { deleteGoal, ErrorType, getGoal } from "@/api/goalAPI";
import { getTodos } from "@/api/todoAPI";
-import useModal from "@/hook/useModal";
+import useModal from "@/hooks/useModal";
import { useTodoStore } from "@/store/todoStore";
import CreateNewTodo from "@/components/CreateNewTodo";
import EditGoalTitleModal from "@/components/EditGoalTitleModal";
diff --git a/src/app/dashboard/note/[noteId]/page.tsx b/src/app/dashboard/note/[noteId]/page.tsx
index 1e73470..c45d066 100644
--- a/src/app/dashboard/note/[noteId]/page.tsx
+++ b/src/app/dashboard/note/[noteId]/page.tsx
@@ -4,7 +4,7 @@ import Image from "next/image";
import { ChangeEvent, useEffect, useState } from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
-import useModal from "@/hook/useModal";
+import useModal from "@/hooks/useModal";
import { getNote, patchNotes, postNotes } from "@/api/noteAPI";
import { getTodos } from "@/api/todoAPI";
import UploadLinkModal from "@/components/UploadLinkModal";
diff --git a/src/app/dashboard/todoboard/page.tsx b/src/app/dashboard/todoboard/page.tsx
index a8a7542..d183c8d 100644
--- a/src/app/dashboard/todoboard/page.tsx
+++ b/src/app/dashboard/todoboard/page.tsx
@@ -4,7 +4,7 @@ import { useEffect, useState } from "react";
import { useTodoStore } from "@/store/todoStore";
import CreateNewTodo from "@/components/CreateNewTodo";
-import useModal from "@/hook/useModal";
+import useModal from "@/hooks/useModal";
import { getAllTodos } from "@/api/todoAPI";
import { TodoType } from "@/type";
diff --git a/src/lib/api.ts b/src/lib/api.ts
index 8faac82..275ec3a 100644
--- a/src/lib/api.ts
+++ b/src/lib/api.ts
@@ -6,9 +6,16 @@ const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL,
});
+// 쿠키에서 특정 키의 값을 가져오는 함수
+const getCookieValue = (key: string) => {
+ const cookies = document.cookie.split("; ");
+ const cookie = cookies.find((cookie) => cookie.startsWith(`${key}=`));
+ return cookie ? cookie.split("=")[1] : null;
+};
+
api.interceptors.request.use(
(config) => {
- const token = document.cookie.replace(/(?:(?:^|.*;\s*)accessToken\s*=\s*([^;]*).*$)|^.*$/, "$1");
+ const token = getCookieValue("accessToken"); // getCookieValue 함수를 사용하여 accessToken을 가져옴
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
@@ -26,6 +33,7 @@ api.interceptors.response.use(
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
const refreshToken = localStorage.getItem("refreshToken");
+
if (refreshToken) {
try {
const { data } = await axios.post("/auth/tokens", { refreshToken });