Fix: 회원가입 인증 리다이렉트 처리와 UI 그라디언트 클래스 수정
app/auth/callback/route.ts - 이메일 인증 완료 판별을 세션 생성 시간 기준에서 쿼리 파라미터(auth_type=signup) 기반으로 변경 - 회원가입 인증인 경우 자동 로그인 세션 종료 후 로그인 페이지로 리다이렉트 처리 features/auth/actions.ts - 회원가입 시 이메일 리다이렉트 URL에 auth_type=signup 쿼리 파라미터 추가 app/forgot-password/page.tsx - 배경 및 카드 아이콘의 Tailwind 클래스명 일부 수정(bg-gradient-to→bg-linear-to, radial-gradient var() 구문 정리) app/login/page.tsx - 배경 및 카드 아이콘의 Tailwind 클래스명 일부 정리 및 일관화 app/reset-password/page.tsx - 배경 및 카드 아이콘의 Tailwind 클래스명 일부 정리 및 일관화 app/signup/page.tsx - 배경 및 카드 아이콘의 Tailwind 클래스명 일부 정리 및 일관화 features/auth/components/reset-password-form.tsx - 버튼 그라디언트 클래스명(bg-gradient-to-r→bg-linear-to-r) 수정
This commit is contained in:
@@ -65,20 +65,14 @@ export async function GET(request: NextRequest) {
|
|||||||
// code 교환으로 세션이 생성된 상태입니다.
|
// code 교환으로 세션이 생성된 상태입니다.
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
const {
|
// 회원가입 인증 여부 확인 (쿼리 파라미터 기반)
|
||||||
data: { user },
|
// actions.ts의 signup 함수에서 emailRedirectTo에 auth_type=signup을 추가해서 보냅니다.
|
||||||
} = await supabase.auth.getUser();
|
const authType = requestUrl.searchParams.get("auth_type");
|
||||||
|
const isSignupVerification = authType === "signup";
|
||||||
// 회원가입 직후 인증 여부 확인 (생성된 지 1분 이내)
|
|
||||||
// 별도의 type 파라미터 없이 데이터 기반으로 판단합니다.
|
|
||||||
const isNewUser =
|
|
||||||
user &&
|
|
||||||
user.created_at &&
|
|
||||||
new Date(user.created_at).getTime() > Date.now() - 60 * 1000; // 1분 이내 생성
|
|
||||||
|
|
||||||
// 회원가입 인증인 경우:
|
// 회원가입 인증인 경우:
|
||||||
// 이메일 인증만 완료하고, 자동 로그인된 세션은 종료시킨 뒤 로그인 페이지로 보냅니다.
|
// 이메일 인증만 완료하고, 자동 로그인된 세션은 종료시킨 뒤 로그인 페이지로 보냅니다.
|
||||||
if (isNewUser) {
|
if (isSignupVerification) {
|
||||||
await supabase.auth.signOut();
|
await supabase.auth.signOut();
|
||||||
return NextResponse.redirect(
|
return NextResponse.redirect(
|
||||||
`${origin}${AUTH_ROUTES.LOGIN}?message=${encodeURIComponent(
|
`${origin}${AUTH_ROUTES.LOGIN}?message=${encodeURIComponent(
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ export default async function ForgotPasswordPage({
|
|||||||
const { message } = await searchParams;
|
const { message } = await searchParams;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex min-h-screen items-center justify-center overflow-hidden bg-gradient-to-br from-gray-50 via-white to-gray-100 px-4 py-12 dark:from-black dark:via-gray-950 dark:to-gray-900">
|
<div className="relative flex min-h-screen items-center justify-center overflow-hidden bg-linear-to-br from-gray-50 via-white to-gray-100 px-4 py-12 dark:from-black dark:via-gray-950 dark:to-gray-900">
|
||||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-gray-200/30 via-gray-100/15 to-transparent dark:from-gray-800/30 dark:via-gray-900/20" />
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,var(--tw-gradient-stops))] from-gray-200/30 via-gray-100/15 to-transparent dark:from-gray-800/30 dark:via-gray-900/20" />
|
||||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_bottom_left,_var(--tw-gradient-stops))] from-gray-300/30 via-gray-200/15 to-transparent dark:from-gray-700/30 dark:via-gray-800/20" />
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_bottom_left,var(--tw-gradient-stops))] from-gray-300/30 via-gray-200/15 to-transparent dark:from-gray-700/30 dark:via-gray-800/20" />
|
||||||
|
|
||||||
<div className="absolute left-1/4 top-1/4 h-64 w-64 animate-pulse rounded-full bg-gray-300/20 blur-3xl dark:bg-gray-700/20" />
|
<div className="absolute left-1/4 top-1/4 h-64 w-64 animate-pulse rounded-full bg-gray-300/20 blur-3xl dark:bg-gray-700/20" />
|
||||||
<div className="absolute bottom-1/4 right-1/4 h-64 w-64 animate-pulse rounded-full bg-gray-400/20 blur-3xl delay-700 dark:bg-gray-600/20" />
|
<div className="absolute bottom-1/4 right-1/4 h-64 w-64 animate-pulse rounded-full bg-gray-400/20 blur-3xl delay-700 dark:bg-gray-600/20" />
|
||||||
@@ -39,7 +39,7 @@ export default async function ForgotPasswordPage({
|
|||||||
|
|
||||||
<Card className="border-white/20 bg-white/70 shadow-2xl backdrop-blur-xl dark:border-white/10 dark:bg-gray-900/70">
|
<Card className="border-white/20 bg-white/70 shadow-2xl backdrop-blur-xl dark:border-white/10 dark:bg-gray-900/70">
|
||||||
<CardHeader className="space-y-3 text-center">
|
<CardHeader className="space-y-3 text-center">
|
||||||
<div className="mx-auto mb-2 flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-gray-800 to-black shadow-lg dark:from-white dark:to-gray-200">
|
<div className="mx-auto mb-2 flex h-16 w-16 items-center justify-center rounded-2xl bg-linear-to-br from-gray-800 to-black shadow-lg dark:from-white dark:to-gray-200">
|
||||||
<span className="text-sm font-semibold">MAIL</span>
|
<span className="text-sm font-semibold">MAIL</span>
|
||||||
</div>
|
</div>
|
||||||
<CardTitle className="text-3xl font-bold tracking-tight">
|
<CardTitle className="text-3xl font-bold tracking-tight">
|
||||||
@@ -72,7 +72,7 @@ export default async function ForgotPasswordPage({
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
formAction={requestPasswordReset}
|
formAction={requestPasswordReset}
|
||||||
className="h-11 w-full bg-gradient-to-r from-gray-900 to-black font-semibold text-white shadow-lg transition-all hover:from-black hover:to-gray-800 hover:shadow-xl dark:from-white dark:to-gray-100 dark:text-black dark:hover:from-gray-100 dark:hover:to-white"
|
className="h-11 w-full bg-linear-to-r from-gray-900 to-black font-semibold text-white shadow-lg transition-all hover:from-black hover:to-gray-800 hover:shadow-xl dark:from-white dark:to-gray-100 dark:text-black dark:hover:from-gray-100 dark:hover:to-white"
|
||||||
>
|
>
|
||||||
재설정 링크 보내기
|
재설정 링크 보내기
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -27,17 +27,17 @@ export default async function LoginPage({
|
|||||||
const { message } = await searchParams;
|
const { message } = await searchParams;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex min-h-screen items-center justify-center overflow-hidden bg-gradient-to-br from-gray-50 via-white to-gray-100 px-4 py-12 dark:from-black dark:via-gray-950 dark:to-gray-900">
|
<div className="relative flex min-h-screen items-center justify-center overflow-hidden bg-linear-to-br from-gray-50 via-white to-gray-100 px-4 py-12 dark:from-black dark:via-gray-950 dark:to-gray-900">
|
||||||
{/* ========== 배경 그라디언트 레이어 ========== */}
|
{/* ========== 배경 그라디언트 레이어 ========== */}
|
||||||
{/* 웹 페이지 전체 배경을 그라디언트로 채웁니다 */}
|
{/* 웹 페이지 전체 배경을 그라디언트로 채웁니다 */}
|
||||||
{/* 라이트 모드: 부드러운 그레이 톤 (gray → white → gray) */}
|
{/* 라이트 모드: 부드러운 그레이 톤 (gray → white → gray) */}
|
||||||
{/* 다크 모드: 깊은 블랙 톤으로 고급스러운 느낌 */}
|
{/* 다크 모드: 깊은 블랙 톤으로 고급스러운 느낌 */}
|
||||||
|
|
||||||
{/* 추가 그라디언트 효과 1: 우상단에서 시작하는 원형 그라디언트 */}
|
{/* 추가 그라디언트 효과 1: 우상단에서 시작하는 원형 그라디언트 */}
|
||||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-gray-200/30 via-gray-100/15 to-transparent dark:from-gray-800/30 dark:via-gray-900/20" />
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,var(--tw-gradient-stops))] from-gray-200/30 via-gray-100/15 to-transparent dark:from-gray-800/30 dark:via-gray-900/20" />
|
||||||
|
|
||||||
{/* 추가 그라디언트 효과 2: 좌하단에서 시작하는 원형 그라디언트 */}
|
{/* 추가 그라디언트 효과 2: 좌하단에서 시작하는 원형 그라디언트 */}
|
||||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_bottom_left,_var(--tw-gradient-stops))] from-gray-300/30 via-gray-200/15 to-transparent dark:from-gray-700/30 dark:via-gray-800/20" />
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_bottom_left,var(--tw-gradient-stops))] from-gray-300/30 via-gray-200/15 to-transparent dark:from-gray-700/30 dark:via-gray-800/20" />
|
||||||
|
|
||||||
{/* ========== 애니메이션 블러 효과 ========== */}
|
{/* ========== 애니메이션 블러 효과 ========== */}
|
||||||
{/* 부드럽게 깜빡이는 원형 블러로 생동감 표현 */}
|
{/* 부드럽게 깜빡이는 원형 블러로 생동감 표현 */}
|
||||||
@@ -61,7 +61,7 @@ export default async function LoginPage({
|
|||||||
{/* ========== 카드 헤더 영역 ========== */}
|
{/* ========== 카드 헤더 영역 ========== */}
|
||||||
<CardHeader className="space-y-3 text-center">
|
<CardHeader className="space-y-3 text-center">
|
||||||
{/* 아이콘 배경: 그라디언트 원형 */}
|
{/* 아이콘 배경: 그라디언트 원형 */}
|
||||||
<div className="mx-auto mb-2 flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-gray-800 to-black shadow-lg dark:from-white dark:to-gray-200">
|
<div className="mx-auto mb-2 flex h-16 w-16 items-center justify-center rounded-2xl bg-linear-to-br from-gray-800 to-black shadow-lg dark:from-white dark:to-gray-200">
|
||||||
<span className="text-4xl">👋</span>
|
<span className="text-4xl">👋</span>
|
||||||
</div>
|
</div>
|
||||||
{/* 페이지 제목 */}
|
{/* 페이지 제목 */}
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ export default async function ResetPasswordPage({
|
|||||||
const { message } = params;
|
const { message } = params;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex min-h-screen items-center justify-center overflow-hidden bg-gradient-to-br from-gray-50 via-white to-gray-100 px-4 py-12 dark:from-black dark:via-gray-950 dark:to-gray-900">
|
<div className="relative flex min-h-screen items-center justify-center overflow-hidden bg-linear-to-br from-gray-50 via-white to-gray-100 px-4 py-12 dark:from-black dark:via-gray-950 dark:to-gray-900">
|
||||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-gray-200/30 via-gray-100/15 to-transparent dark:from-gray-800/30 dark:via-gray-900/20" />
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,var(--tw-gradient-stops))] from-gray-200/30 via-gray-100/15 to-transparent dark:from-gray-800/30 dark:via-gray-900/20" />
|
||||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_bottom_left,_var(--tw-gradient-stops))] from-gray-300/30 via-gray-200/15 to-transparent dark:from-gray-700/30 dark:via-gray-800/20" />
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_bottom_left,var(--tw-gradient-stops))] from-gray-300/30 via-gray-200/15 to-transparent dark:from-gray-700/30 dark:via-gray-800/20" />
|
||||||
|
|
||||||
<div className="absolute left-1/4 top-1/4 h-64 w-64 animate-pulse rounded-full bg-gray-300/20 blur-3xl dark:bg-gray-700/20" />
|
<div className="absolute left-1/4 top-1/4 h-64 w-64 animate-pulse rounded-full bg-gray-300/20 blur-3xl dark:bg-gray-700/20" />
|
||||||
<div className="absolute bottom-1/4 right-1/4 h-64 w-64 animate-pulse rounded-full bg-gray-400/20 blur-3xl delay-700 dark:bg-gray-600/20" />
|
<div className="absolute bottom-1/4 right-1/4 h-64 w-64 animate-pulse rounded-full bg-gray-400/20 blur-3xl delay-700 dark:bg-gray-600/20" />
|
||||||
@@ -51,7 +51,7 @@ export default async function ResetPasswordPage({
|
|||||||
|
|
||||||
<Card className="border-white/20 bg-white/70 shadow-2xl backdrop-blur-xl dark:border-white/10 dark:bg-gray-900/70">
|
<Card className="border-white/20 bg-white/70 shadow-2xl backdrop-blur-xl dark:border-white/10 dark:bg-gray-900/70">
|
||||||
<CardHeader className="space-y-3 text-center">
|
<CardHeader className="space-y-3 text-center">
|
||||||
<div className="mx-auto mb-2 flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-gray-800 to-black shadow-lg dark:from-white dark:to-gray-200">
|
<div className="mx-auto mb-2 flex h-16 w-16 items-center justify-center rounded-2xl bg-linear-to-br from-gray-800 to-black shadow-lg dark:from-white dark:to-gray-200">
|
||||||
<span className="text-sm font-semibold">PW</span>
|
<span className="text-sm font-semibold">PW</span>
|
||||||
</div>
|
</div>
|
||||||
<CardTitle className="text-3xl font-bold tracking-tight">
|
<CardTitle className="text-3xl font-bold tracking-tight">
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ export default async function SignupPage({
|
|||||||
const { message } = await searchParams;
|
const { message } = await searchParams;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex min-h-screen items-center justify-center overflow-hidden bg-gradient-to-br from-gray-50 via-white to-gray-100 px-4 py-12 dark:from-black dark:via-gray-950 dark:to-gray-900">
|
<div className="relative flex min-h-screen items-center justify-center overflow-hidden bg-linear-to-br from-gray-50 via-white to-gray-100 px-4 py-12 dark:from-black dark:via-gray-950 dark:to-gray-900">
|
||||||
{/* 배경 그라데이션 효과 */}
|
{/* 배경 그라데이션 효과 */}
|
||||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-gray-200/30 via-gray-100/15 to-transparent dark:from-gray-800/30 dark:via-gray-900/20" />
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,var(--tw-gradient-stops))] from-gray-200/30 via-gray-100/15 to-transparent dark:from-gray-800/30 dark:via-gray-900/20" />
|
||||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_bottom_left,_var(--tw-gradient-stops))] from-gray-300/30 via-gray-200/15 to-transparent dark:from-gray-700/30 dark:via-gray-800/20" />
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_bottom_left,var(--tw-gradient-stops))] from-gray-300/30 via-gray-200/15 to-transparent dark:from-gray-700/30 dark:via-gray-800/20" />
|
||||||
|
|
||||||
{/* 애니메이션 블러 효과 */}
|
{/* 애니메이션 블러 효과 */}
|
||||||
<div className="absolute left-1/4 top-1/4 h-64 w-64 animate-pulse rounded-full bg-gray-300/20 blur-3xl dark:bg-gray-700/20" />
|
<div className="absolute left-1/4 top-1/4 h-64 w-64 animate-pulse rounded-full bg-gray-300/20 blur-3xl dark:bg-gray-700/20" />
|
||||||
@@ -32,7 +32,7 @@ export default async function SignupPage({
|
|||||||
|
|
||||||
<Card className="border-white/20 bg-white/70 shadow-2xl backdrop-blur-xl dark:border-white/10 dark:bg-gray-900/70">
|
<Card className="border-white/20 bg-white/70 shadow-2xl backdrop-blur-xl dark:border-white/10 dark:bg-gray-900/70">
|
||||||
<CardHeader className="space-y-3 text-center">
|
<CardHeader className="space-y-3 text-center">
|
||||||
<div className="mx-auto mb-2 flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-gray-800 to-black shadow-lg dark:from-white dark:to-gray-200">
|
<div className="mx-auto mb-2 flex h-16 w-16 items-center justify-center rounded-2xl bg-linear-to-br from-gray-800 to-black shadow-lg dark:from-white dark:to-gray-200">
|
||||||
<span className="text-4xl">🚀</span>
|
<span className="text-4xl">🚀</span>
|
||||||
</div>
|
</div>
|
||||||
<CardTitle className="text-3xl font-bold tracking-tight">
|
<CardTitle className="text-3xl font-bold tracking-tight">
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ export async function signup(formData: FormData) {
|
|||||||
// 이메일 인증 완료 후 리다이렉트될 URL
|
// 이메일 인증 완료 후 리다이렉트될 URL
|
||||||
// 로컬 개발 환경: http://localhost:3001/auth/callback
|
// 로컬 개발 환경: http://localhost:3001/auth/callback
|
||||||
// 프로덕션: NEXT_PUBLIC_BASE_URL 환경 변수에 설정된 주소
|
// 프로덕션: NEXT_PUBLIC_BASE_URL 환경 변수에 설정된 주소
|
||||||
emailRedirectTo: `${process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3001"}/auth/callback`,
|
emailRedirectTo: `${process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3001"}/auth/callback?auth_type=signup`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export default function ResetPasswordForm() {
|
|||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
className="h-11 w-full bg-gradient-to-r from-gray-900 to-black font-semibold text-white shadow-lg transition-all hover:from-black hover:to-gray-800 hover:shadow-xl dark:from-white dark:to-gray-100 dark:text-black dark:hover:from-gray-100 dark:hover:to-white"
|
className="h-11 w-full bg-linear-to-r from-gray-900 to-black font-semibold text-white shadow-lg transition-all hover:from-black hover:to-gray-800 hover:shadow-xl dark:from-white dark:to-gray-100 dark:text-black dark:hover:from-gray-100 dark:hover:to-white"
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user