nestjs의 Error customizing하기

0

Nest 에는 애플리케이션 전체에서 처리되지 않은 모든 예외를 처리하는 내장 예외 레이어가 함께 제공된다. 애플리케이션 코드에서 예외가 처리되지 않으면 이 계층에서 이를 포착한 다음 사용자에게 친숙한 적절한 응답을 자동으로 보낸다.

기본적으로 nestjs가 expressjs를 내장하고 있고, express 4버젼 부터는 try-catch를 router에 감싸지 않아도 알아서 예외처리를 해주기 때문에 nest도 이와 같을 거라 생각한다. 이제 router마다 try-catch를 감싸주지 않아도 된다.


export class ForbiddenException extends HttpException {
  constructor() {
    super('Forbidden', HttpStatus.FORBIDDEN);
  }
}

https://docs.nestjs.com/exception-filters#custom-exceptions
nestjs 공식문서에 에러필터를 커스텀하려면 HttpException class를 확장하라고 나와있다.

@Catch(HttpException)
export class ForbiddenException extends HttpException {
  constructor() {
    super('Forbidden', HttpStatus.FORBIDDEN);
  }
}

그리고 위와 같이Catch annotation을 활용하여 특정 에러에 대해 filter를 적용할 수 있다.


ERROR CODE 정의

enum CustomErrorCode {
  // 인증 관련 에러 코드
  INVALID_LOGIN = 1000,
  USER_ALREADY_EXIST = 1001, // 이미 존재하는데 회원가입 하는 경우
  USER_NOT_AUTHENTICATED = 1002, // 회원가입되지 않은 유저를 로그인 하는 경우
  INVALID_NICKNAME = 1100, // 닉네임에 욕설 및 비속어 포함
  DUPLICATE_NICKNAME = 1101, // 이미 존재하는 닉네임

  // 토큰 관련 코드
  NO_ACCESS_TOKEN = 2000,
  EXPIRED_ACCESS_TOKEN = 2001,
  INVALID_ACCESS_TOKEN = 2002,

  NO_REFRESH_TOKEN = 2100,
  EXPIRED_REFRESH_TOKEN = 2101,
  INVALID_REFRESH_TOKEN = 2102,

  // 유저 관련 에러 코드
  USER_NOT_FOUND = 3000,

  // 선물 관련 에러 코드
  PRESENT_NOT_FOUND = 4000,
  PRESENT_GOAL_SHORT_FALL = 4001,
  PRESENT_DEADLINE_SHORT_FALL = 4002,
  PRESENT_NOT_MINE = 4003,
  PRESENT_INVALID_DATE = 4004,

  // 펀딩 관련 에러 코드
  FUNDING_NOT_FOUND = 5000,
  FUNDING_MONEY_SHORT_FALL = 5001,
  FUNDING_ALREADY_END = 5002,
  FUNDING_ALREADY_COMPLETE = 5003,
  FUNDING_NOT_MINE = 5004,

  // 이미지 관련 에러 코드
  IMAGE_NOT_FOUND = 6000,

  // request 요청 에러
  INVALID_PARAM = 7000,
  INVALID_QUERY = 7001,

  // validation 에러
  VALIDATION_ERROR = 8000,

  // 서버 에러
  INTERNAL_SERVER_ERROR = 9000,

  // NOT FOUND 에러
  PAGE_NOT_FOUND = 10000,

  // 예상못한 에러
  UNCATCHED_ERROR = 99999,
}

export default CustomErrorCode;

먼저 위와 같이 에러 코드를 정의해 주었다.
front에서 처리할 수 있는 에러들에 대해서는 status code외에 code를 따로 보내주기로 하였다.


 if (!user) {
      throw new BadRequestException({
        message: '회원가입 되지 않은 유저입니다!',
        code: customErrorCode.USER_NOT_AUTHENTICATED,
      });
    }

그리고 에러가 났을때 이와 같이 message와 code를 에러 객체 안에 넣어주었다.


Error.filter.ts 정의

@Catch()
export class CustomErrorFilter implements ExceptionFilter {
  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
  }
  
  if (exception instanceof BadRequestException) {
    // BadRequest Error
  } else if (exception instanceof InternalServerErrorException) { 
  	// 처리할 수 있는 server Error
  }
  else if (exception instanceof NotFoundException { // NOT FOUND 404 ERROR
  }
  else if (exception instanceof HttpException) {
  	// 위에 속하지 않는 HTTP ERROR
  }
  else {
    // FATAL ERROR
  }
}

먼저 에러를 크게 다섯가지로 나누었다.
client의 실수로 발생한 BadRequestException
서버측의 에러인 InternalServerErrorException
존재하지 않는 API조회시 발생하는 NotFoundException
이외의 HTTP 예외인 HttpException
그리고 이 모든 에러에 잡히지 않는 에러인 FATALError로 정의 하였다.

그리고 FATALError에 대해선 즉각 slack으로 메세지를 보내게 하여 바로 처리할 수 있게 하였다.


CUSTOM ERROR 적용

providers: [
    AppService,
    {
      provide: APP_FILTER,
      useClass: CustomErrorFilter,
    },
  ],

그리고 app.module.ts의 provider에 우리가 정의해준 CustomErrorFilter파일을 넣어주면 된다.


{
    "statusCode": 500,
    "message": "UNCATCHED ERROR",
    "description": "Cannot read properties of undefined (reading 'accessToken')",
    "code": 99999
}

이렇게 하면 Front end는 모든 발생하는 에러에 대해서 똑같은 객체로 값을 받게되고, code로 특정 에러들에 대해 핸들링 할 수 있게 된다.

profile
https://www.youtube.com/watch?v=__9qLP846JE

0개의 댓글

관련 채용 정보