미들웨어는 라우트 핸들러 이전에 호출되는 기능이다 . 미들웨어 기능은 요청 및 응답 개체에 액세스할 수 있으며 요청-응답 주기에 있다 next() 함수를 통해 다음에 따라오는 미들웨어 호출이 이루어진다.
Nest 미들웨어는 기본적으로 Express 미들웨어와 동일하다. 공식 Express 문서는 미들웨어를 다음과 같이 설명한다.
미들웨어 기능은 다음 작업을 수행할 수 있습니다.
- 어떤 코드라도 실행.
- 요청 및 응답 개체를 변경.
- 요청-응답 주기를 종료.
- 스택에서 다음 미들웨어 함수를 호출.
- 현재 미들웨어 기능이 요청-응답 주기를 종료하지 않으면 next()다음 미들웨어 기능으로 제어를 전달하기 위해 호출한다. 그렇지 않으면 요청이 중단된 상태로 유지된다.
함수나 @Injectable() 데코레이터가 있는 클래스에서 맞춤 Nest 미들웨어를 구현한다. 클래스는 NestMiddleware인터페이스를 구현해야 하며 함수에는 특별한 요구 사항이 없다. 클래스 메소드를 사용하여 간단한 미들웨어 기능을 구현하는 것부터 시작해보자.
주의)
Express 와 fastify는 미들웨어를 다르게 처리한다. 자세한 내용은 여기를 참고한다.
// 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();
}
}
의존성 주입
Nest 미들웨어는 종속성 주입을 완벽하게 지원한다. 프로바이더 및 컨트롤러와 마찬가지로 동일한 모듈 내에서 사용 가능한 종속성을 생성자를 통해 주입 할 수 있다.
미들웨어 적용
@Module() 데코레이터 에는 미들웨어를 위한 공간이 없다 . 대신 모듈 클래스의 configure() 메서드를 사용하여 설정한다 . 미들웨어를 포함하는 모듈은 NestModule 인터페이스를 구현해야 한다 . AppModule 레벨에 있는 LoggerMiddleware 을 설정해보자.
// app.module.ts
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');
}
}
위의 예에서는 CatsController 에 등록된 /cats 라우트 핸들러를 위한 LoggerMiddleware 를 설정하였다. 또한 미들웨어를 구성할 때 라우트 path와 요청이 포함된 개체를 메서드에 forRoutes() 메소드에 전달하여 미들웨어를 특정 request의 메서드(get, put, post, delete..)로 제한할 수도 있다. 아래 예에서는 원하는 요청 메서드 유형을 참조하기 위해 RequestMethod 열거형을 가져온다.
// app.module.ts
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 });
}
}
configure 메서드는 async/await 을 사용하여 비동기 메서드가 될 수 있다.
주의) express 어댑터를 사용하는 경우, Nest 는 기본적으로 body-parser 패키지의 json과 urlencoded를 내장하게 된다. MiddlewareConsumer를 통해 bodyParser를 커스텀해야 하는 경우 글로벌 미들웨어에서 bodyParser 플래그를 false로 꺼야 한다.
와일드카드 라우팅
패턴 기반 경로도 지원된다. 예를 들어 별표는 와일드카드 로 사용되며 모든 문자 조합과 일치한다.
forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });
'ab*cd' 경로는 abcd, ab_cd, abecd 등과 일치합니다 . ?, +, * 및 () 문자는 라우팅 경로에 사용될 수 있으며 정규식과 유사하다. 다만, 하이픈( - )과 점( . )은 문자열 기반 경로에 의해 문자 그대로 해석된다.
주의) fastify 패키지는 더 이상 와일드카드 별표를 지원하지 않는 최신 버전의 path-to-regexp 패키지를 사용합니다 . 대신 (.*), :splat* 파라미터를 사용해야 한다.
미들웨어 소비자(Middleware Consumer)
MiddlewareConsumer 는 헬퍼 클래스이다 . 미들웨어를 관리하기 위한 몇 가지 기본 제공 방법을 제공한다. 이들 모두는 유창한 스타일(fluent style) 로 간단하게 연결될 수 있다 . forRoutes() 메서드는 단일 문자열, 여러 문자열, RouteInfo 개체, 컨트롤러 클래스 및 여러 컨트롤러 클래스를 사용할 수 있다. 대부분의 경우 쉼표로 구분된 컨트롤러 목록을 전달할 것이다. 다음은 단일 컨트롤러를 사용한 예이다.
// app.module.ts
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';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(CatsController);
}
}
힌트) apply()메서드는 단일 미들웨어를 사용하거나 여러 미들웨어를 지정하기 위해 여러 인수를 사용할 수 있습니다 .
라우팅 제외
때때로 우리는 미들웨어가 적용되지 않도록 특정 경로를 제외하고 싶을 때가 있다. exclude() 메서드를 호출하는 방법으로 특정 경로를 쉽게 제외할 수 있다. 이 메서드는 아래와 같이 단일 문자열, 여러 문자열 또는 제외할 경로를 식별하는 RouteInfo 개체를 사용할 수 있다.
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
'cats/(.*)',
)
.forRoutes(CatsController);
힌트) exclude() 서드는 path-to-regexp패키지를 사용하여 와일드카드 매개변수를 지원합니다 .
위의 예에서는 exclude() 메서드에 전달된 세 가지 경로를 제외하고 LoggerMiddleware 내부에 정의된 CatsController의 모든 경로에 바인딩된다.
기능적 미들웨어
우리가 사용한 클래스는 LoggerMiddleware 매우 간단합니다 . 여기에는 멤버, 추가 메서드 및 종속성이 없습니다. 그렇다면, 왜 클래스 대신 간단한 함수로 정의할 수 없을까? 라는 의문이 생긴다, 결론적으로 당연히 된다. 이렇게 간단하게 함수로 정의한 유형의 미들웨어를 기능적 미들웨어 라고 한다 . 차이점을 설명하기 위해 LoggerMiddleware 를 클래스 기반에서 기능적 미들웨어로 변환해 보겠다.
// 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
consumer
.apply(logger)
.forRoutes(CatsController);
힌트) 미들웨어에 종속성이 필요하지 않은 경우에는 언제든지 더 간단한 기능적 미들웨어 대안을 사용하는 것을 고려해보자.
다중 미들웨어
위에서 언급했듯이 순차적으로 실행되는 여러 미들웨어를 바인딩하려면 apply() 메서드 내부에 쉼표로 구분된 목록을 제공하면 됩니다.
consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);
글로벌 미들웨어
등록된 모든 경로에 미들웨어를 한 번에 바인딩하려면 INestApplication 인스턴스에서 제공하는 use() 메서드를 사용할 수 있습니다.
// main.ts
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);
힌트) 글로벌 미들웨어에서는 DI 컨테이너에 접근할 수 없다. 그리고, app.use() 를 사용할 때 기능적 미들웨어를 대신 사용할 수 있다.
'Backend(Framework) > NestJS 개요(공식문서 번역)' 카테고리의 다른 글
7. Pipes (0) | 2023.11.19 |
---|---|
6. Exception filters (0) | 2023.11.17 |
4. Modules (1) | 2023.11.16 |
3. Provider (1) | 2023.11.16 |
2. Controller (0) | 2023.11.05 |