인증과 인가란?
인증(Authentication)과 인가(Authorization)는 웹 애플리케이션 보안의 핵심 개념입니다. 인증은 "사용자가 누구인지 확인하는 과정"이고, 인가는 "사용자가 특정 자원에 접근할 권한이 있는지 확인하는 과정"입니다.
일상생활에서 비유하면, 공항에서 신분증을 확인하는 것이 인증이고, 탑승권을 확인하여 특정 항공편에 탑승할 수 있는지 검사하는 것이 인가입니다. 두 과정 모두 통과해야만 원하는 자원(항공기 탑승)에 접근할 수 있습니다.
웹 개발에서는 이 두 개념이 함께 작동하여 애플리케이션의 보안을 보장하며, 특히 프론트엔드에서는 사용자 경험과 보안을 동시에 고려해야 합니다.
핵심 개념
1. 인증(Authentication) 메커니즘
인증은 사용자의 신원을 검증하는 과정으로, 다양한 방식이 존재합니다.
// JWT 기반 인증 구현 예시
interface LoginCredentials {
email: string;
password: string;
}
interface AuthResponse {
token: string;
refreshToken: string;
user: User;
}
class AuthService {
async login(credentials: LoginCredentials): Promise<AuthResponse> {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
});
if (!response.ok) {
throw new Error('Authentication failed');
}
return response.json();
}
async refreshToken(): Promise<string> {
const refreshToken = localStorage.getItem('refreshToken');
const response = await fetch('/api/auth/refresh', {
method: 'POST',
headers: { 'Authorization': `Bearer ${refreshToken}` }
});
return response.json().then(data => data.token);
}
}
비밀번호 기반 인증 외에도 생체 인증(WebAuthn API), OTP 인증, 소셜 로그인 등이 있으며, 보안 강화를 위해 MFA(Multi-Factor Authentication)가 널리 사용됩니다.
2. 인가(Authorization) 시스템
인가는 인증된 사용자가 특정 리소스에 접근할 권한을 확인하는 과정입니다.
// 역할 기반 권한 확인 시스템
enum UserRole {
ADMIN = 'admin',
MANAGER = 'manager',
USER = 'user'
}
interface Permission {
resource: string;
actions: string[];
}
class AuthorizationService {
private rolePermissions: Record<UserRole, Permission[]> = {
[UserRole.ADMIN]: [
{ resource: 'users', actions: ['create', 'read', 'update', 'delete'] },
{ resource: 'reports', actions: ['read', 'export'] }
],
[UserRole.MANAGER]: [
{ resource: 'users', actions: ['read', 'update'] },
{ resource: 'reports', actions: ['read'] }
],
[UserRole.USER]: [
{ resource: 'profile', actions: ['read', 'update'] }
]
};
hasPermission(userRole: UserRole, resource: string, action: string): boolean {
const permissions = this.rolePermissions[userRole] || [];
return permissions.some(permission =>
permission.resource === resource &&
permission.actions.includes(action)
);
}
}
3. 접근 제어 방식
대표적인 접근 제어 방식으로 RBAC, ABAC, ReBAC가 있습니다.
// RBAC (Role-Based Access Control) 구현
interface RBACContext {
user: User;
roles: string[];
}
class RBACGuard {
canAccess(context: RBACContext, requiredRole: string): boolean {
return context.roles.includes(requiredRole);
}
}
// ABAC (Attribute-Based Access Control) 구현
interface ABACContext {
user: User;
resource: Resource;
environment: Environment;
action: string;
}
class ABACEngine {
evaluate(context: ABACContext, policy: Policy): boolean {
// 사용자 속성, 리소스 속성, 환경 정보를 종합하여 판단
return policy.rules.every(rule => {
switch (rule.type) {
case 'user-attribute':
return this.checkUserAttribute(context.user, rule);
case 'time-based':
return this.checkTimeConstraint(context.environment, rule);
case 'resource-ownership':
return context.resource.ownerId === context.user.id;
default:
return false;
}
});
}
}
4. 프론트엔드 보안 고려사항
프론트엔드에서는 토큰 저장, Route Guard, API 호출 보안 등을 고려해야 합니다.
// React Router와 함께 사용하는 Route Guard 예시
import { Navigate } from 'react-router-dom';
interface ProtectedRouteProps {
children: React.ReactNode;
requiredRole?: string;
}
function ProtectedRoute({ children, requiredRole }: ProtectedRouteProps) {
const { isAuthenticated, user } = useAuth();
const authService = new AuthorizationService();
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
if (requiredRole && !authService.hasPermission(user.role, 'page', 'access')) {
return <Navigate to="/unauthorized" replace />;
}
return <>{children}</>;
}
// 사용 예시
function App() {
return (
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route
path="/admin"
element={
<ProtectedRoute requiredRole="admin">
<AdminDashboard />
</ProtectedRoute>
}
/>
</Routes>
);
}
정리
| 구분 | 인증(Authentication) | 인가(Authorization) |
|---|---|---|
| 목적 | 사용자 신원 확인 | 접근 권한 확인 |
| 질문 | "누구인가?" | "무엇을 할 수 있는가?" |
| 방식 | 비밀번호, 생체인증, MFA | RBAC, ABAC, ReBAC |
| 구현 위치 | 로그인 과정 | API 호출, 페이지 접근 시 |
핵심 포인트:
- 인증이 먼저 수행된 후 인가가 진행됩니다
- 프론트엔드에서는 사용자 경험을 해치지 않으면서도 보안을 유지해야 합니다
- 토큰은 안전한 곳에 저장하고, 만료 시 자동 갱신하는 메커니즘이 필요합니다
- 접근 제어 방식은 애플리케이션의 복잡도와 보안 요구사항에 따라 선택해야 합니다