(NestJS-기초강의) 9. 미들웨어 (Middleware)

이 글에서는 Route Handler 전에 처리되는 Middleware에 대해 알아보도록 하겠습니다.

Middleware란 무엇인가?

Middleware는 Route Handler 이전에 호출되는 함수입니다.

출처: https://docs.nestjs.com/middleware

이를 통해 Middleware 함수는 Request 및 Response 객체, 그리고 Application의 Request-Response 주기에서 다음 Middleware 함수 호출을 통해 다른 작업을 처리할 수 있습니다.

예)

  • 공통으로 적용되는 코드 수행 (Logging, 인증 등)
  • Request와 Response 객체의 내용 변경
  • 유효성(Validation) 체크

의존성 주입(DI, Dependency Injection)

Provider와 Controller의 관계와 마찬가지로 동일한 모듈 내에서 사용 가능한 의존성을 주입할 수 있습니다.

이는 마찬가지로 생성자를 통해 이루어집니다.

Middleware 구현 및 적용

사용법

  1. @Injectable() Decorator를 사용합니다.
  2. NestMiddleware 인터페이스를 implements 를 통해 구현합니다.

예제

Middleware 구현

먼저 Request... 로그를 기록하는 logger middleware를 구현합니다.

/core/middlewares/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();
  }
}
Middleware 적용

@Module에는 middleware를 적용하는 속성이 없습니다. 따라서 configure() method를 사용하여 설정합니다.

다음의 경우 /users로의 라우팅을 특정하여 적용하도록 설정하였습니다.
다음과 같이 wild-card(*)를 써서 지정할 수도 있습니다.
.forRoutes({ path: 'ab*cd', method: RequestMethod.ALL })

/app.module.ts

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

// modules
import { UsersModule } from './users/users.module';

// middlewares
import { LoggerMiddleware } from './core/middlewares/logger.middleware';

@Module({
  imports: [UsersModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LoggerMiddleware).forRoutes('users');
  }
}

MiddlewareConsumer

MiddlewareConsumer는 helper class로 middleware를 관리하기 위한 몇가지 method들을 제공합니다.

다음과 같은 작업을 수행할 수 있습니다.

  • 미들웨어 적용(Applying Middleware): apply() method를 사용하여 특정 미들웨어를 적용할 수 있습니다. 이를 통해 Request을 가로채고, Request 또는 Response을 수정하거나, Request 처리 사이클을 조작할 수 있습니다.
  • 특정 path 또는 path/method에 미들웨어 적용(Applying Middleware to Specific Routes or Route/Methods): forRoutes() method를 사용하여 특정 path 또는 path/method에 미들웨어를 적용할 수 있습니다. 이를 통해 미들웨어를 특정 경로 또는 Request 메서드에만 적용할 수 있습니다.
  • 일부 path 제외(Excluding Some Routes): exclude() method를 사용하여 특정 path를 미들웨어 적용에서 제외시킬 수 있습니다. 이를 통해 특정 경로에 대해 미들웨어를 적용하지 않을 수 있습니다.
  • path에 대한 와일드카드 지원(Wildcard Support for Routes): 패턴 기반의 path를 지원하며, 와일드카드를 활용하여 특정 패턴을 매칭시킬 수 있습니다.

제외(exclude) 시키는 예제는 다음과 같습니다

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

Functional Middleware

Middleware의 유형 중 하나로 Class가 아닌 Function으로 Middleware를 정의하는 방식입니다.

Member, 추가 methods, dependencies 가 없습니다.

다음은 위에서 정의한 LoggerMiddleware 를 Functional middleware로 변경한 예제 코드입니다.

/core/middlewares/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

import { UsersController } from './users/users.controller';

// middlewares
import { logger } from './core/middlewares/logger.middleware';

@Module({
  imports: [UsersModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    //consumer.apply(LoggerMiddleware).forRoutes('users');

    // Functional middleware
    consumer.apply(logger).forRoutes(UsersController);
  }
}

Multiple Middleware

여러 개의 Middleware를 순차적으로 실행합니다.

예제

apply() method 내에서 여러 middleware를 쉼표로 구분하여 나열합니다.

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

Global Middleware

모든 등록된 Route에 대하여 적용되는 Middleware로 모든 Request에 적용됩니다.

예제

app.use method를 사용하여 적용합니다.

main.ts

const app = await NestFactory.create(AppModule);
app.use(logger); // logger 미들웨어를 전역으로 적용
await app.listen(3000);

참조 링크

Leave a Comment