YesCoding

Go to English
search:

nestjs response dto, nestjs request dto μž‘μ„±λ²•

thumbnail_nestjs_dto

Nestjs μ—μ„œ Rest API server λ§Œλ“€ λ•Œ μ‚¬μš©ν•˜λ©΄ μœ μš©ν•œ class validator request DTO, response DTO μ‚¬μš©λ²•μ„ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

NestJS 둜 RestAPI server λ₯Ό λ§Œλ“€λ‹€ 보면 DTO 의 μœ μš©ν•¨μ„ μ•Œκ²Œλœλ‹€.

DTO λž€ (Data Transfer Object) 계측간 데이터 κ΅ν™˜μ„ μœ„ν•΄ Data λ₯Ό λ³€ν˜•ν•˜μ—¬ μ‚¬μš©ν•˜λŠ” 객체λ₯Ό μ˜λ―Έν•œλ‹€. μ—¬κΈ°μ„œ κ³„μΈ΅κ°„μ΄λž€ client 와 server κ°„μΌμˆ˜λ„ 있고, server logic κ³Ό data repository κ°„μΌμˆ˜λ„ μžˆλ‹€.

Nestjs 와 ν•¨κ»˜ μ“°κΈ° μœ„ν•œ DTO λΌμ΄λΈŒλŸ¬λ¦¬λŠ” class validator κ°€ μžˆλ‹€. κ³΅μ‹λ¬Έμ„œμ—μ„œλ„ class-validator λ₯Ό μ“°κ³  μžˆλ‹€.

이 κΈ€μ—μ„œλŠ” ν΄λΌμ΄μ–ΈνŠΈ-μ„œλ²„ 계측 κ°„μ—μ„œ μ‚¬μš©λ˜λŠ” DTO λ₯Ό 닀뀄볼 것이닀.

μ§€κΈˆ ν”„λ‘œμ νŠΈμ—μ„œλŠ” ν΄λΌμ΄μ–ΈνŠΈκ°€ μš”μ²­μ„ 보낼 λ•Œμ™€ 응닡을 받을 λ•Œ ν•„μš”ν•œ DTO λ₯Ό request.dto 와 response.dto 둜 κ΅¬λΆ„ν•΄μ„œ μ“°κ³  μžˆλ‹€.

1. nestjs request DTO

μ˜ν™” OTT μ„œλΉ„μŠ€λΌκ³  κ°€μ •ν•΄λ³΄μž. μ˜ν™”λ₯Ό μƒˆλ‘œ μΆ”κ°€ν•˜λŠ” api μ—λŠ” μΈμžκ°’μœΌλ‘œ μ˜ν™”μ— λŒ€ν•œ 정보λ₯Ό λ„£μ–΄ μ„œλ²„μ— 보내야 ν•  것이닀. ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ μ˜ν™” 제λͺ©, 섬넀일 이미지, κ°„λž΅ μ†Œκ°œλ₯Ό μž…λ ₯ν•΄ μ„œλ²„μ— 보낼 λ•Œ POST /movie/create λΌλŠ” api λ₯Ό ν˜ΈμΆœν•˜λ©΄μ„œ body 둜 μ˜ν™” 제λͺ©, 섬넀일 이미지, κ°„λž΅ μ†Œκ°œλ₯Ό λ‹΄μ•„ 보낸닀.

ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ Request λ₯Ό 보낼 λ•ŒλŠ” ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ 보낸 body κ°€ μœ νš¨ν•œ 정보듀을 λ‹΄κ³  μžˆλŠ” 지 μ„œλ²„μ—μ„œ λ°˜λ“œμ‹œ 확인해야 ν•œλ‹€. 예λ₯Ό λ“€λ©΄ κ°„λž΅ μ†Œκ°œκΈ€μžμˆ˜λŠ” μ΅œλŒ€ 100자λ₯Ό λ„˜μ–΄μ„œλŠ” μ•ˆ λ˜λŠ”λ° ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ 100자 λ„˜κ²Œ μž…λ ₯ν•΄ λ³΄λƒˆμ„ λ•Œ μ„œλ²„μ—μ„œ 이λ₯Ό κ±ΈλŸ¬μ£Όμ–΄μ•Ό ν•œλ‹€. λ¬Όλ‘ , ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ μž…λ ₯ν•  λ•Œ 100μžκ°€ λ„˜μœΌλ©΄ submit 을 λͺ»ν•˜κ²Œ 막을 μˆ˜λŠ” μžˆμ§€λ§Œ, curl λ“± api λ₯Ό λ°”λ‘œ ν˜ΈμΆœν•˜λŠ” 경우λ₯Ό λŒ€λΉ„ν•΄ μ΄μ€‘μœΌλ‘œ μ„œλ²„μ—μ„œλ„ λ§‰μ•„μ£ΌλŠ” 것이 μ’‹λ‹€.

