Lsiron
Nest.js에서 Controller와 Service 다루기 본문
이제 기본적인 CRUD API를 만들면서 Nest.js의 핵심 개념인 Controller와 Service의 역할을 알아보자.
controller는 URL을 매핑하고, 리퀘스트를 받고 query를 넘기거나, body나 그 외의 것들을 넘기는 역할을 했었다.
허나, service는 로직을 관리하는 역할을 맡을 것 이다. 이렇게 한 개의 요소가 한 가지 기능은 꼭 책임져야 한다.
이것이 바로 single-respnsibility principle 이다.
( 하나의 module, class 혹은 function이 하나의 기능은 꼭 책임져야 한다는 것 )
이 점을 명심하면서, service를 만들어보겠다.
터미널에 아래와 같이 명령어를 입력하여 서비스를 만들어보자
$ nest g s movies
위와 같이 movies의 service가 만들어진 것을 확인 할 수 있다.
이제 app.module.ts를 보면 service 또한 넣어져있는 것을 확인 할 수 있다.
다음으로 할 것은 service에 데이터베이스를 만들 것 이다. movies.service.ts로 가보자.
service를 만들기 위해 간단하게 가짜 데이터베이스를 만들어 보겠다.
import { Injectable } from '@nestjs/common';
@Injectable()
export class MoviesService {
private movies = [];
}
빈 배열로 설정한 movies에 private을 지정하고, entities 폴더를 하나 만들어 준 뒤, movie.entity.ts 파일을 만들어주자.
( 보통은 entities에 실제로 데이터베이스의 모델을 만들어 주어야 한다. )
이 후, movie.entity.ts 파일에서는 서비스로 보내고 받을 클래스(인터페이스)를 export 해 줄 것이다.
//movie.entity.ts
export class Movie {
id: number;
title: string;
year: number;
genres: string[];
}
위 내용이 movie를 구성하는 내용이다. 다시 movies.service.ts로 가보자.
import { Injectable } from '@nestjs/common';
import { Movie } from './entities/movie.entity';
@Injectable()
export class MoviesService {
private movies: Movie[] = [];
}
위와 같이 movie.entity.ts 로 부터 타입을 가져와서 movies에 배열 타입으로 지정해 주자.
그리곤 이 전에 만들었던 movies.controller.ts 기반으로 서비스 로직을 만들어보자.
아래는 movies.controller.ts 이다. ( search endpoint를 이용한 API만 빼고 모두 그대로 가져왔다. )
import { Controller, Get, Post, Delete, Patch, Param, Body } from '@nestjs/common';
@Controller('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(@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,
};
}
}
아래는 movies.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));
}
create(movieData){
this.movies.push({
id: this.movies.length + 1,
...movieData
})
}
delete(id: string) {
this.getOne(id)
this.movie = this.movies.filter(movie => movie.id !== parseInt(id));
}
update(id: string, updateData) {
const movie = this.getOne(id);
this.delete(id);
this.movies.push({...movie, ...updateData })
}
}
update의 경우 삭제를 하고나서 다시 데이터를 집어넣는 로직으로 구현했는데 가짜 데이터베이스를 사용하기 때문에 어쩔 수 없다......
이제 service 로직을 구현했으니, movies.controller.ts 로 가서, service에 있는 로직을 가져와주자.
@Controller('movies')
export class MoviesController {
constructor(private readonly moviesService: MoviesService) {}
}
수동으로 import 하는 방법이 아닌, constructor로 MovieService를 초기화 시켜 줌으로써 MoviesController 에서 MovieService를 사용할 수 있다.
이제 service 로직과 연결시켜 보자.
import { Controller, Get, Post, Delete, Patch, Param, Body } from '@nestjs/common';
import { MoviesService } from './movies.service';
import { Movie } from './entities/movie.entity';
@Controller('movies')
export class MoviesController {
constructor(private readonly moviesService: MoviesService) {}
@Get()
getAll(): Movie[] {
return this.moviesService.getAll();
}
@Get('/: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.delete(movieId);
}
@Patch('/:id')
patch(@Param('id') movieId: string, @Body() updateData) {
return this.moviesService.update(movieId, updateData)
}
}
이제 Postman으로 가서 API를 테스트 해보자. 먼저 Post 요청을 통해 하나 생성 해 줄 것이다.
201 응답과 함께 요청이 성공적으로 받아들여진 것을 알 수 있다. 이제 Get 요청을 통해 조회를 해 보자.
성공적으로 조회가 되는 것을 확인 할 수 있다.
이제 리팩토링을 한번 해 보자. getOne 을 통해 진행을 해 보겠다.
만약 누군가가 아래와 같이 터무니 없는 URL을 입력한다면?
아직 아무 처리를 해주지 않았으니 자연스레 200 응답을 보내며 요청이 성공 할 것이다. 하지만 이래선 안된다.
movies.service.ts 로 가서 저장되지 않은 movie에 대해 예외처리를 해 주자.
getOne(id:string): Movie {
const movie = this.movies.find(movie => movie.id === parseInt(id));
if(!movie) {
throw new NotFoundException(`Movie with ID ${id} not found`)
}
return movie;
}
이제 Postman으로 가서 똑같이 터무니 없는 아이디로 요청을 보내보자.
신기한 점이 있지 않은가? 나는 404에러를 주지도 않았고 별도의 설정을 해 주지 않았으며 오로지 NotFoundException 에러를 던져 주기만 했는데 에러 처리가 된다.
이는 nest.js에서 기본적으로 제공하는 예외처리이다.
즉, HttpExceptin 에서 확장된 nest.js의 제공 기능인 것. 아주 편리하다.
이제 삭제기능이 잘 되는지 확인 해 보자.
200 응답을 주면서 삭제기능이 잘 작동하는 것을 볼 수 있다. 다시 한번 터무니없는 id값을 URL에 입력해보자.
해당 id의 데이터가 없기 때문에 역시나 404에러가 나온다.
마지막으로 업데이트 기능이 잘 작동하는지 확인 해 보자.
200 응답을 주며 잘 실행이 잘 되는것을 확인 할 수 있다. 이제 조회를 해 보자.
역시나 업데이트가 잘 반영된 것을 확인 할 수 있다.
허나 우리는 지금 유효성 검사를 하지 않고 있다.
다음은 DTO를 통해 유효성 검사를 처리하는 방법을 다뤄보자.
참조 : 노마드코더
'백엔드 > Nest.js' 카테고리의 다른 글
Nest.js에서 모듈과 의존성 주입 이해하기 (0) | 2024.08.25 |
---|---|
Nest.js에서 DTO를 통해 유효성 검사 처리하기 (0) | 2024.08.23 |
Nest.js 에서 API 만들기 (get, post, patch, delete) (0) | 2024.08.22 |
Nest.js 시작하기 (0) | 2024.07.21 |
Nest.js 설치하기 (0) | 2024.07.15 |