본문 바로가기
프로젝트 개발 기록/[개발] node.js | nest, express

[Node.js] Nest.js 폴더 구조 설계하기

by HelloJudy 2022. 9. 25.

0. 개요

[ 기존의 구조 ]

보통 express에서는 컨트롤러, 서비스 별로 쭈욱 파일이 모여있다.

  • 아래는 과거에 진행한 프로젝트 구조이다. 프로젝트를 진행하면서 mvp가 많아질수록 컨트롤러와 서비스는 많아졌고 점점 도메인 별로 코드를 찾기 어려워졌다.
  • 이 구조는 서비스가 확장되면 도메인 별로 흩어져 있기 때문에 파일을 찾아서 개발하는 것이 힘들다.

 

그래서 좀 더 대규모 서비스의 폴더 구조는 어떻게 설계하는지 궁금해졌고, 이번에는 nest에서 최대한 DDD할 수 있는 폴더 구조를 알아보려 한다.

 

 ┣ 📂controllers
 ┃ ┣ 📜bookMarkController.ts
 ┃ ┣ 📜checklistController.ts
 ┃ ┣ 📜scheduleController.ts
 ┃ ┣ 📜subscribeController.ts
 ┃ ┣ 📜supplementController.ts
 ┃ ┗ 📜userController.ts
 ┣ 📂middlewares
 ┃ ┣ 📜errorMiddleware.ts
 ┃ ┣ 📜loginRequired.ts
 ┃ ┣ 📜validator.ts
 ┃ ┣ 📜verifyRefreshToken.ts
 ┃ ┗ 📜verifyToken.ts
 ┣ 📂routes
 ┃ ┣ 📜bookMarkRouter.ts
 ┃ ┣ 📜checklistRouter.ts
 ┃ ┣ 📜scheduleRouter.ts
 ┃ ┣ 📜subscribeRouter.ts
 ┃ ┣ 📜supplementRouter.ts
 ┃ ┗ 📜userRouter.ts
 ┣ 📂services
 ┃ ┣ 📜bookMarkService.ts
 ┃ ┣ 📜checklistService.ts
 ┃ ┣ 📜scheduleService.ts
 ┃ ┣ 📜subscribeService.ts
 ┃ ┣ 📜supplementService.ts
 ┃ ┗ 📜userService.ts

 

[ What is DDD? ]

Domain Driven Design의 약자로 도메인 주도 설계를 말한다.

 


🔅 Domain (도메인)이란?

 

1) 사전적의미: '영역', '집합'

2) DDD에서의 Domain: 비즈니스 Domain

3) 비즈니스 Domain: 유사한 업무의 집합

4) 애플리케이션은 비즈니스 Domain별로 나누어 설계 및 개발 될 수 있다.


nest에서 폴더구조를 세워보자!


1. 폴더 구조

 

이 구조는 일반적인 NestJS 구조가 아니라 새롭게 설정한 구조이다.

'구름톤' 해커톤에서 배운 구조이며 블로그를 참고했다.

(자세한 내용은 해당 블로그를 참고해주세용!)

 

src
├─auth
│  ├─decorators
│  ├─domain
│  ├─guards
│  └─helpers
├─common
│  ├─entities
│  ├─interfaces
│  └─repositories
├─config
│  ├─database
│  │  └─mysql
│  └─jwt
├─database
│  └─migrations
├─modules
│  └─user
│      └─users
│          ├─entities
│          └─repositories
└─providers
    └─database
        └─mysql

 

[ auth ]

인증과 관련된 기능을 작성하는 폴더이다.

 

[ common ]

공통으로 많이 사용되는 코드들 작성한다. 모든 폴더에서 정말 common하게 사용할 코드들이다.

 

[ config ]

기존에 프로젝트에서는 dotenv를 사용했는데 이번에는 @nest/config를 이용해서, 환경변수 설정한다.

 

현업에서는 test, dev, prod 등 여러 환경을 사용하게 되는데 그때마다 다른 환경 변수를 사용할 필요가 있다. @nestjs/config를 통해서 다양한 환경에 다양한 환경 변수를 사용하는 것이 좋다.

 

config를 관리하는 방법은 해당 블로그를 보고 따라해보자.

 

👉 Creating Config Files in NestJS

 

 

1) 패키지 설치

$ npm i --save @nestjs/config

 

2) 폴더 구조

src/
└── config/
    ├── app/
    │   ├── config.module.ts
    │   ├── config.service.ts
    │   └── configuration.ts
    │   └── index.ts
    ├── database/
    │   └── mysql
    │       ├── config.module.ts
    │       ├── config.service.ts
    │       └── configuration.ts
    │       └── index.ts
    └── mail/
        ├── config.module.ts
        ├── config.service.ts
        └── configuration.ts
        └── index.ts

 

  • configuration.ts: 변수들을 등록해준다.
  • configuration.service.ts: 'getter(획득자)' 기반 클래스 함수가 ​​있는 단순한 클래스이다.
@Injectable()
export class MysqlConfigService {
  constructor(private configService: ConfigService) {}

  get host(): string {
    return this.configService.get<string>('mysql.host');
  }
 }

 

  • config.module.ts: 앱 구성 관련 클래스를 가져와서 제공한다.

 

3) 사용 방법

 

일반적으로 dotenv 방식이라면 다음과 같이 사용한다. (process.env.JWT_SECRET_KEY)

 

import dotenv from "dotenv";
dotenv.config();
const secretKey = process.env.JWT_SECRET_KEY;

// access token 발급
const makeToken = (Object: ITokenInput) => {
  const token = jwt.sign(Object, secretKey, { expiresIn: "1h" });
  return token;
};

 

지금 방식에서는 비즈니스 로직을 구현하는 곳에서 다음과 같이 ConfigService를 import한 뒤 this.config.get('환경 변수')로 사용할 수 있다.

 

> this.jwtConfig.accessSecretKey

import { JwtConfigService } from '@config/jwt';

@Injectable()
export class VerifyGuard implements CanActivate {
  constructor(private readonly jwtConfig: JwtConfigService) {}

  private _getPayload(req: Request) {
...
      const payload = jwt.verify(token, this.jwtConfig.accessSecretKey);
  }
}

 

[ database ]

migrations파일이 있는 폴더이다.

 

 

[ providers ]

외부 서비스(앱)를 말그대로 제공받는 폴더이다.

 

 

[ modules ]

비즈니스 로직을 구현하는 곳이다.

 

modules
└─user
    │  user.module.ts
    │
    └─users
        │  users.controller.ts
        │  users.module.ts
        │  users.service.ts
        │
        ├─entities
        │      index.ts
        │      user.entity.ts
        │
        └─repositories
                index.ts
                user.repository.ts

 

이때 user 도메인으로 작성을 하더라도 그 안에서 세부적으로 나뉠 수 있기 때문에 세부적으로 나누고 연관된 도메인끼리 Aggregate한다.

 

예를 들어, User 객체가 있고, User가 하는 운동 UserActive, UserProfile 객체가 있다면 모두 하나의 Aggregate라고 할 수 있다.

 

가장 큰 도메인은 단수형, 세부적인 도메인은 복수형.

 

 

 

 

글이 점점 길어지고 있어 좀 더 자세한 @nest/configDDD Aggregate에 대해서는 따로 포스팅을 작성하겠다,


📌 Reference

 

반응형

댓글