• app.useGlobalGuards(new HttpExceptionFilter()) 이렇게 new 로 생성하는 이유는 main.ts 는 NestJS 의 IoC Container 가 완전히 초기화되기 전이기 때문

왜 이렇게 동작할까?

IoC Container의 생명주기

  1. NestFactory.create() 호출
  2. AppModule 로드 및 분석
  3. Metadata 수집 (데코레이터 정보)
  4. Dependency Injection Container 생성
  5. Provider 인스턴스화 및 의존성 주입
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
import { HttpExceptionFilter } from './common/filters/http-exception/http-exception.filter';
 
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      transform: true,
      forbidNonWhitelisted: true,
      transformOptions: {
        enableImplicitConversion: true,
      },
    }),
  );
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(process.env.PORT ?? 4000);
}
bootstrap();
 
// http-exception.filter.ts
import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
} from '@nestjs/common';
import { Response } from 'express';
 
@Catch(HttpException)
export class HttpExceptionFilter<T extends HttpException>
  implements ExceptionFilter
{
  catch(exception: T, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
 
    const status = exception.getStatus();
    const exceptionResponse = exception.getResponse();
    const error =
      typeof response === 'string'
        ? { message: exceptionResponse }
        : (exceptionResponse as object);
 
    response.status(status).json({
      ...error,
      timestamp: new Date().toISOString(),
    });
  }
}