공식문서 공부하기
https://docs.nestjs.com/middleware
필요한 부분만 발췌해서 정리
Middleware 정의
Middleware는 route handler 이전에 불려지는 함수이다. Middleware는 request와 response객체에 접근할 수 있고, application의 requst-response 사이클을 다룰 수 있는 next()라는 middleware 함수가 있다.
Middleware의 역할
기본적으로 Nest middleware는 express middleware와 동일하다.
express의 공식문서에서는 middleware의 역할을 다음과 같이 소개한다.
Middleware 구현
Nest middleware는 함수, 클래스 중 어떤 형태로 만들어도 상관없고 @Injectable() 데코레이터를 붙여준다. 클래스로 구현할 경우 반드시 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();
}
}
Dependency injection
Nest middleware 역시도 의존성으로서 주입이 가능하다. 보통 module의 constructor내에서 주입된다.
Applying middleware
@Module() 데코레이터 내에서 middleware가 들어갈 수 있는 자리는 없다.
대신 module 클래스 내에서 configure() 메소드 안에서 middleware를 셋업하며, middleware를 포함하는 module은 반드시 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');
}
}
앞서 만든 LoggerMiddleware를 특정 route에 대해 사용하고 싶은 경우 forRoutes() 내에서 이를 정의해주면 된다.
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 사용이 가능하다.
Middleware consumer
MiddlewareConsumer는 일종의 helper 클래스이다. middleware를 관리할 수 있는 built-in 메소드들을 제공한다.
forRoutes()는 single/multi strings, RouteInfo 객체, controller 등을 넘겨줄 수 있다. 보통은 콤마(,)로 구분된 controller들의 배열을 넘겨준다.
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
import { CatsController } from './cats/cats.controller';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(CatsController);
}
}
Excluding Routes
모든 route에 대해 middleware를 적용해야 할 필요가 있지는 않을 것이다. exclude()를 이용하여 특정 route를 제외시킬 수 있다. exclude()에는 single/multi strings, RouteInfo 객체를 넘겨줄 수 있다.
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
'cats/(.*)',
)
.forRoutes(CatsController);
Functional middleware
앞서 만든 LoggerMiddleware는 클래스로 만들었지만 member도 없고, 다른 method나 의존성도 없기 때문에 함수로도 충분히 만들 수 있다.
import { Request, Response, NextFunction } from 'express';
export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
};
이런 식으로 middleware가 다른 의존성이 필요하지 않을 경우 간단하게 함수형으로 구현하는 게 더 깔끔할 수 있다.
Multiple middleware
middleware를 여러 개 쓰고 싶다면 apply() 안에 열거해주면 된다.
consumer
.apply(cors(), helmet(), logger)
.forRoutes(CatsController);
Global middleware
모든 route에 대해 middleware를 적용하고 싶으면 main.ts에서 use()를 사용해 INestApplication 인스턴스에 적용해주면 된다.
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);
단, global middleware에서 DI container에 접근하는 것은 불가능하다. 따라서 app.use()에 middleware를 넣어주고 싶으면 functional middleware를 사용해야 한다. 또다른 방법으로는, class middleware를 사용하되 AppModule 안에서 forRoutes('*')로 셋업하면 된다.