본문 바로가기

Backend/NestJS

[NestJS] 예외 필터(HttpException, Error)

728x90
NestJS 공식문서, 서적을 참고한 예외 필터 공부 기록

개요

Nest는 모든 예외를 처리하는 기본 제공 예외 레이어를 가지고 있다.

만약 코드에서 예외를 처리하지 않으면 예외 레이어에서 자동으로 예외를 보낸다.

 

기본적으로 위 작업은 HttpException 유형의 예외를 처리하는 예외 필터에 의해 수행된다.

예외가 인식되지 않은 경우 500 에러를 보낸다.

{
  "statusCode": 500,
  "message": "Internal server error"
}

 

HttpException

HttpException은 결국 자바스크립트의 Error 객체를 상속한다.

따라서 모든 예외는 Error 객체로부터 파생됐다고 볼 수 있다.

 

HttpException 클래스를 보자.

export declare class HttpException extends Error {
	...
    constructor(response: string | Record<string, any>, status: number);
    ...
}
  • response : JSON 응답 본문. 문자열이나 Record<string, any> 타입의 객체를 전달할 수 있다.
  • status : 에러의 속성을 나타내는 HTTP 상태 코드

상황별 에러들은 아래 링크에 잘 정리되어있다.

 

HTTP 상태 코드 - HTTP | MDN

HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려줍니다. 응답은 5개의 그룹으로 나누어집니다: 정보를 제공하는 응답, 성공적인 응답, 리다이렉트, 클라이언트 에러, 그리고

developer.mozilla.org

예외 필터

Nest에서는 앞서 말했듯 예외 필터 레이어가 있어서 원하는 대로 예외를 다룰 수 있다.

예외가 일어났을 때 로그를 남기거나 응답 객체를 변경하는 등의 동작이 그 예시이다.

 

예외가 발생했을 때 모든 예외를 잡아 요청 URL과 예외가 발생한 시각을 콘솔에 출력하는 예외 필터를 만들어 보자.

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

@Catch()
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: Error, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const req = ctx.getRequest<Request>();
    const res = ctx.getResponse<Response>();

    if (!(exception instanceof HttpException)) {
      exception = new InternalServerErrorException();
    }

    const response = (exception as HttpException).getResponse();

    const log = {
      timeStamp: new Date(),
      url: req.url,
      response,
    };

    console.log(log);

    res.status((exception as HttpException).getStatus()).json(response);
  }
}
  • @Catch : 처리되지 않은 모든 예외를 잡으려고 할 때 사용
  • Nest에서 처리하는 것과 동일하게 HttpException이 아닌 예외는 500 에러로 처리했다.

이제 예외 필터를 적용해 보자.

@Controller('users')
@UseFilters(HttpExceptionFilter)
export class UsersController {
	...
}

필터는 위처럼 컨트롤러 전체에 적용할 수도 있고, 특정 엔드포인트에 적용할 수도 있다.

또한 useGlobalFilters를 이용해 어플리케이션 전체에도 적용할 수 있다.

 

이제 처리하지 못한 예외가 발생하면 아래처럼 로그가 출력되는 것을 확인할 수 있다.

예외 필터에 로거 적용

저번 기록에서 만든 로거를 예외 필터에 적용해 보자.

import { Logger, Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { HttpExceptionFilter } from './HttpException.filter';

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

먼저 ExceptionModule을 만들어준다.

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

@Catch()
export class HttpExceptionFilter implements ExceptionFilter {
  constructor(private logger: Logger) {}
  catch(exception: Error, host: ArgumentsHost) {
    ...
    const stack = exception.stack;

    ...
    const log = {
      timeStamp: new Date(),
      url: req.url,
      response,
      stack,
    };

    this.logger.log(log);
	
    ...
  }
}

그리고 로거를 만든 필터에 주입해서 사용하면 된다.

코드로 처리하지 못한 예외가 정상적으로 출력된 것을 볼 수 있다.

참고

 

12장 모든 것은 항상 실패한다 - 예외 필터

> ### 모든 것은 항상 실패한다(Everything fails all the time) - 버너 보겔스, AWS CTO > 소프트웨어를 개발하면서 예외 처리는 필수 사항입…

wikidocs.net

 

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 Progamming), FP (Functional Programming), and FRP (Functional Reac

docs.nestjs.com

마치며

잘못된 정보에 대한 피드백은 환영입니다.

감사합니다.

728x90