120 lines
3.1 KiB
TypeScript
120 lines
3.1 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, ReactNode } from "react";
|
||
import { useRouter } from "next/navigation";
|
||
import { useAuth } from "@/hooks/useAuth";
|
||
import { LoadingSpinner } from "@/components/common/LoadingSpinner";
|
||
|
||
interface AuthGuardProps {
|
||
children: ReactNode;
|
||
requireAuth?: boolean;
|
||
requireAdmin?: boolean;
|
||
redirectTo?: string;
|
||
fallback?: ReactNode;
|
||
}
|
||
|
||
/**
|
||
* 인증 보호 컴포넌트
|
||
* 로그인 상태 및 권한에 따라 접근을 제어
|
||
*/
|
||
export function AuthGuard({
|
||
children,
|
||
requireAuth = true,
|
||
requireAdmin = false,
|
||
redirectTo = "/login",
|
||
fallback,
|
||
}: AuthGuardProps) {
|
||
const { isLoggedIn, isAdmin, loading, error } = useAuth();
|
||
const router = useRouter();
|
||
|
||
useEffect(() => {
|
||
if (loading) return;
|
||
|
||
// 인증이 필요한데 로그인되지 않은 경우
|
||
if (requireAuth && !isLoggedIn) {
|
||
router.push(redirectTo);
|
||
return;
|
||
}
|
||
|
||
// 관리자 권한이 필요한데 관리자가 아닌 경우
|
||
if (requireAdmin && !isAdmin) {
|
||
router.push("/dashboard"); // 또는 권한 없음 페이지
|
||
return;
|
||
}
|
||
}, [isLoggedIn, isAdmin, loading, requireAuth, requireAdmin, redirectTo, router]);
|
||
|
||
// 로딩 중
|
||
if (loading) {
|
||
return (
|
||
fallback || (
|
||
<div className="flex min-h-screen items-center justify-center">
|
||
<LoadingSpinner size="lg" text="인증 정보를 확인하고 있습니다..." />
|
||
</div>
|
||
)
|
||
);
|
||
}
|
||
|
||
// 에러 발생
|
||
if (error && requireAuth) {
|
||
return (
|
||
<div className="flex min-h-screen items-center justify-center">
|
||
<div className="text-center">
|
||
<div className="mb-2 text-red-500">⚠️</div>
|
||
<p className="mb-4 text-gray-600">{error}</p>
|
||
<button
|
||
onClick={() => router.push("/login")}
|
||
className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600"
|
||
>
|
||
로그인 페이지로 이동
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// 인증 조건을 만족하지 않는 경우
|
||
if (requireAuth && !isLoggedIn) {
|
||
return fallback || null;
|
||
}
|
||
|
||
if (requireAdmin && !isAdmin) {
|
||
return (
|
||
<div className="flex min-h-screen items-center justify-center">
|
||
<div className="text-center">
|
||
<div className="mb-2 text-yellow-500">🔒</div>
|
||
<p className="mb-4 text-gray-600">관리자 권한이 필요합니다.</p>
|
||
<button
|
||
onClick={() => router.push("/dashboard")}
|
||
className="rounded bg-gray-500 px-4 py-2 text-white hover:bg-gray-600"
|
||
>
|
||
메인으로 돌아가기
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// 모든 조건을 만족하는 경우 자식 컴포넌트 렌더링
|
||
return <>{children}</>;
|
||
}
|
||
|
||
/**
|
||
* 로그인 여부만 확인하는 간단한 가드
|
||
*/
|
||
export function RequireAuth({ children }: { children: ReactNode }) {
|
||
return <AuthGuard requireAuth={true}>{children}</AuthGuard>;
|
||
}
|
||
|
||
/**
|
||
* 관리자 권한을 요구하는 가드
|
||
*/
|
||
export function RequireAdmin({ children }: { children: ReactNode }) {
|
||
return (
|
||
<AuthGuard requireAuth={true} requireAdmin={true}>
|
||
{children}
|
||
</AuthGuard>
|
||
);
|
||
}
|
||
|
||
export default AuthGuard;
|