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

© 2026 newgirok

← 글 목록

NestJS Exception Filter — 예외를 중앙에서 처리하는 방법

2025년 3월 16일
NestJSException FilterExceptionFilterHttpException에러 처리

API 서버를 개발하다 보면 Controller마다 try-catch를 반복하거나, 에러 응답 형식이 제각각이 되는 문제가 생긴다. NestJS의 Exception Filter는 애플리케이션 전역에서 발생하는 예외를 한 곳에서 가로채 일관된 응답 형식으로 변환한다. Request 라이프사이클 맨 끝단에서 동작하기 때문에 Controller와 Service 어디서 예외가 던져지든 반드시 거쳐간다.

요청 처리 흐름에서 Exception Filter의 위치

Client Request
      ↓
  Middleware
      ↓
   Guards
      ↓
 Interceptors (pre)
      ↓
  Pipes (validation)
      ↓
  Controller / Service
      ↓ (예외 발생)
 Exception Filter  ←── 여기서 예외를 잡는다
      ↓
  HTTP Response

예외가 발생하면 NestJS 런타임이 Exception Filter 레이어로 제어를 넘긴다. 필터가 없으면 프레임워크 내장 Global Exception Filter가 기본 형식으로 응답한다.

HttpException 기본 사용

NestJS는 HTTP 에러를 표현하는 HttpException 클래스를 제공한다. 직접 던지거나 내장 파생 클래스를 사용한다.

import { NotFoundException, BadRequestException } from '@nestjs/common';

// 내장 파생 클래스 사용
throw new NotFoundException('User not found');

// HttpException 직접 사용
throw new HttpException({ code: 'USER_NOT_FOUND', message: '유저 없음' }, 404);

HttpException: NestJS가 제공하는 HTTP 에러 기반 클래스. status와 response 두 인자를 받는다.

커스텀 Exception Filter 구현

@Catch 데코레이터로 어떤 예외를 처리할지 지정하고, ExceptionFilter 인터페이스를 구현한다.

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { Request, Response } from 'express';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception.getStatus();
    const exceptionResponse = exception.getResponse();

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      message:
        typeof exceptionResponse === 'string'
          ? exceptionResponse
          : (exceptionResponse as any).message,
    });
  }
}

ArgumentsHost: HTTP, WebSocket, RPC 등 실행 컨텍스트를 추상화한 객체. switchToHttp로 HTTP 컨텍스트를 꺼낸다.

글로벌 필터 등록

필터를 등록하는 방법은 두 가지다.

방법범위코드
useGlobalFilters전체 앱app.useGlobalFilters(new HttpExceptionFilter)
APP_FILTER 토큰전체 앱 (DI 지원)Module providers에 등록

DI가 필요한 경우 (예: Logger 주입) APP_FILTER 방식을 사용한다.

// app.module.ts
import { APP_FILTER } from '@nestjs/core';

@Module({
  providers: [
    {
      provide: APP_FILTER,
      useClass: HttpExceptionFilter,
    },
  ],
})
export class AppModule {}

Controller나 Route 핸들러 단위로만 적용하려면 @UseFilters(HttpExceptionFilter) 데코레이터를 붙이면 된다.

모든 예외를 잡는 범용 필터

@Catch에 인자를 주지 않으면 모든 예외를 잡는다. HttpException이 아닌 런타임 에러도 일관된 형식으로 응답할 수 있다.

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();

    const status =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    response.status(status).json({
      statusCode: status,
      message: exception instanceof Error ? exception.message : 'Internal server error',
    });
  }
}

이 패턴으로 예상치 못한 TypeError, DB 연결 오류 등 모든 예외가 클라이언트에 500으로 깔끔하게 반환된다.

← 이전 글NestJS Interceptor — Controller 전후를 가로채는 방법
다음 글 →NestJS Lifecycle — 요청 처리 전체 흐름