본문으로 건너뛰기
김신건의 로그

[NestJS] Middleware

· 수정 · 📖 약 1분 · 493자/단어 #nestjs #middleware #express
NestJS Middleware, NestFactory Middleware, MiddlewareConsumer, NestJS 미들웨어, helmet nestjs, cors nestjs

정의

NestJS Middleware 는 route handler 실행 이전, Guard 도 실행되기 전에 실행되는 함수/클래스입니다. Express/Fastify 표준 middleware 를 그대로 사용할 수 있는 지점입니다. 요청 body 파싱, 로깅, session, cookie, CORS, helmet 등.

Middleware 순서 (전체 lifecycle)

  1. Middleware ← 여기
  2. Guard
  3. Interceptor (before)
  4. Pipe
  5. Controller handler
  6. Interceptor (after)
  7. Exception filter

Middleware 는 raw req, res 만 접근. NestJS 컨텍스트 (ExecutionContext) 없음.

클래스 스타일

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(`${req.method} ${req.url}`);
    next();
  }
}

함수 스타일

export function logger(req: Request, res: Response, next: NextFunction) {
  console.log(`${req.method} ${req.url}`);
  next();
}

차이:

  • 클래스: DI 가능 (@Injectable())
  • 함수: DI 불가, 간단

Module 에서 등록

configure() 메서드에서:

import { Module, MiddlewareConsumer, NestModule } from '@nestjs/common';

@Module({
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes(UsersController);
  }
}

forRoutes 옵션

Controller 이름

consumer.apply(LoggerMiddleware).forRoutes(UsersController, OrdersController);

URL path

consumer.apply(LoggerMiddleware).forRoutes('users');
consumer.apply(LoggerMiddleware).forRoutes('users/*');

Method + path 객체

import { RequestMethod } from '@nestjs/common';

consumer.apply(LoggerMiddleware).forRoutes(
  { path: 'users', method: RequestMethod.GET },
  { path: 'users', method: RequestMethod.POST },
);

모든 route

consumer.apply(LoggerMiddleware).forRoutes('*');

exclude

consumer
  .apply(LoggerMiddleware)
  .exclude(
    { path: 'health', method: RequestMethod.GET },
    { path: 'metrics', method: RequestMethod.GET },
    'admin/*',
  )
  .forRoutes('*');

여러 middleware

consumer
  .apply(LoggerMiddleware, RequestIdMiddleware, SecurityMiddleware)
  .forRoutes('*');

Global Middleware

main.ts 에서:

// main.ts
import * as express from 'express';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(express.json({limit: '1mb'}));
  app.use(logger);   // 함수 middleware
  await app.listen(3000);
}

주의: app.use() 로 등록한 middleware 는 controller 스코프 밖. MiddlewareConsumer 는 route 별 세밀.

Common Middleware 통합

CORS

// main.ts
app.enableCors({
  origin: ['https://app.example.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  credentials: true,
});

또는:

import * as cors from 'cors';
app.use(cors({ ... }));

Helmet

npm i helmet
import helmet from 'helmet';
app.use(helmet());
  • CSP, XSS-Protection, X-Frame-Options 등 자동

Body Parser

Express 는 기본 body parser 포함. Fastify 는:

// main.ts
app.useGlobalPipes(new ValidationPipe());   // DTO 검증

Multer (파일 업로드):

npm i @nestjs/platform-express multer
import { FileInterceptor } from '@nestjs/platform-express';
import { UploadedFile, UseInterceptors } from '@nestjs/common';

@Post('upload')
@UseInterceptors(FileInterceptor('file'))
upload(@UploadedFile() file: Express.Multer.File) { ... }

Session

npm i express-session
npm i @types/express-session -D
import * as session from 'express-session';

app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    maxAge: 3600 * 1000,
  },
}));
npm i cookie-parser
import * as cookieParser from 'cookie-parser';
app.use(cookieParser());

Compression

npm i compression
import * as compression from 'compression';
app.use(compression());

Request ID (분산 트레이싱)

import { v4 as uuidv4 } from 'uuid';

