이 글에서는 Route Handler 전에 처리되는 Middleware에 대해 알아보도록 하겠습니다.
Middleware란 무엇인가?
Middleware는 Route Handler 이전에 호출되는 함수입니다.
이를 통해 Middleware 함수는 Request 및 Response 객체, 그리고 Application의 Request-Response 주기에서 다음 Middleware 함수 호출을 통해 다른 작업을 처리할 수 있습니다.
예)
- 공통으로 적용되는 코드 수행 (Logging, 인증 등)
- Request와 Response 객체의 내용 변경
- 유효성(Validation) 체크
의존성 주입(DI, Dependency Injection)
Provider와 Controller의 관계와 마찬가지로 동일한 모듈 내에서 사용 가능한 의존성을 주입할 수 있습니다.
이는 마찬가지로 생성자를 통해 이루어집니다.
Middleware 구현 및 적용
사용법
@Injectable()
Decorator를 사용합니다.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);