IT/DEV Study

[Nomad Corders] NestJS로 API 만들기 #2.4 to #2.7 정리

Ella.J 2022. 7. 25. 16:53
728x90
반응형

 

2022.07.25 - [IT] - [Nomad Corders] NestJS로 API 만들기 #2.0 to #2.3 정리

 

[Nomad Corders] NestJS로 API 만들기 #2.0 to #2.3 정리

#2 REST API #2.0 Movies Controller #2.1 More Routes #2.2 Movies Service part One #2.3 Movies Service part Two #2.4 DTOs and Validation part One #2.5 DTOs and Validation part Two #2.6 Modules and Dep..

ella-devblog.tistory.com


#2 REST API

#2.0 Movies Controller

#2.1 More Routes

#2.2 Movies Service part One

#2.3 Movies Service part Two

#2.4 DTOs and Validation part One

#2.5 DTOs and Validation part Two

#2.6 Modules and Dependency Injection

#2.7 Express on NestJS


#2.4 DTOs and Validation part One

DTO : Data Transfer Object 데이터 전송 객체

dto 폴더 생성 create.movie.dto.ts 파일 생성

export class CreateMovieDto {

    readonly title: string;

    readonly year: number;

    readonly genres: string[];

}

 

사람들이 보냈으면 하는 것의 타입을 정함.

그래서 create 생성할 @Body 받아오는 데이터의 타입을 CreateMovieDto 정해줌

 

    @Post()

    create(@Body() movieData: CreateMovieDto) {

        return this.moviesService.create(movieData);

    }

 

그리고 main.ts 에서 유효성 검사용 pipe 생성해준다.

import { ValidationPipe } from '@nestjs/common';

import { NestFactory } from '@nestjs/core';

import { AppModule } from './app.module';

async function bootstrap() {

  const app = await NestFactory.create(AppModule);

  //pipe 생성

  app.useGlobalPipes(new ValidationPipe);

  await app.listen(3000); //3000번 포트 리스닝

}

bootstrap();

 

이를 실행하기 위해선 아래에서 가지 설치가 필요함.

[>npm I class-validator class-transformer]

 

그런 다음 dto 파일을 다음과 같이 업데이트 하고,

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

export class CreateMovieDto {

    @IsString()

    readonly title: string;

    @IsNumber()

    readonly year: number;

    @IsString({each: true})

    readonly genres: string[];

}

POST 해주면, 타입이 안맞다고 에러메세지를 띄워줌.

 

  //pipe 생성

  app.useGlobalPipes(new ValidationPipe({

    //options

    whitelist: true,

    forbidNonWhitelisted: true,

  }));

파이프 옵션에 위와같이 추가해주면

property 존재하는지 안하는지 여부도 알려줌.

 

  //pipe 생성

  app.useGlobalPipes(new ValidationPipe({

    //options

    whitelist: true,

    forbidNonWhitelisted: true,

    //transform : 원래 param or body에서 받는 거는 string인데,

    //true로 하면 실제 원하는 값으로 변경해줌.

    transform: true,

  }));

 

transform 옵션을 사용하면 기존에 @Param에서 string으로 받아왔던 id

자동으로 number 받아오기 때문에 변환이 필요 없음.

728x90

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

  //pipe 생성

  app.useGlobalPipes(new ValidationPipe({

    //options

    whitelist: true,

    forbidNonWhitelisted: true,

    //transform : 원래 param or body에서 받는 거는 string인데,

    //true로 하면 실제 원하는 값으로 변경해줌.

    transform: true,

  }));

  await app.listen(3000); //3000번 포트 리스닝

}

bootstrap();

 

movie.service.ts

import { Injectable, NotFoundException } from '@nestjs/common';

import { Movie } from './entities/movie.entity';

import { CreateMovieDto } from './dto/create.movie.dto';

@Injectable()

export class MoviesService {

    private movies: Movie[] = [];

    getAll() : Movie[] {

        return this.movies;

    }

    getOne(id: number): Movie {

        //return this.movies.find(movie => movie.id === parseInt(id));

        const movie = this.movies.find(movie => movie.id === id); //이렇게 해도 int로 변경 가능

        if (!movie) {

            throw new NotFoundException(`Movie with ID ${id} not found.`);

        }

        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,

        })

    }

    update(id: number, updateData) {

        const movie = this.getOne(id);

        this.deleteOne(id);

        this.movies.push({...movie, ...updateData});

    }

}

 

movies.controller.ts

import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common';

import { Movie } from './entities/movie.entity';

import { MoviesService } from './movies.service';

import { CreateMovieDto } from './dto/create.movie.dto';

@Controller('movies') //localhost:3000/movies

export class MoviesController {

    //Service에 접근하는 법

    constructor(private readonly moviesService: MoviesService) {}

    @Get()

    getAll(): Movie[] {

        return this.moviesService.getAll();

    }

    @Get(":id") //localhost:3000/movies/id

    getOne(@Param('id') movieID: number): Movie {

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

    }

}

반응형

#2.5 DTOs and Validation part Two

저번 영상에선 create dto 만들었다면, 이번엔 update dto 만들어 보자.

  • mapped-types : 타입을 변환시키고 사용할 있게 하는 패키지

일단 PartialType 쓰기위해 @nestjs/mapped-types 를 설치해준다.

