IT/DEV Study

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

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

2022.07.25 - [IT] - [Nomad Corders] NestJS로 API 만들기 #0, #1 정리

 

[Nomad Corders] NestJS로 API 만들기 #0, #1 정리

https://nomadcoders.co/nestjs-fundamentals NestJS로 API 만들기 – 노마드 코더 Nomad Coders Enterprise Server Side Applications nomadcoders.co #0 INTRODUCTION #0.1 Welcome NestJS는 Node.js framework...

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 REST API

영화 API를 만들어보자.

 

#2.0 Movies Controller

지난 강의에서도 봤지만, nest 입력하면 사용할 있는 커맨드를 보여줌.

Generate cli으로 NestJS 거의 모든 생성할 있음.

[>nest g co] = [>nest generate controller]

위에서 movies 컨트롤러를 생성해주면, 자동으로 movies 컨트롤러가 생성되고,

app.module.ts 에서도 자동으로 Controller가 등록됨.

 

그럼 이제 movies.controller.ts 파일을 수정해보자.

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

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

export class MoviesController {

    @Get()

    getAll() {

        return `This will return all movies`;

    }

    @Get("/:id")

    getOne(@Param('id') movieID: string) {

        return `This will return one movie with the id: ${movieID}`;

    }

    @Post()

    create() {

        return 'This will create a movie';

    }

    @Delete("/:id")

    remove(@Param('id') movieID: string) {

        return `This will delete a movie with the id: ${movieID}`;

    }

    @Patch("/:id")

    patch(@Param('id') movieID: string) {

        return `This will patch a movie with the id: ${movieID}`;

    }

}

 

@Controller('movies') : 라우터. 요부분이 url Entry Point 컨트롤함.

 

@Get("/:id") : /movies/id로 사용. /movies/다음에 오는 값이 id가 됨.

 

@Param('id') : 이걸로 id 받아와서,  id movieID에 할당해서 사용함.

 

@Put vs. @Patch

Put : 모든 resource update

Patch : 일부 Resource update

 

insomnia에서는 위와 같은 방식으로 Get, Post, Put 등을 테스트해볼 수 있음.

728x90

#2.1 More Routes

@Post 에서 Body 부분(JSON)을 넘겨받고 싶을 어떻게 하나?

    @Post()

    create(@Body() movieData) {

        return movieData;

    }

여기서 @Body 이용해서 받아옴.

@Patch에서도 동일하게 받아올꺼임.

해당 id movie update 하기 위해서

    @Patch(":id")

    patch(@Param('id') movieID: string, @Body() updateData) {

        return {

            updatedMovie: movieID,

            ...updateData,

        };

    }

 

여기서 search 파트를 추가해보자.

    @Get("search") //슬래쉬는 써도 되고 안 써도 되고

    search() {

        return `We are searching for a movie with a title`;

    }

 

http://localhost:3000/movies/search?year=2000

이렇게 해서 검색하고 싶음.

 

근데, 문제가 있음. 결과값을 보면 getOne 함수가 실행되는 것이 보임.

search 부분이 get보다 밑에 있으면 NestJS search id 판단함

그래서 @Get(":id") 위로 순서를 바꿔줌.

 

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

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

export class MoviesController {

    @Get()

    getAll() {

        return `This will return all movies`;

    }

    @Get("search") //슬래쉬는 써도 되고 안 써도 되고

    search() {

        return `We are searching for a movie with a title: `;

    }

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

    getOne(@Param('id') movieID: string) {

        return `This will return one movie with the id: ${movieID}`;

    }

    @Post()

    create(@Body() movieData) {

        return movieData;

    }

    @Delete(":id")

    remove(@Param('id') movieID: string) {

        return `This will delete a movie with the id: ${movieID}`;

    }

    @Patch(":id")

    patch(@Param('id') movieID: string, @Body() updateData) {

        return {

            updatedMovie: movieID,

            ...updateData,

        };

    }

}

그럼 결과값이 제대로 나옴.

 

    @Get("search") //슬래쉬는 써도 되고 안 써도 되고

    search(@Query("year") searchingYear: string) {

        return `We are searching for a movie made after: ${searchingYear}`;

    }

