DTO (Data Transfer Object) 란?
DTO(Data Transfer Object) 는 계층 간 데이터 교환을 하기 위해 사용하는 객체로 , 상대방이 쓰레기 값이나 입력할수 없는 값을 넣을때 검증하는데 쓰일 수 있습니다.
dto 폴더를 새로 만들고 , 새 영화를 post방식으로 등록할때(create) body에 대한 입력값 검증을 위해 CreateMovieDto 객체를 만들었습니다.
기존 영화의 정보를 바꿀때도 입력 값의 유효성을 검증하기 위해 UpdateMovieDto를 만들었습니다.
validator , transformer 모듈 설치
class-validator는 유효값 검증해주는 모델이고, class-transformer는 입력값을 자동으로 형 변환 해주는 모듈입니다.
transformer가 없으면 입력값은 무조건 string이기 때문에 id 같은것을 number로 바꿔주려면 다른 절차가 필요합니다.
우선 터미널에 이렇게 입력해서 두 모듈을 설치해줍니다.
$ npm i class-validator class-transformer
CreateMovieDto
title , year , number를 가져야하고 다른 값/ 자료형을 입력 받으면 안됩니다.
import { IsNumber, isString, IsString } from "class-validator"
// data transform object
// 유효성 검증
export class CreateMovieDto {
@IsString()
readonly title : string
@IsNumber()
readonly year : number
@IsString({each : true})
readonly genres : string[]
}
UpdateMovieDto
기존 영화의 특정 정보를 새로 고침하는 업데이트는 create와 동일한 자료를 갖지만, 변경하려는 자료만 가지고 있으면 됩니다.
그러므로 입력값에서 각 필드를 가질수도 있고 안가질수도 있습니다.
//partialType 이용 안할시 코드
export class UpdateMovieDto {
//필수로 입력해야하는것이 아니기에 ?를 변수 옆에 붙인다.
@IsString()
readonly title? : string
@IsNumber()
readonly year? : number
@IsString({each : true})
readonly genres? : string[]
}
그러나 CreateMovieDto와 동일하고, 상속을 받으면 좋겠다는 생각이 듭니다.
그래서 mapped-types 모듈을 설치해보겠습니다
$ npm i @nestjs/mapped-types
UpdateMovieDto 수정
(CreateMovieDto 상속)
import { IsNumber, isString, IsString } from "class-validator"
import { PartialType } from "@nestjs/mapped-types"
import { CreateMovieDto } from "./create-movie.dto"
// partial type 이용
// 자동으로 기존 dto클래스에서 부분적으로 입력 받을수 있도록
export class UpdateMovieDto extends PartialType(CreateMovieDto) {}
main (useGlobalPipes)
메인 ts파일에 형식 안맞으면 거절하고 , 자동 자료형 변환하도록 json 형식으로 ValidationPipe 인자로 넣어줍니다.
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 유효성 검사 (미들웨어와 흡사)
app.useGlobalPipes(new ValidationPipe({
whitelist: true,
forbidNonWhitelisted : true,//형식 안맞으면 거절
transform : true //자동 자료형 변환
}))
await app.listen(3000);
}
bootstrap();
자동 형변환이 되므로 기존 파일들의 인자도 바꿔줍니다.
movies.controller
moviedata , updatedata를 dto 클래스로 설정해주고,
dto대로 id 등등을 string -> number로 바꿔줍니다.
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common';
import { rename } from 'fs';
import { CreateMovieDto } from './dto/create-movie.dto';
import { UpdateMovieDto } from './dto/update-movie.dto';
import { Movie } from './entities/movie.entities';
import { MoviesService } from './movies.service';
@Controller('movies')
export class MoviesController {
constructor(private readonly moviesService:MoviesService ){}
@Get()
getAll() : Movie[]{
return this.moviesService.getAll()
}
@Get('search')
search(@Query('year') searchingYear: number){
return `we are going to search for a movie after ${searchingYear}`
}
@Get('/:id')
getOne(@Param('id') movieId:number) : Movie{
return this.moviesService.getOne(movieId);
}
@Post()
create(@Body() movieData : CreateMovieDto){
return this.moviesService.create(movieData);
}
@Delete('/:id')
delete(@Param('id') movieId:number){
return this.moviesService.deleteOne(movieId);
}
@Patch('/:id')
patch(@Param('id') movieId : number, @Body() updateData:UpdateMovieDto){
return this.moviesService.update(movieId , updateData);
}
}
movies.service
import { Injectable, NotFoundException } from '@nestjs/common';
import { Movie } from './entities/movie.entities';
@Injectable()
export class MoviesService {
private movies : Movie[] = [];
getAll() : Movie[]{
return this.movies;
}
create(movieData){
this.movies.push({
id: this.movies.length + 1,
...movieData
})
}
getOne(id:number) :Movie{
const movie = this.movies.find(movie => movie.id === id);
if (!movie) {
throw new NotFoundException(`movie with id ${id} not found`);
}
return movie;
}
deleteOne(id:number){
this.movies = this.movies.filter(movie => movie.id !== id)
return;
}
update(id:number, updateData ){
const movie = this.getOne(id)
this.deleteOne(id)
this.movies.push({...movie , ...updateData})
}
}
추가적인 구조 개편
module은 controller와 provider(service)를 가지는데, app.module
은 movie에 대한건 분리시키고, app.controller와 app.service만 가지는게 좋습니다.
그래서 movie.module을 새로 만들어주고, app 모듈에는 app 관련 컨트롤러와 프로바이더(service)를 넣어주고 , movie 모듈을 import해줍시다.
app 모듈은 기본 페이지고, 한 앱에서 여러개의 모듈로 관리합니다.
(이번 예시는 무비 모듈 하나밖에 없지만)
movies 모듈 생성
$ nest g mo
$ movies
app controller 생성(프로바이더는 생성안할게요)
$ nest g co
$ app
app.module
import { Get, Module } from '@nestjs/common';
import { MoviesModule } from './movies/movies.module';
import { AppController } from './app.controller';
@Module({
// movies 모듈이 추가됨
imports: [MoviesModule],
controllers: [AppController],
providers: [],
})
export class AppModule {
}
movies.module
movies에 대한 컨트롤러와 프로바이더는 이제 무비 모듈에서만 설정해줍니다.
import { Module } from '@nestjs/common';
import { MoviesController } from './movies.controller';
import { MoviesService } from './movies.service';
@Module({
controllers : [MoviesController],
providers : [MoviesService]
})
export class MoviesModule {}
app.controller
import { Controller, Get } from '@nestjs/common';
@Controller()
export class AppController {
@Get()
home(){
return 'welcome to my app';
}
@Get('/name')
name(){
return 'creator is donghyun';
}
}
'Back-End > 🐱NestJS (TypeScript) log' 카테고리의 다른 글
[NestJS 일기] 영화 api 만들기(3) - 유닛 테스팅 (unit testing) - jest (0) | 2022.06.07 |
---|---|
[NestJS 일기] 영화 api 만들기(1) - 설치 , 컨트롤러 , 서비스 ts파일 만들기 (0) | 2022.06.03 |
댓글