"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.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}
);
}