Node.js/NestJS

[NestJS - Microservice] NestJS로 마이크로서비스 구축해보기(RabbitMQ, MongoDB, Docker) - 1. 프로젝트 세팅

턴태 2022. 11. 14. 02:01

마이크로 서비스란?

출처: AWS

aws - 소프트웨어가 잘 정의된 API를 통해 통신하는 소규모의 독립적인 서비스

redhat - 마이크로서비스란 소프트웨어를 구축하기 위한 아키텍처이자 하나의 접근 방식으로, 애플리케이션을 상호 독립적인 최소 구성 요소로 분할합니다. 마이크로서비스에서는 모든 요소가 독립적이며 연동되어 동일한 태스크를 완수합니다.

 

위의 정의에서 볼 수 있듯이, 마이크로서비스는 기존의 모든 것을 하나로 통합하는 모놀리식 아키텍처와 달리 애플리케이션을 구성하는 서비스들이 서로 독립한 상태로 존재하는 것을 마이크로서비스라고 하며, 이 마이크로 서비스들을 종합적으로 사용하는 인프라 구조를 마이크로서비스 아키텍처라고 하며 이렇게 설계되는 애플리케이션을 마이크로서비스 애플리케이션 이하 MSA라고 부릅니다.

 

마이크로서비스는 서로 서비스가 분리되어 있기 때문에 서비스의 생성과 삭제가 자유로우며, 하나의 서비스로 인해 전체 서비스가 마비되는 상황이 발생하지 않습니다. 또한, 분리되어 있기 때문에 서비스에 대한 이해가 용이합니다.

 

물론 마이크로서비스가 유토피아적인 아키텍처는 아닙니다. 왜냐하면, 모든 서비스를 독립적으로 분할하기 때문에 모듈 간의 인터페이스를 적절히 고려해야 합니다. 또한, 배포 과정이 복잡한 편이며 데이터베이스 관리가 어렵기에 모놀리식 아키텍처보다 개발 난이도가 어려운 편입니다.

 

하지만, 자동화 도구를 사용하면 어느 정도 단점을 보완할 수 있습니다.

 

이번에도 제가 자주 즐겨보는 유튜브인 Michael Guay 님의 'Build Nest.js Microservices With RabbitMQ, MongoDB & Docker | Tutorial' 영상을 보며 정리한 게시물입니다.

 

https://www.youtube.com/watch?v=yuVVKB0EaOQ

 

모노레포 세팅

