+ {/* 검색창 */}
+
+
setSearch(e.target.value)}
+ placeholder="LP 검색..."
+ className="w-full border border-gray-600 bg-transparent rounded-md px-4 py-2 text-sm outline-none focus:border-pink-400 text-black"
+ />
+ {/* 디바운스 상태 표시 (디버깅용 - 나중에 지워도 됨) */}
+ {search !== debouncedSearch && (
+
입력 감지 중...
+ )}
+
+
+ {/* 정렬 버튼 */}
+
+
+
+
+
+ {/* 검색 결과 안내 */}
+ {debouncedSearch && !isLoading && (
+
+ "{debouncedSearch}" 검색 결과{' '}
+ {lps.length === 0 ? '없음' : `${lps.length}건`}
+
+ )}
+
+ {/* 초기 로딩 스켈레톤 */}
+ {isLoading && (
+
+ {Array.from({ length: 8 }).map((_, i) => (
+
+ ))}
+
+ )}
+
+ {/* 에러 */}
+ {isError && (
+
+
데이터를 불러오는데 실패했습니다.
+
+
+ )}
+
+ {/* LP 목록 */}
+ {!isLoading && !isError && (
+ <>
+ {lps.length === 0 && debouncedSearch ? (
+
+ ) : (
+
+ {lps.map((lp) => (
+
+ ))}
+
+ )}
+
+ {isFetchingNextPage && (
+
+ {Array.from({ length: 4 }).map((_, i) => (
+
+ ))}
+
+ )}
+
+
+ >
+ )}
+
+ {/* 우측 하단 플로팅 버튼 */}
+
+
+ {isModalOpen &&
setIsModalOpen(false)} />}
+
+ );
+};
+
+export default HomePage;
diff --git a/mission/chapter08/mission_1/src/pages/LoginPage.tsx b/mission/chapter08/mission_1/src/pages/LoginPage.tsx
new file mode 100644
index 00000000..554d1fae
--- /dev/null
+++ b/mission/chapter08/mission_1/src/pages/LoginPage.tsx
@@ -0,0 +1,114 @@
+import { useNavigate, useLocation } from 'react-router-dom';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { useMutation } from '@tanstack/react-query';
+import { signinSchema, type SigninFormValues } from '../utils/validate';
+import { useAuth } from '../context/AuthContext';
+import { signin } from '../api/auth';
+import Button from '../components/Button';
+import Input from '../components/Input';
+
+const LoginPage = () => {
+ const navigate = useNavigate();
+ const location = useLocation();
+ const { login } = useAuth();
+
+ const {
+ register,
+ handleSubmit,
+ formState: { errors, isValid },
+ } = useForm