728x90
반응형

 

전 글에서 movieData DTO를 만들어봤다.

이번 글에선 updateData DTO를 만들어보겠다.

 

먼저 update-movie.dto.ts 파일을 만든다.

 

update-movie.dto.ts

import {IsString, IsNumber} from 'class-validator'

export class CreateMovieDto {
    @IsString()
    readonly title?: string;

    @IsNumber()
    readonly year?: number;
    
    @IsString({each:true})
    readonly genres?: string[];
}

 

movieData의 DTO와 다른 점은 물음표가 있다는 점인데, 이는 필수사항은 아니게 한다는 뜻이다.

 

그다음 Controller와 Service에 추가하는 방법까지는 똑같다.

여기서 우리는 부분 객체라는 것을 써볼 것이다. 먼저 아래와 같이 설치해준다.

$npm i @nextjs/mapped-types

 

 

그다음 PartialType을 사용하기 위해 코드를 바꿔준다.

import {IsString, IsNumber} from 'class-validator'
import { PartialType } from '@nestjs/mapped-types';
import { CreateMovieDto } from './create-movie.dto';

export class UpdateMovieDto extends PartialType(CreateMovieDto) {}

 

 

이런식으로 CreateMovieDto를 상속할 수 있게 만들 수 있다. 또한 각각의 요소가 필수사항이 아니게끔 만들어준다.

 

이렇게 하고 POST 메소드로 Create하고 PATCH로 업데이트를 실행해보면 성공!

728x90
반응형
728x90
반응형

 

updateData랑 movieData한테 타입을 부여하기 위해서 우리는 DTO(Data Transfer Object, 데이터 전송 객체)를 만들어야한다. 먼저 create-movie.dto.ts 파일을 만들자.

 

 

create-movie.dto.ts

export class CreateMovieDto {
    readonly title: string;
    readonly year: number;
    readonly genres: string[];
}

 

 

그 다음 Controller 부분과 Service 부분에 타입을 추가해주자. (movieData)

 

Controller

    @Post()
    create(@Body() movieData : CreateMovieDto) {
        return this.moviesService.create(movieData);
    }

 

Service

    create(movieData:CreateMovieDto) {
        this.movies.push({
            id: this.movies.length + 1,
            ...movieData
        })
        return true;
    }

 

이렇게 해두면 아래와 같이 movieData. 만 입력해도 클래스가 가진 속성들이 자동으로 나와서 코딩하기에도 편리하다.

또한 타입에 대한 유효성을 검사할 수도 있다.

 

클래스의 유효성 검사를 위해 main.ts에 다음과같이 pipe를 만들어주겠다.

 

main.ts

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()
  )
  await app.listen(3000);
}
bootstrap();

 

 

또한 아래 명령어를 입력해서  npm 모듈을 설치하자.

$npm i class-validator class-transformer

 

그다음 아까 만들어두었던 dto파일에 다음과같이 데코레이터를 추가하자.

 

create-movie.dto.ts

import {IsString, IsNumber} from 'class-validator'

export class CreateMovieDto {
    @IsString()
    readonly title: string;

    @IsNumber()
    readonly year: number;
    
    @IsString({each:true})
    readonly genres: string[];
}

 

 

 

그 다음 postman에서 다음과 같이 POST 메소드로 {hacked: "by me"} 라는  body를 요청해보면 이런 결과창이 뜬다.

 

ValidationPipe 와 CreateMovieDto를 사용하고 있기 때문에 DTO의 타입을 실시간으로 확인할 수 있는 것이다.

 

ValidationPipe의 옵션 중 유용한 옵션인 whitelist라는 것이 있는데,

true로 설정하면 아무 Decoreator도 없는 어떠한 property의 object를 거룰 수 있다.

 

또한 보안을 위해 forbidNonWhitelisted 라는 옵션도 있다. 

이는 정의를 하지 않은 데이터가 error메세지에 뜨게끔 도와준다

 

transform라는 옵션은

보통 데이터를 body로 받을 때 string으로 받아서 정수형은 parseInt를 써야 했지만,

그럴 필요 없이 바로 원하는 자료형으로 바꿔주는 기능을 한다.

 

 

여기까지 추가를 해보면

 

 

main.ts

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();

 

 

이렇게 되고, 추가했었던 parseInt를 바꿔주면 된다.

 

movies.controller.ts

import { Controller, Get, Param, Post, Delete, Patch, Body, Query } from '@nestjs/common';
import { CreateMovieDto } from './dto/create-movie.dto';
import { Movie } from './entities/movie.entity';
import { MoviesService } from './movies.service';

@Controller('movies')
export class MoviesController {
    constructor(private readonly moviesService: MoviesService) {}

    @Get()
    getAll(): Movie[] {
        return this.moviesService.getAll();
    }

    @Get('/:id')
    getOne(@Param('id') movieId: number){
        return this.moviesService.getOne(movieId)
    }

    @Post()
    create(@Body() movieData : CreateMovieDto) {
        return this.moviesService.create(movieData);
    }

    @Delete('/:id')
    remove(@Param('id') movieId:number) {
        return this.moviesService.deleteOne(movieId);
    }

    @Patch('/:id')
    patch(@Param('id') movieId:number, @Body() updateData) {
        return this.moviesService.update(movieId,updateData)
    }
}

 

 

movies.service.ts

import { Injectable, NotFoundException } from '@nestjs/common';
import { NotFoundError } from 'rxjs';
import { CreateMovieDto } from './dto/create-movie.dto';
import { Movie } from './entities/movie.entity';

@Injectable()
export class MoviesService {
    private movies: Movie[] = [];

    getAll(): Movie[] {
        return this.movies;
    }

    getOne(id:number): Movie {
        
        const movie = this.movies.find(movie => movie.id === id);
        if(!movie) {
            throw new NotFoundException(" ID가 존재하지 않습니다. ")
        }

        return movie;

    } 

    deleteOne(id:number) {
        this.getOne(id)
        this.movies = this.movies.filter(movie => movie.id !== id)
    }

    create(movieData:CreateMovieDto) {
        this.movies.push({
            id: this.movies.length + 1,
            ...movieData
        })
        return true;
    }

    update(id: number, updateData) {
        const movie = this.getOne(id);
        this.deleteOne(id)
        this.movies.push({ ...movie, ...updateData})
    }

}

 

728x90
반응형

+ Recent posts