Skip to content

2. Boards module


1. BoardsModule 생성하기

BoardsModule을 생성하기 위해 다음 명령어를 입력한다.


nest g mo boards


NestJS CLI를 이용하여 BoardsModule을 생성하면 AppModule에 자동으로 등록된다.


src/app.module.ts
import { Module } from "@nestjs/common";
import { BoardsModule } from "./boards/boards.module";

@Module({
  imports: [BoardsModule],
})
export class AppModule {}


2. BoardsController 생성하기

BoardController를 생성하기 위해 다음 명령어를 입력한다. 만약 테스트용 파일을 생성하지 않을 경우 --no-spec 옵션을 추가하면 된다.


nest g co boards


마찬가지로 NestJS CLI를 이용하여 BoardController를 생성하면 BoardsModule에 자동으로 등록된다.


src/boards/boards.module.ts
import { Module } from "@nestjs/common";
import { BoardsController } from "./boards.controller";

@Module({
  controllers: [BoardsController],
})
export class BoardsModule {}


3. BoardsService 생성하기

BoardsService를 생성하기 위해 다음 명령어를 입력한다.


nest g s boards


BoardsService를 생성하면 BoardsModule에 자동으로 등록되며, 이러한 서비스, 리포지터리 등 프로바이더를 등록한 후 사용하려면 해당 모듈 파일의 providers 배열에 추가해야 한다.


src/boads/boards.module.ts
import { Module } from "@nestjs/common";
import { BoardsController } from "./boards.controller";
import { BoardsService } from "./boards.service";

@Module({
  controllers: [BoardsController],
  providers: [BoardsService],
})
export class BoardsModule {}


4. 모든 게시물을 가져오는 서비스/컨트롤러 만들기

NestJS 프로젝트의 비즈니스 로직은 컨트롤러가 아닌 서비스에서 구현한다. 지금은 로컬 메모리를 이용하도록 구현하고 나중에 데이터베이스를 연결할 수 있도록 구현할 것이다.


src/boards/boards.service.ts
import { Injectable } from "@nestjs/common";

@Injectable()
export class BoardsService {
  private boards = [];

  getAllBoards() {
    return this.boards;
  }
}


이 서비스를 BoardsController에 주입하기 위해서 다음과 같이 constructor에 선언하면 된다. 물론 BoardsModuleproviders 배열에 추가된 상태여야만 한다.


src/boards/boards.controller.ts
import { Controller } from "@nestjs/common";
import { BoardsService } from "./boards.service";

@Controller("boards")
export class BoardsController {
  constructor(private boardsService: BoardsService) {}
}


그리고 getAllBoards() 라우트 핸들러에 @Get 데코레이터를 달아준 후 BoardsServicegetAllBoards()를 호출한다. 컨트롤러는 특정 URL의 진입점을 담당하며, 라우트 핸들러는 엔드포인트를 담당하는 것이라고 보면 된다.


src/boards/boards.controller.ts
import { Controller, Get } from "@nestjs/common";
import { BoardsService } from "./boards.service";

@Controller("boards")
export class BoardsController {
  constructor(private boardsService: BoardsService) {}

  @Get("/")
  getAllBoards() {
    return this.boardsService.getAllBoards();
  }
}


5. Board 모델 만들기

게시물을 생성하는 데 필요한 데이터가 어떤 것인지 정의해 주기 위해 모델을 정의한다. 이러한 모델을 정의하기 위해서는 클래스 또는 인터페이스를 이용하여 구현하면 된다. 일단 간단한 모델을 정의하기 위해 인터페이스로 구현해 보자.


src/boards/board.model.ts
export interface Board {
  id: string;
  title: string;
  description: string;
  status: BoardStatus;
}

export enum BoardStatus {
  PUBLIC = "PUBLIC",
  PRIVATE = "PRIVATE",
}


위의 코드에서 BoardStatusEnum 타입으로 정의한 이유는 PUBLIC 또는 PRIVATE 이외의 상태가 나오면 안되기 때문이다.


이렇게 정의한 Board 모델을 다음과 같이 BoardsService에 적용하면, 게시물 데이터에 대한 타입을 정의할 수 있게 된다. 타입을 정의하여 사용하면 린트, 오토 컴플릿 등의 타입스크립트의 유용한 기능을 사용할 수 있다.


src/boards/boards.service.ts
import { Injectable } from "@nestjs/common";
import { Board } from "./board.model";

@Injectable()
export class BoardsService {
  private boards: Board[] = [];

  getAllBoards(): Board[] {
    return this.boards;
  }
}


마찬가지로 BoardsController에도 Board 모델을 적용한다.


src/boards/boards.controller.ts
import { Controller, Get } from "@nestjs/common";
import { Board } from "./board.model";
import { BoardsService } from "./boards.service";

@Controller("boards")
export class BoardsController {
  constructor(private boardsService: BoardsService) {}

  @Get("/")
  getAllBoards(): Board[] {
    return this.boardsService.getAllBoards();
  }
}


