Nest.JS에 대해 알아보자 - middleware 편
우리는 지금까지 Provider와 Controller에 대해서 알아보았습니다. 이전 장까지 보셨다면, Controller는 들어오는 요청을 처리하고 클라이언트에 응답을 반환하는 일을 담당한다는 것을 알 것입니다! 그리고 Provider는 서비스, 리포지토리, 팩토리 등과 같은 의존성을 주입(Dependency Injection)하고 관리하는 객체를 의미한다는 것도요!
이번 시간에는 Express에서도 익히 봐왔던 middleware에 대해서 알아보는 시간을 가지도록 해보겠습니다.
혹시 이 말이 이해가 안 되시는 분들이 있다면 이전 장을 확인해보면 좋을 것 같습니다. :)
Nest.js에 대해서 알아보자 - Provider편 (tistory.com)
Nest.js에 대해서 알아보자 - Provider편
이전 장에서는 Controller에 대한 개념을 다뤄보았고, 이번에는 Provider에 대해서 다뤄볼 예정입니다.혹시 Contorller에 대한 이해가 부족하신 분들은 이전 포스트를 확인해보시면 Provider에 대한 이해
humor12.tistory.com
🤔Middleware가 뭘까?
Middleware는 요청(Request)과 응답(Response) 사이에서 동작하는 함수입니다. 서버가 클라이언트로부터 요청을 받을 때 그 요청이 처리되는 동안 여러 미들웨어를 거쳐가며 데이터가 조작되거나, 요청의 유효성을 검사하고, 인증이 이루어지는 등 다양한 기능을 수행할 수 있습니다.
Express의 Middleware
그럼 본격적으로 Express에서의 미들웨어를 간단하게 살펴보면서 이해를 도와보도록 하겠습니다.
Express에서는 미들웨어가 함수의 형태로 정의됩니다. 기본적으로는 req, res, next라는 세 가지 매개변수를 받습니다.
- req: 클라이언트의 요청 정보
- res: 서버의 응답 정보
- next: 다음 미들웨어로 제어권을 넘겨주는 함수
function middleware(req, res, next) {
// 미들웨어에서 처리할 작업
console.log('Request URL:', req.originalUrl);
// 다음 미들웨어 호출
next();
}
여기서 주의할 점으로는, next()를 호출하지 않으면 요청은 다음 단계로 넘어가지 못하고 멈춰버리게 됩니다. 따라서 미들웨어 끝에서 next()를 호출하는 것을 잊으면 안 됩니다.
Nest.js의 Middleware
Nest.js도 Express 위에 구축되어 있으므로 Express와 비슷한 방식으로 미들웨어를 사용합니다. 다만, Nest.js는 함수의 형태로 정의 되었던 Express와는 달리, 미들웨어를 클래스나 함수 형태로 구현할 수 있으며, 더 구조적이고 모듈화된 방법으로 미들웨어를 적용할 수 있습니다.
역시나 백문이 불여일견이라 하였으니, 실제 코드와 함께 글을 이어나가보도록 하겠습니다.
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...', req.originalUrl);
next();
}
}
위 예제와 같이 클래스 기반으로 미들웨어를 만들 수 있습니다. 생김새가 Express와 별 다를 것 없이 생겼습니다. 그런데, 저 NestMiddleware는 뭐길래 LoggerMiddleware가 구현(implements)하고 있는 걸까요?
NestMiddleware는 Nest.js에서 미들웨어를 정의할 때 사용하는 인터페이스입니다. LoggerMiddleware 클래스에서 이를 구현하는 이유는 Nest.js의 미들웨어 시스템과 호환되기 위함이라고 합니다.
참고로, use 메서드는 필수적으로 구현해야 합니다. NestMiddleware 인터페이스가 정의하는 메서드이기 때문에, 이를 반드시 구현하지 않으면 TypeScript는 해당 클래스가 인터페이스를 완전히 구현하지 않았다고 판단하고 오류를 발생시킵니다.
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';
@Module({})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('*'); // 모든 경로에 미들웨어 적용
}
}
미들웨어는 Provider와 Contorller와 마찬가지로 모듈 내에서 사용할 수 있도록 종속성 주입이 가능합니다. 위와 같이 모듈 내에서 모든 경로에 LoggerMiddleware를 사용할 수 있도록 만들 수 있습니다.
만약, 위 예제와 달리 미들웨어를 특정 request 메서드로 추가로 제한을 하고 싶다면 어떻게 해야할까요?
import { Module, NestModule, RequestMethod, MiddlewareConsumer } from '@nestjs/common';
@Module({})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes({ path: 'cats', method: RequestMethod.GET }); // 특정 요청에 미들웨어 적용
}
}
위와 같이 path에 경로를 지정하고, 원하는 method를 지정해줌으로써 해결이 가능합니다!
이번에는 미들웨어가 적용되는 특정 경로를 제외하고 싶을 때가 있습니다. 그럴 때는 exlclude 메서드를 사용하여 특정 경로를 쉽게 제외 할 수 있습니다. 아래 코드와 같이 exclude 메서드 안에는 문자열 혹은 제외 경로를 식별할 object를 사용할 수 있습니다!
import { Module, NestModule, RequestMethod, MiddlewareConsumer } from '@nestjs/common';
@Module({})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
'cats/(.*)',
)
.forRoutes(CatsController);
}
마무리
오늘은 Nest.js에서의 미들웨어에 대하여 알아보는 시간을 가졌는데요!
Nest.js는 Express의 기능을 잘 추상화했기 때문에 Express에 대한 기본적인 이해가 있으면 Nest.js에서도 동일한 개념을 쉽게 적용할 수 있습니다!
다음 포스트는 Pipe에 대하여 알아보는 글로 찾아뵙겠습니다