diff --git a/README.md b/README.md
index e3ee5a24..bc19b5bf 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,84 @@
-# 카카오 테크 캠퍼스 - 프론트엔드 카카오 선물하기 편
+# 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] 회원가입 로직 구현: 회원가입 성공 시 로그인 페이지로 이동 및 성공 메시지 표시
+  
+- 상품 상세 페이지
+  - [x] 위시 등록 버튼 UI 구현
+  - [x] 위시 등록 버튼 로직 구현: 위시 등록 성공 시 "위시 등록 완료" Alert 메시지 표시
+
+- 마이 페이지
+  - [x] 위시 목록 리스트 UI 구현: Chakra UI 컴포넌트 활용
+  - [x] 위시 목록 API 활용: 선물하기 API 노션의 response 데이터 활용
+  - [x] 위시 목록 리스트 로직 구현: 위시 삭제 성공 시 해당 항목 리스트에서 제거
+
+---
+
+## 3단계 - 질문의 답변을 README에 작성
+
+- **질문 1**: Test code를 작성해보면서 좋았던 점과 아쉬웠던 점에 대해 말해주세요.
+- 테스트 코드 작성은 처음에는 막막한 과정이었다. 하지만 404 에러, 모듈을 찾을 수 없는 에러 등 예상치 못한 문제를 파악할 수 있었다. 생성형 AI와 문서를 참고하여 문제를 해결했지만, 스스로 해결했다기보다는 따라 했다는 느낌이 강했다. 테스트 결과 또한 방대하고 복잡하여 해석하기 어려웠다. 이러한 경험은 아쉬움과 답답함을 남겼다. 그러나 이번 경험을 통해 Jest 도구와 기술을 접할 수 있었다는 점은 긍정적이었다. 문제를 파악할 수 있었지만, 아직 스스로 부족하여 충분히 활용하지 못한 점이 아쉬웠다.
+
+- **질문 2**: 스스로 생각했을 때 좋은 컴포넌트란 무엇인지 본인만의 기준을 세우고 설명해 주세요.
+- 먼저 `재사용성`을 꼽을 수 있다. 컴포넌트를 재사용할 수 있도록 분리하는 것이 좋다고 생각한다. 카테캠을 하면서 스토리북을 알게 되었는데, 이를 통해 다양한 프로젝트에서 동일한 컴포넌트를 옮겨서 사용할 수 있었고, 통일된 기능과 UI를 구현할 수 있었다. 처음에는 생소하게 느껴졌지만, 한번 알고 나니 다른 문서를 봐도 이해하고 사용할 수 있었다.
+- 또한, 나는 아직 함수의 흐름을 이해하는 데 부족하다고 생각해서, 내가 이해할 수 있는 범위의 작은 단위로 쪼개서 구현하는 것을 목표로 하고 있다. 이 점에 대해서는 너무 잘게 쪼갰다는 피드백을 받기도 했지만, 너무 광범위한 역할을 가진 컴포넌트와 스스로 이해할 수 있는 기능을 가진 컴포넌트 사이를 절충해야 한다고 본다. 따라서, 좋은 컴포넌트는 `가독성`이 좋아야 한다고 생각한다. 이를 위해 페이지와 각 페이지의 기능별로 나누고, API와 타입을 기능별로 폴더를 구분하는 것이 좋다고 생각한다.
+
+- **질문 3**: 스스로 생각했을 때 공통 컴포넌트를 만들 때 가장 중요한 요소 2개를 선택하고 이유와 함께 설명해주세요.
+- 위 질문에 어느 정도 답변을 작성한 것 같다. `가독성`과 `재사용성`이다. 가독성은 다른 개발자나 내가 다시 사용할 때 쉽게 이해할 수 있도록 해준다. 재사용성을 위해서는 다른 개발자나 내가 다시 사용해도 편리하도록 가독성이 좋아야 한다. 또한, 재사용성을 확보하기 위해서는 전체 프로젝트의 구조를 파악하고 필요한 기능들을 정리하여 구현해야 한다.
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index ed0d135c..5cc2c59a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,15 +12,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",
@@ -34,8 +34,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",
@@ -11144,20 +11146,22 @@
       "dev": true
     },
     "node_modules/@tanstack/query-core": {
-      "version": "5.24.1",
-      "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.24.1.tgz",
-      "integrity": "sha512-DZ6Nx9p7BhjkG50ayJ+MKPgff+lMeol7QYXkvuU5jr2ryW/4ok5eanaS9W5eooA4xN0A/GPHdLGOZGzArgf5Cg==",
+      "version": "5.51.9",
+      "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.51.9.tgz",
+      "integrity": "sha512-HsAwaY5J19MD18ykZDS3aVVh+bAt0i7m6uQlFC2b77DLV9djo+xEN7MWQAQQTR8IM+7r/zbozTQ7P0xr0bHuew==",
+      "dev": true,
       "funding": {
         "type": "github",
         "url": "https://github.com/sponsors/tannerlinsley"
       }
     },
     "node_modules/@tanstack/react-query": {
-      "version": "5.24.1",
-      "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.24.1.tgz",
-      "integrity": "sha512-4+09JEdO4d6+Gc8Y/g2M/MuxDK5IY0QV8+2wL2304wPKJgJ54cBbULd3nciJ5uvh/as8rrxx6s0mtIwpRuGd1g==",
+      "version": "5.51.11",
+      "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.51.11.tgz",
+      "integrity": "sha512-4Kq2x0XpDlpvSnaLG+8pHNH60zEc3mBvb3B2tOMDjcPCi/o+Du3p/9qpPLwJOTliVxxPJAP27fuIhLrsRdCr7A==",
+      "dev": true,
       "dependencies": {
-        "@tanstack/query-core": "5.24.1"
+        "@tanstack/query-core": "5.51.9"
       },
       "funding": {
         "type": "github",
@@ -11327,6 +11331,52 @@
         "react-dom": "^18.0.0"
       }
     },
+    "node_modules/@testing-library/react-hooks": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz",
+      "integrity": "sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==",
+      "dev": true,
+      "dependencies": {
+        "@babel/runtime": "^7.12.5",
+        "react-error-boundary": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "peerDependencies": {
+        "@types/react": "^16.9.0 || ^17.0.0",
+        "react": "^16.9.0 || ^17.0.0",
+        "react-dom": "^16.9.0 || ^17.0.0",
+        "react-test-renderer": "^16.9.0 || ^17.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "react-dom": {
+          "optional": true
+        },
+        "react-test-renderer": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@testing-library/react-hooks/node_modules/react-error-boundary": {
+      "version": "3.1.4",
+      "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
+      "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/runtime": "^7.12.5"
+      },
+      "engines": {
+        "node": ">=10",
+        "npm": ">=6"
+      },
+      "peerDependencies": {
+        "react": ">=16.13.1"
+      }
+    },
     "node_modules/@testing-library/react/node_modules/@testing-library/dom": {
       "version": "8.20.1",
       "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz",
@@ -13423,11 +13473,11 @@
       }
     },
     "node_modules/axios": {
-      "version": "1.6.7",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
-      "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
+      "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
       "dependencies": {
-        "follow-redirects": "^1.15.4",
+        "follow-redirects": "^1.15.6",
         "form-data": "^4.0.0",
         "proxy-from-env": "^1.1.0"
       }
@@ -19881,9 +19931,9 @@
       }
     },
     "node_modules/follow-redirects": {
-      "version": "1.15.5",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
-      "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
+      "version": "1.15.6",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+      "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
       "funding": [
         {
           "type": "individual",
@@ -35485,6 +35535,14 @@
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
+    },
+    "node_modules/zod": {
+      "version": "3.23.8",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
+      "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
+      "funding": {
+        "url": "https://github.com/sponsors/colinhacks"
+      }
     }
   }
 }
diff --git a/package.json b/package.json
index 1df1cf87..b9cde0af 100644
--- a/package.json
+++ b/package.json
@@ -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": [
@@ -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",
@@ -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",
diff --git a/src/App.tsx b/src/App.tsx
index 24715e67..8f8a2205 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -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 */}
           <Routes />
         </AuthProvider>
       </QueryClientProvider>
diff --git a/src/api/hooks/useGetCategorys.test.tsx b/src/api/hooks/useGetCategorys.test.tsx
new file mode 100644
index 00000000..fbbfe7c1
--- /dev/null
+++ b/src/api/hooks/useGetCategorys.test.tsx
@@ -0,0 +1,19 @@
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { renderHook, waitFor } from '@testing-library/react';
+
+import { CATEGORIES_RESPONSE_DATA } from '../mocks/categories.mock';
+import { useGetCategories } from './useGetCategorys';
+describe('useGetCategories', () => {
+  it('should fetch categories correctly', async () => {
+    const queryClient = new QueryClient();
+
+    const wrapper = ({ children }: { children: React.ReactNode }) => (
+      <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
+    );
+    const { result } = renderHook(() => useGetCategories(), { wrapper });
+
+    await waitFor(() => expect(result.current.isSuccess).toBe(true));
+
+    expect(result.current.data).toEqual(CATEGORIES_RESPONSE_DATA);
+  });
+});
diff --git a/src/api/hooks/useGetCategorys.ts b/src/api/hooks/useGetCategorys.ts
index d93e4fc9..1fb8f690 100644
--- a/src/api/hooks/useGetCategorys.ts
+++ b/src/api/hooks/useGetCategorys.ts
@@ -2,18 +2,21 @@ import { useQuery } from '@tanstack/react-query';
 
 import type { CategoryData } from '@/types';
 
