체인의정석

Nest.js 수정사항 총 정리 02 ) 예외처리 패턴 - Invalid Input 리팩토링 본문

개발/backend

Nest.js 수정사항 총 정리 02 ) 예외처리 패턴 - Invalid Input 리팩토링

체인의정석 2021. 12. 7. 19:03
728x90
반응형

입력값을 수정하는 부분이 nest.js에서 매우 중요한데, 사실 이게 타입스크립트를 쓰는 이유이기도 한다.

타입스크립트를 사용하게 되면, 타입을 모두 지정해 줄 수 있어서 모든 request , response 그리고 그 안에 들어가는 요소들을 객체화 시키고 타입을 지정하여 모든 상황에 대한 예외를 잘 잡아 줄 수 있다.

 

1. Class validator  사용의 경우

이번 프로젝트를 구현하면서 가장 많이 사용한 것은 class-validator 이다.

Validation Pipe 모듈의 경우 클래스 형태로 요청을 받아줄 때 사용가능하기 때문에 일반적인 string의 형태나 단일 타입에 적용 시킬 수는 없다. 따라서 조회를 쿼리 파라미터로 실행하는 경우 class-validator를 실행한다고 보면 된다.

 

먼저 dto에서 constructor를 설정해주는 작업이 필요하다. 의외로 좋았던 점은 이더리움 주소조차 타입체크를 해준다는 점이였다. 내부 로직을 체크해보면 16진수인지 체크하고, 총 길이가 맞는지 그리고 0x로 시작하는지 정도를 검사해준다. 가끔 이더리움 주소 체크를 할때 체크썸까지 적용시키는 모듈도 있는데 조회를 주로 하는 프로그램에서는 체크섬 처리가 되지 않은 데이터를 다루는 경우가 많기 때문에 이런 부분또한 고려해주어야 한다.

export class Request클래스 {
  @IsString()
  flag: 하나의 api에 2개를 실행하고 싶은 경우 flag 설정해주기;

  @IsEthereumAddress() //이더리움 주소타입도 체크할 수 있다.
  fromAddress: string;
//중략

  @IsOptional() // 이런식으로 기본값에 대한 설정을 해줄 수 있다.
  @Type(() => Number)
  @Min(1)
  @Max(30)
  rpp: number;
//중략

  constructor(
    ethAddress: string,
//중략
    rpp: number,
//중략

  ) {
    this.ethAddress = ethAddress;
//중략
    this.rpp = rpp;
//중략
  }
}

 

 

이런식으로 미리 타입을 지정해 준 후 valdiate 함수를 사용해 주면 알아서 타입 체크를 해준다. 아래는 먼저 controller에서 validate 해주는 부분이다. 여기서 input을 체크해주고 서비스 쪽으로 이동해서 잡히지 않는 예외는 모두 internal server Error로 두었다. 

    const errors = await validate(getAdddressRealtedTxRequest); //validate 를 시켜주기 위한 작업
    if (errors.length > 0) { //에러가존재할 경우 에러 처리
      this.logger.error(에러메세지공통모듈(errors));
      return invalidInputError(errors);
    } else { //에러가 존재하지 않을 경우 에러처리
      const 페이징처리된요청클래스 = 페이지네이션설정함수( //페이지네이션 기본값 설정해주는 부분
        페이징기본설정처리되기전요청,
      );
      const 응답 = await this.서비스부분.서비스의함수( // 쿼리조회해주는 부분
        페이징처리된요청클래스,
      );
      return 응답;
    }

이렇게 하면 타입체크를 훨씬 간단한 코드로 한번에 할 수 있다.

2. Validation Pipe 사용의 경우

위와 같은 경우는 쿼리파라미터를 받아올 경우이지만 post 요청으로 들어오는 경우 요청 자체가 객체화 될 수 있기 때문에 굳이 constructor로 만들고 validate 함수를 사용하지 않아도 되기 때문에 validation Pipelin 모듈을 쓰는것이 더 깔끔하다. 클래스에서 constructor를 만드는 부분과 validate 함수를 실행하는 부분이 지워지기 때문에 post요청 및 클래스 형태가 가능한 요청은 해당 모듈을 사용하도록 한다.

 

일단 dto의 경우 constructor 만 빠진다고 보고 생략하겠다.

 

 @Post('/search')
  async search(
    @Body() searchByRequest: SearchByRequest,
  ): Promise<SearchByResponse | CommonErrorResponse> {

다음과 같이 SearchByRequest 부분을 명시만 해주면 알아서 검사가 된다.

 

설정해줄 값은 다음과 같다.

 

  app.useGlobalPipes(
    new ValidationPipe({
      transform: true,
      whitelist: true,
      forbidNonWhitelisted: true,
      validationError: {
        target: false,
      },
      exceptionFactory: (errors) => {
        return new InvalidRequestException(errors);
      },
    }),
  );

main.ts에서 다음과 같이 이런저런 설정값을 넣어주고 실행하면 되며 해당 내용은 공식문서에서 확인이 가능하다.

 

728x90
반응형
Comments