Middleware
•
Middleware 란 route handler 이전에 호출되는 함수이다.
•
미들웨어 함수는 어플리케이션의 요청-응답 사이클에서 request 와 response 오브젝트와 next()(미들웨어 함수) 에 접근한다.
•
next() 미들웨어 함수는 일반적으로 next 라는 이름을 가진 변수로 denoted(표시) 된다.
•
네스트 미들웨어는 기본적으로, express 의 미들웨어와 동일합니다.
•
The following description from the official express documentation describes the capabilities of middleware :
Middleware functions can perform the following tasks:
•
execute any code.
•
make changes to the request and the response objects. (요청과 응답 오브젝트를 변경 할수 있다.)
•
end the request-response cycle. (요청과 응답 사이클을 종료할 수 있다)
•
call the next middleware function in the stack. (스택에 있는 다음 미들웨어 펑션을 콜 할 수 있다.)
if the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging.
(만약 현재 미들웨어 함수가 요청-응답 사이클의 끝이 아니라면, 이 미들웨어 함수는 반드시 next() 함수를 호출하여, 다음 미들웨어 함수에 컨트롤을 넘겨야 한다. 그렇지 않으면 요청은 hanging 으로 남는다.
•
당신은 함수로나, @Injectable() 데코레이션이 있는 class 로 커스텀 네스트 미들웨어를 만들 수 있습니다.
•
함수는 어떤 특별한 요구사항이 있지 않지만, class 는 반드시 NestMiddleware interface 를 implement 해야 합니다.
•
class method 를 이용하여 간단한 미들웨어 기능을 구현해봅시다.
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();
}
}
Plain Text
복사
Dependency Injection
•
네스트 미들웨어는 의존성주입을 완벽하게 지원합니다.
•
프로바이더, 컨트롤러와 마찬가지로, 같은 모듈 안에서 사용 가능한 의존성 주입을 할수 있습니다.
•
평소와 마찬가지로, 이 것은 constructor 를 통해 이루어진다.
Applying middleware
•
@Module() 데코레이터 에는 미들웨어를 위한 장소가 없다.
•
하지만 우리는 모듈 클레스의 configure() 메소드를 사용해 미들웨어를 set 한다.
•
미들웨어를 포함하는 모듈은 반드시 NestModule interface 를 implement 해야 한다.
•
LoggerMiddleware 를 AppModule 레벨에 set up 해보자
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');
}
}
Plain Text
복사
•
위 예제에서, 우리는 CatsController 에 이전에 미리 정의한 /cats route handler 들을 위한 LoggerMiddleware 를 셋업 하였다.
•
우리는 미들웨어를 구성 할 때, forRoutes() 메소드에 path 와 method 가 포함된 오브젝트를 포함시켜 보내는 것으로, 미들웨어가 좀더 특정한 요청 메소드에 타겟팅 되도록 제한 할 수있다.
•
아래 예에서, 원하는 요청 메소드 타입 enum 을 가져오기 위해 RequestMethod 를 import 한것을 보라.
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 });
}
}
Plain Text
복사
HINT : configure() 메소드는 async/await 를 이용하여 비동기로 만들어 질 수 있다.
Route wildcards
•
routes 에 서포트되는 패턴과 같다.
forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });
Plain Text
복사
Middleware consumer
•
MiddlewareConsumer 는 헬퍼 클레스 이다.
•
미들웨어를 매니지하는 몇몇 빌트인 메소드를 제공한다.
•
모든 메소드들은 fluent style 로 chained 된다.
•
forRoute() 메소드는 매개변수로, single string, multiple strings, a RouteInfo object, a controller class, multiple controller classes 를 받을 수 있다.
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.ts';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(CatsController);
}
}
Plain Text
복사
HINT : apply() 메소드도 여러 미들웨어들을 받을 수 있다.
Excluding routes
•
미들웨어 적용에 있어서 특정 라우트들을 제외하고 싶을 때
•
exclude() 메소드를 이용하여 쉽게 제외 할 수 있다.
•
이 메소드는 single string, multiple strings, 제외될 라우트들을 식별하는 RouteInfo object 를 받을 수 있다.
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
'cats/(.*)',
)
.forRoutes(CatsController);
Plain Text
복사
HINT : exclude() 메소드는 path-to-regexp 패키지를 사용하여 와일드 카드 파라미터들을 지원한다.
Functional middleware
•
우리가 위에서 사용한 LoggerMiddleware 클레스는 꽤 간단하다.
•
맴버도 없고, 추가적인 메소드들도 없고, 의존성도 없다.
•
왜 단순한 함수로 구현하지 않고 굳이 클레스로 만들었을까? 사실, 함수로 만들 수 있다. (말 진짜 개어렵게하네 ㅆㅃ)
•
이러한 함수 타입의 미들웨어를 우리는 functional middleware 라고 부른다.
•
두 차이를 설명하기 위해, 클레스 베이스 미들웨어를 함수 미들웨어로 바꿔보자.
import { Request, Response, NextFunction } from 'express';
export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
};
Plain Text
복사
•
AppModule 에는 아래와 같이 쓴다.
consumer
.apply(logger)
.forRoutes(CatsController);
Plain Text
복사
HINT 너의 미들웨어가 어떠한 디펜던시도 요구하지 않을 때는 함수형 미들웨어를 항상 고려해봐야 한다.
Multiple middleware
•
위 설명한 대로, 순차적으로 실행되는 여러 미들웨어를 바인드 하기 위해, 그냥 apply() 메소드에 순차적으로 집어 넣으면된다.
consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);
Plain Text
복사
Global middleware
•
한번에 모든 등록된 라우트에 미들웨어를 바인드하고 싶다면, INestApplication 인스턴스에서 제공되는 use() 메소드를 사용 할 수 있다.
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);
Plain Text
복사