일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- guard
- 파일조회
- 오브젝트
- API 설계
- Connection pool
- monorepo
- 자바
- typeorm
- Mock
- SROOM
- nestjs libraries
- mailerservice
- 티스토리챌린지
- 오블완
- jest
- SW마에스트로
- java
- YouTube Data API
- nestjs library
- fine-grained
- NestJS
- coarse-grained
- API 개발
- 어드민 페이지
- 추상 클래스
- 셀렉트어드민
- 권한검증
- 책임부과
- 멀티테넌시
- nestjs decorator
- Today
- Total
독산구너
NestJS Library 사용 본문
목차
글의 목적
NestJS의 Library를 사용해서 얻을 수 있는 이점을 알아보고, 이를 멀티테넌씨 DB 구조 설계에 이용해보고자 합니다.
NestJS Libraries
NestJS에서는 공통으로 사용되고, 자주 재사용되는 코드를 구성하는 여러가지 방식이 존재합니다.
1. 모듈화
-> NestJS의 핵심 개념으로, 특정 로직을 모듈화시켜 다른 모듈에서 import 받아 사용할 수 있도록 지원합니다. 하지만 Single Application 내에서만 사용 가능하므로, 회사 내 또는 조직 내 공유해 사용하기에는 부족합니다.
2. Npm pachaging
-> 모듈은 재사용을 위해 npm 패키지가 가능합니다. 패키지는 npm install을 통해서 쉽게 사용이 가능합니다. 하지만 이는 조직 내 특화된 기능을 공유하기보다 범용적으로 재사용하도록, 외부적으로 배포하는 용도의 공유 수단입니다.
3. NestJS Libraries
-> monorepo 내 프로젝트 간 코드를 공유하도록 지원하는 NestJS의 기능입니다. 설치가 불필요하고 같은 monorepo 내에서 바로 참조가 가능, 변경사항을 수정 즉시 적용 가능하다는 장점이 있습니다.
이 세 방식의 차이점을 표로 표현하면 다음과 같습니다.
항목 | 모듈화 | NestJS Libraries | npm 패키징 |
설명 | 애플리케이션 내부에서 기능을 분리하여 재사용 가능한 구조로 설계. | Monorepo에서 여러 애플리케이션 간에 재사용 가능한 모듈 설계. | 외부 프로젝트 간에 공유 가능한 패키지로 배포. |
공유 범위 | 애플리케이션 내부에서만 공유. | 같은 Monorepo 내에서만 공유. | 전 세계 또는 조직 내 모든 프로젝트에서 공유 가능. |
구성 위치 | 단일 애플리케이션 내부 (src/ 디렉토리). | Monorepo의 libs/ 디렉토리. | 별도의 독립적인 패키지로 관리. |
의존성 관리 | 애플리케이션의 package.json에 포함. | Monorepo에서 의존성을 공유. | 각 패키지가 독립적으로 의존성 관리. |
변경 사항 반영 | 애플리케이션 내부에서 바로 반영. | Monorepo 내에서 즉시 반영 가능. | 새 버전 배포 후 수동으로 업데이트 필요. |
설치 필요 여부 | 설치 불필요, 내부에서 바로 사용 가능. | 설치 불필요, 같은 Monorepo 내에서 참조 가능. | 설치 필요 (npm install 또는 yarn add). |
용도 | 기능 단위로 코드를 분리하여 유지보수와 테스트 용이. | 내부적으로 재사용 가능한 모듈 설계와 공유. | 외부에 배포 가능한 범용 라이브러리 설계. |
사용 사례 | - 로깅 기능을 모듈로 분리- 인증 모듈 설계 | - Monorepo에서 공통 AuthLibrary- 로깅 Library | - JWT 라이브러리- 데이터베이스 클라이언트 패키지 |
장점 | - 코드 구조가 명확해짐- 유지보수 용이 | - 빠른 개발 및 테스트 가능- 코드 중복 최소화 | - 전 세계 개발자와 공유 가능- 프로젝트 간 코드 재사용 |
단점 | 애플리케이션 외부에서는 재사용 불가능. | Monorepo 외부에서는 사용 불가능. | 배포 및 설치 과정이 필요. |
대상 환경 | 단일 애플리케이션. | Monorepo 구조. | 다중 프로젝트 환경. |
예제 | AppModule 내에서 여러 서비스, 컨트롤러 정의. | libs/auth 또는 libs/logger 생성. | @nestjs/passport와 같은 npm 패키지. |
Monorepo
여기서, Monorepo에 대해 알아보고 넘어가겠습니다. monorepo는 단일 리포지토리에서 여러 프로젝트를 관리하는 소프트웨어 개발 방식입니다. 코드의 버전관리와 공유가 간단해지고, 코드 재사용성이 증가됩니다. 또한 모든 프로젝트가 같은 환경에서 빌드 및 테스트 되므로 end-to-end 테스트를 쉽게 구성 가능하고, 여러 프로젝트에서 사용하는 패키지 의존성을 통합적으로 관리 가능하다는 장점이 있습니다.
구조를 설명하면 다음과 같습니다.
my-monorepo/
├── apps/ # 여러 애플리케이션 디렉토리
│ ├── app1/ # 첫 번째 애플리케이션
│ │ ├── src/
│ │ ├── main.ts
│ │ └── app.module.ts
│ ├── app2/ # 두 번째 애플리케이션
│ │ ├── src/
│ │ ├── main.ts
│ │ └── app.module.ts
│ └── ...
├── libs/ # 공유 라이브러리 디렉토리
│ ├── auth/ # 인증 관련 라이브러리
│ │ ├── src/
│ │ └── auth.module.ts
│ ├── logger/ # 로깅 관련 라이브러리
│ │ ├── src/
│ │ └── logger.module.ts
│ └── ...
├── package.json # 공통 의존성
├── tsconfig.json # 공통 TypeScript 설정
└── nest-cli.json # NestJS CLI 설정 파일
여기서 Libraries는 monorepo 내에서 여러 프로젝트가 공통으로 사용 가능한 코드/모듈입니다.
멀티테넌시 구조설계에 Libraries를 사용하는 이유
- 비즈니스 로직과 멀티테넌시 로직의 분리
- 멀티테넌시 구조의 구현 방식이 변경되더라도 수정 작업을 한곳에서만 수행하면 되므로 유지보수가 용이합니다.
- 애플리케이션에는 멀티테넌시 로직과 분리된 상태로 비즈니스 로직만 집중적으로 수행할 수 있습니다.
- 애플리케이션 확장성 확보
- 현재 개발 중인 업무 관리 플랫폼은 SaaS 형태로 제공되며, 워크스페이스별로 서로 다른 기능을 사용할 수 있도록 설계되어 있습니다. 각 테넌트별로 필요한 애플리케이션이 존재하고, 이러한 애플리케이션이 확장되고 증가함에 따라 데이터베이스 관리가 복잡해질 가능성이 높습니다. 데이터베이스 관련 로직을 분리하여 여러 애플리케이션에서 재사용 가능하도록 설계하면 효율적인 확장과 관리가 가능합니다.
- 멀티테넌시 구조 설계의 복잡성 해결
- 테넌트 생성 및 삭제, 각 테넌트의 데이터베이스 연결 관리, 공통 데이터베이스와 테넌트 데이터베이스의 분리, 트랜잭션 관리 등 다양한 요소를 고려해야 합니다. 이러한 로직을 하나의 애플리케이션 내에서 모듈화하기에는 복잡성이 높으므로, 별도의 라이브러리로 분리하는 것이 효과적입니다.
Libaray 생성
NestJS에서는 다음과 같은 방식으로 라이브러리 생성이 가능합니다.
$ nest g library [생성하고자 하는 라이브러리 이름]
다음과 같은 구조로 라이브러리가 생성됩니다.
libs
ㄴmy-library
ㄴsrc
ㄴindex.ts
ㄴmy-library.module.ts
ㄴmy-library.service.ts
ㄴtsconfig.lib.json
그리고 nest.cli.json에는 다음과 같이 project 의 value로 들어가야 합니다. 이때 type은 application이 아닌 library로, entryFile은 main이 아닌 index로 설정해야 합니다.
** index.ts 파일의 exports를 통해 코드를 내보냅니다.
Library 사용
app.module.ts에 Library로 추가한 모듈을 import 받아 사용 가능합니다.
- Database library 생성
DatabaseService에서는 테넌트 DB 생성과 각 테넌트DB 접근을 위한 getTenantDataSource 메서드가 정의되어 있습니다.
controller에서는 Interceptor를 사용해 해당 워크스페이스 DB datasource를 받고, 트랜잭션을 시작합니다. Exception 발생 시 롤백합니다.
@Injectable()
export class TenantTxInterceptor implements NestInterceptor {
constructor(private readonly databaseService: DatabaseService) {
}
async intercept(
context: ExecutionContext,
next: CallHandler<any>
): Promise<Observable<any>> {
const req = context.switchToHttp().getRequest();
const queryRunner: QueryRunner = await this.init(req.body.workspaceId);
req.tenanatQueryRunnerManager = queryRunner.manager;
return next.handle().pipe(
catchError(async (e) => {
await queryRunner.rollbackTransaction();
await queryRunner.release();
console.log(e);
if (e instanceof ServiceException) {
throw e;
} else if (e instanceof BadRequestException) {
throw e;
} else {
throw new ServiceException(ExceptionCode.DATABASE_ERROR);
}
}),
tap(async () => {
await queryRunner.commitTransaction();
await queryRunner.release();
})
);
}
private async init(workspaceId: string): Promise<QueryRunner> {
const dataSource: DataSource = await this.databaseService.getTenantDataSource(workspaceId);
const queryRunner = dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
return queryRunner;
}
}
NestJS Library 사용을 통해
1. 코드 재사용성 증가
: 이전에는 특정 기능(db 연결, config 처리 등)이 프로젝트 내에서만 사용 가능하거나 중복 구현될 가능성이 높았습니다. Library를 사용하면서 다른 프로젝트에서도 동일 기능을 쉽게 재사용 가능하게 됐습니다.
2. 관심사 분리
: 비지니스 로직, 라우터 설정, 데이터베이스 연결 등이 혼합되어 코드의 역할이 불명확했으나, 멀티테넌시 관련 로직을 라이브러리로 분리해 어플리케이션 코드에는 비지니스 로직만을 구현하도록 관심사를 분리했습니다.
3. 유지보수성 향상
: 모든 로직이 한 프로젝트에 혼합되어 변경시 오류 발생 가능성이 높았으나, 라이브러리 사용을 통해 멀티테넌시 관련 수정사항을 라이브러리 내부에서만 수정하고, 외부에 영향을 주지 않는것이 가능해졌습니다.
4. 테스트 용이성
: NestJS의 Library는 독립적으로 테스트 가능하므로 monk 데이터를 사용해 분리되어 테스트가 가능해졌습니다.
단점은 없는가?
1. 아직은 작은 프로젝트이므로, 확장성과 코드 재사용을 고려한 라이브러리 사용 리팩토링 자체가 불필요한 비용발생이라고 할 수 있습니다.
2. 디펜던시 관리가 복잡해질 수 있다고 생각합니다. 여러 프로젝트에서 공통된 라이브러리를 사용한다면, 어느 프로젝트의 특성에 맞게 라이브러리를 수정하기 어려워집니다. 또한 버전이 구분된다면 어느 프로젝트에서 어떤 버전을 사용할지 결정하고 테스트하는 비용이 발생할 것입니다.
'NestJS' 카테고리의 다른 글
불필요한 기능 무효화로 Jest 테스트 최적화 (2) | 2024.11.28 |
---|---|
NestJS Guard, JWT 기반 권한 레벨 관리 (18) | 2024.11.24 |
멀티테넌시 구조와 NestJS Typeorm 사용 구현 (0) | 2024.11.20 |