혼자 정리

[NestJS] 필터에서 다른 필터로 예외 전달하기 본문

자바스크립트, 타입스크립트

[NestJS] 필터에서 다른 필터로 예외 전달하기

tbonelee 2022. 9. 30. 02:43

NestJS의 기본 Exception 필터는 프레임워크에서 제공하는 기본 HttpException 에러 클래스 류를 제공한다.

 

처음 생각한 방향은 특정 컨트롤러에 서비스 레이어에서 던지는 에러를 catch할 수 있는 필터를 장착한 후, 해당 필터에서 HttpException클래스로 예외 전환을 해서 던지려고 했다. 그렇게 하면 글로벌 기본 필터에서 HttpException을 잡을 것이라고 생각했기 때문이다.

 

하지만 그렇게 해 본 결과 예외가 던져진 후 응답 처리가 되지 않았다.

 

공식 문서를 찾아보니 이유가 자세히 나와 있었다(https://docs.nestjs.com/faq/request-lifecycle#filters).

Filters are the only component that do not resolve global first. Instead, filters resolve from the lowest level possible, meaning execution starts with any route bound filters and proceeding next to controller level, and finally to global filters. Note that exceptions cannot be passed from filter to filter; if a route level filter catches the exception, a controller or global level filter cannot catch the same exception. The only way to achieve an effect like this is to use inheritance between the filters.

따라서 지시대로 필터의 상속을 통해 기본 익셉션 필터로 예외를 던지면 된다.

ex)

import { Catch } from "@nestjs/common";
import { BaseExceptionFilter } from "@nestjs/core";

@Catch(WannabeCatchedError)
export class WannabeCatchedErrorFilter extends BaseExceptionFilter {
  catch(exception: WannabeCatchedError, host: ArgumentsHost) {
    super.catch(new HttpException("response", 400);
  }
}

 


cf) 프레임워크 내부에 어떤 식으로 필터를 적용하기에 필터에서 던진 에러가 필터로 전달이 안 되는지 궁금해서 간단히 확인해보았다. (처음에는 막연히 필터가 필터를 감싸고 있는 형식일 것이라고 추측했다. 그래서 안 쪽 필터에서 던지면 바깥 쪽에서 catch할 수 있는 구조일 것이라고 오해)

확인한 코드 : https://github.com/nestjs/nest/blob/master/packages/core/exceptions/exceptions-handler.ts

 

GitHub - nestjs/nest: A progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applica

A progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications on top of TypeScript & JavaScript (ES6, ES7, ES8) 🚀 - GitHub - nestjs/nest: A pro...

github.com

public invokeCustomFilters<T = any>(
    exception: T,
    ctx: ArgumentsHost,
  ): boolean {
    if (isEmpty(this.filters)) {
      return false;
    }
    const isInstanceOf = (metatype: Type<unknown>) =>
      exception instanceof metatype;

    const filter = this.filters.find(({ exceptionMetatypes }) => {
      const typeExists =
        !exceptionMetatypes.length || exceptionMetatypes.some(isInstanceOf);
      return typeExists;
    });
    filter && filter.func(exception, ctx);
    return !!filter;
  }
}

관련 핸들러 클래스의 처리 로직을 보면, 배열의 find()를 통해 처음 일치하는 필터를 호출하는 것을 확인할 수 있다.