0. 개요
NestJS 프로젝트에서 class-validator
와 class-transformer
라이브러리를 통해 데코레이터 사용만으로 데이터 검증을 할 수 있습니다.
기본적으로 제공하는 데코레이터를 사용해도 충분하지만 특정 값에 대한 검증을 추가하고 싶을때 커스텀 데코레이터를 만들게 됩니다.
이번 포스팅에서는 휴대번호와 비밀번호 검증을 위한 커스텀 데코레이터를 구현해보려고 합니다.
1. 사전 설정
a. 라이브러리 Install
우리가 만들고자 하는 기능에 필요한 두 가지 라이브러리를 설치합니다.
npm install class-transformer class-validator
b. Global 설정
app.useGlobalPipes
는 전역 Pipe
를 추가하는 메소드로 ValidationPipe
를 추가하여 요청 라이프사이클에서 메소드 핸들러가 작동하기 전에 검증을 수행하게 됩니다.
해당 설정은 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);
// 전역 파이프 설정
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // DTO에 정의되지 않은 속성 제거
forbidNonWhitelisted: true, // DTO에 정의되지 않은 속성이 있으면 에러 반환
transform: true, // 요청 데이터를 DTO 인스턴스로 자동 변환
}),
);
await app.listen(3000);
}
bootstrap();
2. 커스텀 데코레이터 구현
휴대번호와 비밀번호 관련 데코레이터를 만들때 정규식을 이용한 문자열을 검증하는 방식으로 진행했습니다.
(정규식 관련 내용은 다른 포스팅에서!)
a. 휴대번호 검증 데코레이터
휴대번호 검증은 xxx-xxx(x)-xxxx
기준으로 작성되었습니다.
import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator';
export function IsPhoneNumber(validationOptions?: ValidationOptions): PropertyDecorator {
return function (object: Object, propertyName: string) {
registerDecorator({
name: 'isPhoneNumber',
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
validator: {
validate(value: any, args: ValidationArguments) {
const phoneRegex = /^\d{3}-\d{3,4}-\d{4}$/; // 예: 123-4567-8901
return typeof value === 'string' && phoneRegex.test(value);
},
},
});
};
}
b. 비밀번호 검증 데코레이터
비밀번호는 보안상의 이유로 복잡한 검증이 필요할 수 있습니다.
아래 예시 코드는 최소 8자, 대문자, 소문자, 숫자, 특수문자를 포함하도록 하는 비밀번호 검증 데코레이터입니다.
export function IsPassword(validationOptions?: ValidationOptions): PropertyDecorator {
return function (object: Object, propertyName: string) {
registerDecorator({
name: 'isPassword',
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
validator: {
validate(value: any, args: ValidationArguments) {
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
return typeof value === 'string' && passwordRegex.test(value);
},
defaultMessage(args: ValidationArguments) {
return 'Password must be at least 8 characters long and include at least one uppercase letter, one lowercase letter, one number, and one special character.';
},
},
});
};
}
3. DTO에 커스텀 데코레이터 적용
위에서 만든 데코레이터를 DTO
에 적용하고 추가적인 검증 데코레이터를 함께 사용합니다.
해당 코드에서는 @IsNotEmpty
를 적용하였지만, 값이 들어올때만 검증 하고싶다면 @IsOptional()
데코레이터와 함께 사용하면 됩니다.
import { IsNotEmpty, IsEmail } from 'class-validator';
import { IsPhoneNumber, IsPassword } from './custom-decorators';
export class CreateUserDto {
// ... 다른 변수들
@IsNotEmpty()
@IsEmail() // 기본 Validation Decorator
email: string;
@IsNotEmpty()
@IsPhoneNumber({ message: 'Invalid phone number format' })
phone: string;
@IsNotEmpty()
@IsPassword({ message: 'Password does not meet the complexity requirements' })
password: string;
}
4. 컨트롤러에서 DTO 적용
Controller
에 테스트 요청에 관한 내용을 정의.
import { Body, Controller, Post } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
@Controller('users')
export class UsersController {
@Post()
createUser(@Body() createUserDto: CreateUserDto) {
// 요청 데이터가 DTO 검증을 통과해야 이 메서드에 도달합니다.
return {
message: 'User created successfully',
data: createUserDto,
};
}
}
5. 테스트
a. 검증 통과
b. 검증 시 실패
6. 마무리
Spring이나 NestJS나 프레임워크 별로 검증 라이브러리는 대부분 존재하기 때문에 하나 공부해두면 다른 부분 프레임워크에서 적용하기 어렵진 않아보입니다.
본 포스팅에서는 많이 사용될만한 휴대번호, 비밀번호에 대한 커스텀이지만 각자 필요한 부분이 다를수있기에 검증 후 Return Boolean Type 만 적용한다면 취향에 맞게 커스텀이 가능합니다.
'DEV > Node' 카테고리의 다른 글
[Node] class-validator decorator 목록 (1) | 2024.11.18 |
---|---|
[Node] Passport 활용하기 (with. NestJS) (0) | 2024.11.10 |
[Node] NestJS 란? (0) | 2023.11.16 |