NestJS로 효율적인 백엔드 개발하기 (07) - 예외 필터

2025. 12. 12. 20:38·NestJS/개념과 구조 정리

Exception Filters는 무엇인가??

Nest 에는 애플리케이션 전체에서 처리되지 않은 모든 예외를 처리하는 내장 예외 처리 계층이 있다. 애플리케이션 코드에서 예외를 처리하지 못하면 이 계층에서 예외를 포착하여 사용자에게 적절한 응답을 전송한다.


예외 필터를 위한 컨트롤러 셋팅

필터를 확인하기 위해서 아래의 명령어를 입력하여 새로운 filter 컨트롤러를 만들어보자.

nest g controller filter

새로운 filter 컨트롤러를 생성한 후 정상적으로 파일이 생성되었는지 확인한다.

filter.controller.ts의 코드를 열어서 경로 하나를 Get()으로 하나 만들어주자.

import { Controller, Get } from '@nestjs/common';

@Controller('filter')
export class FilterController {
  @Get('test')
  filterTest() {
    return 'filter test';
  }
}


500 에러 만들기

import { Controller, Get } from '@nestjs/common';

@Controller('filter')
export class FilterController {
  @Get('test')
  filterTest(test) {
    return test.testHello();
  }
}

코드를 보게 되면 filterTest 함수에 보면 파라미터에 test를 넣어놓았다. 저 타입없이 적어둔 test는 타입스크립트가 컴파일하고 나서 타입이 존재하지 않고, 내부의 정보도 없기 때문에 undefined이 들어가진다. undefined된 변수가 testHello() 함수를 쓰려고 하기 때문에 클라이언트에게 정확한 정보를 반환해 줄 수 없다. 결론적으로 서버가 처리 방법을 모르는 상황이 발생했음을 의미하는 500을 반환한다.


Throwing standard exceptions

import { Controller, Get, HttpException, HttpStatus } from '@nestjs/common';

@Controller('filter')
export class FilterController {
  @Get('throw-501')
  throwError() {
    throw new HttpException('Not Implemented', HttpStatus.NOT_IMPLEMENTED);
  }
}

설명하기 전 제일 상단에 있는 <500 에러 만들기> 챕터의 코드와 어떤 차이가 있는지 눈에 바로 보인다면 아주 다행이다. 첫번째 챕터에서 만들어진 오류는 내장된 예외 필터가 자동으로 JSON응답을 보내준 것이다. 그리고 두번째 챕터의 <Throwing standard exceptions>의 경우에는 자동으로 만들어진 예외처리가 아니라 개발자가 직접 에러를 정의 해준 것이다. NestJS는 HttpException 클래스를 제공하기 때문에 사용자가 직접 에러를 정의 할 수 있다. 이제 커맨드(컨트롤)키를 눌러서 해당 함수를 타고 들어가보자.

  • <챕터2>에서 HttpException 생성자는 2개의 파리미터를 보내주었다.
  • response -> 현재 상태를 알려주는 문자열
  • status -> 현재 상태코드

Exception filters

자 위에서 2개의 경우를 한번 테스트 해보았다. 이러한 테스트를 한 이유는 Exception filters를 사용하기 위함인데 예시로 HttpException을 통해서 여러가지 에러 케이스를 만들었다고 생각해보자. 예시는 아래의 코드를 보며 생각을 해보자.

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post('signup')
  async signup(@Body() dto: CreateUserDto) {
    // 1) validation 관련
    try {
      if (!dto.email) {
        return { success: false, status: 400, message: 'email required' };
      }
    } catch (e) {
      console.error('validation error', e);
      return { success: false, status: 500, message: 'validation failed' };
    }

    // 2) 중복 검사
    try {
      const exists = await this.usersService.exists(dto.email);
      if (exists) {
        return { success: false, status: 409, message: 'email exists' };
      }
    } catch (e) {
      console.error('db error on exists', e);
      return { success: false, status: 500, message: 'db error' };
    }

    // 3) 생성
    try {
      const user = await this.usersService.create(dto);
      return { success: true, status: 201, data: user };
    } catch (e) {
      console.error('db error on create', e);
      return { success: false, status: 500, message: 'create failed' };
    }
  }

  @Get(':id')
  async findOne(@Body('id') id: string) {
    try {
      const user = await this.usersService.findById(id);
      if (!user) {
        return { success: false, status: 404, message: 'not found' };
      }
      return { success: true, status: 200, data: user };
    } catch (e) {
      console.error('db error', e);
      return { success: false, status: 500, message: 'db error' };
    }
  }

  // ... 여러 엔드포인트에 똑같은 패턴 반복 ...
}

