NestJS 미들웨어 가이드
미들웨어는 라우트 핸들러가 요청을 처리하기 전에 실행되는 함수입니다. NestJS의 미들웨어는 Express의 미들웨어와 동일한 개념을 따르며, 요청 및 응답 객체에 접근하여 다양한 작업을 수행할 수 있습니다.
미들웨어의 기본 개념
미들웨어는 다음과 같은 작업을 수행할 수 있습니다:
- 코드 실행
- 요청 및 응답 객체 변경
- 요청-응답 주기 종료
- 다음 미들웨어 함수 호출
- 특정 조건에 따라 라우트 핸들러 건너뛰기
NestJS에서 미들웨어 구현 방법
NestJS에서는 미들웨어를 두 가지 방식으로 구현할 수 있습니다:
미들웨어 구현 방법
NestJS에서 미들웨어를 구현하는 방법에는 두 가지가 있습니다:
1. 함수형 미들웨어
export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
}2. 클래스형 미들웨어 (NestMiddleware 인터페이스 구현)
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...');
next();
}
}미들웨어 적용 방법
미들웨어는 모듈 클래스의 configure() 메서드를 사용하여 적용합니다. 이를 위해 모듈은 NestModule 인터페이스를 구현해야 합니다.
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('cats');
}
}미들웨어 적용 대상 지정 방법
경로 기반 적용
consumer
.apply(LoggerMiddleware)
.forRoutes('cats');컨트롤러 기반 적용
consumer
.apply(LoggerMiddleware)
.forRoutes(CatsController);HTTP 메서드 기반 적용
import { RequestMethod } from '@nestjs/common';
consumer
.apply(LoggerMiddleware)
.forRoutes({ path: 'cats', method: RequestMethod.GET });여러 미들웨어 적용
consumer
.apply(cors(), helmet(), LoggerMiddleware)
.forRoutes(CatsController);특정 경로 제외
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
'cats/(.*)',
)
.forRoutes(CatsController);전역 미들웨어
전체 애플리케이션에 미들웨어를 적용하려면 main.ts 파일에서 use() 메서드를 사용합니다:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { logger } from './common/middleware/logger.middleware';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);
}
bootstrap();미들웨어의 실제 사용 사례
1. 로깅 미들웨어
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
private logger = new Logger('HTTP');
use(req: Request, res: Response, next: NextFunction) {
const { ip, method, originalUrl } = req;
const userAgent = req.get('user-agent') || '';
res.on('finish', () => {
const { statusCode } = res;
const contentLength = res.get('content-length');
this.logger.log(
`${method} ${originalUrl} ${statusCode} ${contentLength} - ${userAgent} ${ip}`,
);
});
next();
}
}2. 인증 미들웨어
@Injectable()
export class AuthMiddleware implements NestMiddleware {
constructor(private readonly jwtService: JwtService) {}
use(req: Request, res: Response, next: NextFunction) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ message: 'No token provided' });
}
try {
const decoded = this.jwtService.verify(token);
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ message: 'Invalid token' });
}
}
}3. CORS 미들웨어
import * as cors from 'cors';
// main.ts
app.use(cors());요청 처리 프로세스에서 미들웨어의 역할
위 다이어그램은 NestJS의 요청 처리 프로세스와 그 안에서 미들웨어의 위치를 보여줍니다. 미들웨어는 요청 처리 파이프라인의 가장 앞부분에 위치하며, 다음과 같은 중요한 역할을 수행합니다:
- 요청 전처리: 클라이언트로부터 받은 요청을 라우트 핸들러가 처리하기 전에 전처리합니다.
- 요청 필터링: 특정 조건에 따라 요청을 필터링하거나 차단할 수 있습니다.
- 공통 기능 제공: 로깅, 인증, CORS 처리 등 여러 라우트에서 공통적으로 필요한 기능을 제공합니다.
- 요청 변형: 요청 객체를 수정하거나 확장하여 추가 정보를 제공할 수 있습니다.
- 응답 수정: 응답 객체에 접근하여 헤더를 추가하거나 다른 수정을 할 수 있습니다.
요청 처리 프로세스 단계별 흐름
NestJS의 요청 처리 프로세스는 다음과 같은 순서로 진행됩니다:
- HTTP 요청 수신: 클라이언트로부터 요청이 들어옵니다.
- 미들웨어 실행: 적용된 모든 글로벌 미들웨어와 라우트별 미들웨어가 순차적으로 실행됩니다.
- 이 단계에서 로깅, 인증, 요청 본문 파싱(JSON, URL 인코딩 등) 등이 처리됩니다.
- 가드 실행: 특정 라우트에 대한 접근 권한을 확인합니다.
- 인터셉터(전처리): 요청 객체를 변환하거나 추가 로직을 실행합니다.
- 파이프: 입력 데이터 유효성 검사 및 변환을 수행합니다.
- 컨트롤러/라우트 핸들러: 비즈니스 로직을 실행하고 응답을 생성합니다.
- 인터셉터(후처리): 응답을 수정하거나 추가 로직을 실행합니다.
- HTTP 응답 반환: 클라이언트에게 응답을 전송합니다.
미들웨어와 다른 NestJS 컴포넌트의 차이점
| 구성 요소 | 실행 시점 | 주요 용도 |
|---|---|---|
| 미들웨어 | 라우트 핸들러 이전 | 로깅, 인증, 요청 파싱 |
| 가드 | 미들웨어 이후, 라우트 핸들러 이전 | 권한 검사, 역할 기반 접근 제어 |
| 인터셉터 | 가드 이후, 라우트 핸들러 전후 | 요청/응답 변환, 캐싱, 로깅 |
| 파이프 | 인터셉터와 라우트 핸들러 사이 | 데이터 유효성 검사 및 변환 |
| 예외 필터 | 전체 프로세스에서 예외 발생 시 | 예외 처리 및 응답 형식 지정 |
실제 미들웨어 활용 시나리오
1. API 요청 로깅
@Injectable()
export class RequestLoggerMiddleware implements NestMiddleware {
private logger = new Logger('API');
use(req: Request, res: Response, next: NextFunction) {
const startAt = process.hrtime();
const { method, originalUrl } = req;
res.on('finish', () => {
const [seconds, nanoseconds] = process.hrtime(startAt);
const duration = seconds * 1000 + nanoseconds / 1000000;
this.logger.log(`${method} ${originalUrl} ${res.statusCode} - ${duration.toFixed(2)}ms`);
});
next();
}
}2. 사용자 인증 및 정보 추가
@Injectable()
export class AuthenticationMiddleware implements NestMiddleware {
constructor(
private readonly authService: AuthService,
private readonly userService: UserService,
) {}
async use(req: Request, res: Response, next: NextFunction) {
const token = req.headers.authorization?.split(' ')[1];
if (token) {
try {
const decoded = this.authService.verifyToken(token);
const user = await this.userService.findById(decoded.userId);
if (user) {
req.user = user; // 요청 객체에 사용자 정보 추가
}
} catch (error) {
// 토큰 검증 실패 - 사용자 정보는 추가하지 않음
}
}
next(); // 인증 여부와 관계없이 다음 단계로 진행 (가드에서 처리)
}
}3. 요청 속도 제한 (Rate Limiting)
@Injectable()
export class RateLimiterMiddleware implements NestMiddleware {
private readonly limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15분
max: 100, // IP당 15분간 최대 100개 요청
standardHeaders: true,
legacyHeaders: false,
});
use(req: Request, res: Response, next: NextFunction) {
this.limiter(req, res, next);
}
}미들웨어 실행 순서 제어하기
미들웨어는 등록된 순서대로 실행됩니다. 따라서 실행 순서가 중요한 경우에는 순서를 명시적으로 지정해야 합니다:
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(CorsMiddleware, HelmetMiddleware) // CORS, 보안 헤더 먼저 처리
.forRoutes('*')
.apply(LoggerMiddleware, AuthMiddleware) // 로깅, 인증은 그 다음 처리
.forRoutes('auth', 'users', 'products');
}
}미들웨어 실행 순서
- 전역 미들웨어
- 모듈 바인딩 미들웨어
- 가드
- 인터셉터 (컨트롤러 이전)
- 파이프
- 컨트롤러 핸들러
- 인터셉터 (컨트롤러 이후)
- 예외 필터
실제 활용 사례
- 인증/인가 미들웨어
- 로깅 미들웨어
- CORS 처리
- 요청 본문 파싱
- 세션 관리
- 성능 모니터링
미들웨어는 NestJS 애플리케이션에서 횡단 관심사(cross-cutting concerns)를 효과적으로 처리하는 강력한 메커니즘을 제공합니다.