[NestJS docs] Middleware

nakkim·2022년 7월 18일
0

NestJS docs

목록 보기
2/10
post-custom-banner

https://docs.nestjs.com/middleware

Middleware

미들웨어는 라우트 핸들러 전(파이프/인터셉터/가드 전)에 호출되는 함수임

  • request, response 객체에 접근 가능
  • next()로 다음 미들웨어 호출 가능

일반적으로 express의 미들웨어와 동일함

express의 미들웨어

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.

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

Dependency injection

Nest 미들웨어는 의존성 주입 가능 (보통은 constructor 이용)

Applying middleware

@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()메소드에 pathmethod를 이용해서 특정 요청에 대해서만 동작하도록 미들웨어를 제한해보자

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() 메소드 바디 내에서 비동기 작업이 완료될 때까지 대기 가능

Route wildcards

패턴 기반 라우팅 지원

forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });

‘ab*cd’는 abcd, ab_cd, abecd 등과 일치

?, +, *, ()는 사용 가능 (-, .는 문자열 기반 경로로 해석됨)

Middleware consumer

MiddlewareConsumer는 헬퍼 클래스이다. 미들웨어를 관리하기 위한 빌트인 메소드를 제공한다.

All of them can be simply chained in the fluent style.

forRoutes() 메소드는

  • 싱글 스트링
  • 멀티 스트링
  • RouteInfo 객체
  • 컨트롤러 클래스
  • 다수의 컨트롤러 클래스

를 받을 수 있음

대부분의 상황에서는 콤마로 구분된 컨트롤러 리스트를 패스할거임

Excluding routes

미들웨어 적용에서 특정 경로를 배제하고 싶을 수 있다.

그럴 때 exclude() 메소드를 사용하렴

  • 싱글 스트링
  • 멀티 스트링
  • RouteInfo 객체

를 받아서 제외시킬 경로를 정의함

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

Functional middleware

위에서 만들어본 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 미들웨어 사용을 고려해보렴!

Multiple middleware

apply() 메소드 안에 콤마로 구분해서 적어주면 여러개 적용 가능

consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);

Global middleware

만약 모든 라우트에 한번에 바인드하고싶다? use() 메소드를 사용하자

const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);

글로벌 미들웨어에서 DI 컨테이너에 접근하는 것은 불가능하다.
You can use a functional middleware instead when using app.use(). Alternatively, you can use a class middleware and consume it with .forRoutes('*') within the AppModule (or any other module).

profile
nakkim.hashnode.dev로 이사합니다
post-custom-banner

0개의 댓글