(Node.js) Nestjs + MSA 기반 서버 구축 (3) - 게시물 CRUD

2025. 6. 21. 13:00Node.js

반응형

 

https://developerjjh.tistory.com/212

 

(Node.js) Nestjs + MSA기반 서버 구축 (2) - MongoDB & Docker 연동

2025.06.05 - [Node.js] - (Node.js) Nestjs + MSA 기반 서버 구축 (1) (Node.js) Nestjs + MSA 기반 서버 구축 (1)● 순서 요지환경 설정 및 Lib 설치코드 구현테스트 통신마무리 1. 요지우연히 NestJS + MSA + MongoDB를 기반

developerjjh.tistory.com

 

 

● 순서

  1. 요지
  2. 기능 설명
  3. 예제 코드
  4. 테스트 통신 결과
  5. 마무리

 

1. 요지

이전 게시물에서는 NestJS에 MSA 패턴을 적용한 후 데이터베이스 중 MongoDB를 연동하였다

이제 해당 요청에 따른 기능을 만들어보려고 한다

 

2. 기능 설명

- 게시물 전체 조회

  • 등록되어 있는 모든 게시물을 조회한다

- 게시물 등록

  • 게시물의 제목과 내용으로 요청
  • 제목은 필수 값이지만 내용은 없어도 등록되도록 설정
  • 제목은 중복을 방지

- 게시물 수정

  • 게시물의 id값과 수정하고자 하는 내용을 요청
  • 해당 id값에 대한 존재여부도 확인하여 유효성 검사를 실시

- 게시물 삭제

  • 수정과 마찬가지로 id값으로 삭제하고자 하는 게시물을 삭제
  • 존재하지 않는 게시물의 id값으로 요청 시 에러를 반환하도록 한다

 

3. 예제 코드

gateway controller

-  API의 진입점으로 요청의 필수값에 대한 존재 여부를 검사

// gateway/src/app.controller.ts

import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Delete,
  Param,
} from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }

  @Get('boards')
  async getBoards() {
    return this.appService.getBoards();
  }

  @Post('board')
  async createBoard(@Body() data: { title: string; text?: string }) {
    if (!data.title) throw new Error('Title is required for creating a board');
    return this.appService.createBoard(data);
  }

  @Patch('board/update/:id')
  async updateBoard(
    @Param('id') id: string,
    @Body() data: { title: string; text?: string },
  ) {
    if (!id || !data.title) {
      throw new Error('ID and title are required for updating a board');
    }
    return this.appService.updateBoard(id, data);
  }

  @Delete('board/delete/:id')
  async deleteBoard(@Param('id') id: string) {
    if (!id) throw new Error('ID is required for deleting a board');

    return this.appService.deleteBoard(id);
  }
}

 

gateway service 

- board 서비스와 TCP 연결 후 일치하는 통신을 요청

// gateway/src/app.service.ts

import { Injectable } from '@nestjs/common';
import {
  ClientProxy,
  ClientProxyFactory,
  Transport,
} from '@nestjs/microservices';

@Injectable()
export class AppService {
  private boardClient: ClientProxy;

  constructor() {
    this.boardClient = ClientProxyFactory.create({
      transport: Transport.TCP,
      options: { host: '0.0.0.0', port: 4002 },
    });
  }

  getHello(): string {
    return 'Hello World!';
  }

  getBoards() {
    return this.boardClient.send({ cmd: 'get-boards' }, {});
  }

  createBoard(data: any) {
    return this.boardClient.send({ cmd: 'create-board' }, data);
  }

  updateBoard(id: string, data: { title: string; text?: string }) {
    return this.boardClient.send({ cmd: 'update-board' }, { id, ...data });
  }

  deleteBoard(id: string) {
    return this.boardClient.send({ cmd: 'delete-board' }, { id });
  }
}

 

board controller

- gateway에서 TCP 통신에 대한 service 메서드를 호출

import { Controller } from '@nestjs/common';
import { MessagePattern, Payload } from '@nestjs/microservices';

import { AppService } from './app.service';
import { Board } from './schema/board.schema';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @MessagePattern({ cmd: 'get-boards' })
  getBoards() {
    return this.appService.findAll();
  }

  @MessagePattern({ cmd: 'create-board' })
  createBoard(@Payload() data: Partial<Board>) {
    return this.appService.create(data);
  }

  @MessagePattern({ cmd: 'update-board' })
  updateBoard(@Payload() data: { id: string; title: string; text?: string }) {
    return this.appService.updateBoard(data.id, {
      title: data.title,
      text: data.text,
    });
  }

  @MessagePattern({ cmd: 'delete-board' })
  deleteBoard(@Payload() data: { id: string }) {
    return this.appService.deleteBoard(data.id);
  }
}

 

board service 

- 각 메서드 별 비즈니스 및 DB 로직을 작성

import { Injectable } from '@nestjs/common';
import { Model } from 'mongoose';
import { InjectModel } from '@nestjs/mongoose';

import { Board } from './schema/board.schema';

@Injectable()
export class AppService {
  constructor(@InjectModel(Board.name) private boardModel: Model<Board>) {}

  async findAll(): Promise<Board[]> {
    return this.boardModel.find().exec();
  }

  async create(data: Partial<Board>): Promise<Board> {
    const isBoard = await this.boardModel.findOne({ title: data.title }).exec();
    if (isBoard) throw new Error('Board with this title already exists');

    if (!data.text) data.text = '';

    const createdBoard = new this.boardModel(data);

    await createdBoard.save();

    return createdBoard;
  }

  async updateBoard(id: string, data: Partial<Board>): Promise<Board | null> {
    const board = await this.boardModel.findById(id).exec();
    if (!board) throw new Error('Board not found');

    if (data.title) board.title = data.title;

    if (data.text) board.text = data.text;

    await board.save();

    return board;
  }

  async deleteBoard(id: string): Promise<Board | null> {
    const board = await this.boardModel.findByIdAndDelete(id).exec();

    if (!board) throw new Error('Board not found');

    return board;
  }
}

 

4. 테스트 통신 결과

  1. 전체 게시물 호출
    전체 게시물 호출
  2. 게시물 등록
    게시물 등록
  3. 특정 게시물 수정
    수정 결과 및 수정 후 전체 조회


  4. 특정 게시물 삭제

삭제 전 전체 목록, 삭제 결과, 삭제 후 전체 목록

 

 

5. 마무리

간단하게 최소한의 중복이나 존재 여부 정도만 검사하고 CRUD 하는 과정을 담아봤습니다

아직 부족한 점이 많지만 새로운 패턴과 환경 등을 적용하는 과정이니 참고 정도만 해주시면 감사하겠습니다

 

다음에는 또 다른 서비스(서버)를 연결하여 더 많은 기능들을 내포하는 프로젝트로 확장하는 게 목적입니다

유저 서비스를 별도로 생성하여서 로그인 상태를 검증하는 거를 생각하고 있으며 더 나아가 권한까지 부여하여 점차 커져가는 게 목표입니다

천천히 과정을 담아 볼테니 많은 관심 부탁드려요!!