@Injectable()
export class RequestIdMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const reqId = (req.headers['x-request-id'] as string) || uuidv4();
    req['requestId'] = reqId;
    res.setHeader('X-Request-Id', reqId);
    next();
  }
}

Interceptor 에서도 접근:

const req = context.switchToHttp().getRequest();
const reqId = req.requestId;

Rate Limiter (Middleware 대안: Guard)

npm i @nestjs/throttler
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';

@Module({
  imports: [ThrottlerModule.forRoot([{ttl: 60000, limit: 100}])],
  providers: [{provide: APP_GUARD, useClass: ThrottlerGuard}],
})

Guard 로 처리하는 편이 관용. Middleware 는 인프라 (nginx, ALB) 로 처리.

Middleware 안에서 DI

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  constructor(private authService: AuthService) {}

  async use(req: Request, res: Response, next: NextFunction) {
    const token = req.headers.authorization?.split(' ')[1];
    if (token) {
      req['user'] = await this.authService.verify(token);
    }
    next();
  }
}

주의: 인증은 Middleware 보다 Guard 가 관용. Guard 는 예외 던지기, decorator 지원, ExecutionContext 등이 유리.

Fastify 와의 차이

Fastify 는 Express middleware 와 호환 아님. Fastify plugin 을 사용해야 함:

import { fastifyHelmet } from '@fastify/helmet';
import { fastifyCors } from '@fastify/cors';

await app.register(fastifyHelmet);
await app.register(fastifyCors);

함정

WARNING

Middleware 는 Guard 보다 먼저. 인증을 middleware 로 하면 @Public() 데코레이터 활용 못 함. Guard 로 이관.

CAUTION

next() 안 호출 하면 요청 hanging. 조건 분기에서 조심.

WARNING

Fastify 에서 Express middleware 오작동. 예: express-session 은 Fastify 에서 작동 X. @fastify/session.

IMPORTANT

app.use() 는 global, MiddlewareConsumer 는 route 별. 둘의 역할 구분.

CAUTION

Middleware DI 순서. configure() 는 module 로딩 시점에 실행. Async 초기화 필요 시 dynamic module.

관련 위키

이 글의 용어 (9개)
[FastAPI] Middlewarefastapi
정의 FastAPI Middleware 는 모든 요청과 응답을 가로채 로깅, 인증, 압축, CORS 등 공통 처리를 하는 계층입니다. Starlette 의 ASGI middlew…
[Flask] Request & Responseflask
정의 Flask Request/Response 는 Werkzeug 의 / 래퍼를 확장한 것입니다. Thread-local 프록시 로 뷰 함수 어디서든 현재 요청에 접근하고, re…
[Framework] NestJSframeworks
정의 NestJS 는 Kamil Myśliwiec 이 2017년 발표한 Node.js 서버측 프레임워크 입니다. TypeScript first, Angular 에서 영감받은 모듈…
[Koa] Middleware (Onion Model)frameworks
정의 Koa Middleware 는 요청/응답 사이에 실행되는 async 함수입니다. 각 미들웨어는 (context) 와 (다음 미들웨어를 실행하는 함수) 를 받고, 로 down…
[NestJS] Controllersframeworks
정의 NestJS Controller 는 데코레이터가 붙은 클래스로, HTTP 요청을 라우팅하고 응답을 반환합니다. Method 별로 , , , , 데코레이터를 붙여 URL + …
[NestJS] Guardsframeworks
정의 NestJS Guard 는 특정 요청이 controller method 에 도달하기 전에 실행되어 접근 허용 여부 를 결정합니다. Middleware 이후, Intercep…
[NestJS] Interceptorsframeworks
정의 NestJS Interceptor 는 요청 처리 전/후에 로직을 삽입하는 클래스입니다. AOP (Aspect-Oriented Programming) 패턴의 구현체로, 로깅,…
[NestJS] Modulesframeworks
정의 NestJS Module 은 데코레이터가 붙은 클래스로, 관련된 controller, provider, import/export 를 하나로 묶는 조직 단위입니다. Angul…
[NestJS] Pipes (Validation, Transformation)frameworks
정의 NestJS Pipe 는 controller method 로 들어가는 인자를 변환 (transformation) 하거나 검증 (validation) 하는 클래스입니다. Gu…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기