Nest JS #4 Rest API(3)
유효성 검사
DTO
DTO : 데이터 전송 객체(Data Transfer Object)
-> 코드를 간결하게 하기 위해 사용
-> NestJS 가 들어오는 query에 대해 유효성 검사
- movies에 dto 폴더 생성
1.create-movie.dto.ts 파일 생성
-
create-movie.dto.ts
export class CreateMovieDto{ readonly title: string; readonly year: number; readonly genres: string[]; }
-
movies.controller
@Post() create(@Body() movieData: CreateMovieDto){ //type은 dto return this.moviesService.create(movieData); }
-
movies.service.ts
create(movieData: CreateMovieDto){ this.movies.push({ id: this.movies.length +1, ...movieData }) }
-
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()) //ValidationPipe가 유효성 검사 await app.listen(3000); } bootstrap();
-
class-validator 와 class-transformer설치
-> npm i class-validator class-transformer
-
create-movie.dto.ts
import { IsNumber, IsString } from "class-validator"; export class CreateMovieDto{ @IsString() //IsString 데코레이터 readonly title: string; @IsNumber() readonly year: number; @IsString({each: true}) readonly genres: string[]; }
-
ValidationPipe
-
옵션
-
whitelist
-> 잘못된 정보는 Validator에 도달하지 X
-
forbidNonWhitelisted
-> 리퀘스트 자체를 막기
-
transform
-> 입력데이터를 원하는 타입으로 바꿈
=> controller에서 수동으로 string을 number로 바꾸지 않아도 됨
-
-
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})) //ValidationPipe가 유효성 검사 await app.listen(3000); } bootstrap();
-
movies.controller.ts
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, 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){}//읽기 전용 movieService 클래스를 가짐 @Get() getAll(): Movie[]{ //Movie array 리턴 return this.moviesService.getAll(); //consturctor 덕분에 쓸 수 있음 } @Get("/:id") getOne(@Param("id") movieId: number): Movie{ //Movie 리턴 console.log(typeof movieId); return this.moviesService.getOne(movieId); } @Post() create(@Body() movieData: CreateMovieDto){ //type은 dto 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 { 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); //+id는 string을 number로 변환 가능 if(!movie){ throw new NotFoundException(`Movie with ID ${id} not found.`) // 없는 경우 에러 메세지 } return movie; } deleteOne(id:number){ this.getOne(id); //id가 있는 경우 remove this.movies = this.movies.filter(movie => movie.id !== id); } create(movieData: CreateMovieDto){ this.movies.push({ id: this.movies.length +1, ...movieData }) } update(id:number, updateData){ const movie = this.getOne(id); this.deleteOne(id); //원래 id 지우기 this.movies.push({...movie, ...updateData }); } }
-
2. update-movie.dto.ts 파일 생성
-
update-movie.dto.ts
import { IsNumber, IsString } from "class-validator"; export class UpdateMovieDto{ @IsString() //IsString 데코레이터 readonly title?: string; //읽기 전용 필수가 아니도록 @IsNumber() readonly year?: number; @IsString({each: true}) readonly genres?: string[]; }
또는
-
npm i @nestjs/mapped-types -> 타입을 변환시키고 사용할 수 있게 하는 패키지
-
update-movie.dto.ts
import { PartialType } from "@nestjs/mapped-types"; import { IsNumber, IsString } from "class-validator"; import { CreateMovieDto } from "./create-movie.dto"; export class UpdateMovieDto extends PartialType(CreateMovieDto){} //CreateMovieDto와 같지만 전부 필수사항은 아니게
-
-
movies.controller.ts
@Patch('/:id') patch(@Param('id') movieId: number, @Body() updateData: UpdateMovieDto){ return this.moviesService.update(movieId, updateData); }
-
movies.service.ts
update(id:number, updateData: UpdateMovieDto){ const movie = this.getOne(id); this.deleteOne(id); //원래 id 지우기 this.movies.push({...movie, ...updateData }); }
Modules and Dependency Injection
-
app.module 은 AppController와 AppProvider만 있어야 함
=> MoviesService와 MoviesController를 movies.module로 옮기기(모듈 분리)
-
모듈 생성
- nest g m
- 이름: movies
=> movies.module 생성
-
app.module에서 MovieService랑 Moviescontroller삭제
import { Module } from '@nestjs/common'; import { MoviesModule } from './movies/movies.module'; @Module({ imports: [MoviesModule], controllers: [], providers: [], }) export class AppModule {}
-
movies.modules.ts에 추가
import { Module } from '@nestjs/common'; import { MoviesController } from './movies.controller'; import { MoviesService } from './movies.service'; @Module({ controllers:[MoviesController], providers:[MoviesService] }) export class MoviesModule {}
-
controller 생성
-> cli nest g co
->이름: app
(controller는 src로 옮기기 )
-
app.controllers.ts
import { Controller, Get } from '@nestjs/common'; @Controller('') export class AppController { @Get() home(){ return 'Welcome to my Movie API' } }//홈페이지 가져오기
-