추가로, year 받아오고 싶으면 @Query 이용

반응형

#2.2 Movies Service part One

Single-responsibility principle : 하나의 module, class 혹은 function 하나의 기능은 책임져야 한다.

 

여태까지 controller 만들었다면, 이번에는 service 부분을 만들어 볼꺼임.

위에서 controller url 매핑하고, 리퀘스트를 받고, query 넘기거나 body 외의 것들을 넘기는 역할을 하고 있음.

service movies 로직을 관리하는 역할.

Controller 만들 때처럼 service도 terminal에서 생성해주자.

[>nest g s] = [>nest generate service]

같은 이름으로 movies 서비스 생성해줌.

 

일단, 먼저 데이터베이스를 만들어보자. (실제 DB는 아니고 같은 역할을 하는 것)

위 사진처럼 entities 폴더 생성하고, movie.entitiy.ts 파일을 만듦.

그리고 Movie 클래스를 만들어 줌.

그러고 나서 Controller, Service 파일을 변경해보자.

 

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

@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: string): Movie {

        return this.moviesService.getOne(movieID);

    }

    @Post()

    create(@Body() movieData) {

        return this.moviesService.create(movieData);

    }

    @Delete(":id")

    remove(@Param('id') movieID: string) {

        return this.moviesService.deleteOne(movieID);

    }

    @Patch(":id")

    patch(@Param('id') movieID: string, @Body() updateData) {

        return {

            updatedMovie: movieID,

            ...updateData,

        };

    }

}

 

movie.service.ts

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

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

@Injectable()

export class MoviesService {

    private movies: Movie[] = [];

    getAll() : Movie[] {

        return this.movies;

    }

    getOne(id: string): Movie {

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

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

    }

    deleteOne(id: string): boolean {

        this.movies.filter(movie => movie.id !== +id);

        return true;

    }

    create(movieData) {

        this.movies.push({

            id: this.movies.length + 1,

            ...movieData,

        })

    }

}

 

그럼 이제 테스트 해보자.

 


#2.3 Movies Service part Two

지금 Movies entitiy는서버가 종료 혹은 리셋되면 데이터가 날라감.

실제 데이터베이스가 아니니깐 당연하지?

그래서, 만약 데이터가 없으면? -> 예외처리 메시지를 내보내게 해주자.

    getOne(id: string): 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;

    }

 

delete 할 때도영화 정보가 없으면 에러메세지가 나도록.

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

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

@Injectable()

export class MoviesService {

    private movies: Movie[] = [];

    getAll() : Movie[] {

        return this.movies;

    }

    getOne(id: string): 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: string) {

        this.getOne(id);

        this.movies.filter(movie => movie.id !== +id);

    }

    create(movieData) {

        this.movies.push({

            id: this.movies.length + 1,

            ...movieData,

        })

    }

}

 

이번엔 update 하는 부분을 수정해보자.

movie.service.ts

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

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

@Injectable()

export class MoviesService {

    private movies: Movie [] = [];

    getAll() : Movie [] {

        return this.movies;

    }

    getOne(id: string): 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: string) {

        this.getOne(id);

        this.movies = this.movies.filter(movie => movie.id!== +id);

    }

    create(movieData) {

        this.movies.push({

            id: this.movies.length + 1,

            ... movieData,

        })

    }

    update(id: string, updateData) {

        const movie = this.getOne(id);

        this.deleteOne(id);

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

    }

}

 

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

@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: string): Movie {

        return this.moviesService.getOne(movieID);

    }

    @Post()

    create(@Body() movieData) {

        return this.moviesService.create(movieData);

    }

    @Delete(":id")

    remove(@Param('id') movieID: string) {

        return this.moviesService.deleteOne(movieID);

    }

    @Patch(":id")

    patch(@Param('id') movieID: string, @Body() updateData) {

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

    }

}

 

하지만 여기엔 문제가 있음.

문제가 뭐냐면 우리가 updateData 유효성을 검사하지 않고 있음.

Movie 타입과 다른 보낼 있기 때문에, Body 유효성을 검사해야할 필요가 있음.

이건 다음 강의에서!


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

 

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

#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

 

 

728x90
반응형