Search
Duplicate

[오버뷰] 05.Middleware

Created
2021/07/02 02:53
Tags

Middleware

Middleware 란 route handler 이전에 호출되는 함수이다.
미들웨어 함수는 어플리케이션의 요청-응답 사이클에서 requestresponse 오브젝트와 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 해야 한다.
LoggerMiddlewareAppModule 레벨에 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() 메소드에 pathmethod 가 포함된 오브젝트를 포함시켜 보내는 것으로, 미들웨어가 좀더 특정한 요청 메소드에 타겟팅 되도록 제한 할 수있다.
아래 예에서, 원하는 요청 메소드 타입 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 stylechained 된다.
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
복사

HINT : 글로벌 미들웨어에서 DI 컨테이너에 접근하는 것은 불가능하다. app.use() 를 사용할때 대신 함수형 미들웨어를 사용 할 수 있다. 아니면, 클레스 미들웨어를 사용하고 AppModule (아니면 모든 다른 모듈) 에서 .forRoutes('*') 를 사용해라