"use client"; import React from "react"; import { useAuth, usePermissions, usePermissionGuard } from "./hooks"; import { UserRole } from "./middleware"; import { Permission } from "./rbac"; // 타입 정의 interface ProtectedRouteProps { children: React.ReactNode; requiredRoles?: UserRole[]; requiredPermissions?: Permission[]; fallback?: React.ReactNode; redirectTo?: string; } interface PermissionGuardProps { children: React.ReactNode; permissions: Permission[]; fallback?: React.ReactNode; requireAll?: boolean; } interface RoleGuardProps { children: React.ReactNode; roles: UserRole[]; fallback?: React.ReactNode; requireExact?: boolean; } interface ConditionalRenderProps { children: React.ReactNode; condition: boolean; fallback?: React.ReactNode; } /** * 인증이 필요한 라우트를 보호하는 컴포넌트 */ export function ProtectedRoute({ children, requiredRoles, requiredPermissions, fallback, redirectTo, }: ProtectedRouteProps) { const { user, isLoading } = useAuth(); const { hasAnyPermission } = usePermissions(); // 로딩 중 if (isLoading) { return (
); } // 인증되지 않은 사용자 if (!user) { if (redirectTo) { window.location.href = `${redirectTo}?callbackUrl=${encodeURIComponent( window.location.pathname )}`; return null; } return ( fallback || (

로그인이 필요합니다

이 페이지에 접근하려면 로그인해야 합니다.

로그인
) ); } // 역할 확인 if (requiredRoles && !requiredRoles.includes(user.role)) { return ( fallback || (

접근 권한이 없습니다

이 페이지에 접근할 권한이 없습니다.

) ); } // 권한 확인 if (requiredPermissions && !hasAnyPermission(requiredPermissions)) { return ( fallback || (

권한이 부족합니다

이 기능을 사용할 권한이 없습니다.

) ); } return <>{children}; } /** * 특정 권한이 있는 경우에만 렌더링하는 컴포넌트 */ export function PermissionGuard({ children, permissions, fallback, requireAll = false, }: PermissionGuardProps) { const { hasPermission, hasAllPermissions, hasAnyPermission } = usePermissions(); const hasRequiredPermissions = requireAll ? hasAllPermissions(permissions) : hasAnyPermission(permissions); if (!hasRequiredPermissions) { return <>{fallback}; } return <>{children}; } /** * 특정 역할이 있는 경우에만 렌더링하는 컴포넌트 */ export function RoleGuard({ children, roles, fallback, requireExact = false, }: RoleGuardProps) { const { user } = useAuth(); if (!user) { return <>{fallback}; } const hasRequiredRole = requireExact ? roles.includes(user.role) : roles.some((role) => { // 역할 계층 구조 고려 if (role === UserRole.GUEST) return true; if (role === UserRole.VIEWER) return [UserRole.VIEWER, UserRole.EDITOR, UserRole.ADMIN].includes( user.role ); if (role === UserRole.EDITOR) return [UserRole.EDITOR, UserRole.ADMIN].includes(user.role); if (role === UserRole.ADMIN) return user.role === UserRole.ADMIN; return false; }); if (!hasRequiredRole) { return <>{fallback}; } return <>{children}; } /** * 조건부 렌더링 컴포넌트 */ export function ConditionalRender({ children, condition, fallback, }: ConditionalRenderProps) { return condition ? <>{children} : <>{fallback}; } /** * 관리자 전용 컴포넌트 */ export function AdminOnly({ children, fallback, }: { children: React.ReactNode; fallback?: React.ReactNode; }) { return ( {children} ); } /** * 편집자 이상 컴포넌트 */ export function EditorOrAbove({ children, fallback, }: { children: React.ReactNode; fallback?: React.ReactNode; }) { return ( {children} ); } /** * 인증된 사용자 전용 컴포넌트 */ export function AuthenticatedOnly({ children, fallback, }: { children: React.ReactNode; fallback?: React.ReactNode; }) { return ( {children} ); } /** * 권한 기반 버튼 컴포넌트 */ interface PermissionButtonProps extends React.ButtonHTMLAttributes { permissions: Permission[]; requireAll?: boolean; children: React.ReactNode; } export function PermissionButton({ permissions, requireAll = false, children, ...props }: PermissionButtonProps) { const canRender = usePermissionGuard(permissions); if (!canRender) { return null; } return ; } /** * 권한 기반 링크 컴포넌트 */ interface PermissionLinkProps extends React.AnchorHTMLAttributes { permissions: Permission[]; requireAll?: boolean; children: React.ReactNode; } export function PermissionLink({ permissions, requireAll = false, children, ...props }: PermissionLinkProps) { const canRender = usePermissionGuard(permissions); if (!canRender) { return null; } return {children}; } /** * 사용자 정보 표시 컴포넌트 */ export function UserInfo({ className }: { className?: string }) { const { user } = useAuth(); if (!user) { return null; } const getRoleDisplayName = (role: UserRole) => { switch (role) { case UserRole.ADMIN: return "관리자"; case UserRole.EDITOR: return "편집자"; case UserRole.VIEWER: return "뷰어"; case UserRole.GUEST: return "게스트"; default: return role; } }; const getRoleBadgeColor = (role: UserRole) => { switch (role) { case UserRole.ADMIN: return "bg-red-100 text-red-800"; case UserRole.EDITOR: return "bg-blue-100 text-blue-800"; case UserRole.VIEWER: return "bg-green-100 text-green-800"; case UserRole.GUEST: return "bg-gray-100 text-gray-800"; default: return "bg-gray-100 text-gray-800"; } }; return (
{user.image && ( {user.name )}
{user.name || user.email}
{user.email} {getRoleDisplayName(user.role)}
); } /** * 권한 기반 메뉴 아이템 컴포넌트 */ interface PermissionMenuItemProps { permissions: Permission[]; children: React.ReactNode; className?: string; onClick?: () => void; } export function PermissionMenuItem({ permissions, children, className, onClick, }: PermissionMenuItemProps) { const canRender = usePermissionGuard(permissions); if (!canRender) { return null; } return (
{children}
); }