해당 포스팅의 목적은 NestJS를 공부하면서 구현한 API를 정리해두는 포스팅입니다.
이론 포스팅과 실습 포스팅을 분리하기 위해서 만들어진 포스팅이고,
해당 포스팅은 실습 포스팅입니다.
블로그를 참고하실 때 유의해주시길 바랍니다.
해당 포스팅은 NestJS를 공부하면서 꾸준히 추가될 예정이고,
추가될 때마다 포스팅 업데이트를 진행합니다.
Part. 1-1 | 모든 게시글 조회 API { Read API | Get }
해당 API는 데이터베이스와 연동하지 않고 Nest 로컬변수에 게시글 데이터를 저장해두고,
구현했음을 참고하길 바란다.
그리고, Module, Controller, Service 파일의 구성은 이전 포스팅에서 다뤘기에, 생략하도록 하겠다.
아래의 구현된 코드들은 각 파일의 클래스 내부에 작성했다. (Controller에서 Service 클래스의 Dependency Injection이 생략됨)
엔드포인트 = 'boards/'
Params
not exiest
Response =
Controller 구현
/*
게시글 조회 API Part. 1-1 (로컬 변수에 게시글 저장한 것)
*/
// 엔드포인트 : '/'
@Get()
getAllBoard(){
return this.boardsService.getAllBoards();
}
Service 구현
데이터베이스는 해당 API에서는 연동하지 않고,
로컬 변수 boards를 생성해 게시글을 저장했다.
/*
게시글 조회 API Part.1-1
*/
private boards = [];
getAllBoards(){
return this.boards;
}
# 게시글 조회 API 동작과정
Part. 1-2 | 게시글 생성 API { Create API | Post}
본 API는 Model -> Service -> Controller 순서대로 구현을 하였다.
엔드포인트 = '/'
Params =
Response =
Model 구현
/*
게시글 생성 API를 위한 게시글 모델구성
*/
export interface Board{
id: string;
title: string;
description: string;
status: BoardStatus;
}
// enumeration을 이용해 공개, 비공개 게시글 설정
export enum BoardStatus {
PUBLIC = 'PUBLIC',
PRIVATE = 'PRIVATE'
}
Service 구현
import { v1 as uuid } from 'uuid';
/*
게시글 생성 API Part. 1-2
*/
createBoard(title: string, description: string){
const board: Board = {
// uuid 모듈을 불러와 로컬에서 유니크한 id값으로 지정
id: uuid(),
title: title,
description: description,
status: BoardStatus.PUBLIC
}
// 생성된 게시글 정보를 return 한다.
this.boards.push(board);
return board;
}
Controller 구현
/*
게시글 생성 API Part. 1-2
*/
// 엔드포인트 : '/'
@Post()
createBoard(@Body('title') title: string,
@Body('decription') description: string,
): Board {
return this.boardsService.createBoard(title, description);
}
[ 확장 ] Part. 1-2 게시글 생성 API { DTO & Pipe 적용 }
엔드포인트 = '/'
Params =
Success Response =
Model 구현
/*
게시글 생성 API를 위한 게시글 모델구성
*/
export interface Board{
id: string;
title: string;
description: string;
status: BoardStatus;
}
// enumeration을 이용해 공개, 비공개 게시글 설정
export enum BoardStatus {
PUBLIC = 'PUBLIC',
PRIVATE = 'PRIVATE'
}
{DTO를 이용하는} Service 구현
import { v1 as uuid } from 'uuid';
import { CreateBoardDto } from './dto/create-board.dto';
/*
게시글 생성 API Part. 1-2
*/
createBoard(createBoaradDto: CreateBoardDto){
const {title, description} = createBoaradDto;
const board: Board = {
// uuid 모듈을 불러와 로컬에서 유니크한 id값으로 지정
id: uuid(),
title,
description,
status: BoardStatus.PUBLIC
}
// 생성된 게시글 정보를 return 한다.
this.boards.push(board);
return board;
}
DTO (Data Transfer Object) 구현 - Pipe (형식상 Validation) 적용
import { IsNotEmpty } from "class-validator";
// 클라이언트 request 객체
// Validation Pipe 사용
export class CreateBoardDto{
@IsNotEmpty()
title: string;
@IsNotEmpty()
description: string;
}
{DTO를 이용하는} Controller 구현 - Pipe (Data Validation) 적용
import { Post, UsePipes, ValidationPipe } from '@nestjs/common';
/*
게시글 생성 API Part. 1-2
*/
// 엔드포인트 : '/'
// dto 사용 & Pipe 사용해 Validation 처리
@Post()
// Handler Pipe 이용
@UsePipes(ValidationPipe)
createBoard(
@Body() createBoaradDto: CreateBoardDto
): Board {
return this.boardsService.createBoard(createBoaradDto);
}
# Request 데이터 DTO로 받기 (Request 객체로 구성해서 활용하기)
# Pipe 사용법 (Data Validation)
Part. 1-3 | 특정 ID 게시글 조회 API { Read API | Get }
본 API는 Service -> Controller 순서대로 구현을 하였다.
엔드포인트 = '/:id'
Params
{Path Variable} = id: string
Response =
Service 구현
/*
특정 ID 게시글 조회 API Part. 1-3
*/
getBoardById(id: string): Board{
// 만약 DB랑 연동이 된다면, DB에 대한 쿼리문을 만들고, id에 대한 board를 조회 후 return
return this.boards.find((board) => board.id == id);
}
Controller 구현
Params는 Path Variable로 Id를 받았다.
이때, Express의 Get 메소드의 @Param을 사용해 Path Variable를 받는다.
/*
특정 ID 게시글 조회 API Part. 1-3
*/
// 엔드포인트 : '/:id'
// Params : PathVariable {id}
@Get('/:id')
getBoardById(@Param('id') id: string): Board {
return this.boardsService.getBoardById(id);
}
[ 확장 ] Part. 1-3 | 특정 ID 게시글 조회 API { 의미상 Validation 적용 }
엔드포인트 = '/:id'
Params
{Path Variable} = id: string
Success Response =
Error Response =
Service 구현 - 의미상 Validation
Error 타입에는 throw 구문을 사용하는 것이 바람직하다.
그래서, throw 구문을 통해 Error 객체를 반환한다.
import { NotFoundException } from '@nestjs/common';
/*
특정 ID 게시글 조회 API Part. 1-3
*/
getBoardById(id: string): Board{
const found = this.boards.find((board) => board.id == id);
if (!found){
throw new NotFoundException();
// Error 객체 커스텀해서 출력가능
//throw new NotFoundException(`Can't find Board with id ${id}`);
}
return found;
}
Controller 구현
/*
특정 ID 게시글 조회 API Part. 1-3
*/
// 엔드포인트 : '/:id'
// Params : PathVariable {id}
@Get('/:id')
getBoardById(@Param('id') id: string): Board {
return this.boardsService.getBoardById(id);
}
Part. 1-4 | 특정 ID 게시글 삭제 API { Delete API | Delete}
본 API는 Service -> Controller 순서대로 구현을 하였다.
엔드포인트 = '/:id'
Params
{Path Variable} = id: string
Response =
{void}
Service 구현
filter() 메소드는 id 값이 일치하지 않은 것들은 그대로 두고, 일치한 것들을 삭제하는 메소드
/*
특정 ID 게시글 삭제 API Part. 1-4
*/
deleteBoard(id: string): void {
this.boards = this.boards.filter((board) => board.id !== id);
}
Controller 구현
게시글 삭제 API는 클라이언트에게 응답 값을 줄 필요가 없기에,
API 요청 시 Return 값을 void로 설정해둔다.
/*
특정 ID 게시글 삭제 API Part. 1-4
*/
// 엔드포인트 : '/:id'
// Params : Path Variable {id}
@Delete('/:id')
deleteBoard(@Param('id') id: string): void {
this.boardsService.deleteBoard(id);
}
[ 확장 ] Part. 1-4 | 특정 ID 게시글 삭제 API { 의미상 Validation 적용 }
엔드포인트 = '/:id'
Params
{Path Variable} = id: string
Success Response =
{void}
Error Response =
Service 구현 - 의미상 Validation
아래 코드는 단축 개발전략이 적용된다.
Service 클래스에 특정 ID 게시글 조회 API를 위해 구현된 getBoardById() 메소드를 사용해
의미상 Validation 검증을 진행했다. (getBoardById() 메소드 내에 ID Validation이 적용되었기 때문.)
이렇게, 코드를 재사용하여 개발시간을 감축시킬 수 있다.
그래서 이번 NestJS를 공부하면서 코드 재사용률을 늘리는 방법을 터득해보겠다!
/*
특정 ID 게시글 삭제 API Part. 1-4
*/
deleteBoard(id: string): void {
// 개발 단축전략 - 클래스내에 구현된 메소드들을 이해하여 개발영역을 감축함.
const found = this.getBoardById(id);
this.boards = this.boards.filter((found) => found.id !== id);
}
Controller 구현
/*
특정 ID 게시글 삭제 API Part. 1-4
*/
// 엔드포인트 : '/:id'
// Params : Path Variable {id}
@Delete('/:id')
deleteBoard(@Param('id') id: string): void {
this.boardsService.deleteBoard(id);
}
Part. 1-5 | 특정 ID 게시물 상태 업데이트 API { Update API | Patch }
본 API는 Service -> Controller 순서대로 구현을 하였다.
엔드포인트 = '/:id/status'
Params
{Path Variable} = id: string
{Body} =
status: BoardStatus (모델 클래스의 객체)
Response =
Service 구현
getBoardById() 메소드를 재활용해서 특정 ID 게시글을 가져왔다.
이처럼, Service내에서 구현된 메소드들을 재활용하면, API 구현시간을 절약할 수 있다. (이런 능력은 많은 경험에서 비롯된다.)
Controller 구현
@Body의 타입은 모델 클래스의 객체이다.
그래서, Body에 값을 넣어줄 때는 모델 클래스 객체에 맞추어 값을 넣어주어야 한다. (객체지향 프로그래밍의 묘미 | 체계적인 동작)
/*
특정 ID 게시글 상태 업데이트 API Part. 1-5
*/
@Patch('/:id/status')
updateBoardStatus(
@Param('id') id: string,
@Body('status') status: BoardStatus
): Board {
return this.boardsService.updateBoardStatus(id, status);
}
[ 확장 ] Part. 1-5 | 특정 ID 게시물 상태 업데이트 API
{커스텀 Pipe 적용 | Status Validation 검증}
본 API는 Service -> Controller 순서대로 구현을 하였다.
엔드포인트 = '/:id/status'
Params
{Path Variable} = id: string
{Body} =
or
Success Response =
Erorr Response =
커스텀 Pipe 구현 - Status Validation을 위한 Pipe 구성
// Pipes 폴더 내의 Pipes.ts 파일
import { ArgumentMetadata, BadRequestException, PipeTransform } from "@nestjs/common";
import { BoardStatus } from "../board.model";
// PipeTransform 인터페이스를 토대로 커스텀 파이프 구성
export class BoardStatusValidationPipe implements PipeTransform {
// Status 값에 대해 규정함. - 모델 객체에 정의한 게시글 데이터 규칙을 토대로 pipe에서도 규정함.
// 개발에 대한 규칙을 구조화 - 체계적인 개발가능
// readonly 타입은 외부에서 읽기로만 가능한 타입이다.
readonly StatusOption = [
BoardStatus.PRIVATE,
BoardStatus.PUBLIC
]
// transform() 메소드를 이용해 커스텀 파이프를 구현한다.
transform(value: any, metadata: ArgumentMetadata){
value = value.toUpperCase();
if(!this.isStatusValid(value)){
throw new BadRequestException(`${value} isn't in the status options`);
}
return value;
}
// Params의 Status와 게시글 Status가 데이터형태가 일치한 지 검사
private isStatusValid(status: any){
const index = this.StatusOption.indexOf(status);
return index !== -1;
}
}
Controller 구현 - Params level 커스텀 Pipe 적용
import { BoardStatusValidationPipe } from './pipes/board-status-validation.pipe';
/*
특정 ID 게시글 상태 업데이트 API Part. 1-5
*/
@Patch('/:id/status')
updateBoardStatus(
@Param('id') id: string,
// Params-level Pipe을 이용해 커스텀 파이프 적용
@Body('status', BoardStatusValidationPipe) status: BoardStatus
): Board {
return this.boardsService.updateBoardStatus(id, status);
}
Service 구현
/*
특정 ID 게시글 상태 업데이트 API Part. 1-5
*/
updateBoardStatus(id: string, status: BoardStatus): Board{
// 코드의 재활용 : 구현된 getBoardById() 메소드 사용
const board = this.getBoardById(id);
board.status = status
return board;
}
'📚 스터디 > 백엔드' 카테고리의 다른 글
[개인 스터디] NestJS 정복하기 #04 - DTO | Data Transfer Object (0) | 2022.10.29 |
---|---|
[개인 스터디] NestJS 정복하기 #03 - 게시글 생성 API | Create API (0) | 2022.10.28 |
[개인 스터디] NestJS 정복하기 #02 - NestJS | src 폴더 로직알아보기 (0) | 2022.10.27 |
[개인 스터디] NestJS 정복하기 #01 - NestJS 템플릿의 이해 (0) | 2022.10.27 |
[개인 스터디] NestJS을 시작하며 - NestJS 개발환경 셋팅 (2) | 2022.10.22 |