Framework/NestJS

NestJS - Custom Exception

CHOIDR 2022. 9. 18. 01:59

NestJS Custom Exception

기본적으로 예외를 처리하는 방법은 아래 코드의 방법과 같습니다.
표준 예외와 메시지를 통해 충분히 어떤 예외를 발생하는지 추측하고 확인할 수 있습니다.

if (!product) {
  throw new NotFoundException('제품을 찾을 수 없습니다');
}

1. 가독성

하지만 아래처럼 Custom Exception 을 사용한다면 로직상 가독성을 높일 수 있습니다..
Custom Exception 은 클래스의 네이밍을 통해 일차적으로 어떤 예외가 발생했는지 유추가 가능합니다.

// exception/product-not-founct-exception.ts
import {NotFoundException} from "@nestjs/common";

export class ProductNotFoundException extends NotFoundException {
    constructor() {
        super('제품을 찾을 수 없습니다');
    }
}

// service logic
if (!product) {
    throw new ProductNotFoundException();
}

2. 응집도

그리고 인자를 통해 상세한 예외 정보를 제공할 수도 있으며,
예외에 대한 응집도가 향상되어서 예외에 필요한 메시지, 전달할 정보의 데이터, 데이터 가공 메소드들을 한 곳에서 관리할 수 있게 된다.

import { NotFoundException } from "@nestjs/common";

export class ProductNotFoundException extends NotFoundException {
    private static message = '제품을 찾을 수 없습니다';

    constructor(id: number) {
        super(`${id} 인 ${ProductNotFoundException.message}`);
    }
}

// service logic
const productId = 1;
if (!product) {
    throw new ProductNotFoundException(productId);
}

3. 재사용성 및 유지보수

Custom Exception 을 만들면 exception 폴더 안에 많은 커스텀 예외 클래스들이 생기는 단점이 존재하지만
같은 커스텀 예외 클래스가 여러 곳에서 사용될수록 장점은 더욱 극대화 됩니다.

물론 Message 에 대한 내용을 담은 클래스, 모듈을 만들어서 재사용한다면 Custom Exception 을 만드는 것만큼 재사용성 측면에서 좋겠지만,

// product-error.ts
export class ProductError {
    static PRODUCT_NOT_FOUND = '제품을 찾을 수 없습니다';
}

// service logic
if (!product) {
    throw new NotFoundException(ProductError.PRODUCT_NOT_FOUND);
}

위의 가독성, 응집도 등의 특징들을 생각한다면 Custom Exception 을 사용하는 방법이 더 좋은 방법이라는 생각이 듭니다.

그리고 만약 어떤 로직에 대한 공통되는 Exception 이 NotfoundException 에서 BadRequestException 으로 바껴야 되는 상황이 온다면
표준 예외를 사용한다면 변경되는 예외에 대한 모든 로직을 찾아서 수정해줘야 하는 단점도 있을 것이라고 생각됩니다.

3-1. 표준 예외 - 직접 변경

// service logic1
if (!product) {
    // throw new NotFoundException(ProductError.PRODUCT_NOT_FOUND);
    throw new BadRequestException(ProductError.PRODUCT_NOT_FOUND);
}

// service logic2
if (!product) {
    // throw new NotFoundException(ProductError.PRODUCT_NOT_FOUND);
    throw new BadRequestException(ProductError.PRODUCT_NOT_FOUND);
}

3-2. 커스텀 예외 - 커스텀 예외 클래스만 수정

import { BadRequestException } from "@nestjs/common";

// export class ProductNotFoundException extends NotFoundException {
export class ProductNotFoundException extends BadRequestException {
    private static message = '제품을 찾을 수 없습니다';

    constructor(id: number) {
        super(`${id} 인 ${ProductNotFoundException.message}`);
    }
}

// service logic1
if (!product) {
    throw new ProductNotFoundException(ProductError.PRODUCT_NOT_FOUND);
}

// service logic2
if (!product) {
    throw new ProductNotFoundException(ProductError.PRODUCT_NOT_FOUND);
}

4. Swagger 의 @ApiException 와의 호환성

@ApiException(() => [ProductNotFoundException])
@Get('/product')
async getProduct() {
    //...
}

표준 예외를 통해 충분히 어떤 상황의 예외인지 유추가 가능하고 알 수 있기 때문에 커스텀 예외를 만들어서 사용하는 방법이 무조건 정답이라고 할 수는 없을 것 같습니다.

 

따라서 팀원, 동료들과 충분한 얘기를 나눈 뒤 커스텀 예외를 사용하는 것이 좋아보입니다.

 

참고 자료
https://nanogiants.github.io/nestjs-swagger-api-exception-decorator/gettingstarted/usage/custom/
https://www.baeldung.com/java-new-custom-exception
https://tecoble.techcourse.co.kr/post/2020-08-17-custom-exception/