nestJs에서 middleware사용하기

양진영·2023년 2월 7일
0

nestJs

목록 보기
2/10

nestJs는 모듈로 기능을 조립한다고 이전 포스팅에서 언급했었다. 그렇다면 nestJs는 middleware를 사용하지 않는다는 것인가?? 답은 전혀 그렇지 않다 이다. nestJs도 middleware를 사용할수있고 또 많이 사용한다. express에서는 morgan이라는 라이브러리를 import해와서 로그기능을 띄워주는데 사용된다. 물론 nestJs에서도 사용가능하다 하지만 이번에 해보고자 하는것은 커스텀 middleware를 만들어 붙여보는것이다. LoggerMiddleware라는 middleware를 만들어 로깅하는데 사용해보겠다.

loggerMiddleware를 만들 경로
/src/middleware/logger.middleware.ts

loggerMiddleware: express에서는 morgan이 logging을 해주는 middleware였고 app.use(morgan('dev/common..etc'))로 실행을 해줄수있었다. nest에서는 비슷한 역할을 하는 middleware를 모듈화 하여 사용하수 있다.

logger.middleware.ts
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  
  private logger = new Logger('HTTP');

  use(req: Request, res: Response, next: NextFunction): void {
    const { ip, method, originalUrl } = req;
    const userAgent = req.get('user-agent') || '';

    res.on('finish', () => {
      const { statusCode } = res;
      const contentLength = res.get('content-type');
      this.logger.log(
        `${method} ${originalUrl} ${statusCode} ${contentLength} - ${userAgent} ${ip}`,
      );
    });

    next();
  }
}
}

implements라는 키워드를 위에서 볼수있을텐대 이것은 NestMiddleware의 기능을 강제로 사용하게 만드는 것이다. 예시로 만약 LoggerMiddleware가 use 라는 함수를 안에서 사용해주지 않으면 에러를 이르킨다. 참고로 use함수는 express의 app.use와 비슷하여 middleware로서 사용하려면 next()를 넣어주어야한다. 이는 LoggerMiddleware라는 class가 NestMiddleware를 implements 함으로서 어떤것을 구현한다. 라고 보면 쉬울것같은데 더쉽게 말하자면 걍 어떤 class를 implements 받았으면 강제로 implements를 해주는 대상의 기능을 사용해야 한다는 뜻으로 이해하면 될것같다.

  • middleware를 사용할때는 express도 마찬가지고 항상 next()를 해줘야 다음으로 넘어간다 => next()없이는 middleware로서 작동안함

  • context: new Logger로 선언하고 그안에 'HTTP'를 컨텍스트라고 한다. 이 컨텍스트의 역할은 특정 요청으로 들어온것에 한해서 기능을 보여준다. 즉 우리는 HTTP로 들어온 요청은 아래에서 구현한대로 사용이가능하다. => 우리 케이스는 http로 들어온 요청은 아래에서 구현한 것처럼 logging이 가능해진다.

위와 같이 Injectable로 선언해서 주입이 가능한 상태로 만들어 두고 사용하고자 하는 곳에서 주입하여 사용한다. 우리 예시는 app.module에 주입하여 main.ts에서 타고 들어오는 모든 요청이 걸러지는 곳에 주입하여 사용할 것이다.

app.module.ts


const getEnv = async () => {
  const response = await axios.get('비밀키를 요청하는 url');
  return response.data;
};

@Module({
  imports: [ConfigModule.forRoot({ isGlobal: true, load: [getEnv] })],

  controllers: [AppController],
  providers: [AppService, ConfigService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes({ path: '*', method: RequestMethod.ALL });
  }
}

우선 app module세팅부터 잠시 설명을 하자면

  • forRoot,forFeature,register와 같은것은 안에 설정을 넣어주기 위한 함수이다.

  • config설정에 load는 나중에 클라우드에 환경설정을 저장해놓고 그것을 받아와 사용할때 쓰는데 보통은 함수를 만들어 그것을 return값에 담아준다. 이번경우에는 getEnv의 return값이 클라우드에서 가져온 환경변수 값이 될것이고 그것을 load가 받아 원하는곳에서 configService.get('환경변수명')을 통해 사용할수있을것이다.

  • AppModule에 NestModule을 implements하여 아까 만들어둔 LoggerMiddleware를 사용 할수있다. consumer를 매개변수로 하여 LoggerMiddleware를 apply(적용)해주는데 forRoutes경로로 요청온것에 적용해준다. 우리 케이스는 와일드카드('*')를 사용하여 모든 경로의 요청에 LoggerMiddleware를 적용하게 만들었다.

implements를 사용하는 이유 => 위에 언급했다 싶히 사용해야 하는(만들어야 하는) 함수 또는 변수를 강제화 시킨다. 이것이 좋은점은 ts의 장점을 극대화 할수있기 때문이다. 어차피 구현해야 하는건데 implements를 사용하면 오타나 어떤걸 빼먹었을때 에러를 띠우기 때문에 타입검사를 해준다.

이렇게 커스텀 middleware를 만드는 것까지 해보았다. 사실 nestjs에서 middleware를 만들어 사용해본적은 아직 없지만 이번 기회에 어떻게 하는지 한번 체험해봐서 좋았고 항상 implements가 무슨기능일까 궁금했는데 조금 자세히 알아보는 기회가 되어 좋았던것 같다.

profile
왜? 라는 질문을 중요시하는 서버 개발자입니다

0개의 댓글