포시코딩

[Nest.js] 3. 게시판 만들기 - Controller 본문

Node.js

[Nest.js] 3. 게시판 만들기 - Controller

포시 2023. 2. 9. 19:14
728x90

시작하기 전에

JavaScript로 코딩할 때 유용하게 사용할 수 있는 유틸성 패키지인 lodash를 설치하자

npm i lodash

 

설치 후

tsconfig.json 파일을 열어 아래 속성 추가

"esModuleInterop": true

해당 속성을 추가함으로써 ES6 모듈 사양을 준수하여 CommonJS 모듈을 가져올 수 있게 한다.

 

서비스 주입

board.controller.ts

import { Controller } from '@nestjs/common';
import { BoardService } from './board.service';

@Controller('board')
export class BoardController {
  // 서비스 주입
  constructor(private readonly boardService: BoardService) {}
}

 

컨트롤러 메서드 세팅

서비스를 주입했으니 controller 메서드들을 만들어보자

import { Controller, Delete, Get, Post, Put } from '@nestjs/common';
import { BoardService } from './board.service';

@Controller('board')
export class BoardController {
  // 서비스 주입
  constructor(private readonly boardService: BoardService) {}

  // 게시물 목록을 가져오는 API
  @Get('/articles')
  getArticles() {
    return this.boardService.getArticles();
  }

  // 게시물 상세보기 -> 게시물 ID로 확인
  @Get('/articles/:id')
  getArticleById() {
    return this.boardService.getArticleById(id);
  }

  // 게시물 작성
  @Post('/articles')
  createArticle() {
    return this.boardService.createArticle();
  }

  // 게시물 수정
  @Put('/articles/:id')
  updateArticle() {
    return this.boardService.updateArticle(id);
  }

  // 게시물 삭제
  @Delete('/articles/:id')
  deleteArticle() {
    return this.boardService.deleteArticle(id);
  }
}

아직 service에 해당 메서드들을 안만들어서 빨간 밑줄이 뜨는건 당연하다.

 

근데 위 코드를 보면 POST, PUT의 경우 클라이언트로부터 데이터를 받아와야 생성이든 수정이든 할텐데

그런 코드가 보이지 않는다.

 

Nest.js에서는 클라이언트로부터 데이터를 받거나 데이터를 줘야 할 때 DTO를 사용해야 한다.
DTO란? 
(Data Transfer Object)의 약자로 데이터를 전송하기 위해 작성된 객체이다.
Nest.js에서는 모든 데이터에 대해 DTO를 통해 운반된다.

 

DTO 생성

DTO를 사용해보기 전에 DTO로 전달되는 입력값의 유효성 검사를 위해

  • class-validator
  • class-transformer

두 패키지를 설치해주자

npm i class-validator class-transformer

 

설치했으면 DTO 파일을 만들어보자

 

create-article.dto.ts

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

export class CreateArticleDto {
  @IsString()
  readonly title: string;

  @IsString()
  readonly content: string;

  @IsNumber()
  readonly password: number;
}

 

update-article.dto.ts

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

export class UpdateArticleDto {
  @IsString()
  readonly title: string;

  @IsString()
  readonly content: string;

  @IsNumber()
  readonly password: number;
}

 

delete-article.dto.ts

import { IsNumber } from 'class-validator';

export class DeleteArticleDto {
  @IsNumber()
  readonly password: number;
}

 

이렇게 DTO를 3개 작성한 이유는 각각의 요청마다 해당 요청으로부터 전달받는 데이터가 조금씩 다 달라서

통상적으로 요청 1 : 1 DTO 비율을 유지해야 되기 때문이다.

그런데 코드를 보면 내용물이 비슷하다. 심지어 create 와 update는 아예 똑같다.

만드는 과정도 복붙만 했음..

 

이럴 때 @nestjs/mapped-types의 PartialType을 상속받으면 깔끔하게 해결할 수 있다.

PartialType을 상속받으면 UpdateArticleDto는 CreateArticleDto 클래스의 부분 집합이 된다.

부분 집합은 해당 필드가 전부 포함되어도 성립되고 특정 필드들이 생략되어도 성립이 된다는 특징을 이용

 

DeleteArticleDto도 동일하지만 title, content에 대해 선택적으로 받게끔 할 필요가 없다.

여기서는 @nestjs/mapped-types의 PickType을 상속받아 특정 필드만 필요하게끔 선언할 수 있다.

 

자세한건 https://4sii.tistory.com/391 참고

 

 

@nestjs/mapped-types 설치

npm i @nestjs/mapped-types

* 만약 class-validator 의존성 이슈로 설치에 실패한다면 아래와 같이 진행

npm uninstall class-validator
npm i @nestjs/mapped-types
npm i class-validator

 

이제 UpdateArticleDto, DeleteArticleDto 클래스를 수정해보자

 

UpdateArticleDto.ts

import { PartialType } from '@nestjs/mapped-types';
import { CreateArticleDto } from './create-article.dto';

export class UpdateArticleDto extends PartialType(CreateArticleDto) {}

 

DeleteArticleDto.ts

import { PickType } from '@nestjs/mapped-types';
import { CreateArticleDto } from './create-article.dto';

export class DeleteArticleDto extends PickType(CreateArticleDto, [
  'password',
] as const) {}

 

이렇게 새로운 유형의 DTO를 작성하는 것이 아니라면 항상 

@nestjs/mapped-types를 활용해서 생산성을 높이도록 하자.

 

유효성 검사 세팅

위에서 DTO를 세팅했다면 

이제 DTO의 유효성 검사를 하기 위해 main.ts에 ValidationPipe를 주입해야 하는데

방법은 아래와 같다.

 

main.ts

app.useGlobalPipes(new ValidationPipe());

 

컨트롤러에서 DTO 사용

 

board.controller.ts

import { Body, Controller, Delete, Get, Post, Put } from '@nestjs/common';
import { BoardService } from './board.service';
import { CreateArticleDto } from './create-article.dto';

@Controller('board')
export class BoardController {
  // 서비스 주입
  constructor(private readonly boardService: BoardService) {}
// ...생략

  // 게시물 작성
  @Post('/articles')
  createArticle(@Body() data: CreateArticleDto) {
    return this.boardService.createArticle(
      data.title,
      data.content,
      data.password,
    );
  }

  // 게시물 수정
  @Put('/articles/:id')
  updateArticle(
    @Param('id') articleId: number,
    @Body() data: UpdateArticleDto,
  ) {
    return this.boardService.updateArticle(
      articleId,
      data.title,
      data.content,
      data.password,
    );
  }

  // 게시물 삭제
  @Delete('/articles/:id')
  deleteArticle(
    @Param('id') articleId: number,
    @Body() data: DeleteArticleDto,
  ) {
    return this.boardService.deleteArticle(articleId, data.password);
  }
}

이렇게 하면 데이터를 DTO로 받아오기 때문에 

잘못된 값이 있어도 400을 자동으로 리턴하게 된다.

728x90