출처: 라인 엔지니어링(https://engineering.linecorp.com/ko/blog/monorepo-with-turborepo#2)

모노레포는 하나의 레포지토리, 하나의 저장소에 두 개 이상의 프로젝트를 저장하는 것을 의미합니다. 이렇게 하나의 저장소에서 여러 개의 프로젝트를 관리하면, 의존성 패키지를 공유하기 때문에 의존성을 쉽게 관리할 수 있으며, 관리 포인트를 하나의 저장소에만 맞추면 되어 DevOps에 용이하고, 프로젝트 생성이 쉽다는 장점이 있습니다.

 

1. 프로젝트 생성

먼저 터미널을 열어서 nest cli를 통해 새롭게 프로젝트를 생성해줍시다. 프로젝트 이름은 ordering-app으로 정했습니다. 패키지 매니저는 yarn을 주로 써와서 yarn으로 정했습니다.

nest new ordering-app

맨 처음에 ordering-app으로 프로젝트를 생성했기에 NestJS의 기본 애플리케이션은 ordering-app입니다. 이 애플리케이션을 모노레포로 만들기 위해서는 다시 nest cli 명령어를 사용해주어야 합니다.

 

먼저 방금 만든 ordering-app 디렉터리로 이동합니다.

cd ordering-app

본 화면에서 nest cli 를 통해서 다른 마이크로서비스 애플리케이션을 생성해주도록 합시다. 우리의 주 애플리케이션이 될 orders 애플리케이션을 생성합니다. orders 애플리케이션은 요청을 처리하고 다른 서비스에게 적절한 이벤트를 전달하는 역할을 맡을 것입니다.

nest g app orders

orders 애플리케이션을 생성했고 우리의 프로젝트를 모노레포로 변환하였습니다. 이제 VSCode 나 다른 IDE 프로그램에서 직접 눈으로 확인해봅시다.

기존 NestJS 프로젝트 디렉터리 구조와는 살짝 다릅니다. 기본적으로 src 폴더를 가지고 있으나 여기에서는 apps 폴더를 갖고 있습니다. nest-cli에서 자세히 확인해봅시다.

5번째 줄 코드를 보면 monorepo 속성의 값이 true로 설정되어 있습니다. 이로서 우리의 프로젝트가 모노레포로 변환된 것을 다시 확인할 수 있습니다.


또한, 기본적으로 우리가 세팅한 ordering-app을 애플리케이션으로 변경하였으며, 방금 생성한 orders 애플리케이션이 하단에 추가된 것을 볼 수 있습니다. 이러한 구조의 장점은 루트 디렉터리에 있는 tsConfig를 공유한다는 점이며, eslint나 패키지들의 의존성 또한 공유한다는 것입니다.

 

그 다음으로 기본적으로 생성된 apps/ordering-app 디렉터리를 삭제해줍시다. ordering-app을 삭제했으므로 nest-cli.json에서도 해당 내용을 지워야겠죠? 그리고 루트 애플리케이션을 orders로 할 예정이므로 nest-cli.json의 root, sourceRoot, tsConfigPath 또한 orders를 기준으로 변경시켜줍시다.

더보기

변경 전

{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "apps/ordering-app/src",
  "monorepo": true,
  "root": "apps/ordering-app",
  "compilerOptions": {
    "webpack": true,
    "tsConfigPath": "apps/ordering-app/tsconfig.app.json"
  },
  "projects": {
    "ordering-app": {
      "type": "application",
      "root": "apps/ordering-app",
      "entryFile": "main",
      "sourceRoot": "apps/ordering-app/src",
      "compilerOptions": {
        "tsConfigPath": "apps/ordering-app/tsconfig.app.json"
      }
    },
    "orders": {
      "type": "application",
      "root": "apps/orders",
      "entryFile": "main",
      "sourceRoot": "apps/orders/src",
      "compilerOptions": {
        "tsConfigPath": "apps/orders/tsconfig.app.json"
      }
    }
  }
}

 

변경 후

{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "apps/orders/src",
  "monorepo": true,
  "root": "apps/orders",
  "compilerOptions": {
    "webpack": true,
    "tsConfigPath": "apps/orders/tsconfig.app.json"
  },
  "projects": {
    "orders": {
      "type": "application",
      "root": "apps/orders",
      "entryFile": "main",
      "sourceRoot": "apps/orders/src",
      "compilerOptions": {
        "tsConfigPath": "apps/orders/tsconfig.app.json"
      }
    }
  }
}

이렇게 해서 간단하게 모노레포를 만들어보았습니다.

 

2. 애플리케이션 추가

다른 애플리케이션을 추가해봅시다.

 

두 번째로 생성할 애플리케이션은 billing 애플리케이션입니다.

nest g app billing

마지막으로 auth라는 애플리케이션을 만듭니다.

nest g app auth

이제 다시 VSCode 로 돌아가면 우리가 추가한 애플리케이션이 디렉터리에 반영된 것을 볼 수 있으며, 각 애플리케이션마다 루트 tsconfig를 확장할 수 있도록 tsconfig.app.json 파일이 만들어진 것을 볼 수 있습니다. 당연하게 nest-cli.json에서도 내용이 업데이트된 것을 볼 수 있습니다.

이제 한 번 애플리케이션을 실행해볼까요?

yarn start:dev
# npm run start:dev

그러면 우리가 처음에 생성한 기본 애플리케이션이 실행되는 것을 로그에서 확인할 수 있습니다.

다른 애플리케이션을 실행하기 위해서는 옵션으로 애플리케이션 이름을 넣어주면 됩니다. 예를 들어, billing 애플리케이션을 구동하기 위해서는 아래와 같이 명령어를 입력하면 됩니다.

yarn start:dev billing
# npm run start:dev billing

추가로 공용 라이브러리 애플리케이션을 만듭니다. 만약 마이크로서비스끼리 코드를 공유하고 싶다면, 라이브러리에 코드를 작성하는 것입니다. 위 명령어를 입력하면, 어떤 prefix를 사용할 것이냐는 질문이 들어올 텐데 기본값으로 넘기겠습니다.

nest g lib common
# nest g library common

설치가 완료되면 이전과 다르게 apps에 추가되는 것이 아니라 루트 디렉터리에 libs라는 폴더가 추가된 것을 볼 수 있습니다. libs 폴더 안에는 우리가 추가한 common 라이브러리를 볼 수 있으며 그 아래에는 src 디렉터리가 있고 index.ts 파일이 있습니다. index.ts 파일을 통해서 간단하게 common 서비스와 모듈 및 유틸리티 기능을 공유할 수 있습니다.

 

nest-cli.json에서도 해당 애플리케이션은 라이브러리로 분류된 것을 확인할 수 있습니다.

"common": {
      "type": "library",
      "root": "libs/common",
      "entryFile": "index",
      "sourceRoot": "libs/common/src",
      "compilerOptions": {
        "tsConfigPath": "libs/common/tsconfig.lib.json"
      }
    }

 

그래서 프로젝트 디렉터리 구조를 나열해보자면 아래와 같습니다.

.
├── README.md
├── apps
│   ├── auth
│   │   ├── src
│   │   │   ├── auth.controller.spec.ts
│   │   │   ├── auth.controller.ts
│   │   │   ├── auth.module.ts
│   │   │   ├── auth.service.ts
│   │   │   └── main.ts
│   │   ├── test
│   │   │   ├── app.e2e-spec.ts
│   │   │   └── jest-e2e.json
│   │   └── tsconfig.app.json
│   ├── billing
│   │   ├── src
│   │   │   ├── billing.controller.spec.ts
│   │   │   ├── billing.controller.ts
│   │   │   ├── billing.module.ts
│   │   │   ├── billing.service.ts
│   │   │   └── main.ts
│   │   ├── test
│   │   │   ├── app.e2e-spec.ts
│   │   │   └── jest-e2e.json
│   │   └── tsconfig.app.json
│   └── orders
│       ├── src
│       │   ├── main.ts
│       │   ├── orders.controller.spec.ts
│       │   ├── orders.controller.ts
│       │   ├── orders.module.ts
│       │   └── orders.service.ts
│       ├── test
│       │   ├── app.e2e-spec.ts
│       │   └── jest-e2e.json
│       └── tsconfig.app.json
├── dist
│   └── apps
│       ├── billing
│       │   └── main.js
│       └── orders
│           └── main.js
├── libs
│   └── common
│       ├── src
│       │   ├── common.module.ts
│       │   ├── common.service.spec.ts
│       │   ├── common.service.ts
│       │   └── index.ts
│       └── tsconfig.lib.json
├── nest-cli.json
├── package.json
├── tsconfig.build.json
├── tsconfig.json
└── yarn.lock

 

다음에는 MongoDB 추가와 Orders 앱 설정을 해보겠습니다.