This commit is contained in:
@@ -1,83 +1,101 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Suspense, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { login } from '@/api/auth';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { login } from '@/api/auth';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
|
||||
export default function LoginPage() {
|
||||
const getSafeRedirectPath = (value: string | null) => {
|
||||
if (!value) return '/';
|
||||
if (!value.startsWith('/') || value.startsWith('//')) return '/';
|
||||
if (value.startsWith('/login')) return '/';
|
||||
if (value.includes('://')) return '/';
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
const getErrorMessage = (error: unknown) => {
|
||||
if (typeof error === 'object' && error !== null && 'response' in error) {
|
||||
const response = (error as { response?: { data?: { message?: string } } }).response;
|
||||
if (response?.data?.message) return response.data.message;
|
||||
}
|
||||
|
||||
if (error instanceof Error) return error.message;
|
||||
|
||||
return '로그인 중 오류가 발생했습니다.';
|
||||
};
|
||||
|
||||
function LoginForm() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const { login: setLoginState } = useAuthStore();
|
||||
|
||||
const redirectPath = getSafeRedirectPath(searchParams.get('redirect'));
|
||||
|
||||
const [formData, setFormData] = useState({ email: '', password: '' });
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const handleSubmit = async (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
setLoading(true);
|
||||
setError('');
|
||||
|
||||
try {
|
||||
const res = await login(formData);
|
||||
if (res.code === 'SUCCESS' && res.data) {
|
||||
// ✨ 수정됨: AccessToken과 RefreshToken 모두 저장
|
||||
setLoginState(res.data.accessToken, res.data.refreshToken);
|
||||
|
||||
// 로그인 성공 후 메인으로 이동
|
||||
router.push('/');
|
||||
const response = await login(formData);
|
||||
if (response.code === 'SUCCESS' && response.data) {
|
||||
setLoginState(response.data.accessToken, response.data.refreshToken);
|
||||
router.push(redirectPath);
|
||||
} else {
|
||||
setError(res.message || '로그인에 실패했습니다.');
|
||||
setError(response.message || '로그인에 실패했습니다.');
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.response?.data?.message || '로그인 중 오류가 발생했습니다.');
|
||||
} catch (loginError) {
|
||||
setError(getErrorMessage(loginError));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
// 🎨 배경색 수정: bg-gray-50 -> bg-white
|
||||
<div className="flex flex-col items-center justify-center min-h-screen p-4 bg-white">
|
||||
<div className="w-full max-w-md bg-white rounded-2xl shadow-xl overflow-hidden p-8 border border-gray-100">
|
||||
<div className="text-center mb-8">
|
||||
<div className="flex min-h-screen flex-col items-center justify-center bg-white p-4">
|
||||
<div className="w-full max-w-md overflow-hidden rounded-2xl border border-gray-100 bg-white p-8 shadow-xl">
|
||||
<div className="mb-8 text-center">
|
||||
<h1 className="text-3xl font-bold text-gray-900">로그인</h1>
|
||||
<p className="text-gray-500 mt-2">블로그에 오신 것을 환영합니다.</p>
|
||||
<p className="mt-2 text-gray-500">블로그에 오신 것을 환영합니다.</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">이메일</label>
|
||||
<label className="mb-1 block text-sm font-medium text-gray-700">이메일</label>
|
||||
<input
|
||||
type="email"
|
||||
value={formData.email}
|
||||
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||
className="w-full px-4 py-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-blue-500 outline-none transition-all"
|
||||
onChange={(event) => setFormData({ ...formData, email: event.target.value })}
|
||||
className="w-full rounded-lg border border-gray-200 px-4 py-2 outline-none transition-all focus:ring-2 focus:ring-blue-500"
|
||||
placeholder="example@email.com"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">비밀번호</label>
|
||||
<label className="mb-1 block text-sm font-medium text-gray-700">비밀번호</label>
|
||||
<input
|
||||
type="password"
|
||||
value={formData.password}
|
||||
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
|
||||
className="w-full px-4 py-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-blue-500 outline-none transition-all"
|
||||
onChange={(event) => setFormData({ ...formData, password: event.target.value })}
|
||||
className="w-full rounded-lg border border-gray-200 px-4 py-2 outline-none transition-all focus:ring-2 focus:ring-blue-500"
|
||||
placeholder="••••••••"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && <p className="text-sm text-red-500 text-center">{error}</p>}
|
||||
{error && <p className="text-center text-sm text-red-500">{error}</p>}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full py-3 bg-blue-600 text-white rounded-lg font-bold hover:bg-blue-700 transition-colors disabled:bg-blue-400 flex justify-center items-center gap-2"
|
||||
className="flex w-full items-center justify-center gap-2 rounded-lg bg-blue-600 py-3 font-bold text-white transition-colors hover:bg-blue-700 disabled:bg-blue-400"
|
||||
>
|
||||
{loading && <Loader2 className="animate-spin" size={20} />}
|
||||
로그인
|
||||
@@ -86,11 +104,25 @@ export default function LoginPage() {
|
||||
|
||||
<div className="mt-6 text-center text-sm text-gray-500">
|
||||
계정이 없으신가요?{' '}
|
||||
<Link href="/signup" className="text-blue-600 font-medium hover:underline">
|
||||
<Link href="/signup" className="font-medium text-blue-600 hover:underline">
|
||||
회원가입
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
<Loader2 className="animate-spin text-blue-500" size={36} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<LoginForm />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user