[공식문서] Middleware

Haeseo Lee·2023년 4월 7일
0

NestJS 공식문서

목록 보기
4/4
post-thumbnail

공식문서 공부하기
https://docs.nestjs.com/middleware

필요한 부분만 발췌해서 정리

Middleware 정의

Middleware는 route handler 이전에 불려지는 함수이다. Middleware는 request와 response객체에 접근할 수 있고, application의 requst-response 사이클을 다룰 수 있는 next()라는 middleware 함수가 있다.

Middleware의 역할

기본적으로 Nest middleware는 express middleware와 동일하다.
express의 공식문서에서는 middleware의 역할을 다음과 같이 소개한다.

1. 어떤 코드든지 실행
2. request, response 객체 조작 가능
3. requset-response 사이클 종료
4. 스택의 next middleware 호출
5. 현재 middleware가 request-response 사이클을 종료시키지 못했을 경우, next()를 호출하여 다음 middleware 함수로 제어를 넘겨야 한다.
(그렇지 않으면 request가 처리되지 못한 상태로 남겨짐)

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('*')로 셋업하면 된다.

profile
잡생각 많은 인간

0개의 댓글