6. 게시물을 생성하는 서비스/컨트롤러 만들기

게시물을 생성하는 서비스를 구현해 보자.


src/boards/boards.service.ts
import { Injectable } from "@nestjs/common";
import { Board, BoardStatus } from "./board.model";

@Injectable()
export class BoardsService {
  private boards: Board[] = [];

  getAllBoards(): Board[] {
    return this.boards;
  }

  createBoard(title: string, description: string): Board {
    const board: Board = {
      // id: 유니크한 값
      title,
      description,
      status: BoardStatus.PUBLIC,
    };

    this.boards.push(board);
    return board;
  }
}


createBoard() 메서드를 구현할 때 Board 모델의 id 프로퍼티는 유니크한 값이어야 한다. 유니크한 값을 생성하기 위해 uuid 모듈을 설치한 후 사용한다.


npm install --save uuid


uuid 모듈을 설치한 후 다음과 같이 id 프로퍼티에 유니크한 값을 넣어준다.


src/boards/boards.service.ts
import { Injectable } from "@nestjs/common";
import { Board, BoardStatus } from "./board.model";
import { v1 as uuid } from "uuid";

@Injectable()
export class BoardsService {
  private boards: Board[] = [];

  getAllBoards(): Board[] {
    return this.boards;
  }

  createBoard(title: string, description: string): Board {
    const board: Board = {
      id: uuid(),
      title,
      description,
      status: BoardStatus.PUBLIC,
    };

    this.boards.push(board);
    return board;
  }
}


이제 컨트롤러에서도 게시물을 생성할 수 있도록 createBoard() 라우트 핸들러를 구현한다.


src/boards/boards.controller.ts
import { Body, Controller, Get, Post } from "@nestjs/common";
import { Board } from "./board.model";
import { BoardsService } from "./boards.service";

@Controller("boards")
export class BoardsController {
  constructor(private boardsService: BoardsService) {}

  @Get("/")
  getAllBoards(): Board[] {
    return this.boardsService.getAllBoards();
  }

  @Post("/")
  createBoard(
    @Body("title") title: string,
    @Body("description") description: string
  ): Board {
    return this.boardsService.createBoard(title, description);
  }
}


7. DTO(Data Transfer Object)

DTO는 계층 간 데이터 교환을 위한 객체를 의미한다. DTO를 사용하는 이유는 데이터 유효성을 검사하는 데 효율적이고, 코드 관리를 용이하게 해준다. 현재 클라이언트로부터 게시물을 생성하는 요청을 받을 때 필요한 데이터는 titledescription이다. 이 두 데이터를 DTO로 생성하여 관리할 수 있도록 한다.


다음과 같이 src/boards 디렉터리에 dto 디렉터리를 생성한 후 CreateBoardDto 클래스를 정의한다.


mkdir src/boards/dto
src/boards/dto/create-board.dto.ts
export class CreateBoardDto {
  title: string;
  description: string;
}


CreateBoardDto를 정의한 후 서비스와 컨트롤러 각각에 적용해 보자.


import { Injectable } from "@nestjs/common";
import { Board, BoardStatus } from "./board.model";
import { v1 as uuid } from "uuid";
import { CreateBoardDto } from "./dto/create-board.dto";

@Injectable()
export class BoardsService {
  private boards: Board[] = [];

  getAllBoards(): Board[] {
    return this.boards;
  }

  createBoard(createBoardDto: CreateBoardDto): Board {
    const { title, description } = createBoardDto;
    const board: Board = {
      id: uuid(),
      title,
      description,
      status: BoardStatus.PUBLIC,
    };

    this.boards.push(board);
    return board;
  }
}
import { Body, Controller, Get, Post } from "@nestjs/common";
import { Board } from "./board.model";
import { BoardsService } from "./boards.service";
import { CreateBoardDto } from "./dto/create-board.dto";

@Controller("boards")
export class BoardsController {
  constructor(private boardsService: BoardsService) {}

  @Get("/")
  getAllBoards(): Board[] {
    return this.boardsService.getAllBoards();
  }

  @Post("/")
  createBoard(@Body() createBoardDto: CreateBoardDto): Board {
    return this.boardsService.createBoard(createBoardDto);
  }
}


8. ID로 특정 게시물을 가져오는 서비스/컨트롤러 만들기

ID를 이용하여 특정 게시물을 가져오는 서비스와 컨트롤러를 구현해 보자.


import { Injectable } from "@nestjs/common";
import { Board, BoardStatus } from "./board.model";
import { v1 as uuid } from "uuid";
import { CreateBoardDto } from "./dto/create-board.dto";

@Injectable()
export class BoardsService {
  private boards: Board[] = [];

  getAllBoards(): Board[] {
    return this.boards;
  }

  createBoard(createBoardDto: CreateBoardDto): Board {
    const { title, description } = createBoardDto;
    const board: Board = {
      id: uuid(),
      title,
      description,
      status: BoardStatus.PUBLIC,
    };

    this.boards.push(board);
    return board;
  }

