ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • NestJS 마스터 시리즈 6화. DTO와 Validation – 데이터 무결성과 API 품질의 시작
    기술과 산업/언어 및 프레임워크 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
Designed by Tistory.