Skip to content
Jwoo Blog
LinkedInGitHub

NestJS - DTO๋Š” ์™œ class๋กœ ๋งŒ๋“ค๊นŒ?

โ€” NestJS, DTO โ€” 5 min read

์˜๋ฌธ์  ๐Ÿค”

TypeScript์™€ NestJS๋กœ ๊ฐœ๋ฐœํ•˜๋˜ ๋„์ค‘, ๋ฌธ๋“ ๊ทธ๋Ÿฐ ์˜๋ฌธ์ด ๋“ค์—ˆ๋‹ค.

'DTO(Data Transfer Object)๋Š” ์™œ class๋กœ ๊ตฌํ˜„ํ•˜๋Š”๊ฑธ๊นŒ..?'

๊ทธ์ € ํƒ€์ž… ์ฒดํฌ๋ฅผ ์œ„ํ•ด์„œ๋ผ๋ฉด interface๋‚˜ type์œผ๋กœ๋„ ๊ฐ€๋Šฅํ•œ๊ฒŒ ์•„๋‹Œ๊ฐ€?
๊ทธ๋Ÿฐ๋ฐ ์™œ ๋งŽ์€ ๋ ˆํผ๋Ÿฐ์Šค์—์„œ, ๊ทธ๋ฆฌ๊ณ  ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋„ class๋กœ DTO๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฑธ๊นŒ?



๐Ÿ’ก ๊ถ๊ธˆ์ฆ ํ•ด๊ฒฐ..!

์˜์™ธ๋กœ ๊ถ๊ธˆ์ฆ์€ ๋น ๋ฅด๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค..
์‹ฌ์ง€์–ด ๊ณต์‹ ๋ฌธ์„œ์— ๊ธฐ์žฌ ๋˜์–ด ์žˆ๋˜ ๋‚ด์šฉ์ด์—ˆ๋˜ ๊ฒƒ...

NestJS DTO vs Interface
NestJS ๊ณต์‹๋ฌธ์„œ - Controllers - Request payloads

slack

But first (if you use TypeScript), we need to determine the DTO (Data Transfer Object) schema. A DTO is an object that defines how the data will be sent over the network. We could determine the DTO schema by using TypeScript interfaces, or by simple classes. Interestingly, we recommend using classes here. Why? Classes are part of the JavaScript ES6 standard, and therefore they are preserved as real entities in the compiled JavaScript. On the other hand, since TypeScript interfaces are removed during the transpilation, Nest can't refer to them at runtime. This is important because features such as Pipes enable additional possibilities when they have access to the metatype of the variable at runtime.

TypeScript๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ DTO ์Šคํ‚ค๋งˆ๋ฅผ ๊ฒฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. DTO๋Š” ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๊ฐ€ ์ „์†ก๋˜๋Š” ๋ฐฉ๋ฒ•์„ ์ •์˜ํ•˜๋Š” ๊ฐœ์ฒด์ž…๋‹ˆ๋‹ค. TypeScript Interface๋‚˜ ๊ฐ„๋‹จํ•œ class๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ DTO ์Šคํ‚ค๋งˆ๋ฅผ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํฅ๋ฏธ๋กญ๊ฒŒ๋„ ์ด ์ƒํ™ฉ์—์„œ๋Š” class๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. class๋Š” JavaScript ES6 ํ‘œ์ค€์˜ ์ผ๋ถ€์ด๋ฏ€๋กœ JavaScript๋กœ ์ปดํŒŒ์ผํ•ด๋„ ์‹ค์ œ ๊ฐ์ฒด๋กœ ๋‚จ์•„์žˆ์ง€๋งŒ, TypeScript interface๋Š” ๋ณ€ํ™˜ ์ค‘์— ์ œ๊ฑฐ๋˜๋ฏ€๋กœ Nest๊ฐ€ ๋Ÿฐํƒ€์ž„์— interface๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. Pipe์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ์€ ๋Ÿฐํƒ€์ž„์— ๋ณ€์ˆ˜์˜ ๋ฉ”ํƒ€ํƒ€์ž…์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์„ ๋•Œ ์œ ์šฉํ•˜๋ฏ€๋กœ, ๋Ÿฐํƒ€์ž„ ์‹œ ์ฐธ์กฐ ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋Š” ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