  getBoardById(id: string): Board {
    return this.boards.find((board) => board.id === id);
  }
}
import { Body, Controller, Get, Param, Post } from "@nestjs/common";
import { Board } from "./board.model";
import { BoardsService } from "./boards.service";
import { CreateBoardDto } from "./dto/create-board.dto";

@Controller("boards")
export class BoardsController {
  constructor(private boardsService: BoardsService) {}

  @Get("/")
  getAllBoards(): Board[] {
    return this.boardsService.getAllBoards();
  }

  @Post("/")
  createBoard(@Body() createBoardDto: CreateBoardDto): Board {
    return this.boardsService.createBoard(createBoardDto);
  }

  @Get("/:id")
  getBoardById(@Param("id") id: string): Board {
    return this.boardsService.getBoardById(id);
  }
}


9. ID로 특정 게시물을 지우는 서비스/컨트롤러 만들기

ID를 이용하여 특정 게시물을 지우는 서비스와 컨트롤러를 구현해 보자.


import { Injectable } from "@nestjs/common";
import { Board, BoardStatus } from "./board.model";
import { v1 as uuid } from "uuid";
import { CreateBoardDto } from "./dto/create-board.dto";

@Injectable()
export class BoardsService {
  private boards: Board[] = [];

  getAllBoards(): Board[] {
    return this.boards;
  }

  createBoard(createBoardDto: CreateBoardDto): Board {
    const { title, description } = createBoardDto;
    const board: Board = {
      id: uuid(),
      title,
      description,
      status: BoardStatus.PUBLIC,
    };

    this.boards.push(board);
    return board;
  }

  getBoardById(id: string): Board {
    return this.boards.find((board) => board.id === id);
  }

  deleteBoard(id: string): void {
    this.boards = this.boards.filter((board) => board.id !== id);
  }
}
import { Body, Controller, Delete, Get, Param, Post } from "@nestjs/common";
import { Board } from "./board.model";
import { BoardsService } from "./boards.service";
import { CreateBoardDto } from "./dto/create-board.dto";

@Controller("boards")
export class BoardsController {
  constructor(private boardsService: BoardsService) {}

  @Get("/")
  getAllBoards(): Board[] {
    return this.boardsService.getAllBoards();
  }

  @Post("/")
  createBoard(@Body() createBoardDto: CreateBoardDto): Board {
    return this.boardsService.createBoard(createBoardDto);
  }

  @Get("/:id")
  getBoardById(@Param("id") id: string): Board {
    return this.boardsService.getBoardById(id);
  }

  @Delete("/:id")
  deleteBoard(@Param("id") id: string): void {
    this.boardsService.deleteBoard(id);
  }
}


10. 특정 게시물을 업데이트하는 서비스/컨트롤러 만들기

특정 게시물을 업데이트하는 서비스와 컨트롤러를 구현해 보자.


import { Injectable } from "@nestjs/common";
import { Board, BoardStatus } from "./board.model";
import { v1 as uuid } from "uuid";
import { CreateBoardDto } from "./dto/create-board.dto";

@Injectable()
export class BoardsService {
  private boards: Board[] = [];

  getAllBoards(): Board[] {
    return this.boards;
  }

  createBoard(createBoardDto: CreateBoardDto): Board {
    const { title, description } = createBoardDto;
    const board: Board = {
      id: uuid(),
      title,
      description,
      status: BoardStatus.PUBLIC,
    };

    this.boards.push(board);
    return board;
  }

  getBoardById(id: string): Board {
    return this.boards.find((board) => board.id === id);
  }

  deleteBoard(id: string): void {
    this.boards = this.boards.filter((board) => board.id !== id);
  }

  updateBoardStatus(id: string, status: BoardStatus): Board {
    const board = this.getBoardById(id);

    board.status = status;
    return board;
  }
}
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Patch,
  Post,
} from "@nestjs/common";
import { Board, BoardStatus } from "./board.model";
import { BoardsService } from "./boards.service";
import { CreateBoardDto } from "./dto/create-board.dto";

@Controller("boards")
export class BoardsController {
  constructor(private boardsService: BoardsService) {}

  @Get("/")
  getAllBoards(): Board[] {
    return this.boardsService.getAllBoards();
  }

  @Post("/")
  createBoard(@Body() createBoardDto: CreateBoardDto): Board {
    return this.boardsService.createBoard(createBoardDto);
  }

  @Get("/:id")
  getBoardById(@Param("id") id: string): Board {
    return this.boardsService.getBoardById(id);
  }

  @Delete("/:id")
  deleteBoard(@Param("id") id: string): void {
    this.boardsService.deleteBoard(id);
  }

  @Patch("/:id/status")
  updateBoardStatus(
    @Param("id") id: string,
    @Body("status") status: BoardStatus
  ): Board {
    return this.boardsService.updateBoardStatus(id, status);
  }
}

References