-
[Nest.js 공식문서 읽기] MiddlewareServer/Nest.js 2023. 1. 27. 15:04
Middleware
미들웨어란 라우트핸들러 전에 호출되는 함수이다.
미들웨어 함수는 req, res 객체에 접근 가능하며 애플리케이션의 요청-응답 주기에서 next() 미들웨어 함수에 접근할 수 있다.
Nest 미들웨어는 express의 미들웨어와 같다.
📌 Official express documentation describes the capabilities of middleware. Middleware functions can perform the following tasks:
execute any code. make changes to the request and the response objects. end the request-response cycle. call the next middleware function in the stack. if the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging.
- 출처 : nest.js 공식문서Nest에서는 커스텀 미들웨어를 함수 또는 클래스로 구현할 수 있는데, 클래스로 구현할 때는 @Injectable() 데코레이터를 사용해야 하고, 반드시 NestMiddleware 인터페이스를 구현(implement)해야 한다.(함수로 구현할 때는 별다른 조치를 하지 않아도 됨)
// logger.middleware.ts 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는 미들웨어의 의존성 주입도 지원하기 때문에 providers나 controller와 마찬가지로 동일한 모듈내의 providers나 controller에서 constructor를 사용하여 의존성을 주입해줄 수 있다.
함수형 미들웨어는 express와 동일하게 만들면 된다.
// logger.middleware.ts import { Request, Response, NextFunction } from 'express'; export function logger(req: Request, res: Response, next: NextFunction) { console.log(`Request...`); next(); };
Applying middleware
@Module() 데코레이터에는 미들웨어를 등록할 수 없다. 대신에 모듈 클래스의 configure() 메소드를 사용하여 등록할 수 있다. 그리고 미들웨어를 포함한 모듈은 반드시 NestModule 인터페이스를 구현(implement)해야 한다.
//app.module.ts import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { BoardsModule } from './boards/boards.module'; import { typeORMConfig } from './configs/typeorm.config'; import { LoggerMiddleware } from './common/middleware/logger.middleware'; @Module({ imports: [TypeOrmModule.forRoot(typeORMConfig), BoardsModule**]**, }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(**LoggerMiddleware**) .forRoutes(**'boards'**); } }
위 코드 예시에서 LoggerMiddleware를 /boards 라우터 핸들러(BoardsController에서 정의한)에 등록했다.
이 방법 말고도, 미들웨어를 구성할 때 루트 경로와 요청 메소드를 포함하는 객체를 forRoutes()메서드에 전달하여 미들웨어를 특정 요청방법으로 제한할 수도 있다.
//app.module.ts import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { BoardsModule } from './boards/boards.module'; import { typeORMConfig } from './configs/typeorm.config'; import { LoggerMiddleware } from './common/middleware/logger.middleware'; @Module({ imports: [TypeOrmModule.forRoot(typeORMConfig), BoardsModule**]**, }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) .forRoutes(**{ path : 'boards', method: RequestMethod.GET }**); } }
또한, configure() 메소드는 async/await 방식으로 비동기로 만들수 있다.
함수형 미들웨어를 루트모듈에 등록하는 것은 클래스 미들웨어를 등록하는 것과 방식은 동일하다.
Middleware consumer
MiddlewareConsumer는 helper class이다. 이 클래스는 미들웨어를 제어할 수 있는 몇 개의 빌트인 메소드를 제공하고, 그 메소드들은 간단하게 fluent style로 체이닝할 수 있다.
- forRoutes() : 이 메소드는 인자로 라우팅 경로(a string, multiple strings)를 받거나 RouteInfo 객체, controller class(하나 또는 여러개)를 받는다. 여러개의 컨트롤러 클래스를 인자로 받을 경우 콤마(,)로 나열한다.
- apply() : 이 메소드는 미들웨어를 적용하는 메소드이다. 하나의 미들웨어를 받거나 여러개의 인수를 사용하여 여러개의 미들웨어를 지정할 수 있다.
//app.module.ts import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { BoardsModule } from './boards/boards.module'; import { typeORMConfig } from './configs/typeorm.config'; import { LoggerMiddleware } from './common/middleware/logger.middleware'; import { BoardsController } from './boards/boards.controller'; @Module({ imports: [TypeOrmModule.forRoot(typeORMConfig), BoardsModule**]**, }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) .forRoutes(BoardsController); } }
Excluding routes
특정 라우트를 미들웨어 적용에서 제외하고 싶을 때 exclude() 메서드를 사용한다.
이 메서드는 인자로 라우팅 경로(a string, multiple strings)를 받거나 RouteInfo 객체를 받을 수 있다.
forRoutes() 메서드처럼 와일드카드 변수를 받을 수 있지만, path-to-regexp 패키지를 사용해야 한다.
consumer .apply(LoggerMiddleware) .exclude( { path: 'boards', method: RequestMethod.GET }, { path: 'boards', method: RequestMethod.POST }, 'boards/(.*)', ) .forRoutes(BoardsController);
Multiple middleware
순차적으로 실행되는 여러개의 미들웨어를 등록하려면 apply() 메소드 내부에 콤마로 구분된 목록을 인자로 보내면 된다.
consumer.apply(cors(), helmet(), logger).forRoutes(BoardsController);
Global middleware
미들웨어를 모든 라우트에 한번에 바인딩하려면 INestApplication 인스턴스에서 제공하는 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();
'Server > Nest.js' 카테고리의 다른 글
[Nest.js] 로컬에서 https 서버 구동하고 postman으로 테스트 하기 (0) 2023.04.04 효율적인 API 문서 관리를 위한 Swagger 도입 (0) 2023.03.10 [Nest.js 공식문서 읽기] Provider (0) 2023.01.27 [Nest.js 공식문서 읽기] Module (0) 2023.01.27 [Nest.js 공식문서 읽기] Controller (0) 2023.01.27