극단적으로 위의 에러를 예외처리하는 코드가 한 클래스에 30개씩 있다고 생각을 해보자. 처음에는 이정도면 그냥 할만하다고 생각한다. 클래스가 더 추가되고, 반환해야 하는 새로운 데이터가 추가 되고 try catch 구문이 여러개가 보이면서 내가 지금 뭘 하고 있던건지 착각하게 되는 순간이 오면 이때부터 작업을 하는 개발자는 눈물을 흘리게 된다. 진정한 문제는 유지보수를 하는 과정에서 만나게 된다.

 

HttpExceptionFilter 클래스를 하나 만들고, 내부에 코드를 한번 짜보자. 하단의 코드는 NestJS 공식 문서 Exception filters 부분에서 제공하는 코드이다.

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
} 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();

    response.status(status).json({
      statusCode: status,
      message: exception.message,
      path: request.url,
    });
  }
}

모든 예외 필터는 제네릭 인터페이스를 구현해야 한다. 

 

위와 같이 클래스를 하나 만들고 ExceptionFilter를 extends 받아서 셋팅을 한다. 그리고 아까 위에서 만든 컨트롤러 위에다가 이렇게 추가해보자. 코드는 아래의 코드를 확인한다. 아래의 코드는 해당 컨트롤러만 필터를 사용해서 잡는건데 모든 컨트롤러에 전역적으로 적용도 가능하다. 그 부분은 조금 있다가 설명을 할 예정이다.

@UseFilters(HttpExceptionFilter)// <- 이부분이 추가 됨
@Controller('filter')
export class FilterController

접속을 하게 되면 path가 추가된 것을 확인할 수 있다. 분명 메시지와 상태코드밖에 지정을 안했지만 path가 추가된 것을 볼 수 있다. 아직까지는 그냥 추가된거 아니야? 라고 할 수 있지만 위의 예시를 다시 한번 생각해보자. 한 클래스에 30개의 try catch 코드, 그런식으로 짠 코드를 팀원에게 또 하나 받은 것이다. 그 상황에서 반환하는 json 포멧에다가 현재시간도 넣자는 이야기가 나오고 있다. 그럼 나는 총 60개의 try catch문을 찾아가면서 현재시간을 추가해야한다. 하지만 그 부분은 기존에 만들어둔 HttpExceptionFilter의 코드에 한 줄 추가로 최악의 상황을 막을 수 있다.

@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();

    response.status(status).json({
      statusCode: status,
      message: exception.message,
      path: request.url,
      time: new Date().toISOString(),// <- 추가 된 코드
    });
  }
}

 


참고

https://tristy.tistory.com/52

 

[Nest Js] Nest Js 공식 문서 파헤치기 - Exception Filter(예외 필터)

트리스티가 JavaScript를 공부하며 남긴 기록입니다. 틀린 내용은 언제든지 말씀해주세요 ~! 지난 시간에는 Middleware에 대해 알아보았습니다. 이번 시간에는 Nest Js의 예외 처리에 대해서 알아보도록

tristy.tistory.com

https://docs.nestjs.com/exception-filters

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Rea

docs.nestjs.com

'NestJS > 개념과 구조 정리' 카테고리의 다른 글

NestJS로 효율적인 백엔드 개발하기 (09) - 가드  (0) 2025.12.14
NestJS로 효율적인 백엔드 개발하기 (08) - 파이프  (3) 2025.12.13
NestJS로 효율적인 백엔드 개발하기 (06) - 미들웨어(클래스형, 함수형)  (0) 2025.12.12
NestJS로 효율적인 백엔드 개발하기 (05) - 모듈에 대해 알아보자  (0) 2025.12.12
NestJS로 효율적인 백엔드 개발하기 (04) - 서비스 사용하기  (0) 2025.12.11
'NestJS/개념과 구조 정리' 카테고리의 다른 글
  • NestJS로 효율적인 백엔드 개발하기 (09) - 가드
  • NestJS로 효율적인 백엔드 개발하기 (08) - 파이프
  • NestJS로 효율적인 백엔드 개발하기 (06) - 미들웨어(클래스형, 함수형)
  • NestJS로 효율적인 백엔드 개발하기 (05) - 모듈에 대해 알아보자
나는지토
나는지토
  • 나는지토
    안녕은헬로입니다.
    나는지토
  • 전체
    오늘
    어제
    • 분류 전체보기 (27)
      • Backend Design (1)
      • NestJS (19)
        • 개발 (9)
        • 개념과 구조 정리 (10)
      • SpringBoot (0)
      • Java (4)
        • 코테 (0)
      • PostgreSQL (2)
      • Docker (1)
  • 블로그 메뉴

    • 홈
    • 태그
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    nestjs
    ArrayList
    Collections
    서비스
    Java
    조회 방식
    채팅
    코딩테스트
    토큰 검사
    컨트롤러
    JWT
    db 연결 오류
    역할 검사
    Redis
    자료구조
    인증 가드
    커서기반 조회
    nestjs/jwt
    PostgreSQL
    BullMQ
  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
나는지토
NestJS로 효율적인 백엔드 개발하기 (07) - 예외 필터
상단으로

티스토리툴바