NestJS의 미들웨어, 파이프, 가드 - maeng2418/zzik-meok GitHub Wiki
NestJS에서 HTTP 요청 처리 순서:
- 미들웨어 (Middleware)
- 가드 (Guard)
- 인터셉터 (Interceptor) - 요청 전
- 파이프 (Pipe)
- 컨트롤러 메서드 실행
- 인터셉터 (Interceptor) - 응답 후
- 예외 필터 (Exception Filter) - 예외 발생 시
미들웨어는 라우트 핸들러가 실행되기 전에 호출되는 함수로, 요청 및 응답 객체에 접근하여 수정할 수 있습니다.
- 실행 시점: 요청 처리 사이클에서 가장 먼저 실행됨
- 기능: 요청/응답 객체 수정, 요청-응답 사이클 종료, 다음 미들웨어로 제어 전달
- 사용 사례: 로깅, CORS 처리, 요청 파싱, 헤더 수정, 압축/해제
- 적용 범위: 전역 또는 특정 경로 패턴에 적용 가능
-
구현 방법: 함수 또는
NestMiddleware
인터페이스 구현 클래스
// 함수형 미들웨어
export function logger(req: Request, res: Response, next: Function) {
console.log(`Request... ${req.method} ${req.path}`)
next()
}
// 클래스형 미들웨어
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: Function) {
console.log(`Request... ${req.method} ${req.path}`)
next()
}
}
// 적용 예시 (AppModule)
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('users')
}
}
가드는 특정 라우트에 대한 접근 권한을 결정하는 역할을 합니다. 주로 인증 및 권한 부여에 사용됩니다.
- 실행 시점: 미들웨어 이후, 파이프 이전에 실행됨
- 기능: 특정 요청의 처리 여부를 결정(true/false 반환)
- 사용 사례: 인증, 권한 부여, 역할 기반 접근 제어(RBAC)
- 적용 범위: 전역, 컨트롤러, 메서드 레벨에 적용 가능
-
구현 방법:
CanActivate
인터페이스 구현 클래스
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private jwtService: JwtService) {}
canActivate(context: ExecutionContext): boolean | Promise<boolean> {
const request = context.switchToHttp().getRequest()
try {
const token = request.headers.authorization.split(' ')[1]
const payload = this.jwtService.verify(token)
request.user = payload
return true
} catch (e) {
return false
}
}
}
// 적용 예시
@UseGuards(AuthGuard)
@Controller('users')
export class UsersController {
// ...
}
파이프는 클라이언트 요청에서 들어온 데이터를 변환하거나 검증하는 역할을 합니다.
- 실행 시점: 라우트 핸들러가 실행되기 직전, 가드 이후 실행됨
- 기능: 입력 데이터 변환 및 유효성 검사
- 사용 사례: DTO 유효성 검사, 파라미터 타입 변환, 데이터 정제
- 적용 범위: 전역, 컨트롤러, 메서드, 파라미터 레벨에 적용 가능
-
구현 방법:
PipeTransform
인터페이스 구현 클래스
// 내장 ValidationPipe 사용
@Post()
createUser(@Body(new ValidationPipe()) createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
// 커스텀 파이프 예시
@Injectable()
export class ParseIntPipe implements PipeTransform<string, number> {
transform(value: string, metadata: ArgumentMetadata): number {
const val = parseInt(value, 10);
if (isNaN(val)) {
throw new BadRequestException('Validation failed');
}
return val;
}
}
// 적용 예시
@Get(':id')
findOne(@Param('id', new ParseIntPipe()) id: number) {
return this.usersService.findOne(id);
}
특성 | 미들웨어 | 가드 | 파이프 |
---|---|---|---|
주요 목적 | 요청 전처리, 로깅 | 인증, 인가 | 데이터 변환, 유효성 검사 |
실행 순서 | 1️⃣ 첫 번째 | 2️⃣ 두 번째 | 3️⃣ 세 번째 |
요청 차단 가능성 | 가능 (next() 호출 안 함) |
가능 (false 반환) |
예외 발생으로 간접 차단 |
접근 가능한 컨텍스트 |
req , res 객체 |
ExecutionContext |
파라미터 값 |
적용 범위 | 전역, 경로 패턴 | 전역, 컨트롤러, 메서드 | 전역, 컨트롤러, 메서드, 파라미터 |
-
미들웨어
- Express 미들웨어와 호환되어 생태계의 다양한 미들웨어 활용 가능
- 라우트 특정 로직보다는 애플리케이션 전반적인 기능에 적합
- 경로 패턴별로 적용 가능하지만 메서드 단위 적용은 불가능
-
가드
- 권한 부여 로직을 컨트롤러에서 분리하여 코드 재사용성 향상
-
ExecutionContext
를 통해 실행 컨텍스트에 대한 접근 가능 - 메타데이터를 활용한 세밀한 권한 제어 가능 (ex:
@Roles()
데코레이터)
-
파이프
- 클래스 유효성 검사를 위해
class-validator
와 함께 사용하면 효과적 - 파라미터 레벨까지 적용 가능하여 세밀한 데이터 검증 가능
- 전역 파이프를 설정하여 모든 엔드포인트의 입력 데이터 검증 가능
- 클래스 유효성 검사를 위해