개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← 글 목록

NestJS Middleware — Controller 이전에 실행되는 함수

2025년 3월 8일
NestJSMiddlewareLoggingNestMiddlewareapply

HTTP 요청이 Controller에 닿기 전, 그 사이 어딘가에서 로직을 실행하고 싶을 때 Middleware를 사용한다. Express의 app.use와 동일한 개념이며, NestJS는 이를 클래스 기반으로 구조화해 제공한다. 요청 로깅, 인증 토큰 파싱, CORS 전처리처럼 "모든 요청에 공통으로 적용할 작업"이 주요 사용처다.

요청 처리 흐름

Client Request
      │
      ▼
 Middleware        ← 여기서 실행
      │
      ▼
   Guard
      │
      ▼
 Interceptor (pre)
      │
      ▼
  Controller
      │
      ▼
 Interceptor (post)
      │
      ▼
 Exception Filter
      │
      ▼
Client Response

Middleware는 파이프라인의 가장 앞단에 위치한다. next를 호출하지 않으면 요청이 다음 단계로 전달되지 않으므로, 반드시 next 호출을 잊지 않아야 한다.

NestMiddleware 구현

NestMiddleware 인터페이스를 구현하는 클래스를 만든다.

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) {
    const { method, originalUrl } = req;
    const start = Date.now();

    res.on('finish', () => {
      const duration = Date.now() - start;
      console.log(`[${method}] ${originalUrl} — ${res.statusCode} (${duration}ms)`);
    });

    next();
  }
}

NestMiddleware — use(req, res, next) 메서드 하나를 강제하는 인터페이스. @Injectable을 붙여 DI 컨테이너에 등록한다.

configure에 apply로 등록

Middleware는 @Module 데코레이터가 아닌 configure 메서드로 등록한다. 모듈 클래스가 NestModule 인터페이스를 구현해야 한다.

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';
import { UserController } from './user.controller';

@Module({
  controllers: [UserController],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('*');          // 모든 경로에 적용
  }
}

forRoutes에는 문자열 경로, Controller 클래스, 또는 경로 + 메서드 객체를 넘길 수 있다.

// 특정 Controller에만 적용
consumer.apply(LoggerMiddleware).forRoutes(UserController);

// 특정 경로와 HTTP 메서드 조합
consumer
  .apply(LoggerMiddleware)
  .forRoutes({ path: 'user', method: RequestMethod.GET });

Guard / Interceptor와의 차이

구분MiddlewareGuardInterceptor
실행 시점Route 매칭 전Route 매칭 후, Handler 전Handler 전/후
실행 컨텍스트Express req/resExecutionContextExecutionContext
주요 용도로깅, 파싱, CORS인증/인가응답 변환, 캐싱
next 방식Express NextFunctionboolean 반환Observable 체인

Guard는 canActivate의 반환값으로 요청 통과 여부를 결정한다. Interceptor는 RxJS Observable을 반환해 응답 스트림을 조작할 수 있다.

Middleware는 NestJS의 DI 시스템을 활용하면서도 Express 미들웨어 생태계와 호환된다. 함수형 Middleware((req, res, next) => void)도 apply에 바로 전달할 수 있어, helmet이나 compression 같은 서드파티 패키지를 별도 래핑 없이 등록할 수 있다.

import helmet from 'helmet';

configure(consumer: MiddlewareConsumer) {
  consumer.apply(helmet(), LoggerMiddleware).forRoutes('*');
}
← 이전 글NestJS Entity — 데이터베이스 테이블과 매핑되는 클래스
다음 글 →NestJS Guard — 인증과 인가를 처리하는 실행 가드