Posts nestjs에서 custom validator 구현
Post
Cancel

nestjs에서 custom validator 구현

개요

  • class-validator를 이용하여 custom validator를 생성하고 적용한다.

class-validator란?

  • 유효성 검사 라이브러리로 클래스 기반의 객체 유효성을
    간편하게 검사할 수 있다.
  • 클래스의 프로퍼티 위에 데코레이터를 선언하여
    유효성 검사 규칙을 적용한다.
  • [참고1 - 데코레이터 목록]

설치

1
npm install --save class-validator  

class-validator 예시

  • 설명
    • post 목록을 조회하는 Controller를 만들고
      Query에 담긴 page-num과 page-size의 유효성 검사를 하고 싶다.
    • 중요 부분만 작성한다.
  • post/dto/post-list-query.dto.ts
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    import { IsNumberString, IsOptional } from "class-validator";  
                
    export class PostListQueryDto{  
      // 숫자가 담긴 문자열  
      @IsNumberString()  
      // 필수값이 아님  
      @IsOptional()   
      // Query 필드명이 page-num  
      @Expose({ name: "page-num" })   
      pageNum: number;  
                
      // 숫자가 담긴 문자열  
      @IsNumberString()  
      // 필수값이 아님  
      @IsOptional()  
      // Query 필드명이 page-size  
      @Expose({ name: "page-size" })  
      pageSize: number;  
    }  
    
  • post/post.controller.ts
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
     @Controller()  
    export class PostController {  
      constructor(  
        private postService: PostService  
      ) {}  
                
      @Get("/v1/posts")  
      async listPosts(@Query() query: PostListQueryDto) {  
        const pageNum = query.pageNum ? query.pageNum : 1;  
        const pageSize = query.pageSize ? query.pageSize : 9;  
        const posts = await this.postService.listPosts(pageNum, pageSize);  
                
        return posts;  
      }  
    

Custom validator

  • 설명
    • 유효성 규칙이 담긴 ValidatorConstraint를 생성하고
      @Validate 데코레이터로 적용한다.
    • 자연수인지 확인하는 custom validator를 생성해보자
  • global/validation/is-natural-validation.ts
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    import {  
      ValidationArguments,  
      ValidatorConstraint,  
      ValidatorConstraintInterface,  
    } from "class-validator";  
                
    @ValidatorConstraint({ name: "isNaturalNumber", async: false })  
    export class IsNaturalNumber implements ValidatorConstraintInterface {  
                
      // 유효성 검사 규칙, boolean만 리턴할 수 있고 false면 에러가 나게 된다.  
      validate(  
        value: any,  
        validationArguments?: ValidationArguments  
      ): boolean | Promise<boolean> {  
        return /^[1-9][0-9]*/.test(value);  
      }  
                
      // 유효성 검사 통과 실패 시 리턴되는 에러 메세지를 정의한다.  
      defaultMessage(validationArguments?: ValidationArguments): string {  
        const fieldName = validationArguments ? validationArguments.property : "it";  
        return `${fieldName} is not natural value`;  
      }  
    }  
    
  • post/dto/post-list-query.dto.ts
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    import { IsNumberString, IsOptional, Validate } from "class-validator";  
                
    export class PostListQueryDto{  
     // 자연수 인지 확인  
     @Validate(IsNaturalNumber)  
     @IsNumberString()  
     @IsOptional()   
     @Expose({ name: "page-num" })   
     pageNum: number;  
                
     // 자연수 인지 확인  
     @Validate(IsNaturalNumber)  
     @IsNumberString()  
     @IsOptional()  
     @Expose({ name: "page-size" })  
     pageSize: number;  
    }  
    

class-validator 전역 오류 메세지 수정하기

  • 설명
    • class-validator 오류 메세지 자체를 수정할 수 있다.
    • 새 ValidationPipe에서 exceptionFactory를 수정하고
      이를 globalPipes에 추가하여 적용한다.
  • main.js
    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
    32
    
    import { BadRequestException, ValidationPipe } from "@nestjs/common";  
    import { ValidationError } from "class-validator";  
                
    async function bootstrap() {  
      const port = 8080;  
      const app = await NestFactory.create(AppModule);  
                
      // 유효성 검증  
      app.useGlobalPipes(  
        new ValidationPipe({  
          transform: true,  
          transformOptions: {  
            enableImplicitConversion: false,  
          },  
          exceptionFactory: (validationErrors: ValidationError[] = []) => {  
            const errorInfos = validationErrors.map((error) => ({  
              field: error.property,  
              error: Object.values(error.constraints),  
            }));  
                
            const errorRespDto = {  
              description: "my-custom-error description",  
              message: errorInfos,  
            };  
                
            return new BadRequestException(errorRespDto);  
          },  
        })  
      await app.listen(port);  
    }  
    bootstrap();  
                
    

참고

This post is licensed under CC BY 4.0 by the author.