Files
saju-web/app/login/page.tsx

164 lines
7.3 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useState } from 'react';
import { createBrowserClient } from '@supabase/ssr'
import { useRouter } from 'next/navigation';
export default function LoginPage() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [isSignUp, setIsSignUp] = useState(false);
const [loading, setLoading] = useState(false);
const router = useRouter();
const supabase = createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
const handleAuth = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
if (isSignUp) {
// Sign Up with Password
const { data, error } = await supabase.auth.signUp({
email,
password,
options: {
emailRedirectTo: `${window.location.origin}/auth/callback`,
}
});
if (error) {
alert('회원가입 실패: ' + error.message);
} else if (data.user && data.user.identities && data.user.identities.length === 0) {
alert('이미 가입된 이메일입니다. 로그인해주세요.');
} else {
alert('가입이 완료되었습니다! 이메일 인증이 필요할 수 있습니다. 로그인을 시도해주세요.');
setIsSignUp(false); // Switch to login mode
}
} else {
// Sign In with Password
const { error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) {
alert('로그인 실패: ' + error.message);
} else {
router.push('/mypage');
router.refresh();
}
}
setLoading(false);
};
const handleSocialLogin = async (provider: 'google' | 'kakao') => {
const { error } = await supabase.auth.signInWithOAuth({
provider,
options: {
redirectTo: `${window.location.origin}/auth/callback`,
},
});
if (error) alert('소셜 로그인 오류: ' + error.message);
};
return (
<div className="min-h-screen bg-gradient-to-br from-indigo-50 to-purple-50 flex items-center justify-center p-4">
<div className="bg-white rounded-2xl shadow-xl w-full max-w-md p-8">
<div className="text-center mb-8">
<h1 className="text-3xl font-bold text-gray-900 mb-2">
{isSignUp ? '회원가입' : '로그인'}
</h1>
<p className="text-gray-500"> </p>
</div>
<form onSubmit={handleAuth} className="space-y-4 mb-6">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">
</label>
<input
id="email"
type="email"
placeholder="name@example.com"
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1">
</label>
<input
id="password"
type="password"
placeholder="••••••••"
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
minLength={6}
/>
</div>
<button
type="submit"
disabled={loading}
className="w-full bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-3 px-4 rounded-lg transition disabled:opacity-50"
>
{loading ? '처리 중...' : (isSignUp ? '회원가입' : '로그인')}
</button>
</form>
<div className="text-center mb-6">
<button
type="button"
className="text-sm text-indigo-600 hover:text-indigo-800 font-medium"
onClick={() => setIsSignUp(!isSignUp)}
>
{isSignUp ? '이미 계정이 있으신가요? 로그인' : '계정이 없으신가요? 회원가입'}
</button>
</div>
<div className="relative my-6">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-200"></div>
</div>
<div className="relative flex justify-center text-sm">
<span className="px-2 bg-white text-gray-500"> </span>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<button
onClick={() => handleSocialLogin('google')}
className="flex items-center justify-center px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition"
>
<span className="ml-2">Google</span>
</button>
<button
onClick={() => handleSocialLogin('kakao')}
className="flex items-center justify-center px-4 py-2 bg-[#FEE500] border border-[#FEE500] rounded-lg hover:bg-[#FDD835] transition"
>
<span className="ml-2 text-[#000000bd]">Kakao</span>
</button>
</div>
<div className="mt-6 p-4 bg-yellow-50 rounded-lg text-xs text-yellow-800 border border-yellow-200">
<h4 className="font-bold mb-1"> !</h4>
<p className="mb-2">
Supabase는 .
.
</p>
<p className="font-semibold text-gray-700">
( : Supabase &gt; Authentication &gt; Providers &gt; Email &gt; "Confirm email" )
</p>
</div>
</div>
</div>
);
}