"use client"; import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; import Link from "next/link"; import MainLogo from "@/app/svgs/mainlogosvg" import LoginCheckboxActiveSvg from "@/app/svgs/logincheckboxactivesvg"; import LoginCheckboxInactiveSvg from "@/app/svgs/logincheckboxinactivesvg"; import LoginInputSvg from "@/app/svgs/inputformx"; import LoginErrorModal from "./LoginErrorModal"; import LoginOption from "@/app/login/LoginOption"; export default function LoginPage() { const router = useRouter(); const [userId, setUserId] = useState(""); const [password, setPassword] = useState(""); const [rememberId, setRememberId] = useState(false); const [autoLogin, setAutoLogin] = useState(false); const [isUserIdFocused, setIsUserIdFocused] = useState(false); const [isPasswordFocused, setIsPasswordFocused] = useState(false); const [isLoginErrorOpen, setIsLoginErrorOpen] = useState(false); const [idError, setIdError] = useState(""); const [passwordError, setPasswordError] = useState(""); // 컴포넌트 마운트 시 저장된 아이디 불러오기 및 자동 로그인 확인 useEffect(() => { const savedId = localStorage.getItem('savedUserId'); if (savedId) { setUserId(savedId); setRememberId(true); } // 자동 로그인 확인: localStorage에 토큰이 있고 쿠키에도 토큰이 있으면 자동 로그인 const savedToken = localStorage.getItem('token'); const cookieToken = document.cookie .split('; ') .find(row => row.startsWith('token=')) ?.split('=')[1]; if (savedToken && cookieToken && savedToken === cookieToken) { // 토큰이 유효한지 확인 fetch('https://hrdi.coconutmeet.net/auth/me', { method: 'GET', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${savedToken}`, }, }) .then(response => { if (response.ok) { // 토큰이 유효하면 메인 페이지로 리다이렉트 const searchParams = new URLSearchParams(window.location.search); const redirectPath = searchParams.get('redirect') || '/'; router.push(redirectPath); } else { // 토큰이 유효하지 않으면 삭제 localStorage.removeItem('token'); document.cookie = 'token=; path=/; max-age=0'; } }) .catch(() => { // 에러 발생 시 토큰 삭제 localStorage.removeItem('token'); document.cookie = 'token=; path=/; max-age=0'; }); } }, [router]); // 아이디 기억하기 상태나 아이디가 변경될 때마다 저장 처리 useEffect(() => { if (rememberId && userId.trim()) { localStorage.setItem('savedUserId', userId); } else if (!rememberId) { localStorage.removeItem('savedUserId'); } }, [rememberId, userId]); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); // 에러 초기화 setIdError(""); setPasswordError(""); // 입력 검증 let hasError = false; if (userId.trim().length === 0) { setIdError("아이디를 입력해 주세요."); hasError = true; } if (password.trim().length === 0) { setPasswordError("비밀번호를 입력해 주세요."); hasError = true; } if (hasError) { return; } try { const response = await fetch("https://hrdi.coconutmeet.net/auth/login", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ email: userId, password: password }) }); if (!response.ok) { let errorMessage = `로그인 실패 (${response.status})`; try { const errorData = await response.json(); if (errorData.error) { errorMessage = errorData.error; } else if (errorData.message) { errorMessage = errorData.message; } else if (response.statusText) { errorMessage = `${response.statusText} (${response.status})`; } } catch (parseError) { if (response.statusText) { errorMessage = `${response.statusText} (${response.status})`; } } console.error("로그인 실패:", errorMessage); setIsLoginErrorOpen(true); return; } const data = await response.json(); console.log("로그인 성공:", data); // 로그인 성공 시 토큰 저장 (다양한 필드명 지원) const token = data.token || data.accessToken || data.access_token; if (token) { if (autoLogin) { // 자동 로그인이 체크되어 있으면 localStorage와 쿠키에 장기 저장 (30일) localStorage.setItem('token', token); document.cookie = `token=${token}; path=/; max-age=${30 * 24 * 60 * 60}; SameSite=Lax`; console.log("자동 로그인 토큰 저장 완료 (30일 유지)"); } else { // 자동 로그인이 체크되어 있지 않으면 쿠키에만 세션 쿠키로 저장 (브라우저 종료 시 삭제) // localStorage에는 저장하지 않음 document.cookie = `token=${token}; path=/; SameSite=Lax`; console.log("세션 토큰 저장 완료 (브라우저 종료 시 삭제)"); } } else { console.warn("토큰이 응답에 없습니다. 응답 데이터:", data); // 토큰이 없어도 로그인은 성공했으므로 진행 } // 리다이렉트 경로 확인 const searchParams = new URLSearchParams(window.location.search); const redirectPath = searchParams.get('redirect') || '/'; // 메인 페이지로 이동 router.push(redirectPath); } catch (error) { const errorMessage = error instanceof Error ? error.message : "네트워크 오류가 발생했습니다."; console.error("로그인 오류:", errorMessage); setIsLoginErrorOpen(true); } } return (
setIsLoginErrorOpen(false)} /> setIsLoginErrorOpen(true)} loginErrorModalEnabled={isLoginErrorOpen} setLoginErrorModalEnabled={setIsLoginErrorOpen} />
{/* 로고 영역 */}
XR LMS
{/* 폼 */}
{/* 아이디 */}
{ setUserId(e.target.value); if (idError) setIdError(""); }} onFocus={() => setIsUserIdFocused(true)} onBlur={() => setIsUserIdFocused(false)} placeholder="아이디(이메일)" className={`h-[56px] px-[12px] py-[7px] w-full rounded-[8px] border focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none focus:appearance-none text-[18px] text-neutral-700 font-normal leading-[150%] placeholder:text-input-placeholder-text pr-[40px] ${idError ? 'border-error' : 'border-neutral-40 focus:border-neutral-700'}`} /> {userId.trim().length > 0 && isUserIdFocused && ( )}
{idError &&

{idError}

} {/* 비밀번호 */}
{ setPassword(e.target.value); if (passwordError) setPasswordError(""); }} onFocus={() => setIsPasswordFocused(true)} onBlur={() => setIsPasswordFocused(false)} placeholder="비밀번호 입력" className={`h-[56px] px-[12px] py-[7px] rounded-[8px] w-full border focus:outline-none focus:ring-0 focus:ring-offset-0 focus:shadow-none focus:appearance-none text-[18px] text-neutral-700 font-normal leading-[150%] placeholder:text-input-placeholder-text pr-[40px] ${passwordError ? 'border-error' : 'border-neutral-40 focus:border-neutral-700'}`} /> {password.trim().length > 0 && isPasswordFocused && ( )}
{passwordError &&

{passwordError}

}
{/* 체크박스들 */}
{/* 로그인 버튼 */} {/* 하단 링크들 */}
회원가입
아이디 찾기 비밀번호 재설정

Copyright ⓒ 2025 XL LMS. All rights reserved

); }