-import { BASE_URL, fetchInstance } from '../instance';
+import { CATEGORIES_RESPONSE_DATA, getCategoriesPath } from '../mocks/categories.mock';
 
+// 모킹 데이터 타입 정의
 export type CategoryResponseData = CategoryData[];
 
-export const getCategoriesPath = () => `${BASE_URL}/api/categories`;
+// React Query에서 사용할 쿼리 키
 const categoriesQueryKey = [getCategoriesPath()];
 
+// 모킹 데이터를 반환하는 함수
 export const getCategories = async () => {
-  const response = await fetchInstance.get<CategoryResponseData>(getCategoriesPath());
-  return response.data;
+  // 실제 API 호출 대신 모킹 데이터를 반환합니다.
+  return CATEGORIES_RESPONSE_DATA;
 };
 
+// useGetCategories 훅
 export const useGetCategories = () =>
   useQuery({
     queryKey: categoriesQueryKey,
diff --git a/src/api/hooks/useGetLogin.test.tsx b/src/api/hooks/useGetLogin.test.tsx
new file mode 100644
index 00000000..83014650
--- /dev/null
+++ b/src/api/hooks/useGetLogin.test.tsx
@@ -0,0 +1,39 @@
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { act, renderHook } from '@testing-library/react-hooks';
+import type { ReactNode } from 'react'; // ReactNode 타입 import
+
+import { useLogin } from './useGetLogin';
+
+// QueryClient 생성
+const queryClient = new QueryClient();
+
+// QueryClientProvider로 useLogin 훅을 감싼 Wrapper 컴포넌트 생성
+const wrapper = ({ children }: { children: ReactNode }) => (
+  <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
+);
+
+describe('useLogin Hook', () => {
+  beforeEach(() => {
+    global.fetch = jest.fn(() =>
+      Promise.resolve({
+        ok: true,
+        json: () => Promise.resolve({ email: 'test@example.com', token: 'mockToken' }),
+      }),
+    ) as jest.Mock;
+  });
+  it('should successfully login with valid credentials', async () => {
+    const { result, waitForNextUpdate } = renderHook(() => useLogin(), { wrapper });
+
+    // 로그인 시도
+    act(() => {
+      result.current.mutate({ email: 'test@example.com', password: 'password123' });
+    });
+
+    // useMutation의 상태가 'success'로 바뀔 때까지 기다림
+    await waitForNextUpdate();
+
+    // 결과 확인
+    expect(result.current.isSuccess).toBe(true);
+    expect(result.current.data).toEqual({ email: 'test@example.com', token: 'mockToken' });
+  });
+});
diff --git a/src/api/hooks/useGetLogin.ts b/src/api/hooks/useGetLogin.ts
new file mode 100644
index 00000000..5378c276
--- /dev/null
+++ b/src/api/hooks/useGetLogin.ts
@@ -0,0 +1,42 @@
+import type { UseMutationOptions } from '@tanstack/react-query';
+import { useMutation } from '@tanstack/react-query';
+
+type LoginRequestBody = {
+  email: string;
+  password: string;
+};
+
+type LoginSuccessResponse = {
+  email: string;
+  token: string;
+};
+
+const BASE_URL = 'http://localhost:3000';
+
+const login = async (loginData: LoginRequestBody): Promise<LoginSuccessResponse> => {
+  const response = await fetch(`${BASE_URL}/api/members/login`, {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+    },
+    body: JSON.stringify(loginData),
+  });
+
+  if (!response.ok) {
+    if (response.status === 403) {
+      throw new Error('Invalid email or password');
+    }
+    throw new Error('An error occurred during login');
+  }
+
+  return response.json();
+};
+
+export const useLogin = (
+  options?: UseMutationOptions<LoginSuccessResponse, Error, LoginRequestBody>,
+) => {
+  return useMutation<LoginSuccessResponse, Error, LoginRequestBody>({
+    mutationFn: login,
+    ...options,
+  });
+};
diff --git a/src/api/hooks/useGetProductDetail.ts b/src/api/hooks/useGetProductDetail.ts
index 539de019..a52b8518 100644
--- a/src/api/hooks/useGetProductDetail.ts
+++ b/src/api/hooks/useGetProductDetail.ts
@@ -1,30 +1,43 @@
-import { useSuspenseQuery } from '@tanstack/react-query';
+import type { UseQueryOptions } from '@tanstack/react-query';
+import { useQuery } from '@tanstack/react-query';
 
-import type { ProductData } from '@/types';
+const BASE_URL = 'http://localhost:3000';
 
-import { BASE_URL, fetchInstance } from '../instance';
+export interface ProductDetail {
+  id: number;
+  name: string;
+  price: number;
+  imageUrl: string;
+  categoryId: number;
+}
 
-export type ProductDetailRequestParams = {
-  productId: string;
-};
-
-type Props = ProductDetailRequestParams;
-
-export type GoodsDetailResponseData = ProductData;
+export interface ProductDetailRequestParams {
+  productId: number;
+}
 
-export const getProductDetailPath = (productId: string) => `${BASE_URL}/api/products/${productId}`;
+const fetchProductDetail = async ({
+  productId,
+}: ProductDetailRequestParams): Promise<ProductDetail> => {
+  const response = await fetch(`${BASE_URL}/api/products/${productId}`);
 
-export const getProductDetail = async (params: ProductDetailRequestParams) => {
-  const response = await fetchInstance.get<GoodsDetailResponseData>(
-    getProductDetailPath(params.productId),
-  );
+  if (!response.ok) {
+    throw new Error('Failed to fetch product detail');
+  }
 
-  return response.data;
+  return response.json();
 };
 
-export const useGetProductDetail = ({ productId }: Props) => {
-  return useSuspenseQuery({
-    queryKey: [getProductDetailPath(productId)],
-    queryFn: () => getProductDetail({ productId }),
+export const useGetProductDetail = (
+  { productId }: ProductDetailRequestParams,
+  options?: Omit<
+    UseQueryOptions<ProductDetail, Error, ProductDetail, [string, number]>,
+    'queryKey' | 'queryFn'
+  >,
+) => {
+  return useQuery({
+    queryKey: ['productDetail', productId],
+    queryFn: () => fetchProductDetail({ productId }),
+    staleTime: 5 * 60 * 1000, // 5 minutes
+    ...options,
   });
 };
diff --git a/src/api/hooks/useGetProductOptions.ts b/src/api/hooks/useGetProductOptions.ts
index a3bdc538..11a3dbe2 100644
--- a/src/api/hooks/useGetProductOptions.ts
+++ b/src/api/hooks/useGetProductOptions.ts
@@ -1,27 +1,44 @@
-import { useSuspenseQuery } from '@tanstack/react-query';
+import type { UseQueryOptions } from '@tanstack/react-query';
+import { useQuery } from '@tanstack/react-query';
 
-import type { ProductOptionsData } from '@/types';
+const BASE_URL = 'http://localhost:3000';
 
-import { BASE_URL, fetchInstance } from '../instance';
-import type { ProductDetailRequestParams } from './useGetProductDetail';
+export interface ProductOption {
+  id: number;
+  name: string;
+  quantity: number;
+  productId: number;
+}
 
-type Props = ProductDetailRequestParams;
+export interface ProductOptionsRequestParams {
+  productId: number;
+}
 
-export type ProductOptionsResponseData = ProductOptionsData[];
+const fetchProductOptions = async ({
+  productId,
+}: ProductOptionsRequestParams): Promise<ProductOption[]> => {
+  const response = await fetch(`${BASE_URL}/api/products/${productId}/options`);
 
-export const getProductOptionsPath = (productId: string) =>
-  `${BASE_URL}/api/products/${productId}/options`;
+  if (!response.ok) {
+    if (response.status === 404) {
+      throw new Error('Product not found');
+    }
+    throw new Error('Failed to fetch product options');
+  }
 
-export const getProductOptions = async (params: ProductDetailRequestParams) => {
-  const response = await fetchInstance.get<ProductOptionsResponseData>(
-    getProductOptionsPath(params.productId),
-  );
-  return response.data;
+  return response.json();
 };
 
-export const useGetProductOptions = ({ productId }: Props) => {
-  return useSuspenseQuery({
-    queryKey: [getProductOptionsPath(productId)],
-    queryFn: () => getProductOptions({ productId }),
+export const useGetProductOptions = (
+  { productId }: ProductOptionsRequestParams,
+  options?: Omit<
+    UseQueryOptions<ProductOption[], Error, ProductOption[], [string, number]>,
+    'queryKey' | 'queryFn'
+  >,
+) => {
+  return useQuery({
+    queryKey: ['productOptions', productId],
+    queryFn: () => fetchProductOptions({ productId }),
+    ...options,
   });
 };
diff --git a/src/api/hooks/useGetProducts.test.tsx b/src/api/hooks/useGetProducts.test.tsx
new file mode 100644
index 00000000..0c4b7b5c
--- /dev/null
+++ b/src/api/hooks/useGetProducts.test.tsx
@@ -0,0 +1,58 @@
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { renderHook, waitFor } from '@testing-library/react';
+import React from 'react';
+
+import { PRODUCTS_MOCK_DATA } from '../mocks/products.mock';
+import { useGetProducts } from './useGetProducts';
+
+describe('useGetProducts', () => {
+  it('should fetch products correctly', async () => {
+    const queryClient = new QueryClient();
+
+    const wrapper = ({ children }: { children: React.ReactNode }) => (
+      <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
+    );
+
+    const { result } = renderHook(() => useGetProducts({ categoryId: '1', maxResults: 20 }), {
+      wrapper,
+    });
+
+    await waitFor(() => expect(result.current.isSuccess).toBe(true));
+
+    expect(result.current.data?.pages[0]).toEqual({
+      products: PRODUCTS_MOCK_DATA.content,
+      nextPageToken:
+        PRODUCTS_MOCK_DATA.last === false ? (PRODUCTS_MOCK_DATA.number + 1).toString() : undefined,
+      pageInfo: {
+        totalResults: PRODUCTS_MOCK_DATA.totalElements,
+        resultsPerPage: PRODUCTS_MOCK_DATA.size,
+      },
+    });
+  });
+
+  it('should handle pagination correctly', async () => {
+    const queryClient = new QueryClient();
+
+    const wrapper = ({ children }: { children: React.ReactNode }) => (
+      <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
+    );
+
+    const { result } = renderHook(() => useGetProducts({ categoryId: '1', maxResults: 20 }), {
+      wrapper,
+    });
+
+    await waitFor(() => expect(result.current.isSuccess).toBe(true));
+
+    // Fetch next page
+    if (result.current.hasNextPage) {
+      await result.current.fetchNextPage();
+
+      await waitFor(() => expect(result.current.data?.pages.length).toBe(2));
+    }
+
+    // Check if the nextPageToken is handled correctly
+    expect(result.current.data?.pages[1]?.nextPageToken).toBe(
+      PRODUCTS_MOCK_DATA.last === false ? (PRODUCTS_MOCK_DATA.number + 2).toString() : undefined,
+    );
+  });
+});
diff --git a/src/api/hooks/useGetProducts.ts b/src/api/hooks/useGetProducts.ts
index 432f90d9..e2bc24a4 100644
--- a/src/api/hooks/useGetProducts.ts
+++ b/src/api/hooks/useGetProducts.ts
@@ -6,8 +6,7 @@ import {
 
 import type { ProductData } from '@/types';
 
-import { BASE_URL } from '../instance';
-import { fetchInstance } from './../instance/index';
+import { PRODUCTS_MOCK_DATA } from '../mocks/products.mock';
 
 type RequestParams = {
   categoryId: string;
@@ -24,28 +23,9 @@ type ProductsResponseData = {
   };
 };
 
-type ProductsResponseRawData = {
-  content: ProductData[];
-  number: number;
-  totalElements: number;
-  size: number;
-  last: boolean;
-};
-
-export const getProductsPath = ({ categoryId, pageToken, maxResults }: RequestParams) => {
-  const params = new URLSearchParams();
-
-  params.append('categoryId', categoryId);
-  params.append('sort', 'name,asc');
-  if (pageToken) params.append('page', pageToken);
-  if (maxResults) params.append('size', maxResults.toString());
-
-  return `${BASE_URL}/api/products?${params.toString()}`;
-};
-
-export const getProducts = async (params: RequestParams): Promise<ProductsResponseData> => {
-  const response = await fetchInstance.get<ProductsResponseRawData>(getProductsPath(params));
-  const data = response.data;
+// 모킹 데이터를 반환하는 함수
+export const getProducts = async (): Promise<ProductsResponseData> => {
+  const data = PRODUCTS_MOCK_DATA;
 
   return {
     products: data.content,
@@ -65,8 +45,8 @@ export const useGetProducts = ({
 }: Params): UseInfiniteQueryResult<InfiniteData<ProductsResponseData>> => {
   return useInfiniteQuery({
     queryKey: ['products', categoryId, maxResults, initPageToken],
-    queryFn: async ({ pageParam = initPageToken }) => {
-      return getProducts({ categoryId, pageToken: pageParam, maxResults });
+    queryFn: async () => {
+      return getProducts();
     },
     initialPageParam: initPageToken,
     getNextPageParam: (lastPage) => lastPage.nextPageToken,
diff --git a/src/api/hooks/useGetRegister.ts b/src/api/hooks/useGetRegister.ts
new file mode 100644
index 00000000..a8163eda
--- /dev/null
+++ b/src/api/hooks/useGetRegister.ts
@@ -0,0 +1,40 @@
+import type { UseMutationOptions } from '@tanstack/react-query';
+import { useMutation } from '@tanstack/react-query';
+
+const BASE_URL = 'http://localhost:3000';
+
+interface RegisterRequestBody {
+  email: string;
+  password: string;
+}
+
+interface RegisterSuccessResponse {
+  email: string;
+  token: string;
+}
+
+const register = async (registerData: RegisterRequestBody): Promise<RegisterSuccessResponse> => {
+  const response = await fetch(`${BASE_URL}/api/members/register`, {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+    },
+    body: JSON.stringify(registerData),
+  });
+
+  if (!response.ok) {
+    const errorData = await response.json(); // Get error details from the response
+    throw new Error(errorData.message || 'Registration failed'); // Throw a specific error
+  }
+
+  return response.json();
+};
+
+export const useGetRegister = (
+  options?: UseMutationOptions<RegisterSuccessResponse, Error, RegisterRequestBody>,
+) => {
+  return useMutation<RegisterSuccessResponse, Error, RegisterRequestBody>({
+    mutationFn: register,
+    ...options,
+  });
+};
diff --git a/src/api/hooks/useGetWishList.ts b/src/api/hooks/useGetWishList.ts
new file mode 100644
index 00000000..3dd2e470
--- /dev/null
+++ b/src/api/hooks/useGetWishList.ts
@@ -0,0 +1,55 @@
+import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
+import axios from 'axios';
+
+const API_URL = 'http://localhost:3000/api';
+
+export interface WishItem {
+  id: number;
+  product: {
+    id: number;
+    name: string;
+    price: number;
+    imageUrl: string;
+  };
+}
+
+interface WishListResponse {
+  content: WishItem[];
+  totalPages: number;
+  totalElements: number;
+}
+
+export const useWishList = (page: number = 0, size: number = 10) => {
+  const fetchWishList = async (): Promise<WishListResponse> => {
+    const response = await axios.get(
+      `${API_URL}/wishes?page=${page}&size=${size}&sort=createdDate,desc`,
+      {
+        headers: {
+          Authorization: `Bearer ${localStorage.getItem('token')}`,
+        },
+      },
+    );
+    return response.data;
+  };
+
+  return useQuery<WishListResponse, Error>({
+    queryKey: ['wishList', page, size],
+    queryFn: fetchWishList,
+  });
+};
+
+export const useRemoveWish = () => {
+  const queryClient = useQueryClient();
+
+  return useMutation({
+    mutationFn: (wishId: number) =>
+      axios.delete(`${API_URL}/wishes/${wishId}`, {
+        headers: {
+          Authorization: `Bearer ${localStorage.getItem('token')}`,
+        },
+      }),
+    onSuccess: () => {
+      queryClient.invalidateQueries({ queryKey: ['wishList'] });
+    },
+  });
+};
diff --git a/src/api/hooks/categories.mock.ts b/src/api/mocks/categories.mock.ts
similarity index 79%
rename from src/api/hooks/categories.mock.ts
rename to src/api/mocks/categories.mock.ts
index f8d65843..5fdf90be 100644
--- a/src/api/hooks/categories.mock.ts
+++ b/src/api/mocks/categories.mock.ts
@@ -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: '생일',
@@ -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));
+  }),
+];
diff --git a/src/api/mocks/login.mock.ts b/src/api/mocks/login.mock.ts
new file mode 100644
index 00000000..08e3af4e
--- /dev/null
+++ b/src/api/mocks/login.mock.ts
@@ -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));
+  }),
+];
diff --git a/src/api/mocks/productDetail.mock.ts b/src/api/mocks/productDetail.mock.ts
new file mode 100644
index 00000000..fc4fade5
--- /dev/null
+++ b/src/api/mocks/productDetail.mock.ts
@@ -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' }));
+    }
+  }),
+];
diff --git a/src/api/mocks/productOptions.mock.ts b/src/api/mocks/productOptions.mock.ts
new file mode 100644
index 00000000..41326609
--- /dev/null
+++ b/src/api/mocks/productOptions.mock.ts
@@ -0,0 +1,30 @@
+import { rest } from 'msw';
+
+const BASE_URL = 'http://localhost:3000';
+
+interface ProductOption {
+  id: number;
+  name: string;
+  quantity: number;
+  productId: number;
+}
+
+// 동적으로 옵션을 생성하는 함수
+const generateOptions = (productId: number): ProductOption[] => {
+  const optionCount = Math.floor(Math.random() * 3) + 1; // 1에서 3개의 옵션 생성
+  return Array.from({ length: optionCount }, (_, index) => ({
+    id: index + 1,
+    name: `Option ${String.fromCharCode(65 + index)}`, // A, B, C...
+    quantity: Math.floor(Math.random() * 50) + 10, // 10에서 59 사이의 수량
+    productId: productId,
+  }));
+};
+
+export const productOptionsMockHandler = [
+  rest.get(`${BASE_URL}/api/products/:productId/options`, (req, res, ctx) => {
+    const { productId } = req.params;
+    const options = generateOptions(Number(productId));
+
+    return res(ctx.status(200), ctx.json(options));
+  }),
+];
diff --git a/src/api/hooks/products.mock.ts b/src/api/mocks/products.mock.ts
similarity index 59%
rename from src/api/hooks/products.mock.ts
rename to src/api/mocks/products.mock.ts
index 6cef1123..f9c2b910 100644
--- a/src/api/hooks/products.mock.ts
+++ b/src/api/mocks/products.mock.ts
@@ -1,50 +1,14 @@
 import { rest } from 'msw';
 
-import { getProductDetailPath } from './useGetProductDetail';
-import { getProductOptionsPath } from './useGetProductOptions';
-import { getProductsPath } from './useGetProducts';
+const BASE_URL = 'http://localhost:3000';
 
-export const productsMockHandler = [
-  rest.get(
-    getProductsPath({
-      categoryId: '2920',
-    }),
-    (_, res, ctx) => {
-      return res(ctx.json(PRODUCTS_MOCK_DATA));
-    },
-  ),
-  rest.get(
-    getProductsPath({
-      categoryId: '2930',
-    }),
-    (_, res, ctx) => {
-      return res(ctx.json(PRODUCTS_MOCK_DATA));
-    },
-  ),
-  rest.get(getProductDetailPath(':productId'), (_, res, ctx) => {
-    return res(ctx.json(PRODUCTS_MOCK_DATA.content[0]));
-  }),
-  rest.get(getProductOptionsPath(':productId'), (_, res, ctx) => {
-    return res(
-      ctx.json([
-        {
-          id: 1,
-          name: 'Option A',
-          quantity: 10,
-          productId: 1,
-        },
-        {
-          id: 2,
-          name: 'Option B',
-          quantity: 20,
-          productId: 1,
-        },
-      ]),
-    );
-  }),
-];
+type RequestParams = {
+  categoryId: string;
+  pageToken?: string;
+  maxResults?: number;
+};
 
-const PRODUCTS_MOCK_DATA = {
+export const PRODUCTS_MOCK_DATA = {
   content: [
     {
       id: 3245119,
@@ -52,6 +16,7 @@ const PRODUCTS_MOCK_DATA = {
       imageUrl:
         'https://st.kakaocdn.net/product/gift/product/20240215083306_8e1db057580145829542463a84971ae3.png',
       price: 145000,
+      categoryId: 2920,
     },
     {
       id: 2263833,
@@ -59,6 +24,7 @@ const PRODUCTS_MOCK_DATA = {
       imageUrl:
         'https://st.kakaocdn.net/product/gift/product/20200513102805_4867c1e4a7ae43b5825e9ae14e2830e3.png',
       price: 100000,
+      categoryId: 2930,
     },
     {
       id: 6502823,
@@ -66,6 +32,7 @@ const PRODUCTS_MOCK_DATA = {
       imageUrl:
         'https://st.kakaocdn.net/product/gift/product/20240215112140_11f857e972bc4de6ac1d2f1af47ce182.jpg',
       price: 108000,
+      categoryId: 2930,
     },
     {
       id: 1181831,
@@ -73,6 +40,7 @@ const PRODUCTS_MOCK_DATA = {
       imageUrl:
         'https://st.kakaocdn.net/product/gift/product/20240214150740_ad25267defa64912a7c030a7b57dc090.jpg',
       price: 122000,
+      categoryId: 2920,
     },
     {
       id: 1379982,
@@ -80,6 +48,7 @@ const PRODUCTS_MOCK_DATA = {
       imageUrl:
         'https://st.kakaocdn.net/product/gift/product/20240118135914_a6e1a7442ea04aa49add5e02ed62b4c3.jpg',
       price: 133000,
+      categoryId: 2920,
     },
   ],
   number: 0,
@@ -87,3 +56,25 @@ const PRODUCTS_MOCK_DATA = {
   size: 10,
   last: true,
 };
+
+// 카테고리별 제품 목록 API 경로
+export const getProductsPath = ({ categoryId, pageToken, maxResults }: RequestParams) => {
+  const params = new URLSearchParams();
+
+  params.append('categoryId', categoryId);
+  params.append('sort', 'name,asc');
+  if (pageToken) params.append('page', pageToken);
+  if (maxResults) params.append('size', maxResults.toString());
+
+  return `${BASE_URL}/api/products?${params.toString()}`;
+};
+
+// MSW 핸들러 정의
+export const productsMockHandler = [
+  rest.get(getProductsPath({ categoryId: '2920' }), (_, res, ctx) => {
+    return res(ctx.json(PRODUCTS_MOCK_DATA));
+  }),
+  rest.get(getProductsPath({ categoryId: '2930' }), (_, res, ctx) => {
+    return res(ctx.json(PRODUCTS_MOCK_DATA));
+  }),
+];
diff --git a/src/api/mocks/register.mock.ts b/src/api/mocks/register.mock.ts
new file mode 100644
index 00000000..a694a566
--- /dev/null
+++ b/src/api/mocks/register.mock.ts
@@ -0,0 +1,31 @@
+import { rest } from 'msw';
+
+const BASE_URL = 'http://localhost:3000';
+
+interface RegisterRequestBody {
+  email: string;
+  password: string;
+}
+
+interface RegisterSuccessResponse {
+  email: string;
+  token: string;
+}
+
+export const registerMockHandler = [
+  rest.post<RegisterRequestBody>(`${BASE_URL}/api/members/register`, (req, res, ctx) => {
+    const { email, password } = req.body;
+
+    // Basic validation (add more robust validation as needed)
+    if (!email || !password) {
+      return res(ctx.status(400), ctx.json({ message: 'Invalid input' }));
+    }
+
+    // Simulate successful registration
+    const response: RegisterSuccessResponse = {
+      email,
+      token: 'mocked-registration-token',
+    };
+    return res(ctx.status(201), ctx.json(response));
+  }),
+];
diff --git a/src/api/mocks/wishlist.mock.ts b/src/api/mocks/wishlist.mock.ts
new file mode 100644
index 00000000..2318aa75
--- /dev/null
+++ b/src/api/mocks/wishlist.mock.ts
@@ -0,0 +1,110 @@
+import { rest } from 'msw';
+
+const BASE_URL = 'http://localhost:3000/api';
+
+// 모의 데이터
+const generateRandomName = (): string => {
+  const products = [
+    'Product A',
+    'Product B',
+    'Product C',
+    'Product D',
+    'Product E',
+    'Product F',
+    'Product G',
+    'Product H',
+    'Product I',
+    'Product J',
+  ];
+  return products[Math.floor(Math.random() * products.length)];
+};
+
+const generateRandomPrice = (): number => {
+  return Math.floor(Math.random() * 10000) + 1;
+};
+
+const generateRandomImageUrl = (): string => {
+  return 'https://www.jungle.co.kr/image/ea06cd0346fa8777cb624e3f'; // Placeholder image URL
+};
+
+let wishlist = Array.from({ length: 30 }, (_, id) => ({
+  id: id + 1,
+  product: {
+    id: id + 1,
+    name: generateRandomName(),
+    price: generateRandomPrice(),
+    imageUrl: generateRandomImageUrl(),
+  },
+}));
+export const wishlistMockHandlers = [
+  // 위시리스트 상품 추가
+  rest.post(`${BASE_URL}/wishes`, (req, res, ctx) => {
+    const { productId } = req.body as { productId: number };
+    const newWish = {
+      id: wishlist.length + 1,
+      product: {
+        id: productId,
+        name: `Product ${productId}`,
+        price: Math.floor(Math.random() * 1000),
+        imageUrl: `http://example.com/product-${productId}.jpg`,
+      },
+    };
+    wishlist.push(newWish);
+    return res(ctx.status(201), ctx.json({ id: newWish.id, productId }));
+  }),
+
+  // 위시리스트 상품 삭제
+  rest.delete(`${BASE_URL}/wishes/:wishId`, (req, res, ctx) => {
+    const { wishId } = req.params;
+    wishlist = wishlist.filter((wish) => wish.id !== Number(wishId));
+    return res(ctx.status(204));
+  }),
+
+  // 위시리스트 상품 조회 (페이지네이션 적용)
+  rest.get(`${BASE_URL}/wishes`, (req, res, ctx) => {
+    const page = Number(req.url.searchParams.get('page') || '0');
+    const size = Number(req.url.searchParams.get('size') || '10');
+    const sortParam = req.url.searchParams.get('sort') || 'createdDate,desc';
+
+    const startIndex = page * size;
+    const endIndex = startIndex + size;
+
+    // 정렬 로직 구현 (여기서는 간단히 id로 정렬)
+    const sortedWishlist = [...wishlist].sort((a, b) => {
+      const [field, order] = sortParam.split(',');
+      if (field === 'createdDate') {
+        return order === 'desc' ? b.id - a.id : a.id - b.id;
+      }
+      return 0;
+    });
+
+    const paginatedWishes = sortedWishlist.slice(startIndex, endIndex);
+
+    return res(
+      ctx.status(200),
+      ctx.json({
+        content: paginatedWishes,
+        pageable: {
+          sort: {
+            sorted: true,
+            unsorted: false,
+            empty: false,
+          },
+          pageNumber: page,
+          pageSize: size,
+          offset: startIndex,
+          unpaged: false,
+          paged: true,
+        },
+        totalPages: Math.ceil(wishlist.length / size),
+        totalElements: wishlist.length,
+        last: endIndex >= wishlist.length,
+        number: page,
+        size: size,
+        numberOfElements: paginatedWishes.length,
+        first: page === 0,
+        empty: paginatedWishes.length === 0,
+      }),
+    );
+  }),
+];
diff --git a/src/components/common/Icons/HeartIcon.tsx b/src/components/common/Icons/HeartIcon.tsx
new file mode 100644
index 00000000..3a25a97f
--- /dev/null
+++ b/src/components/common/Icons/HeartIcon.tsx
@@ -0,0 +1,30 @@
+import React from 'react';
+
+interface HeartIconProps {
+  fill?: string;
+  stroke?: string;
+  width?: number;
+  height?: number;
+}
+
+export const HeartIcon: React.FC<HeartIconProps> = ({
+  fill = 'none',
+  stroke = 'currentColor',
+  width = 24,
+  height = 24,
+}) => {
+  return (
+    <svg
+      width={width}
+      height={height}
+      viewBox="0 0 24 24"
+      fill={fill}
+      stroke={stroke}
+      strokeWidth="2"
+      strokeLinecap="round"
+      strokeLinejoin="round"
+    >
+      <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
+    </svg>
+  );
+};
diff --git a/src/components/features/Goods/Detail/Header.tsx b/src/components/features/Goods/Detail/Header.tsx
index 1888f597..6a849e46 100644
--- a/src/components/features/Goods/Detail/Header.tsx
+++ b/src/components/features/Goods/Detail/Header.tsx
@@ -8,7 +8,19 @@ import { breakpoints } from '@/styles/variants';
 type Props = ProductDetailRequestParams;
 
 export const GoodsDetailHeader = ({ productId }: Props) => {
-  const { data: detail } = useGetProductDetail({ productId });
+  const { data: detail, isLoading, error } = useGetProductDetail({ productId });
+
+  if (isLoading) {
+    return <div>Loading...</div>;
+  }
+
+  if (error) {
+    return <div>Error: {error.message}</div>;
+  }
+
+  if (!detail) {
+    return <div>No data available</div>;
+  }
 
   return (
     <Wrapper>
diff --git a/src/components/features/Goods/Detail/OptionSection.tsx b/src/components/features/Goods/Detail/OptionSection.tsx
index 7951a26c..8c77f770 100644
--- a/src/components/features/Goods/Detail/OptionSection.tsx
+++ b/src/components/features/Goods/Detail/OptionSection.tsx
@@ -8,6 +8,7 @@ import {
 } from '@/api/hooks/useGetProductDetail';
 import { useGetProductOptions } from '@/api/hooks/useGetProductOptions';
 import { Button } from '@/components/common/Button';
+import { HeartIcon } from '@/components/common/Icons/HeartIcon';
 import { useAuth } from '@/provider/Auth';
 import { getDynamicPath, RouterPath } from '@/routes/path';
 import { orderHistorySessionStorage } from '@/utils/storage';
@@ -17,16 +18,24 @@ import { CountOptionItem } from './OptionItem/CountOptionItem';
 type Props = ProductDetailRequestParams;
 
 export const OptionSection = ({ productId }: Props) => {
-  const { data: detail } = useGetProductDetail({ productId });
-  const { data: options } = useGetProductOptions({ productId });
+  const { data: detail, error: detailError } = useGetProductDetail({ productId });
+  const { data: options, error: optionsError } = useGetProductOptions({ productId });
 
+  const [selectedOptionId, setSelectedOptionId] = useState<number | null>(null);
   const [countAsString, setCountAsString] = useState('1');
+
+  const selectedOption = useMemo(() => {
+    return options?.find((option) => option.id === selectedOptionId);
+  }, [options, selectedOptionId]);
+
   const totalPrice = useMemo(() => {
-    return detail.price * Number(countAsString);
-  }, [detail, countAsString]);
+    const price = detail?.price ?? 0;
+    return price * Number(countAsString);
+  }, [detail?.price, countAsString]);
 
   const navigate = useNavigate();
   const authInfo = useAuth();
+
   const handleClick = () => {
     if (!authInfo) {
       const isConfirm = window.confirm(
@@ -37,22 +46,61 @@ export const OptionSection = ({ productId }: Props) => {
       return navigate(getDynamicPath.login());
     }
 
+    if (!selectedOption) {
+      alert('옵션을 선택해주세요.');
+      return;
+    }
+
     orderHistorySessionStorage.set({
-      id: parseInt(productId),
+      id: productId,
+      optionId: selectedOption.id,
       count: parseInt(countAsString),
     });
 
     navigate(RouterPath.order);
   };
 
+  const handleWishClick = () => {
+    // 위시리스트 추가 로직 구현 (아직 구현되지 않음)
+    alert('위시리스트에 추가되었습니다.');
+  };
+
+  if (!detail || !options) {
+    return <div>Loading...</div>;
+  }
+
+  if (optionsError || detailError) {
+    return <div>Error loading product details or options.</div>;
+  }
+
   return (
     <Wrapper>
-      <CountOptionItem name={options[0].name} value={countAsString} onChange={setCountAsString} />
+      <select onChange={(e) => setSelectedOptionId(Number(e.target.value))}>
+        <option value="">옵션을 선택하세요</option>
+        {options.map((option) => (
+          <option key={option.id} value={option.id}>
+            {option.name} (재고: {option.quantity})
+          </option>
+        ))}
+      </select>
+      {selectedOption && (
+        <CountOptionItem
+          name={selectedOption.name}
+          value={countAsString}
+          onChange={setCountAsString}
+          minValues={1}
+          maxValues={selectedOption.quantity}
+        />
+      )}
       <BottomWrapper>
         <PricingWrapper>
           총 결제 금액 <span>{totalPrice}원</span>
         </PricingWrapper>
-        <Button theme="black" size="large" onClick={handleClick}>
+        <WishButton onClick={handleWishClick}>
+          <HeartIcon width={160} height={20} />
+          위시리스트에 추가
+        </WishButton>
+        <Button theme="black" size="large" onClick={handleClick} disabled={!selectedOption}>
           나에게 선물하기
         </Button>
       </BottomWrapper>
@@ -91,3 +139,20 @@ const PricingWrapper = styled.div`
     letter-spacing: -0.02em;
   }
 `;
+
+const WishButton = styled.button`
+  margin-bottom: 30px;
+  padding: 18px 20px;
+  border-radius: 4px;
+  background-color: #f5f5f5;
+  display: flex;
+
+  font-size: 14px;
+  font-weight: 700;
+  line-height: 14px;
+  color: #111;
+
+  &:hover {
+    background-color: #e0e0e0;
+  }
+`;
diff --git a/src/components/features/Order/OrderForm/Fields/CashReceiptFields.test.tsx b/src/components/features/Order/OrderForm/Fields/CashReceiptFields.test.tsx
new file mode 100644
index 00000000..bf2e051c
--- /dev/null
+++ b/src/components/features/Order/OrderForm/Fields/CashReceiptFields.test.tsx
@@ -0,0 +1,62 @@
+import { ChakraProvider } from '@chakra-ui/react';
+import { fireEvent, render, screen } from '@testing-library/react';
+import { FormProvider, useForm } from 'react-hook-form';
+
+import { CashReceiptFields } from './CashReceiptFields';
+
+describe('CashReceiptFields', () => {
+  const TestComponent = () => {
+    const methods = useForm();
+
+    return (
+      <ChakraProvider>
+        <FormProvider {...methods}>
+          <CashReceiptFields />
+        </FormProvider>
+      </ChakraProvider>
+    );
+  };
+
+  test('Checkbox 상태에 따른 필드 활성화/비활성화 테스트', async () => {
+    render(<TestComponent />);
+
+    // 1. 초기 상태: 체크박스가 체크되지 않은 상태에서 필드들이 비활성화 되어 있는지 확인
+    const cashReceiptTypeSelect = screen.getByRole('combobox', { name: 'cashReceiptType' });
+    const cashReceiptNumberInput = screen.getByPlaceholderText('(-없이) 숫자만 입력해주세요.');
+    expect(cashReceiptTypeSelect).toBeDisabled();
+    expect(cashReceiptNumberInput).toBeDisabled();
+
+    // 2. 체크박스 클릭: 체크박스를 클릭하여 필드들을 활성화
+    const cashReceiptCheckbox = screen.getByLabelText('현금영수증 신청');
+    fireEvent.click(cashReceiptCheckbox);
+
+    // 3. 필드 활성화 확인: 필드들이 활성화 되었는지 확인
+    expect(cashReceiptTypeSelect).toBeEnabled();
+    expect(cashReceiptNumberInput).toBeEnabled();
+  });
+
+  test('Checkbox가 true인 경우 필드 값 입력 테스트', async () => {
+    const methods = useForm();
+    render(
+      <ChakraProvider>
+        <FormProvider {...methods}>
+          <CashReceiptFields />
+        </FormProvider>
+      </ChakraProvider>,
+    );
+
+    // 1. 체크박스 클릭
+    const cashReceiptCheckbox = screen.getByLabelText('현금영수증 신청');
+    fireEvent.click(cashReceiptCheckbox);
+
+    // 2. 필드 값 입력
+    const cashReceiptTypeSelect = screen.getByRole('combobox', { name: 'cashReceiptType' });
+    const cashReceiptNumberInput = screen.getByPlaceholderText('(-없이) 숫자만 입력해주세요.');
+    fireEvent.change(cashReceiptTypeSelect, { target: { value: 'BUSINESS' } });
+    fireEvent.change(cashReceiptNumberInput, { target: { value: '1234567890' } });
+
+    // 3. 입력 값 확인
+    expect(methods.getValues('cashReceiptType')).toBe('BUSINESS');
+    expect(methods.getValues('cashReceiptNumber')).toBe('1234567890');
+  });
+});
diff --git a/src/components/features/Order/OrderForm/Fields/MessageCardFields.test.tsx b/src/components/features/Order/OrderForm/Fields/MessageCardFields.test.tsx
new file mode 100644
index 00000000..7c20abd2
--- /dev/null
+++ b/src/components/features/Order/OrderForm/Fields/MessageCardFields.test.tsx
@@ -0,0 +1,71 @@
+import { ChakraProvider } from '@chakra-ui/react';
+import { fireEvent, render, screen } from '@testing-library/react';
+import { FormProvider, useForm } from 'react-hook-form';
+
+import { MessageCardFields } from './MessageCardFields'; // 컴포넌트 경로에 맞게 수정
+
+describe('MessageCardFields', () => {
+  // 폼 제출 함수 (실제 폼 제출 로직은 생략)
+  const handleSubmit = jest.fn();
+
+  const TestComponent = () => {
+    const methods = useForm();
+
+    return (
+      <ChakraProvider>
+        <FormProvider {...methods}>
+          <form onSubmit={methods.handleSubmit(handleSubmit)}>
+            <MessageCardFields />
+            <button type="submit">제출</button>
+          </form>
+        </FormProvider>
+      </ChakraProvider>
+    );
+  };
+
+  test('필수 입력 필드 검사', async () => {
+    render(<TestComponent />);
+
+    // 1. 폼 제출 시도
+    const submitButton = screen.getByText('제출');
+    fireEvent.click(submitButton);
+
+    // 2. 에러 메시지 확인
+    const errorMessage = await screen.findByText('메시지를 입력해주세요.'); // 실제 에러 메시지에 맞게 수정
+    expect(errorMessage).toBeInTheDocument();
+  });
+
+  test('입력 값 형식 검사 (최대 길이 초과)', async () => {
+    render(<TestComponent />);
+
+    // 1. 텍스트 입력 (최대 길이 초과)
+    const textArea = screen.getByPlaceholderText('선물과 함께 보낼 메시지를 적어보세요');
+    fireEvent.change(textArea, { target: { value: 'a'.repeat(501) } }); // 최대 길이 가정: 500자
+
+    // 2. 폼 제출 시도
+    const submitButton = screen.getByText('제출');
+    fireEvent.click(submitButton);
+
+    // 3. 에러 메시지 확인
+    const errorMessage = await screen.findByText('메시지는 500자를 넘을 수 없습니다.'); // 실제 에러 메시지에 맞게 수정
+    expect(errorMessage).toBeInTheDocument();
+  });
+
+  test('정상 입력 검사', async () => {
+    render(<TestComponent />);
+
+    // 1. 텍스트 입력 (정상)
+    const textArea = screen.getByPlaceholderText('선물과 함께 보낼 메시지를 적어보세요');
+    fireEvent.change(textArea, { target: { value: '테스트 메시지' } });
+
+    // 2. 폼 제출
+    const submitButton = screen.getByText('제출');
+    fireEvent.click(submitButton);
+
+    // 3. 폼 제출 확인 및 값 전달 확인
+    expect(handleSubmit).toHaveBeenCalledTimes(1);
+    expect(handleSubmit).toHaveBeenCalledWith({
+      messageCardTextMessage: '테스트 메시지',
+    });
+  });
+});
diff --git a/src/components/features/Order/OrderForm/GoodsInfo/index.tsx b/src/components/features/Order/OrderForm/GoodsInfo/index.tsx
index caced66c..a5d93152 100644
--- a/src/components/features/Order/OrderForm/GoodsInfo/index.tsx
+++ b/src/components/features/Order/OrderForm/GoodsInfo/index.tsx
@@ -10,9 +10,40 @@ import { LabelText } from '../Common/LabelText';
 type Props = {
   orderHistory: OrderHistory;
 };
+
 export const GoodsInfo = ({ orderHistory }: Props) => {
   const { id, count } = orderHistory;
-  const { data: detail } = useGetProductDetail({ productId: id.toString() });
+  const { data: detail, isLoading, error } = useGetProductDetail({ productId: id });
+
+  if (isLoading) {
+    return (
+      <Wrapper>
+        <LabelText>선물내역</LabelText>
+        <Spacing />
+        <GoodsWrapper>
+          <GoodsInfoWrapper>
+            <div>Loading...</div>
+          </GoodsInfoWrapper>
+        </GoodsWrapper>
+      </Wrapper>
+    );
+  }
+
+  if (error || !detail) {
+    return (
+      <Wrapper>
+        <LabelText>선물내역</LabelText>
+        <Spacing />
+        <GoodsWrapper>
+          <GoodsInfoWrapper>
+            <div>Error loading product details</div>
+          </GoodsInfoWrapper>
+        </GoodsWrapper>
+      </Wrapper>
+    );
+  }
+
+  const totalPrice = detail.price * count;
 
   return (
     <Wrapper>
@@ -27,13 +58,13 @@ export const GoodsInfo = ({ orderHistory }: Props) => {
             <GoodsInfoTextTitle>
               {detail.name} X {count}개
             </GoodsInfoTextTitle>
+            <TotalPrice>총 결제 금액: {totalPrice}원</TotalPrice>
           </GoodsInfoTextWrapper>
         </GoodsInfoWrapper>
       </GoodsWrapper>
     </Wrapper>
   );
 };
-
 const Wrapper = styled.section`
   width: 100%;
   padding: 16px;
@@ -68,5 +99,10 @@ const GoodsInfoTextTitle = styled.p`
   margin-top: 3px;
   color: #222;
   overflow: hidden;
-  font-weight: 400;
+`;
+
+const TotalPrice = styled.p`
+  font-size: 16px;
+  font-weight: 700;
+  color: #111;
 `;
diff --git a/src/components/features/Order/OrderForm/OrderInfo/index.tsx b/src/components/features/Order/OrderForm/OrderInfo/index.tsx
index 97124149..b84a652b 100644
--- a/src/components/features/Order/OrderForm/OrderInfo/index.tsx
+++ b/src/components/features/Order/OrderForm/OrderInfo/index.tsx
@@ -13,10 +13,20 @@ import { CashReceiptFields } from '../Fields/CashReceiptFields';
 type Props = {
   orderHistory: OrderHistory;
 };
+
 export const OrderFormOrderInfo = ({ orderHistory }: Props) => {
   const { id, count } = orderHistory;
 
-  const { data: detail } = useGetProductDetail({ productId: id.toString() });
+  const { data: detail, isLoading, error } = useGetProductDetail({ productId: id });
+
+  if (isLoading) {
+    return <div>Loading...</div>;
+  }
+
+  if (error || !detail) {
+    return <div>Error loading product details</div>;
+  }
+
   const totalPrice = detail.price * count;
 
   return (
diff --git a/src/components/features/WishList/index.tsx b/src/components/features/WishList/index.tsx
new file mode 100644
index 00000000..4b6e62ef
--- /dev/null
+++ b/src/components/features/WishList/index.tsx
@@ -0,0 +1,77 @@
+import {
+  Box,
+  Button,
+  Image,
+  SimpleGrid,
+  Text,
+  useColorModeValue,
+  useToast,
+  VStack,
+} from '@chakra-ui/react';
+import React from 'react';
+
+interface WishItem {
+  id: number;
+  product: {
+    id: number;
+    name: string;
+    price: number;
+    imageUrl: string;
+  };
+}
+
+interface WishListProps {
+  wishes: WishItem[];
+  onRemove: (wishId: number) => void;
+}
+
+export const WishList: React.FC<WishListProps> = ({ wishes, onRemove }) => {
+  const toast = useToast();
+  const cardBg = useColorModeValue('white', 'gray.800');
+  const cardBorder = useColorModeValue('gray.200', 'gray.700');
+
+  const handleRemove = (wishId: number) => {
+    onRemove(wishId);
+    toast({
+      title: '상품이 위시리스트에서 제거되었습니다.',
+      status: 'success',
+      duration: 3000,
+      isClosable: true,
+    });
+  };
+
+  return (
+    <SimpleGrid columns={[1, 2, 3, 4]} spacing={6}>
+      {wishes.map((wish) => (
+        <Box
+          key={wish.id}
+          borderWidth="1px"
+          borderRadius="lg"
+          overflow="hidden"
+          bg={cardBg}
+          borderColor={cardBorder}
+          shadow="md"
+          _hover={{ transform: 'scale(1.02)', transition: 'transform 0.2s' }}
+        >
+          <Image src={wish.product.imageUrl} alt={wish.product.name} />
+          <VStack p={4} align="start" spacing={2}>
+            <Text fontWeight="bold" fontSize="lg">
+              {wish.product.name}
+            </Text>
+            <Text fontSize="md" color="gray.500">
+              {wish.product.price.toLocaleString()}원
+            </Text>
+            <Button
+              colorScheme="red"
+              variant="outline"
+              size="sm"
+              onClick={() => handleRemove(wish.id)}
+            >
+              삭제
+            </Button>
+          </VStack>
+        </Box>
+      ))}
+    </SimpleGrid>
+  );
+};
diff --git a/src/mocks/browser.ts b/src/mocks/browser.ts
index 7f2332e9..b536bbca 100644
--- a/src/mocks/browser.ts
+++ b/src/mocks/browser.ts
@@ -1,6 +1,18 @@
 import { setupWorker } from 'msw';
 
-import { categoriesMockHandler } from '@/api/hooks/categories.mock';
-import { productsMockHandler } from '@/api/hooks/products.mock';
-
-export const worker = setupWorker(...categoriesMockHandler, ...productsMockHandler);
+import { categoriesMockHandler } from '@/api/mocks/categories.mock';
+import { loginMockHandler } from '@/api/mocks/login.mock';
+import { productDetailMockHandler } from '@/api/mocks/productDetail.mock';
+import { productOptionsMockHandler } from '@/api/mocks/productOptions.mock';
+import { productsMockHandler } from '@/api/mocks/products.mock';
+import { registerMockHandler } from '@/api/mocks/register.mock';
+import { wishlistMockHandlers } from '@/api/mocks/wishlist.mock';
+export const worker = setupWorker(
+  ...categoriesMockHandler,
+  ...productsMockHandler,
+  ...productDetailMockHandler,
+  ...productOptionsMockHandler,
+  ...loginMockHandler,
+  ...registerMockHandler,
+  ...wishlistMockHandlers,
+);
diff --git a/src/mocks/setupTests.ts b/src/mocks/setupTests.ts
new file mode 100644
index 00000000..7caca389
--- /dev/null
+++ b/src/mocks/setupTests.ts
@@ -0,0 +1,7 @@
+import '@testing-library/jest-dom';
+
+import { worker } from './browser';
+
+beforeAll(() => worker.start());
+afterEach(() => worker.resetHandlers());
+afterAll(() => worker.stop());
diff --git a/src/pages/Goods/Detail/index.tsx b/src/pages/Goods/Detail/index.tsx
index b2b32dcd..b4e86375 100644
--- a/src/pages/Goods/Detail/index.tsx
+++ b/src/pages/Goods/Detail/index.tsx
@@ -1,6 +1,5 @@
 import { useParams } from 'react-router-dom';
 
-import type { ProductDetailRequestParams } from '@/api/hooks/useGetProductDetail';
 import { AsyncBoundary } from '@/components/common/AsyncBoundary';
 import { SplitLayout } from '@/components/common/layouts/SplitLayout';
 import { LoadingView } from '@/components/common/View/LoadingView';
@@ -8,13 +7,21 @@ import { GoodsDetail } from '@/components/features/Goods/Detail';
 import { OptionSection } from '@/components/features/Goods/Detail/OptionSection';
 
 export const GoodsDetailPage = () => {
-  const { productId = '' } = useParams<ProductDetailRequestParams>();
+  const { productId = '' } = useParams<{ productId: string }>();
+
+  // productId를 숫자로 변환
+  const numericProductId = parseInt(productId, 10);
+
+  // productId가 유효한 숫자가 아닐 경우 에러 처리
+  if (isNaN(numericProductId)) {
+    return <div>Invalid product ID</div>;
+  }
 
   return (
     <>
       <AsyncBoundary pendingFallback={<LoadingView />} rejectedFallback={<div>에러 페이지</div>}>
-        <SplitLayout sidebar={<OptionSection productId={productId} />}>
-          <GoodsDetail productId={productId} />
+        <SplitLayout sidebar={<OptionSection productId={numericProductId} />}>
+          <GoodsDetail productId={numericProductId} />
         </SplitLayout>
       </AsyncBoundary>
     </>
diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx
index 28c095f8..62aea2a0 100644
--- a/src/pages/Login/index.tsx
+++ b/src/pages/Login/index.tsx
@@ -1,39 +1,61 @@
 import styled from '@emotion/styled';
-import { useState } from 'react';
-import { useSearchParams } from 'react-router-dom';
+import { useEffect, useState } from 'react';
+import { Link, useLocation, useNavigate } from 'react-router-dom';
 
+import { useLogin } from '@/api/hooks/useGetLogin';
 import KAKAO_LOGO from '@/assets/kakao_logo.svg';
 import { Button } from '@/components/common/Button';
 import { UnderlineTextField } from '@/components/common/Form/Input/UnderlineTextField';
 import { Spacing } from '@/components/common/layouts/Spacing';
+import { RouterPath } from '@/routes/path';
 import { breakpoints } from '@/styles/variants';
 import { authSessionStorage } from '@/utils/storage';
 
 export const LoginPage = () => {
-  const [id, setId] = useState('');
+  const [email, setEmail] = useState('');
   const [password, setPassword] = useState('');
-  const [queryParams] = useSearchParams();
+  const [successMessage, setSuccessMessage] = useState('');
+  const navigate = useNavigate();
+  const location = useLocation();
 
-  const handleConfirm = () => {
-    if (!id || !password) {
-      alert('아이디와 비밀번호를 입력해주세요.');
-      return;
+  useEffect(() => {
+    const message = location.state?.successMessage;
+    if (message) {
+      setSuccessMessage(message);
+      navigate(RouterPath.login, { replace: true, state: {} });
     }
+  }, [location, navigate]);
 
-    // TODO: API 연동
+  const { mutate: login, isPending } = useLogin({
+    onSuccess: (data) => {
+      authSessionStorage.set(data.token);
+      navigate(RouterPath.home);
+    },
+    onError: (error) => {
+      // 에러 처리 로직은 제거하거나 주석 처리합니다.
+      console.error('Login error:', error);
+    },
+  });
 
-    // TODO: API 연동 전까지 임시 로그인 처리
-    authSessionStorage.set(id);
+  const handleConfirm = () => {
+    if (!email || !password) {
+      alert('이메일과 비밀번호를 입력해주세요.');
+      return;
+    }
 
-    const redirectUrl = queryParams.get('redirect') ?? `${window.location.origin}/`;
-    return window.location.replace(redirectUrl);
+    login({ email, password });
   };
 
   return (
     <Wrapper>
-      <Logo src={KAKAO_LOGO} alt="카카고 CI" />
+      <Logo src={KAKAO_LOGO} alt="카카오 CI" />
+      {successMessage && <SuccessMessage>{successMessage}</SuccessMessage>}
       <FormWrapper>
-        <UnderlineTextField placeholder="이름" value={id} onChange={(e) => setId(e.target.value)} />
+        <UnderlineTextField
+          placeholder="이메일"
+          value={email}
+          onChange={(e) => setEmail(e.target.value)}
+        />
         <Spacing />
         <UnderlineTextField
           type="password"
@@ -41,14 +63,13 @@ export const LoginPage = () => {
           value={password}
           onChange={(e) => setPassword(e.target.value)}
         />
-
-        <Spacing
-          height={{
-            initial: 40,
-            sm: 60,
-          }}
-        />
-        <Button onClick={handleConfirm}>로그인</Button>
+        <Spacing height={{ initial: 40, sm: 60 }} />
+        <Button onClick={handleConfirm} disabled={isPending}>
+          {isPending ? '로그인 중...' : '로그인'}
+        </Button>
+        <SignUpLinkWrapper>
+          <SignUpLink to={RouterPath.register}>회원가입</SignUpLink>
+        </SignUpLinkWrapper>
       </FormWrapper>
     </Wrapper>
   );
@@ -78,3 +99,22 @@ const FormWrapper = styled.article`
     padding: 60px 52px;
   }
 `;
+
+const SignUpLinkWrapper = styled.div`
+  margin-top: 16px;
+  text-align: center;
+`;
+
+const SignUpLink = styled(Link)`
+  color: #1a73e8;
+  text-decoration: none;
+  &:hover {
+    text-decoration: underline;
+  }
+`;
+
+const SuccessMessage = styled.div`
+  color: green;
+  margin-bottom: 20px;
+  text-align: center;
+`;
diff --git a/src/pages/MyAccount/index.tsx b/src/pages/MyAccount/index.tsx
index 446257b5..d79d6a38 100644
--- a/src/pages/MyAccount/index.tsx
+++ b/src/pages/MyAccount/index.tsx
@@ -1,34 +1,57 @@
+import { Box, Heading, Spinner, Text, VStack } from '@chakra-ui/react';
 import styled from '@emotion/styled';
 
+import { useRemoveWish, useWishList } from '@/api/hooks/useGetWishList';
 import { Button } from '@/components/common/Button';
 import { Spacing } from '@/components/common/layouts/Spacing';
+import { WishList } from '@/components/features/WishList';
 import { useAuth } from '@/provider/Auth';
 import { RouterPath } from '@/routes/path';
 import { authSessionStorage } from '@/utils/storage';
 
 export const MyAccountPage = () => {
   const authInfo = useAuth();
+  const { data, isLoading, error } = useWishList();
+  const removeWishMutation = useRemoveWish();
 
   const handleLogout = () => {
     authSessionStorage.set(undefined);
-
     const redirectURL = `${window.location.origin}${RouterPath.home}`;
     window.location.replace(redirectURL);
   };
 
+  const handleRemoveWish = (wishId: number) => {
+    removeWishMutation.mutate(wishId);
+  };
+
   return (
     <Wrapper>
-      {authInfo?.name}님 안녕하세요! <Spacing height={64} />
-      <Button
-        size="small"
-        theme="darkGray"
-        onClick={handleLogout}
-        style={{
-          maxWidth: '200px',
-        }}
-      >
-        로그아웃
-      </Button>
+      <Box p={6}>
+        <VStack spacing={6} align="stretch">
+          <Heading size="lg">{authInfo?.name}님 안녕하세요!</Heading>
+          <Spacing height={64} />
+          <Button
+            size="small"
+            theme="darkGray"
+            onClick={handleLogout}
+            style={{
+              maxWidth: '200px',
+            }}
+          >
+            로그아웃
+          </Button>
+          <Heading size="md">위시 리스트</Heading>
+          {isLoading ? (
+            <Spinner />
+          ) : error ? (
+            <Text>에러가 발생했습니다: {(error as Error).message}</Text>
+          ) : data && data.content.length > 0 ? (
+            <WishList wishes={data.content} onRemove={handleRemoveWish} />
+          ) : (
+            <Text>위시리스트가 비어있습니다.</Text>
+          )}
+        </VStack>
+      </Box>
     </Wrapper>
   );
 };
diff --git a/src/pages/Register/index.tsx b/src/pages/Register/index.tsx
new file mode 100644
index 00000000..cb7e9f3a
--- /dev/null
+++ b/src/pages/Register/index.tsx
@@ -0,0 +1,105 @@
+import styled from '@emotion/styled';
+import { useState } from 'react';
+import { Link, useNavigate } from 'react-router-dom';
+
+import { useGetRegister } from '@/api/hooks/useGetRegister';
+import KAKAO_LOGO from '@/assets/kakao_logo.svg';
+import { Button } from '@/components/common/Button';
+import { UnderlineTextField } from '@/components/common/Form/Input/UnderlineTextField';
+import { Spacing } from '@/components/common/layouts/Spacing';
+import { RouterPath } from '@/routes/path';
+import { breakpoints } from '@/styles/variants';
+
+export const RegisterPage = () => {
+  const [email, setEmail] = useState('');
+  const [password, setPassword] = useState('');
+  const navigate = useNavigate();
+
+  const { mutate: register, isPending } = useGetRegister({
+    onSuccess: () => {
+      // 회원가입 성공 시 로그인 페이지로 이동
+      navigate(RouterPath.login, {
+        state: {
+          successMessage: '회원가입이 완료되었습니다. 로그인해 주세요.',
+        },
+      });
+    },
+    onError: (error) => {
+      alert(error.message);
+    },
+  });
+
+  const handleConfirm = () => {
+    if (!email || !password) {
+      alert('이메일과 비밀번호를 입력해주세요.');
+      return;
+    }
+
+    register({ email, password });
+  };
+
+  return (
+    <Wrapper>
+      <Logo src={KAKAO_LOGO} alt="카카오 CI" />
+      <FormWrapper>
+        <UnderlineTextField
+          placeholder="이메일"
+          value={email}
+          onChange={(e) => setEmail(e.target.value)}
+        />
+        <Spacing />
+        <UnderlineTextField
+          type="password"
+          placeholder="비밀번호"
+          value={password}
+          onChange={(e) => setPassword(e.target.value)}
+        />
+        <Spacing height={{ initial: 40, sm: 60 }} />
+        <Button onClick={handleConfirm} disabled={isPending}>
+          {isPending ? '가입 중...' : '회원가입'}
+        </Button>
+        <LoginLinkWrapper>
+          <LoginLink to={RouterPath.login}>로그인</LoginLink>
+        </LoginLinkWrapper>
+      </FormWrapper>
+    </Wrapper>
+  );
+};
+
+const Wrapper = styled.div`
+  width: 100vw;
+  height: 100vh;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-direction: column;
+`;
+
+const Logo = styled.img`
+  width: 88px;
+  color: #333;
+`;
+
+const FormWrapper = styled.article`
+  width: 100%;
+  max-width: 580px;
+  padding: 16px;
+
+  @media screen and (min-width: ${breakpoints.sm}) {
+    border: 1px solid rgba(0, 0, 0, 0.12);
+    padding: 60px 52px;
+  }
+`;
+
+const LoginLinkWrapper = styled.div`
+  margin-top: 16px;
+  text-align: center;
+`;
+
+const LoginLink = styled(Link)`
+  color: #1a73e8;
+  text-decoration: none;
+  &:hover {
+    text-decoration: underline;
+  }
+`;
diff --git a/src/routes/index.tsx b/src/routes/index.tsx
index e7c59cdf..2f54122e 100644
--- a/src/routes/index.tsx
+++ b/src/routes/index.tsx
@@ -7,6 +7,7 @@ import { HomePage } from '@/pages/Home';
 import { LoginPage } from '@/pages/Login';
 import { MyAccountPage } from '@/pages/MyAccount';
 import { OrderPage } from '@/pages/Order';
+import { RegisterPage } from '@/pages/Register'; // 회원가입 페이지 import
 
 import { PrivateRoute } from './components/PrivateRoute';
 import { RouterPath } from './path';
@@ -58,6 +59,10 @@ const router = createBrowserRouter([
     path: RouterPath.login,
     element: <LoginPage />,
   },
+  {
+    path: RouterPath.register, // 회원가입 경로 추가
+    element: <RegisterPage />,
+  },
 ]);
 
 export const Routes = () => {
diff --git a/src/routes/path.ts b/src/routes/path.ts
index 0133b031..921ff6dc 100644
--- a/src/routes/path.ts
+++ b/src/routes/path.ts
@@ -6,6 +6,7 @@ export const RouterPath = {
   productsDetail: '/products/:productId',
   order: '/order',
   login: '/login',
+  register: '/register',
   notFound: '*',
 };
 
diff --git a/src/types/index.ts b/src/types/index.ts
index 33c39745..6a90c0f0 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -33,6 +33,7 @@ export type GoodsDetailOptionItemData = {
 
 export type OrderHistory = {
   id: number;
+  optionId: number;
   count: number;
 };