2022.07.25 - [IT] - [Nomad Corders] NestJS로 API 만들기 #0, #1 정리
#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 등을 테스트해볼 수 있음.
#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 정리
'IT > DEV Study' 카테고리의 다른 글
[Nomad Corders] NestJS로 API 만들기 #3 정리 (358) | 2022.07.26 |
---|---|
[Nomad Corders] NestJS로 API 만들기 #2.4 to #2.7 정리 (413) | 2022.07.25 |
[Nomad Corders] NestJS로 API 만들기 #0, #1 정리 (2) | 2022.07.25 |
[TypeScript] 작은 따옴표(')와 백틱(`)을 구분 못한 바보의 한탄 TT (421) | 2022.07.11 |
[Nomad Coders] TypeScript로 블록체인 만들기 #5.5 to #5.8 정리 (완강) (0) | 2022.06.17 |