언어 및 프레임워크/NestJS

NestJS 마스터 시리즈 6화. DTO와 Validation – 데이터 무결성과 API 품질의 시작

B컷개발자 2025. 4. 30. 12:43
728x90

"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 확장 방식까지 다룬다.

 

728x90