[nestjs] middleware

Creating the dots·2022년 5월 13일
0

nestjs

nestjs 공식문서를 부분적으로 정리한 내용입니다.

middleware 작성하기

미들웨어는 라우트 핸들러가 실행되기 전에 실행되는 함수이다. 미들웨어는 요청과 응답 객체에 접근할 수 있으며 next()로 미들웨어를 호출한다.
네스트 미들웨어는 기본적으로 익스프레스 미들웨어와 동일하다. 다음은 익스프레스 공식문서에 작성된 미들웨어에 대한 설명이다.


미들웨어 함수는 다음과 같은 기능을 수행한다: - 코드를 실행한다. - 요청과 응답객체를 변경할 수 있다. - 요청-응답 사이클을 끝낼 수 있다. - 다음 미들웨어를 호출한다. - 만약 현재 실행중인 미들웨어가 요청-응답 사이클을 끝내지 않는다면, `next()`를 호출해서 다음 미들웨어가 실행되도록 해야한다.
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('LoggerMiddleware is working now');
    next();
  }
}
  • 네스트에서는 커스텀 미들웨어를 함수 또는 클래스의 형태로 적용할 수 있으며 @Injectable() 데코레이터를 사용한다.
  • 함수가 특별한 요구사항이 없는 경우, 클래스는 NestMiddleware 인터페이스를 implement해야한다.
  • 네스트 미들웨어 역시 dependency injection이 가능하다. 기존과 마찬가지로 constructor를 통해 설정할 수 있다.

middleware 적용하기

생성한 미들웨어를 적용해보자. @Module에서 미들웨어를 불러올때는 모듈 클래스의 configure() 메소드를 사용한다. 미들웨어를 사용하는 모듈은 NestModule을 implement해야한다. 다음은 LoggerMidleware를 AppModule 레벨에 적용한 예시이다.

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');
  }
}

//만약 특정 메소드에 대해서만 미들웨어를 실행시키고 싶은 경우
.forRoutes({ path: 'cats', method: RequestMethod.GET });
  • 위의 예시는 LoggerMiddleware를 /cats 라우트 핸들러에 적용했다. 따라서 /cats로 들어오는 모든 요청에 대해서는 LoggerMiddleware가 실행될 것이다.
  • 또한, forRoutes 메소드에 객체를 전달하여 미들웨어가 요청들 중 특정 메소드에 대해서만 실행되도록 설정할 수 있다.
    • route의 path에 와일드카드를 사용할 수도 있다.
  • configure 메소드는 async/await 키워드를 사용해서 비동기로 실행시킬 수 있다.

middleware consumer

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);
  }
}

위의 예시에 configure 메소드 내에 consumer의 타입이 MiddlewareConsumer로 지정되어있다. 이는 helper class로 미들웨어를 다루기 위한 내장 메소드를 제공한다. 위에서 작성된대로 fluent style로 체이닝할 수 있다.

  • forRoutes 메소드 인자로는 string, multiple strings, a RouteInfo Object, a controller class, multiple controller classes 등이 올 수 있다. 일반적으로는, 여러개의 controller들을 쉼표로 이어 작성할 것이다.

    • 위의 예시에서는 하나의 컨트롤러(CatsController)에 대한 미들웨어를 설정해주었다.
  • apply 메소드는 하나의 미들웨어를 입력받거나 여러개를 입력받을 수 있다.

    • consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);
    • 이 부분은 공식문서 참고!
  • 특정 라우트에 대해서 설정해줄 수 있는 것과 마찬가지로 exclude 메소드를 사용하여 특정 라우트를 제외시킬 수도 있다. exclude 메소드는 string, multiple strings, a RouteInfo Object 등을 인자로 받는다. 아래의 예시는 GET /cats, POST /cats, cats/.* 등에 대해서는 미들웨어가 실행되지 않는다.

consumer
  .apply(LoggerMiddleware)
  .exclude(
    { path: 'cats', method: RequestMethod.GET },
    { path: 'cats', method: RequestMethod.POST },
    'cats/(.*)',
  )
  .forRoutes(CatsController);

Functional middleware

앞선 예시들에서는 클래스로 미들웨어를 생성했는데, 함수로도 할 수 있다. 다음은 클래스 기반 미들웨어를 함수형태로 변경한 예시이다.

//logger.middleware.ts
import { Request, Response, NextFunction } from 'express';

export function logger(req: Request, res: Response, next: NextFunction) {
  console.log(`Request...`);
  next();
};

//app.module.ts
mport { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatsController } from './cats/cats.controller';
import { CatsModule } from './cats/cats.module';
import { logger } from './common/middleware/logger.middleware';

@Module({
  imports: [CatsModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(logger).forRoutes(CatsController); //apply 시키는 함수명만 변경됨
  }
}

Global middleware

모든 라우트에서 실행되는 미들웨어를 만들고 싶다면, main.ts 파일에서 생성된 app에 대해 use()를 사용하면 된다.

//main.ts
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);
  • 함수형 미들웨어를 쓰는 경우, app.use를 사용할 수 있고, 클래스 미들웨어를 쓴다면, .forRoutes('*')와 같이 작성해줄 수 있다.
profile
어제보다 나은 오늘을 만드는 중

0개의 댓글