[NestJS] Validation Pipe 적용기

주형(Jureamer)·2023년 3월 7일
1

Validation Pipe란?

validation pipeclass-validator 패키지와 해당 패키지에서 제공하는 데코레이터를 활용하여 요청 데이터의 유효성을 검증하는 기능입니다.

이를 통해 개발자는 간단한 어노테이션을 사용하여 요청 데이터에 대한 검증 규칙을 선언하고, 이를 통해 모든 클라이언트 요청 데이터에 대한 유효성 검사를 간편하게 수행할 수 있습니다.


Validation Pipe 적용

1. 먼저 class-validatorclass-transformer를 설치 해줍니다.

npm i --save class-validator class-transformer

2. 다음으로, DTO(Data Transfer Object)를 만듭니다.

이 DTO는 request body로 들어오는 데이터를 담는 클래스입니다. 예를 들어 다음과 같은 코드가 있을 때,

interface CreateUserDto {
    firstName: string;
    lastName: string;
    email: string;
    password: string;
}

이를 class-validator를 이용해 검증하려면 다음과 같이 코드를 작성합니다.

import { IsString, IsEmail } from 'class-validator';

export class CreateUserDto {
    @IsString()
    readonly firstName: string;

    @IsString()
    readonly lastName: string;

    @IsEmail()
    readonly email: string;

    @IsString()
    readonly password: string;
}

여기서 @IsString()이나 @IsEmail()과 같은 데코레이터는 해당 클래스 프로퍼티가 맞는 형식인지 검증합니다.
이를 사용하기 위해서는 class-validator에서 제공하는 데코레이터를 불러와 사용하면 됩니다.


3. 다음은 validation pipe를 생성 해줍니다.
파이프 옵션으로 whitelist, errorHttpStatusCode 등의 옵션을 추가할 수 있으며, 전체 옵션에 대한 내용은 공식 문서를 참고하실 수 있습니다.

// main.ts
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

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

  app.useGlobalPipes(new ValidationPipe());

  await app.listen(3000);
}
bootstrap();

4. 해당 파이프를 사용하기 위해서는 해당 controller의 메소드에 @Body() 데코레이터 안에서 Dto를 사용하면 됩니다.

import { Controller, Post, Body } from '@nestjs/common';
import { CreateUserDto } from './create-user.dto';

@Controller('users')
export class UsersController {
  @Post()
  async create(@Body() createUserDto: CreateUserDto) {
  }
}

유효성 검사에 실패할 경우 아래와 같이 실패 응답(400 Bad Request)을 반환합니다.

{
  "statusCode": 400,
  "error": "Bad Request",
  "message": ["email must be an email"]
}

그 외 사용 방법

1. IsIn

정해진 배열 값만 사용


import {
  ArrayNotEmpty,
  IsArray,
  IsIn
} from 'class-validator'

const weekdays = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'];

export class SpecificString {
  @IsArray()
  @ArrayNotEmpty()
  @IsIn(weekdays)
  day: string[]
}

2. 유효성 제약조건 (커스텀)

  • each는 배열의 모든 값이 유효한 지 체크합니다.
  • @Matches로 정규식 조건을 사용할 수 있습니다.
  • @Contains는 문자 또는 숫자를 포함하고 있는 지 체크합니다.
import {
  ArrayNotEmpty,
  Contains,
  IsArray,
  IsString,
  Matches,
  MaxLength
} from 'class-validator'

export class StringArray {
  @IsArray()
  @ArrayNotEmpty()
  // each: 배열의 모든 값이 유효한지 체크
  @IsString({ each: true }) 
  @MaxLength(6, { each: true })
  
  // 알파벳으로 이루어져있는지 체크
  @Matches('^[a-zA-Z\\s]+$', undefined, { each: true })
  
  // 'hello' 문자를 포함하고 있는 지 체크
  @Contains('hello', { each: true })
  stringArray: string[]
}

3. Custom Validator

Validator Constraint를 이용하여 Custom Validator를 생성할 수 있습니다.

// example.dto.ts

import {
  IsNumber,
  IsNotEmpty,
  MinLength,
  MaxLength,
  ValidatorConstraint,
  ValidatorConstraintInterface
} from 'class-validator'

@ValidatorConstraint({ name: 'IsEvenNumber' })
export class IsEvenNumber implements ValidatorConstraintInterface {
  validate(value: number): boolean {
    if (value {
      return value % 2 === 0
    }
    return false
  }
}

export class SpecificNumber {
  @IsNumber()
  @IsNotEmpty()

  // 짝수인지 체크
  @Validate(IsEvenNumber, { message: 'No Even Number' })
  specificNumber: number
}

참고

profile
작게라도 꾸준히 성장하는게 목표입니다.

0개의 댓글