NestJS 마스터 시리즈 6화. DTO와 Validation – 데이터 무결성과 API 품질의 시작
"API는 설계의 언어다. DTO는 그 언어의 문법이다"
NestJS에서 DTO와 유효성 검사를 어떻게 설계하고 적용할지, class-validator, Pipes, Transform 전략 등 실전 API 개발에서 꼭 필요한 개념을 설명합니다.
DTO란 무엇인가 – 단순한 포맷이 아니다
DTO(Data Transfer Object)는 클라이언트로부터 받거나 클라이언트로 전달할 데이터 구조의 명세서다.
NestJS는 TypeScript의 클래스 기반 구조를 활용해 DTO를 정의하며, 이를 통해 다음을 보장할 수 있다:
- 입력값의 명확한 스펙 정의
- 자동 유효성 검사 및 에러 응답
- Swagger 등 문서화 도구와의 자연스러운 연계
"DTO는 단순히 타입을 정하는 것이 아니라, API의 규칙과 문화를 정하는 것이다."
실전 예제 – 사용자 생성 DTO
import { IsString, IsEmail, Length } from 'class-validator';
export class CreateUserDto {
@IsEmail()
email: string;
@IsString()
@Length(4, 20)
password: string;
@IsString()
name: string;
}
- class-validator의 데코레이터를 통해 유효성 검사를 선언
- 클래스 자체가 타입 정의와 validation 룰을 동시에 수행
Validation Pipe로 검사를 자동화하기
NestJS는 ValidationPipe를 통해 DTO를 기반으로 유효성 검사를 자동으로 수행할 수 있다.
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true }));
await app.listen(3000);
}
옵션 설명:
- whitelist: true – DTO에 정의되지 않은 속성 제거
- forbidNonWhitelisted: true – 허용되지 않은 속성이 들어오면 요청 거절
- transform: true – DTO의 타입을 자동으로 변환
컨트롤러에 적용하기
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
- Nest는 @Body()에 지정된 클래스를 기준으로 유효성 검사를 수행하고, 실패 시 400 에러를 반환한다.
고급 전략 – Custom Validator 만들기
복잡한 검증 조건이 필요한 경우 커스텀 데코레이터를 만들 수 있다.
import { registerDecorator, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator';
@ValidatorConstraint({ async: true })
export class IsEmailUniqueConstraint implements ValidatorConstraintInterface {
async validate(email: string) {
// 이메일 중복 체크 로직 예시
return !(await UserRepository.existsByEmail(email));
}
}
export function IsEmailUnique(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
target: object.constructor,
propertyName,
options: validationOptions,
constraints: [],
validator: IsEmailUniqueConstraint,
});
};
}
데이터 변환과 자동 타입 캐스팅 – Transform 활용
NestJS는 class-transformer와 함께 @Type()을 활용해 JSON 데이터를 객체로 자동 변환할 수 있다.
import { Type } from 'class-transformer';
import { IsDate } from 'class-validator';
export class BookingDto {
@IsDate()
@Type(() => Date)
bookingDate: Date;
}
- 클라이언트가 문자열로 보낸 날짜도 Date 객체로 자동 변환 가능
유효성 검사 실패 시 반환 구조
NestJS는 유효성 검사 실패 시 기본적으로 다음과 같은 구조로 응답한다:
{
"statusCode": 400,
"message": [
"email must be an email",
"password must be longer than or equal to 4 characters"
],
"error": "Bad Request"
}
이 구조는 글로벌 예외 필터(ExceptionFilter)를 통해 커스터마이징할 수 있다.
마무리 인사이트
DTO와 Validation은 단순한 데이터 검증을 넘어 API의 신뢰성과 품질을 결정짓는 핵심 요소다.
NestJS는 TypeScript의 타입 시스템과 데코레이터 기반 유효성 검사를 통해 이를 구조화된 방식으로 제공한다.
- DTO는 명세서이며, 개발팀 간 계약이다.
- Validation은 방어적 설계이며, 실서비스의 무결성을 지키는 방패다.
제대로 설계된 API는 문서보다 DTO가 말해준다.
다음 회차 예고
7화. ConfigModule과 환경 설정 – .env 관리와 설정 계층 구조의 원칙
환경별 설정 분리 전략과 ConfigModule 확장 방식까지 다룬다.