DTO (Data Transfer Object)
계층 간 데이터를 전달하기 위해 사용되는 객체를 말합니다.
주요 역할
데이터 전송 : 데이터를 효율적으로 전달
유효성 검사 : 데이터 구조를 명확히 정의해 오류 방지
불필요한 데이터 제거 : 필요한 필드만 포함하여 전송 성능 최적화
사용하는 이유
유지보수성 증가 : DTO로 데이터 구조를 명확히 정의하여 코드 가독성, 유지보수성 증가
보안 강화 : 민감한 정보를 제외하고 필요한 데이터만 노출 가능
효율성 향상 : 데이터 크기를 줄여 네트워크 트래픽 감소
확장성 : 새로운 필드 추가 시에도 구조화된 데이터를 유지
DTO 정의
TypeScript에서는 DTO를 정의할 때는 Interface, Class, Generic 등을 사용합니다.
Interface 기반
인터페이스 기반 DTO는 주로 타입 안전성을 제공하고 컴파일 타임 검사를 수행하는 목적으로 사용됩니다.
컴파일 타임에서만 존재
Interface는 컴파일 이후 JS 코드에서 제거됩니다.
즉, 런타임에서는 아무 영향도 주지 않음
경량 & 빠름
클래스보다 가벼워 성능 부담이 없습니다.
데이터 변환이나 유효성 검사가 필요 없는 경우 적합.
1
2
3
4
5
export interface CreateUserDTO {
name: string;
email: string;
password: string;
}
Class 기반
클래스 기반 DTO는 런타임에서 실제 인스턴스를 가지며, 데코레이터와 같은 기능을 활용하여 유효성 검사를 적용할 수 있습니다.
런타임에서 동작
인터페이스는 컴파일 타임에만 존재하지만, 클래스는 런타임에서도 유지됩니다.
유효성 검사 및 변환이 쉬움
class-validator를 사용하여 DTO의 속성을 검증할 수 있습니다.
class-transformer와 함께 사용하면 객체를 자동 변환 가능합니다.
NestJS와의 높은 호환성
NestJS에서는 클래스 기반 DTO를 적극적으로 사용하며, @nestjs/mapped-types 같은 유틸을 제공합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import { IsString, IsInt, Min, Max } from "class-validator";
export class CreateMovieDto {
@IsString()
title: string;
@IsString()
director: string;
@IsInt()
@Min(1900)
@Max(new Date().getFullYear()) // 현재 연도까지 허용
releaseYear: number;
}
////////////////////////////////////////////////
import { plainToInstance } from "class-transformer";
import { validate } from "class-validator";
import { CreateMovieDto } from "./dto/create-movie.dto";
const input = {
title: "Inception",
director: "Christopher Nolan",
releaseYear: 2010,
};
// 객체 변환
const movieDto = plainToInstance(CreateMovieDto, input);
// 유효성 검사
validate(movieDto).then((errors) => {
if (errors.length > 0) {
console.log("Validation failed:", errors);
} else {
console.log("Validation passed:", movieDto);
}
});