[NestJS] Middleware 개념 및 실습

JeungJoo Lee
CrocusEnergy
Published in
6 min readSep 17, 2020

지난 시간 [NestJS] Modules 개념 및 실습 에 대하여 알아보았다. 이번 시간에는 NestJS 개념 중 하나인 Middleware 에 대해 알아보도록 하겠다.

Middleware

미들웨어는 클라이언트 사이드에서 요청한 Http Request 에서 Router Handler 로 가기 전에 기능을 공통화 하여 사용하거나 처리하게 할 수 있는데 그 이후 next 라는 함수를 호출해서 다음 작업이 진행 될 수 있게 한다.

도식화를 하자면 아래와 같다.

핵심은 클라이언트로 부터 들어온 Request 에서 Response 과정의 전체 라이프 사이클에서 next 라는 함수를 통해 특정 함수를 실행하고 처리할 수 있다. 백문이 불여일견이라는 말이 있듯이 Nest 에서는 Middleware 처리를 어떻게 하는지 아래 코드를 보고 살펴보도록 하자.

우선 Middleware를 만들기 위해 NestMiddleware 라는 인터페이스를 살펴볼 필요가 있다.

// @nestjs/common 패키지 아래 존재하는 NestMiddleware
export interface NestMiddleware<TRequest = any, TResponse = any> {
use(req: TRequest, res: TResponse, next: () => void): any;
}

Middleware를 NestJS 에서 구현하기 위해서는 위의 Interface 를 implements 하여 구현해야 한다. middleware 라는 폴더를 src 밑에 만들고 LoggerMiddleware.ts 라는 소스 파일을 만들어보겠다.

src/middleware/LoggerMiddleware.ts

import { Injectable, NestMiddleware } from '@nestjs/common';@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void): any {
**console.log('Client Request 요청됨.');
console.log(req);**
next();
}
}

NestMiddleware 를 implements 하여 정의된 use 함수를 LoggerMiddleware 에서 구현하는 모습이다. 위의 미들웨어의 기능으로 단순하게 console.log 를 통해 어떤식으로 작동이 되는지 로그를 찍었고 use 함수의 첫 번째 파라미터인 any 타입으로 된 req 객체를 로깅해보는 역할이다. 사실 req 는 HttpRequest 타입으로 정의할 수 있다. 로깅을 한 이후에 next() 를 하지 않는다면 Response 가 되지 않는 상태로 아마 클라이언트 쪽에서는 타임아웃이 발생할 때까지 hang이 걸릴 것이라 추측된다. 여튼 이 LoggerMiddleware 를 Injectable 데코레이터로 디펜던시로 사용하고 이 LoggerMiddleware 를 아래 AppModule 에서 NestModule 을 implement 하여 configure 를 통해 MiddlewareConsumer 객체를 통해 apply 시켜보도록하겠다.

src/app.module.ts

import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
import { TestModule } from './test/test.module';
import { LoggerMiddleware } from './middleware/LoggerMiddleware';
@Module({
imports: [UserModule, TestModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements N**estModule {
configure(consumer: MiddlewareConsumer): any {
consumer
.apply(LoggerMiddleware)
.forRoutes('user')
}
}**

위의 Bold 체로 한 부분이 코드가 추가 된 부분인데 forRoutes 의 경우 어떤 Route Path 가 들어왔을 때 미들웨어를 사용할 것인지도 구분할 수 있다. 이 설정이 끝나면 정상적으로 Context Reload가 될 것이고 api 를 호출하면 다음과 같은 로그가 출력될 것이다.

Client Request 요청됨 메시지와 console.log로 출력했던 HttpRequest 객체가 로깅되는데 이러한 부분을 통해 Header를 조작하거나 출력되는 Response를 조작하여 공통화 처리할 수 있다.

만약, 특정 URL 에 대해서 Middleware를 사용하지 않을 수도 있는데 exclude를 통해 Route path와 Method를 정의 하므로 특정을 예외로 처리할 수도 있다.

export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer): any {
consumer
.apply(LoggerMiddleware)
**.exclude({path:'user/list', method: RequestMethod.GET})**
.forRoutes('user')
}}

Apply를 진행할 때도 하나의 기능만이 아니라 여러 기능들을 설정할 수 있는데 다음 과 같이 설정할 수 있다.

configure(consumer: MiddlewareConsumer): any {
consumer
.apply(**cors(), LoggerMiddleware)**
.exclude({path:'user/list', method: RequestMethod.GET})
.forRoutes('user')
}

위에는 cors 를 설정할 것인데 helmet 이나 기타 미들웨어들을 직접 만들거나 라이브러리를 활용하여 적용 시킬 수 있다.

--

--