NestJs 스터디(4)

연꽃·2022년 2월 17일
0

NestJs

목록 보기
4/7
post-thumbnail

미들웨어(Middleware)

🏀 미들웨어란?

  • 미들웨어는 라우팅 핸들러가 실행되기 이전에 호출되는 함수다.
  • 미들웨어는 요청 객체와 응답 객체에 접근할 수 있으며, 애플리케이션의 요청-응답 사이클의 미들웨어 함수인 next() 함수까지 접근할 수 있다.
  • 참고로 라우트 핸들러는 엔드포인트마다 동작을 수행하는 컴포넌트로서, 요청 경로와 컨트롤러를 매핑해 준다.
  • NestJs의 미들웨어는 기본적으로 Express와 동일하다고 한다.
  • 그래서 미들웨어에서 할 수 있는 작업은 다음과 같다.
    - 어떤 코드든 실행할 수 있다.
    - 요청 객체나 응답 객체에 접근하고 조작할 수 있다.
    - 요청-응답 사이클을 끝낼 수 있다.
    - 스택 상의 다음 미들웨어를 호출할 수 있다.
    - 현재 미들웨어 함수가 요청-응답 사이클을 끝내지 않는다면, 반드시 next() 함수를 호출해서 다음 미들웨어 함수에게 제어권을 넘겨줘야한다. 그러지 않으면 HTTP 요청-응답 사이클은 타임아웃될 때까지 무한정 대기상태가 된다.

🏀 커스텀 미들웨어

  • 커스텀 미들웨어는 함수형이나 클래스형으로 구현할 수 있다.

먼저 클래스형 미들웨어는 다음과 같다.

// /src/common/middleware/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();
  }
}

다음으로 함수형 미들웨어는 다음과 같다.

// /src/common/middleware/logger.middleware.ts

import { Request, Response, NextFunction } from 'express';

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

그런데 공식문서에 의하면 위와 같은 상황에서는 멤버가 없고, 추가 메서드도 없고, 의존성도 없으므로 함수형으로 만드는 방법이 더 좋다고 한다.(막연하게만 알겠어)

🏀 의존성 주입

  • 클래스형 미들웨어에도 의존성 주입이 가능하다. 프로바이더나 컨트롤러와 마찬가지로 동일 모듈 내에서 주입 가능한 의존성을 생성자 메서드를 통해서 주입할 수 있다.
  • 다른 것들과 의존성 주입에 차이가 없는 것처럼 보인다.

🏀 모듈에 등록

  • 다른 것과 달리 미들웨어는 @Module() 데코레이터에서 등록하는 것이 아니다!!!
  • 대신에 모듈 클래스에서 configure() 메서드를 선언하여 NestModule 인터페이스를 구현하도록 한다.

예시를 통해 살펴보자

// app.module.ts

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() 메서드에 사용할 미들웨어를 전달하고, forRoutes() 매개변수로 라우팅 핸들러를 특정한다.
  • 즉 위 예시에서는 LoggerMiddleware라는 미들웨어를 전달하고, '/cats'라는 라우팅 핸들러를 특정지어 여기에서만 사용되게 하는 상황이다.
  • 추가적으로 미들웨어가 특정 HTTP 요청 메서드까지 한정해서 작동하도록 하게 할 수도 있다.

아래의 예시는 Get요청에서 미들웨어가 작동하게 한 모습니다.

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

🏀 다중 미들웨어 적용

  • apply() 메서드에 여러 인수를 제공해서 다중 미들웨어를 지정할 수도 있다.
  • 미들웨어는 인수로 제공한 순서에 따라 작동합니다.
consumer.apply(
  cors(),
  helmet(),
  logger
).forRoutes(CatsController);

즉, 위 예시에서는 cors(),helmet(),logger의 순서로 작동한다는 이야기이다.

🏀 경로 제외

  • exclude() 메서드로 특정 경로의 라우팅에는 미들웨어가 적용되지 않게 할 수 있다.

다음과 같은 방법으로 활용할 수 있다고 한다.

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

위 예시에서는 '/cats'라는 라우팅 핸들러에서 Get,Post 요청을 제외하고 미들웨어를 실행하는 것으로 보인다.

🏀 전역으로 등록

  • 모든 라우팅에 미들웨어를 전역으로 등록하려면 INestApplication 인스턴스에 use() 메서드를 이용한다.
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);

추가적으로 다음과 같은 문구가 공식문서에 존재한다.

use() 메서드로 등록하는 전역 미들웨어는 DI 컨테이너 접근이 불가합니다. 따라서 의존성 주입이 되지 않으니, use() 메서드로는 함수형 미들웨어를 등록하는게 좋습니다. 의존성 주입이 필요한 클래스형 미들웨어를 전역으로 등록하려면 루트 모듈에서 .forRoutes('*') 로 미들웨어를 등록하는 등의 다른 방법을 쓰세요.

즉, 함수형의 경우 위와 같은 방법을 이용하고 클래스형의 경우에는 App모듈에 .forRoutes('*') 을 통해 등록하라는 이야기처럼 보인다.

참고: 네스트 공식문서

profile
우물에서 자라나는 중

0개의 댓글