=> 나는 이걸로 설치 안돼서 @nestjs/swagger 로 설치함.

@nestjs/graphql 로 사용 가능.

 

그리고 [>npm I @nestjs/swagger] 설치했더니 버전 충돌 에러나서

[>npm I --force @nestjs/swagger]로 강제설치 해줬다.

 

update-movie.dto.ts

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

import {PartialType} from '@nestjs/swagger';

import {CreateMovieDto} from './create-movie.dto';

export class UpdateMovieDto extends PartialType(CreateMovieDto) { }

 

요렇게 dto 생성해줌.

  • class-validator : 거의 모든 것의 유효성을 검사할 있음.

 

create-movie.dto.ts

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

export class CreateMovieDto {

    @IsString()

    readonly title: string;

   

    @IsNumber()

    readonly year: number;

    @IsOptional()

    @IsString({each: true})

    readonly genres: string[];

}

 

movie.controller.ts

import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common';

import { Movie } from './entities/movie.entity';

import { MoviesService } from './movies.service';

import { CreateMovieDto } from './dto/create-movie.dto';

import { UpdateMovieDto } from './dto/update-movie.dto';

@Controller('movies') //localhost:3000/movies

export class MoviesController {

    //Service에 접근하는 법

    constructor(private readonly moviesService: MoviesService) {}

    @Get()

    getAll(): Movie[] {

        return this.moviesService.getAll();

    }

    @Get(":id") //localhost:3000/movies/id

    getOne(@Param('id') movieID: number): Movie {

        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: UpdateMovieDto) {

        return this.moviesService.update(movieID, updateData);

    }

}

 

movie.service.ts

import { Injectable, NotFoundException } from '@nestjs/common';

import { Movie } from './entities/movie.entity';

import { CreateMovieDto } from './dto/create-movie.dto';

import { UpdateMovieDto } from './dto/update-movie.dto';

@Injectable()

export class MoviesService {

    private movies: Movie[] = [];

    getAll() : Movie[] {

        return this.movies;

    }

    getOne(id: number): Movie {

        //return this.movies.find(movie => movie.id === parseInt(id));

        const movie = this.movies.find(movie => movie.id === id); //이렇게 해도 int로 변경 가능

        if (!movie) {

            throw new NotFoundException(`Movie with ID ${id} not found.`);

        }

        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,

        })

    }

    update(id: number, updateData: UpdateMovieDto) {

        const movie = this.getOne(id);

        this.deleteOne(id);

        this.movies.push({...movie, ...updateData});

    }

}


#2.6 Modules and Dependency Injection

우리가 만든 api 좋은 구조로 만들어 보자.

app.module AppService AppController 가지고있어야 함. (아래에서 설명)

[>nest g mo] = [>nest generate module]

모듈을 하나 만들어 주면, app.module.ts 자동으로 모듈이 생성, right?

app.module.ts 있는 MoviesController, MoviesService 삭제하고,

movies.module.ts controllers providers 등록해줌.

그리고,

[>nest g co] name? app

AppController 하나 새로 만들어 주고, 다음과 같이 작성.

 

app.controller.ts

import { Controller, Get } from '@nestjs/common';

@Controller('')

export class AppController {

    @Get()

    home() {

        return "Welcome to my Movie API";

    }

}

 

app.module.ts

import { Module } from '@nestjs/common';

import { MoviesModule } from './movies/movies.module';

import { AppController } from './movies/app.controller';

//@(Decorator) : 클래스에 함수 기능 추가

@Module({

  imports: [MoviesModule],

  controllers: [AppController],

  providers: [],

})

export class AppModule {}

 

movies.module.ts

import { Module } from '@nestjs/common';

import { MoviesController } from './movies.controller';

import { MoviesService } from './movies.service';

@Module({

    controllers: [MoviesController],

    providers: [MoviesService],

})

export class MoviesModule {}

 

=> 실행해보면 다음과 같음. 결국, AppController 홈페이지 메인 역할을 하는 .

 

 

  • Dependency Injection

#2.7 Express on NestJS

앞에서 언급했듯이 NestJS Express 위에서 돌아감.

그래서 Express 객체인 @Req(), @Res() 사용해서 접근할 있음. But not 추천.

Why?

nestjs express fastify 다 사용가능하고 서로 전환가능 함.

(fastify express보다 2 정도 빠름.)

개의 프레임워크에서 동시에 돌아가는 , nestjs 쓰면 전환은 알아서 해줌.

근데 Express 객체를 쓰면 느리고 효율이 떨어짐.

그래서 nestjs에서 req, res 객체를 많이 사용하는 것은 안좋음.

So, nestjs에서는 어떻게 사용되는지 이해해야 .


2022.07.26 - [IT] - [Nomad Corders] NestJS로 API 만들기 #3 정리

 

[Nomad Corders] NestJS로 API 만들기 #3 정리

2022.07.25 - [IT] - [Nomad Corders] NestJS로 API 만들기 #2.4 to #2.7 정리 [Nomad Corders] NestJS로 API 만들기 #2.4 to #2.7 정리 2022.07.25 - [IT] - [Nomad Corders] NestJS로 API 만들기 #2.0 to #2.3..

ella-devblog.tistory.com

 

728x90
반응형