티스토리 뷰

728x90
반응형

목표: 커스텀 에러 예외처리를 만들기

throw new NotFoundMarketData()

 

- 커스텀 에러 메시지를 만든다.

- 커스텀 에러 발생시, 프로젝트가종료되지 않는다.

- 커스텀 에러 발생시, 로그를 남긴다.


문제사항

예외가 발생했을 때, 로그의 기록도 내장예외를 사용했을 때와 달랐다.

커스텀 에러도 내장예외때처럼 사용할 수 있는 방법이 없을까?

 

- posts-exception.ts

export class EmptyPostPasswordException extends HttpException {
  constructor() {
    super('비밀번호를 입력해주세요!', HttpStatusCode.BadRequest);
  }
}

 

- posts.service.ts

async createPost(
    user: Users,
    createPostDto: CreatePostDto,
  ): Promise<InsertResult> {
    if (createPostDto.postType === PostType.PRIVATE_POST) {
      const originPassword = createPostDto.postPassword;
      if (!originPassword) {
      	// 커스텀 에러
        throw new EmptyPostPasswordException();
        
      }

      const isRegex = originPassword.match(PRIVATE_PASSWORD_REGEX);
      if (!isRegex){
      	// 기본 내장 에러
        throw new BadRequestException('비밀번호는 6자리 이상이며, 숫자는 최소 1개가 필요합니다.')
      };
      
      ...
}

 

 

- 커스텀 에러가 발생했을 때는 500 에러가 뜬다. 내가 원하는 방향과 다르게  BadRequest 에러가 아닌 500에러가 뜬다.

그리고 에러문구도 '비밀번호를 입력해주세요!' 가 아닌 'Internal Server error' 라는 메시지를 리턴했다.... 🤯

 

- 내장에러(BadRequestException)을 사용했을 때

 


(해결과정)

어쩌다 해결을 했다. HttpException을 BadRequest 으로 수정했다.

 

- post-exceptions.ts

export class EmptyPostPasswordException extends BadRequestException {
  constructor() {
    super('비밀번호를 입력해주세요!');
  }
}

 

수정후 결과

 

 

Next Level....

커스텀 필터를 적용해서 에러를 만들어서 에러를 핸들링하고 싶다.

공식다큐먼트와 다른사람들의 문제해결 방안을 참고해서

에러 핸들링을 해보기로 했다.


exception filter 왜 필요해?

Nest의 내장 예외필터는 인식할 수 없는 에러를 InternalServerErrorException (StatusCode = 500)으로 변환시킨다.

NestJS에서 제공하는 예외필터 이외 직접 예외 필터 레이어를 둬서 사용자가 원하는 대로 예외를 다룰 수 있다.

유저 데이터가 존재하지 않으면, 'Not found Error' 메시지를 보내주는 에러가 아닌

'유저 데이터가 존재하지 않습니다.'  에러메시지를 보내주는 것 처럼

요구사항에 알맞는 예외를 만드는 것을 목표로 하고있다.

 

- file-paths

📦src
 ┣ 📂filters
 ┃ ┣ 📂all-exceptions
 ┃ ┃ ┗ 📜all-exceptions.filter.ts
 ┃ ┣ 📂http-exception
 ┃ ┃ ┗ 📜http-exception.filter.ts
 ┃ ┗ 📜product-exception.filter.ts
 ┃
 ┣ 📂markets
 ┃ ┣ 📂dto
 ┃ ┃ ┣ 📜create-market.dto.ts
 ┃ ┃ ┗ 📜update-market.dto.ts
 ┃ ┣ 📂entities
 ┃ ┃ ┗ 📜market.entity.ts
 ┃ ┣ 📜market-exception.ts
 ┃ ┣ 📜markets.controller.spec.ts
 ┃ ┣ 📜markets.controller.ts
 ┃ ┣ 📜markets.module.ts
 ┃ ┣ 📜markets.service.spec.ts
 ┃ ┗ 📜markets.service.ts
 ┃ 
 ┣ 📜app.controller.ts
 ┣ 📜app.module.ts
 ┣ 📜app.service.ts
 ┗ 📜main.ts

 

 

(1) http-exception.filter 작성

- 참고: 공식다큐먼트

import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
  HttpStatus,
} from '@nestjs/common';

import { 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 statusCode =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    response.status(statusCode).json({
      statusCode: statusCode,
      timestamp: new Date().toISOString(),
      path: request.url,
      message: exception?.getResponse(),
    });
  }
}

 

(2) 전역 스코프에서 에러를 캐치할 수 있도록 설정

- all-exceptions-filter.ts

import { ArgumentsHost, Catch } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';

@Catch()
export class AllExceptionsFilter extends BaseExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    super.catch(exception, host);
  }
}

 

- main.ts

import { ValidationPipe } from '@nestjs/common';
import { HttpAdapterHost, NestFactory } from '@nestjs/core';
import { AllExceptionsFilter } from './filters/all-exceptions/all-exceptions.filter';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './filters/http-exception/http-exception.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const { httpAdapter } = app.get(HttpAdapterHost);
  app.useGlobalFilters(new AllExceptionsFilter(httpAdapter));
  app.useGlobalPipes(new ValidationPipe());

  // HttpExceptionFilter을 전역 스코프에 적용.
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(3000);
}
bootstrap();

 

 

(3) 커스텀 에러

- market-exception.ts

import { HttpException } from '@nestjs/common';

export class MarketCustomException extends HttpException {
  constructor(message: string, statusCode: number) {
    super(message, statusCode);
  }
}

 

 

(4) 커스텀 에러를  특정 모듈에 적용

- markets.module.ts

import { Module } from '@nestjs/common';
import { MarketsService } from './markets.service';
import { MarketsController } from './markets.controller';
import { APP_FILTER } from '@nestjs/core';
import { HttpExceptionFilter } from 'src/filters/http-exception/http-exception.filter';

@Module({
  controllers: [MarketsController],
  providers: [
    MarketsService,
    
    // 커스텀 에러필터를 적용한다.
    { provide: APP_FILTER, useClass: HttpExceptionFilter },
  ],
})
export class MarketsModule {}

 

 

(5) 컨트롤러에 적용

...
import { MarketCustomException } from './market-exception';
import { HttpExceptionFilter } from 'src/filters/http-exception/http-exception.filter';

// HttpExceptionFilter 을 컨트롤러에서 적용.
@UseFilters(new HttpExceptionFilter())
@Controller('markets')
export class MarketsController {
  constructor(private readonly marketsService: MarketsService) {}

  @Post()
  create(@Body() createMarketDto: CreateMarketDto) {
  	// 일부러 커스텀 에러를 발생시켜봤다.
    throw new MarketCustomException('커스텀에러', HttpStatus.BAD_REQUEST);
  }
  
  ...
  
}

 

(result) 일부러 커스텀 예외를 발생시켰을 때, 내장에러 호출한것처럼 url, message, statusCode 를 나타낸 결과이다!

 

 

[참고]

 

[Nest.js] Exception filter 적용하기

들어가며 많은 기업들이 Nest.js를 활용해서 서버를 구축하는 것을 알았습니다. 더 나은 개발자가 되기 위해서는 지금부터라도 Nest.js에 대해 기초부터 공부해야겠다고 생각했습니다. 왜 많은 기

overcome-the-limits.tistory.com

 

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

 

NestJs Exception Filters: Part 02

Introduction

medium.com

 

728x90
반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함