์ถœ์ฒ˜: NestJS ๊ณต์‹ ๋ฌธ์„œ


class๋Š” ์ปดํŒŒ์ผ ์ดํ›„์—๋„ ๋‚จ์•„์žˆ์–ด ๋Ÿฐํƒ€์ž„์—๋„ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด DTO๋ฅผ class๋กœ ๊ตฌํ˜„ํ•˜๋Š” ํ•ต์‹ฌ์ ์ธ ์ด์œ ์˜€๋‹ค.
DTO๋Š” ๋ฐ์ดํ„ฐ ์ „์†ก์— ์“ฐ๋Š” ๊ฐ์ฒด์ธ ๋งŒํผ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด๋‚˜ ๊ฐ’์— ๋Œ€ํ•œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์€๋ฐ, ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” ๋Ÿฐํƒ€์ž„์—๋„ DTO์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•œ๋‹ค...!


export class CreateMeetupDto {
@ApiProperty({
description: '์ด๋ฒคํŠธ ์ œ๋ชฉ (์ตœ๋Œ€ 50์ž)',
minLength: 1,
maxLength: 50,
})
@Transform((params) =>
typeof params.value === 'string' ? params.value.trim() : params.value
)
@IsString()
@IsNotEmpty()
@MaxLength(50)
title: string;
@ApiProperty({
description: '์ด๋ฒคํŠธ ์„ค๋ช… (์ตœ๋Œ€ 255์ž)',
minLength: 1,
maxLength: 255,
})
@IsString()
@Transform((params) => params.value.trim())
@IsNotEmpty()
@MaxLength(255)
description: string;
}

์œ„๋Š” ๋‚ด๊ฐ€ ์‹ค์ œ๋กœ ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” DTO ์ค‘ ํ•˜๋‚˜๋‹ค.
ํ•ด๋‹น DTO์—์„œ๋„ @IsString(), @IsNotEmpty(), @MaxLength(255) ๋“ฑ์„ ํ™œ์šฉํ•˜์—ฌ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.



ํƒ€์ž…๊ณผ ๊ฐ’ ๊ตฌ๋ถ„ํ•˜๊ธฐ

์ฐธ๊ณ : ์ดํŽ™ํ‹ฐ๋ธŒ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ - ์•„์ดํ…œ 8 ํƒ€์ž… ๊ณต๊ฐ„๊ณผ ๊ฐ’ ๊ณต๊ฐ„์˜ ์‹ฌ๋ฒŒ ๊ตฌ๋ถ„ํ•˜๊ธฐ

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ”Œ๋ ˆ์ด๊ทธ๋ผ์šด๋“œ๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ๋ณ€ํ™˜ํ•œ ๊ฒฐ๊ณผ๋ฌผ์„ ๋ณด์—ฌ์ค€๋‹ค.
์ปดํŒŒ์ผ ๊ณผ์ •์—์„œ ํƒ€์ž…์€ ์ œ๊ฑฐ๋˜๊ธฐ ๋•Œ๋ฌธ์—, ์‚ฌ๋ผ์ง€๋Š” ๊ฒƒ์ด ์žˆ๋‹ค๋ฉด ํƒ€์ž…์ผ ๊ฒƒ์ด๋‹ค.

playground

type๊ณผ interface ์˜ˆ์•ฝ์–ด๋ฅผ ์ด์šฉํ•˜์—ฌ ์„ ์–ธํ•˜๋ฉด ํƒ€์ž…์ด ๋˜๊ณ , class๋‚˜ const, let๋กœ ์„ ์–ธํ•˜๋ฉด ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
ํŠนํžˆ, class์™€ enum์€ ์ƒํ™ฉ์— ๋”ฐ๋ผ ํƒ€์ž…๊ณผ ๊ฐ’์ด ์ „๋ถ€ ๊ฐ€๋Šฅํ•œ ์˜ˆ์•ฝ์–ด์ด๋‹ค.