NestJS 가드(Guards) 가이드
가드는 NestJS에서 특정 라우트 핸들러가 요청을 처리해야 하는지 여부를 결정하는 역할을 합니다. 주로 인증과 권한 부여(인가)에 사용되며, 미들웨어와 달리 실행 컨텍스트에 접근할 수 있어 더 정교한 제어가 가능합니다.
가드의 주요 특징
@Injectable()데코레이터를 사용하여 정의CanActivate인터페이스 구현 필요- 요청 처리 여부를 결정하는 boolean 값 반환
- 미들웨어 다음, 인터셉터와 파이프 이전에 실행
가드 구현 방법
모든 가드는 CanActivate 인터페이스를 구현해야 하며, canActivate() 메서드를 제공해야 합니다:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
return this.validateRequest(request);
}
private validateRequest(request: any): boolean {
// 요청의 유효성을 검증하는 로직
// 예: JWT 토큰 확인, 사용자 인증 상태 확인 등
return true; // 또는 false
}
}가드 적용 방법
컨트롤러 레벨 적용
import { Controller, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';
@Controller('cats')
@UseGuards(AuthGuard)
export class CatsController {
// ...
}메서드 레벨 적용
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';
@Controller('cats')
export class CatsController {
@Get()
@UseGuards(AuthGuard)
findAll() {
return this.catsService.findAll();
}
}전역 가드 적용
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AuthGuard } from './auth.guard';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new AuthGuard());
await app.listen(3000);
}
bootstrap();의존성 주입을 통한 전역 가드 적용
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './auth.guard';
@Module({
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
})
export class AppModule {}실행 컨텍스트 활용
가드의 핵심 기능 중 하나는 ExecutionContext 인스턴스에 접근할 수 있다는 것입니다. 이를 통해 현재 실행 중인 핸들러와 그 클래스에 대한 정보를 얻을 수 있습니다:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler());
if (!requiredRoles) {
return true;
}
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some((role) => user.roles?.includes(role));
}
}메타데이터와 가드 결합
@SetMetadata() 데코레이터와 커스텀 데코레이터를 사용하여 가드에 메타데이터를 제공할 수 있습니다:
// roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);// cats.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { RolesGuard } from './roles.guard';
import { Roles } from './roles.decorator';
@Controller('cats')
@UseGuards(RolesGuard)
export class CatsController {
@Get()
@Roles('admin')
findAll() {
return this.catsService.findAll();
}
}비동기 가드
가드는 비동기 작업도 지원합니다:
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request);
if (!token) {
return false;
}
try {
const user = await this.authService.validateToken(token);
request.user = user;
return true;
} catch (error) {
return false;
}
}
private extractTokenFromHeader(request: any): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}예외 처리
가드에서 false를 반환하면 NestJS는 자동으로 ForbiddenException을 발생시킵니다. 더 구체적인 예외를 발생시키려면 다음과 같이 작성할 수 있습니다:
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const isAuthenticated = this.validateRequest(request);
if (!isAuthenticated) {
throw new UnauthorizedException('인증이 필요합니다');
}
return true;
}
private validateRequest(request: any): boolean {
// 인증 로직
return false;
}
}실제 활용 사례
- JWT 인증 가드
- 역할 기반 접근 제어(RBAC)
- API 키 검증
- 요율 제한(Rate limiting)
- IP 기반 접근 제어
가드는 NestJS에서 인증과 권한 부여를 처리하는 강력한 방법을 제공하며, 애플리케이션의 보안을 강화하는 데 중요한 역할을 합니다.