이번 포스팅에서는 NestJS에서 class-validator를 사용해서 나만의 Custom Validator를 만드는 방법에 대해 알아보고자 한다.
< 목차 >
- class-validator
- Custom Validator 만들기
1. class-validator
우선 class-validator는 NestJS에서만 사용할 수 있는 것이 아니다. TypeScript를 사용하기만 한다면 어디서든 사용할 수 있는 라이브러리이다. 실제 어떤 식으로 class-validator를 사용해서 클래스를 검증하는지 살펴보자.
https://www.npmjs.com/package/class-validator
import { validate, IsNotEmpty, IsEmail } from 'class-validator';
class User {
@IsNotEmpty()
name: string;
@IsEmail()
email: string;
}
const user = new User();
user.name = '';
user.email = 'helloWorld';
validate(user).then(errors => {
// 에러 반환
});
위와 같이 특정 클래스의 프로퍼티를 검증하고 싶은 경우 class-validator에서 제공해주는 어떤 validator든 검증하고 싶은 프로퍼티에 데코레이터 형태로 제공해주면 된다. class-validator는 IsNotEmpty, IsEmail, IsInt, IsString 등 직관적이고 우리가 쉽게 알 수 있는 이름으로 annotation들을 제공해주고 있다. 그리고 validate() 함수를 사용해 객체를 검증했을 때 class-validator에 부합하지 않은 값이 입력됐다면 해당되는 에러를 반환하게 된다.
👉 class-validator 반환 에러 구조
- target : 검증한 객체
- property : 검증 실패한 프로퍼티
- value : 검증 실패한 값
- constraints : 검증 실패한 제약조건
- children : 프로퍼티의 모든 검증 실패 제약조건
{
target: Object;
property: string;
value: any;
constraints?: {
[type: string]: string;
};
children?: ValidationError[];
}
👉 NestJS에서 class-validator 적용하기
실제 NestJS에서 class-validator를 적용하기 위해서는 우선 class-validator와 class-transformer 패키지를 설치해준다.
$ npm i --save class-validator class-transformer
설치한 class-validator를 사용하기 위해서는 src/main.ts 파일에서 다음과 같이 ValidationPipe를 등록해주면 된다. (ValidationPipe에서 class-validator 와 class-transformer를 사용하기 때문에 두 패키지를 반드시 설치해줘야 한다)
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// ValidationPipe uses the class-validator and class-transformer libraries
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
}),
);
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
ValidationPipe() 옵션
-> whitelist : DTO에서 데코레이터가 명시된 프로퍼티만 허용하고 나머지 프로퍼티들은 자동으로 제거
-> forbidNonWhitelisted : whitelist 옵션을 사용한 상태에서 DTO에 정의되지 않은 프로퍼티가 들어오면 Exception 발생
다음은 알아두면 유용한 기본 제공 class-validator들이다.
👉 공통 Validator
- @IsDefined
- @IsOptional
- @Equals
- @NotEquals
- @IsEmpty
- @IsNotEmpty
- @IsIn
- @IsNotIn
👉 타입 Validator
- @IsBoolean
- @IsDate
- @IsDateString
- @IsString
- @IsNumber
- @IsInt
- @IsArray
- @IsEnum
👉 숫자 Validator
- @IsDivisibleBy
- @IsPositive
- @IsNegative
- @Min
- @Max
👉 문자 Validator
- @Contains
- @NotContains
- @IsAlphanumeric
- @IsCreditCard
- @IsHexColor
- @MaxLength
- @MinLength
- @IsUUID
- @IsLatLong
2. Custom Validator 만들기
앞서 class-validator에서 기본으로 제공해주는 validator들을 살펴보았다. 하지만 이 validator들이 항상 우리 입맛에 맞을 수는 없다. 그럴 때는 나만의 Custom Validator를 직접 만들어서 사용하는 것이 꽤 유용하다.
class-validator에서 가져온 ValidatorConstraintInterface를 implements 하여 PasswordValidator 같은 나만의 validator를 만들 수 있다. 그리고 이렇게 만든 PasswordValidator 클래스에 @ValidatorConstraint() annotation을 붙여줌으로써 해당 클래스를 validator로 사용 가능하다. Custom Validator를 적용할 때는 class-validator에서 가져온 @Validate() annotation의 인수로 validator를 전달해주면 된다.
import {
Validate,
ValidationArguments,
ValidatorConstraint,
ValidatorConstraintInterface,
} from 'class-validator';
// Custom Validator
@ValidatorConstraint()
class PasswordValidator implements ValidatorConstraintInterface {
validate(value: any, validationArguments?: ValidationArguments): Promise<boolean> | boolean {
// 비밀번호 길이는 4~8
return value.length >= 4 && value.length <= 8;
}
defaultMessage?(validationArguments?: ValidationArguments): string {
return '비밀번호의 길이는 4~8자여야 합니다. 입력된 비밀번호: ($value)';
}
}
// Custom Validator 적용
class CreateUserDto {
@Validate(PasswordValidator)
password: string;
}
PasswordValidator라는 나만의 Custom Validator를 만들고 @Validate() annotation의 인수로 전달해주는 방식 외에도 @Validate(PasswordValidator)를 하나로 묶어주는 IsValidPassword라는 커스텀 데코레이터 함수를 만들어서 좀 더 편하고 쉽게 사용할 수도 있다. 이 때 registerDecorator를 사용해 Custom Validator를 validator로 등록해 함수를 만들어주면 된다.
import {
registerDecorator,
ValidationOptions,
} from 'class-validator';
function IsValidPassword(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
target: object.constructor,
propertyName,
options: validationOptions,
validator: PasswordValidator, // validator로 Custom Validator 등록
});
};
}
// IsValidPassword 적용
class CreateUserDto {
@IsValidPassword()
password: string;
}
'Node > NestJS' 카테고리의 다른 글
NestJS - MongoDB 연결하기 & 환경 변수 설정 (0) | 2023.04.08 |
---|---|
NestJS - Pipes & Interceptors (0) | 2023.03.26 |
NestJS - Exception filter 설정 (0) | 2023.03.16 |
NestJS - LoggerMiddleware 설정 (0) | 2023.03.13 |
NestJS - 캡슐화(Encapsulation) & Modules (0) | 2023.03.10 |