NestJS에서의 미들웨어

bin-lee·2022년 1월 4일
0

미들웨어(middleware)

미들웨어는 라우트 핸들러 이전에 호출되는 컴포넌트다. 라우트 핸들러란 엔드포인트마다 동작을 수행하는 컴포넌트를 뜻한다. 라우트 핸들러는 요청 경로와 컨트롤러를 매핑해 주고, 미들웨어는 이런 라우트 핸들러 이전에 실행된다.

미들웨어는 다음과 같은 동작을 할 수 있다.

  • 어떤 코드든 실행 가능하다
  • 요청과 응답에 변형을 가할 수 있다
  • 요청-응답 생명주기를 끝낼 수 있다
  • 미들웨어는 순서가 있다. 여러 개의 미들웨어를 사용 중이라면 next()로 다음 미들웨어에게 제어권을 전달한다.

요청-응답 생명주기를 끝낸다는 것은 (1) 응답을 보내거나 (2) 에러 처리를 하는 것을 뜻한다. 생명주기를 끝내지 않을 때에는 next() 호출로 다음 미들웨어에게 제어권을 주어야 한다.


미들웨어로 수행하는 작업들은 보통 다음과 같다.

  • 쿠키 파싱 : 쿠키를 파싱하여 사용하기 쉬운 데이터 구조로 변경한다.
  • 세션 관리 : 세션 쿠키를 찾고 상태를 조회해서 요청에 세션 정보를 추가한다.
  • 인증/인가 : 사용자가 서비스에 접근 가능한 권한이 있는지 확인한다.
  • body 파싱 : POST/PUT 요청으로 들어오는 json 타입의 본문 및 파일 스트림과 같은 데이터들을 유형에 따라 해석하여 파라미터에 넣는 작업을 한다.

🌼 CLI를 통한 미들웨어 생성과 구현

$ nest g middleware 미들웨어명

일전에 모듈을 생성했던 것과 같은 방식으로 미들웨어를 생성할 수 있다. 생성한 미들웨어의 기본 코드 기반으로 logger를 구현해 봤다.

import { Injectable, Logger, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  private logger = new Logger();

  use(req: Request, res: Response, next: NextFunction) {
    res.on('finish', () => {
      this.logger.log(
        `${req.ip} ${req.method} ${res.statusCode}`,
        req.originalUrl,
      );
    });
    next();
  }
}

첫 번째 특징으로는 express에서 봤었던 usenext()다. 실제로 Nest의 미들웨어는 기본적으로 express 미들웨어와 동일하다. Nest 역시 express의 미들웨어처럼 use를 이용해서 미들웨어를 사용할 수 있다.

두 번째 특징으로는 서비스단에서 봤었던 @Injectable()가 미들웨어에서도 보인다는 점이다. 즉, 의존성 주입으로 모듈단에서 공급자로 취급되었던 서비스단처럼 미들웨어 역시 의존성 주입을 할 수 있다.

이렇게 생성된 미들웨어는 바로 사용할 수 없다. 공급자는 만들어졌으나, app.module 모듈에 보내 주어야(의존성 주입을 해 주어야) 실행할 수 있다. 다만 다른 공급자들처럼 @module() 데코레이터 내에 넣는 게 아니라 모듈 클래스의configure() 메서드를 통해 미들웨어를 설정한다. 공식 홈페이지에서 제공하는 코드를 적용한 예는 다음과 같다.

import { CatsModule } from './cats/cats.module';
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './logger.middleware';

@Module({
  imports: [CatsModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LoggerMiddleware).forRoutes('cats');
  }
}

LoggerMiddleware는 위 코드에서 볼 수 있듯이 의존성 주입이 가능한 제공자(provider)다. 그리고 consumer.apply(LoggerMiddleware) 는 말 그대로 consumer 소비자에게 LoggerMiddleware를 제공해 준다는 뜻이다. NestModule은 약속을 추가해 준 인터페이스를 의미한다.

forRoutes('cats')cats 라우터에 바인딩을 해 주는 것이다. '*' 와일드카드를 넣으면 전체 경로를 뜻하게 된다. 이렇게 .forRoutes 뒤에 오는 경로로 들어오는 요청을 수행하면 logger.middleware의 use 안의 내용이 실행되는 것을 확인할 수 있다.

profile
🚀 오늘 배운 건 오늘 적자

0개의 댓글

관련 채용 정보