https://docs.nestjs.com/middleware
미들웨어는 라우트 핸들러 전(파이프/인터셉터/가드 전)에 호출되는 함수임
일반적으로 express의 미들웨어와 동일함
Middleware functions can perform the following tasks:
next()
to pass control to the next middleware function. Otherwise, the request will be left hanging.@Injectable() 데코레이터를 이용한 함수나 클래스로 미들웨어를 구현 가능
클래스는 NestMiddleware
인터페이스를 implements 해야하고, 함수는 구현에 특별한 요구사항 없음
아래는 간단 예시
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();
}
}
Nest 미들웨어는 의존성 주입 가능 (보통은 constructor
이용)
@Module 데코레이터에 미들웨어를 위한 자리는 없다. 대신, 모듈 클래스의 configure()
메소드를 이용해서 설정한다.
미들웨어를 포함한 모듈은 NestModule
인터페이스를 implement 해야함
아래는 간단 예시
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');
}
}
위 코드는 /cats로 가는 요청에 적용되는 미들웨어를 나타낸다.
forRoutes()
메소드에 path
와 method
를 이용해서 특정 요청에 대해서만 동작하도록 미들웨어를 제한해보자
import { Module, NestModule, RequestMethod, 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({ path: 'cats', method: RequestMethod.GET });
}
}
configure()
메소드는 async/await를 이용해서 비동기로 만들 수 있음
configure() 메소드 바디 내에서 비동기 작업이 완료될 때까지 대기 가능
패턴 기반 라우팅 지원
forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });
‘ab*cd’는 abcd
, ab_cd
, abecd
등과 일치
?
, +
, *
, ()
는 사용 가능 (-
, .
는 문자열 기반 경로로 해석됨)
MiddlewareConsumer
는 헬퍼 클래스이다. 미들웨어를 관리하기 위한 빌트인 메소드를 제공한다.
All of them can be simply chained in the fluent style.
forRoutes()
메소드는
RouteInfo
객체를 받을 수 있음
대부분의 상황에서는 콤마로 구분된 컨트롤러 리스트를 패스할거임
미들웨어 적용에서 특정 경로를 배제하고 싶을 수 있다.
그럴 때 exclude()
메소드를 사용하렴
RouteInfo
객체를 받아서 제외시킬 경로를 정의함
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
'cats/(.*)',
)
.forRoutes(CatsController);
위에서 만들어본 LoggerMiddleware
클래스는 너무 단순함!
멤버변수도, 추가 메소드도, 의존성도 없다.
그럼 클래스 대신 간단한 함수로 정의하면 안됨?
→ 된다.
이런 형식의 미들웨어를 functional middleware라고 부름
아래는 functional middleware로 바꾼 우리의 로거 미들웨어
import { Request, Response, NextFunction } from 'express';
export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
};
consumer
.apply(logger)
.forRoutes(CatsController);
미들웨어가 어떤 의존성도 사용하지 않으면 간단한 functional 미들웨어 사용을 고려해보렴!
apply()
메소드 안에 콤마로 구분해서 적어주면 여러개 적용 가능
consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);
만약 모든 라우트에 한번에 바인드하고싶다? use()
메소드를 사용하자
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);
글로벌 미들웨어에서 DI 컨테이너에 접근하는 것은 불가능하다.
You can use a functional middleware instead when usingapp.use()
. Alternatively, you can use a class middleware and consume it with.forRoutes('*')
within theAppModule
(or any other module).