λ˜ν•œ ν•˜λ‚˜μ˜ μ„œλ²„μ™€ 두 개 μ΄μƒμ˜ ν΄λΌμ΄μ–ΈνŠΈκ°€ μžˆλŠ” 경우, POST /movie/create λΌλŠ” ν•˜λ‚˜μ˜ api λ₯Ό ν˜ΈμΆœν•  λ•Œμ—λŠ” ν΄λΌμ΄μ–ΈνŠΈλ“€μ΄ Request body ν˜•μ‹μ„ λ§žμΆ°μ£Όμ–΄μ•Ό ν•˜λŠ”λ°, DTO κ°€ μžˆμ„ 경우 api μ„œλΉ„μŠ€ 둜직 호좜 전에 body λ₯Ό validate ν•΄μ€ŒμœΌλ‘œμ¨ ν΄λΌμ΄μ–ΈνŠΈλ“€μ΄ μ œλŒ€λ‘œ ν˜•μ‹μ„ λ§žμΆ”μ—ˆλŠ” 지 validate ν•œ ν›„ error λ₯Ό throw ν•΄μ€€λ‹€.

nestjs request DTO μž‘μ„±λ²• μ˜ˆμ‹œ

src/domains/movie/dto/create-movie.request.dto import { ApiProperty } from '@nestjs/swagger'; import { IsBoolean, IsEnum, IsString, Length, Validate } from 'class-validator'; import { MovieType } from '../enum/movie-type.enum'; export class CreateMovieRequestDto { @ApiProperty() @IsString() @Length(1, 30, { message: 'movie 제λͺ©μ€ 1자 이상 30자 이내여야 ν•©λ‹ˆλ‹€.' }) movieTitle: string; @ApiProperty() @IsString() thumbnailImage: string; @ApiProperty() @IsEnum(MovieType) movieType: MovieType; @ApiProperty() @IsString() @MaxLength(100) description: string; }
src/main.ts app.useGlobalPipes( new ValidationPipe({ // decorator κ°€ 달린 field 만 μœ νš¨μ„± 검사λ₯Ό ν•˜κ² λ‹€λŠ” μ˜΅μ…˜ whitelist: true, // string 으둜 λ“€μ–΄μ˜€λŠ” param 을 entity type 에 맞좰 λ³€ν˜•μ‹œμΌœμ£ΌλŠ” μ˜΅μ…˜. // 예) id: number κ°€ param 에 string 으둜 듀어와도 number 둜 λ³€ν™˜μ‹œμΌœ controller 에 λ„˜κ²¨μ€Œ transform: true, exceptionFactory(errors) { const errorProperties = errors.map((error) => error.property); return new BadRequestException( // ex) "message": "Validation failed: ~", `Validation failed: ${errorProperties.join(',')}`, ); }, }), );

exceptionFactory λ₯Ό μ‚¬μš©ν•˜λ©΄ validator λ₯Ό ν†΅κ³Όν•˜μ§€ λͺ»ν•΄ λ‚΄λ±‰λŠ” μ—λŸ¬ 메세지λ₯Ό custom ν•  수 μžˆλ‹€.

2. nestjs response DTO

Response DTO λŠ” μœ μš©ν•˜λ‹€. μ§€κΈˆ ν”„λ‘œμ νŠΈμ—μ„œ NestJS + TypeORM 을 μ“°κ³  μžˆλŠ”λ°,

MovieModule (MovieService, MovieRepository) μ—μ„œ movie κ΄€λ ¨ data λ₯Ό κ°€μ Έμ˜¬ λ•Œ this.MovieRepository.find 같은 ν•¨μˆ˜λ“€μ„ ν˜ΈμΆœν•œλ‹€. μ΄λ•Œ κ°€μ Έμ™€μ§€λŠ” data λŠ” movie.entity.ts 에 μ„ μ–Έν•œ 데이터듀이닀.

μ˜ν™” OTT μ„œλΉ„μŠ€λ₯Ό λ§Œλ“€ λ•Œ μ˜ν™” λͺ©λ‘ 만 κ°€μ Έμ˜€λŠ” κ²½μš°κ°€ 있고, μ˜ν™” 상세 정보λ₯Ό κ°€μ Έμ˜€λŠ” κ²½μš°κ°€ μžˆλ‹€.

GET /movie/list λ₯Ό λ§Œλ“€ λ•ŒλŠ” this.MovieRepository.findAll 을 ν•  것이고,

GET /movie/:movieId λ₯Ό λ§Œλ“€ λ•ŒλŠ” this.MovieRepostory.find λ₯Ό ν•  것이닀.

λ‘˜ λ‹€ movie entity μ—μ„œ κ°€μ Έμ˜€λŠ” 것이기 λ•Œλ¬Έμ— λ”°λ‘œ selet μ ˆμ„ 넣지 μ•ŠλŠ” 이상은 entity ν†΅μ±„λ‘œ κ°€μ Έμ˜¨λ‹€.

그런데 ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ μ˜ν™” λͺ©λ‘μ„ 보여쀄 λ•ŒλŠ” movieTitle, thumbnailImage 만 ν•„μš”ν•œλ° movieType, description κΉŒμ§€ κ°€μ Έμ˜¬ ν•„μš”κ°€ μ—†λ‹€. 이럴 λ•Œ response.dto κ°€ μœ μš©ν•˜λ‹€.

ν¬μΈνŠΈλŠ”, 같은 movie entity λ₯Ό μ‚¬μš©ν•˜μ§€λ§Œ μ˜ν™” λͺ©λ‘μ„ 보여쀄 λ•ŒλŠ” MovieListInfoResponseDto λ₯Ό λ§Œλ“€μ–΄ μ‚¬μš©ν•˜κ³ , μ˜ν™” detail 데이터가 ν•„μš”ν•  λ•ŒλŠ” MovieDetailResponseDto λ₯Ό ꡬ뢄지어 λ§Œλ“œλŠ” 것이닀.

src/domains/movie/dto/movie-list-info.response.dto import { IsString } from 'class-validator'; export class MovieListInfoResponseDto { @IsString() readonly movieTitle: string; @IsString() readonly thumbnailImage: string; @Exclude() @IsString() readonly description: string; @Exclude() @IsEnum() readonly movieType: MovieType }
src/domains/movie/dto/movie-detail.response.dto import { IsEnum, IsString, Exclude } from 'class-validator'; import { MovieType } from '../enum/movie-type.enum'; export class MovieDetailResponseDto { @IsString() readonly movieTitle: string; @IsString() readonly thumbnailImage: string; @IsString() readonly description: string; @Exclude() @IsEnum() readonly movieType: MovieType }

@Exclude() λ°μ½”λ ˆμ΄ν„°λ₯Ό λΉΌκ³  싢은 ν•„λ“œμ— λΆ™μ—¬μ£Όλ©΄ λœλ‹€.

μœ„μ²˜λŸΌ dto λ₯Ό λ§Œλ“€κ³ , μ„œλΉ„μŠ€ λ‘œμ§μ—μ„œ μ•„λž˜μ²˜λŸΌ ν•΄μ£Όλ©΄ λœλ‹€.

/src/domain/movie/service.ts import { plainToInstance } from 'class-transformer'; import { MovieListInfoResponseDto } from './dto/movie-list-info.response.dto async getMovieList = () => { ... const movies = await this.movieRepository.findAll({ where: { ... } }) return plainToInstance(MovieListInfoResponseDto, movies); }

κ²°κ³Ό

{ movieTitle: β€˜Avatar2’, thumbnailImage: β€˜~’}

πŸ’‘πŸ’‘πŸ’‘πŸ‘©πŸ»β€πŸ’»πŸ‘©πŸ»β€πŸ’»

nestjs response DTO, nestjs request DTO λ₯Ό μ•ˆμ“°κ³ λ„ this.movieRepositor.find() μ•ˆμ— selet λ₯Ό μ“Έ μˆ˜λŠ” μžˆμ§€λ§Œ, κ°œμΈμ μœΌλ‘œλŠ” DTO λ₯Ό μ‚¬μš©ν•΄ 결과값을 κ°€κ³΅ν•˜λŠ” 것이 μ’€ 더 μœ μ§€λ³΄μˆ˜μ— 쒋은 방법이라고 생각이 λ“€μ—ˆλ‹€. ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ λŒλ €μ£ΌλŠ” 결과값을 κ°€κ³΅ν•˜λŠ” 곳은 λ°μ΄ν„°λ² μ΄μŠ€ 쿼리가 μ•„λ‹ˆλΌ ν΄λΌμ΄μ–ΈνŠΈμ™€ μ„œλ²„κ°€ λ§Œλ‚˜λŠ” μ ‘μ μ—μ„œ μ΄λ£¨μ–΄μ§€λŠ” 것이 μ—­ν•  μ±…μž„ μ†Œμž¬λ₯Ό λͺ…ν™•νžˆ ν•  수 있기 λ•Œλ¬Έμ΄λ‹€.

Reference

https://docs.nestjs.com/techniques/validation

Recommend Post
nestjs ejsadapter μ‚¬μš©ν•΄μ„œ email 보내기
nestjs ejsadapter μ‚¬μš©ν•΄μ„œ email 보내기
nestjs env variable troubleshooting
nestjs env variable troubleshooting
TypeORM soft delete on cascade
TypeORM soft delete on cascade
tyepORM entity μ—μ„œ λΉ„λ°€λ²ˆν˜Έ hash ν•˜κΈ°
tyepORM entity μ—μ„œ λΉ„λ°€λ²ˆν˜Έ hash ν•˜κΈ°
Β© Copyright 2022, YesCoding