일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- aws m2
- mongo 4.4
- nestjs
- docker m2
- 개발자 면접 팁
- 개발자 회고록
- 팩토리 패턴 예제
- mongo 4.4.18
- ECS
- 라즈베리바이4 mongo
- github accesstoken
- OSI 7계층
- 신입 개발자 면접
- 신입 면접 팁
- 밸런스 게임
- index like
- 개발자 취업 준비
- 회고록
- index in
- token 탈취
- VUE
- mysql index 속도차이
- kubernetes
- 인텔리제이 github 로그인
- mysql like
- 쿠버네티스
- git
- 퇴사
- 개발자 전직
- 팩토리 패턴 언제
- Today
- Total
주니어 개발자 1호
node-json-db를 TypeORM과 비슷하게 만들어 보기 도전 본문
간단하게 활용할 db가 없나? 라는 과정중에서 node-json-db 라는 라이브러리를 알게 되었습니다.
하지만 그냥 일반 node project와 동일하게 생성자를 통해 관리하기보단, NestJS로 Module를 시켜보고 싶었습니다.
아래 문서를 참고해서 진행할 수 있을 것 같아요.
DynamicModule: https://docs.nestjs.com/fundamentals/dynamic-modules
CustomProviders: https://docs.nestjs.com/fundamentals/custom-providers
( *TypeORM 내부 안보고 도전!! )
//구현 목표
JsonDBModule.forRoot({path:'경로'}),
JsonDBModule.forFeature([Entity,... ])
프로젝트 세팅
NestJs에 대한 프로젝트 세팅을 진행하겠습니다.
nest new practice-dynamic-module
node-json-db 설치 및 경로세팅
링크: https://github.com/Belphemur/node-json-db
설치를 진행해줍니다.
npm i node-json-db
경로를 설정해줍니다.
테스트 용도의 목적이니, library pattern을 사용하지 않고 src아래에 위치 시킬려고 합니다.
src/common/json-db.module.ts
src/common/json-db.serivce.ts
그리고 class에 대한 기본 설정을 진행해줍니다.
import {Module} from "@nestjs/common";
@Module({})
export class JsonDbModule {}
---
import {Injectable} from "@nestjs/common";
@Injectable()
export class JsonDbService {
constructor() {}
}
그리고 생성하기 위한 provider가 무엇이 필요한지 파악하기 위해 library를 살펴봅니다.
- 첫 번째 인수는 데이터베이스 파일명입니다. 확장자를 사용하지 않으면 '.json'으로 가정하여 자동으로 추가합니다.
- 두 번째 인수는 푸시할 때마다 DB에 저장하도록 지시하는 데 사용됩니다. 두 번째 인수를 false로 설정하면 save() 메서드를 호출해야 합니다.
- 세 번째 인수는 사람이 읽을 수 있는 형식으로 데이터베이스를 저장하도록 JsonDB에 요청하는 데 사용됩니다. (기본값은 false)
- 마지막 인수는 구분 기호입니다. 기본값은 슬래시(/)입니다.
라고 합니다.
forRoot 만들어보기
파악해보니 2, 3, 4에 대해서는 default 값을 사용하고 1은 사용자에게 받아야 합니다. Module을 등록할 때 위치 값을 조정하게 해봅니다.
// JsonDBService
import { Inject, Injectable } from '@nestjs/common';
import { JsonDB } from 'node-json-db';
@Injectable()
export class JsonDBService {
private db: JsonDB;
private fileName: string = '';
constructor(@Inject('DB_PATH') private dbPath: { path: string }) {
console.log('dbPath', dbPath);
}
}
그리고 공급을 받을 수 있는지 app.service.ts에서 확인 해 보기 위해 Module에 장착해줍니다.
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { JsonDBService } from './common/json-DB.service';
@Module({
imports: [],
controllers: [AppController],
providers: [
AppService,
JsonDBService,
// 공급!!
{
provide: 'DB_PATH',
useValue: {
path: './data',
},
},
],
})
export class AppModule {}
네, 아래 사진과 같이 정상적으로 공급 받는 것을 확인했습니다.
그럼 해당 부분에 대한 forRoot에 대한 메소드를 생성하여 공급할 수 있도록 합니다. 그리고 다른 Module에서도 DB_PATH를 공급할 수 있도록 Global Decorator로 처리합니다.
import { DynamicModule, Module } from '@nestjs/common';
@Global()
@Module({})
export class JsonDBModule {
static forRoot(path: { path: string }): DynamicModule {
return {
module: JsonDBModule,
providers: [
{
provide: 'DB_PATH',
useValue: path,
},
],
exports: ['DB_PATH'],
};
}
}
를 통해 JsonDBModule.forRoot를 통해 path에 대한 inject를 수행했습니다. 이제 AppModule에 장착하여 줍니다.
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { JsonDBModule } from './common/json-DB.module';
@Module({
imports: [JsonDBModule.forRoot({ path: '/data' })],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
forFeature 만들어보기
이제 가상의 Entity를 만들고 이것을 사용하기 위해 forFeature를 만들어보겠습니다.
static forFeature(entities: any): DynamicModule {
const providers = entities.map((value) => ({
provide: value.name,
useFactory: (path: { path: string }) => {
return new JsonDBService<typeof value>(path);
},
inject: ['DB_PATH'],
}));
return {
module: JsonDBModule,
providers,
exports: entities.map((entity) => entity.name),
};
}
공급되는 provide는 아직 타입등이 추상화되지 않았습니다. 일련의 테스트과정 이후 Interface등을 깔끔하게 나누어볼게요.
그리고 테스트를 하기 위해 필요한 UserEntity, BoardEntity등을 만들겠습니다.
src/entities/user.entity.ts
src/entities/board.entity.ts
export class UserEntity {
id: string
name: string;
age: number;
}
export class BoardEntity {
id: number;
userId: string;
title: string;
description: string;
}
그리고 User에 관한 resource를 생성하여 줍니다.
nest g res user
그리고 entity는 만들었기에 user/entites 에 위치한 폴더는 삭제합니다.
이제 forFeature를 장착해보겠습니다.
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { JsonDBModule } from '../common/json-DB.module';
import { UserEntity } from '../entites/user.entity';
import { BoardEntity } from '../entites/board.entity';
@Module({
imports: [JsonDBModule.forFeature([UserEntity, BoardEntity])],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
이상없이 App이 동작함을 확인합니다.
이후, UserService에서 필요한 사항을 주입합니다.
@Injectable()
export class UserService {
constructor(
@Inject(UserEntity.name)
private readonly userJsonDBService: JsonDBService<UserEntity>,
) {}
//...
}
그리고 json-db를 만들어야 하기에 JsonDBService에 대해 생성자에서 db를 만드는 것을 수행하겠습니다. any 타입은 추후 추상화를 통해 없앨 예정 입니다. forFeature에서도 entity인자를 넘겨 줍니다.
import { Inject, Injectable } from '@nestjs/common';
import { Config, JsonDB } from 'node-json-db';
@Injectable()
export class JsonDBService<T> {
private db: JsonDB;
private fileName: string = '';
constructor(
@Inject('DB_PATH') private dbPath: { path: string },
entity: any,
) {
this.db = new JsonDB(
new Config(`${dbPath.path}/${entity.name}`, true, true, '/'),
);
}
}
static forFeature(entities: any): DynamicModule {
const providers = entities.map((value) => ({
provide: value.name,
useFactory: (path: { path: string }) => {
return new JsonDBService<typeof value>(path, value);
},
inject: ['DB_PATH'],
}));
return {
module: JsonDBModule,
providers,
exports: entities.map((entity) => entity.name),
};
}
이를 통해 dbPath: data 인 곳에서 파일이 생성 되었습니다.
Type 추상화로 깔끔하게 하기
이 때 까지 쓰였던 any Type 회수와 forFeature에 대한 제약을 걸어보겠습니다.
아래 파일을 생성 하여 줍니다.
src/common/entity.type.ts
그리고 Entity에 대한 모음 파일을 아래에서 export 해 줍니다.
src/common/config/db.config.ts
//---
import { BoardEntity } from '../../entites/board.entity';
import { UserEntity } from '../../entites/user.entity';
export const DB_MODULES = [BoardEntity, UserEntity];
이제 entity.type.ts에서 추상화를 진행하겠습니다.
import { DB_MODULES } from '../type/entity.type';
export type Entity = typeof DB_MODULES[number];
Entity에 대한 type을 뽑았습니다.
이를 토대로
JsonDBModule의 forFeature에서 inputArgument Type을 수정해줍니다.
(entities: any) =>
---
(entities: Entity[])
// 그리고 useFactory에서 generic을 활용합니다.
useFactory: (path: { path: string }) => {
return new JsonDBService<typeof value>(path, value);
},
마지막으로 JsonDBService에서 entity:any를 수정해줍니다.
entity: any,
-->
entity: Entity,
테스트
마지막 테스트를 진행해보겠습니다.
UserService에서 User에 대해 저장하고 데이터를 확인하는 것을 진행합니다.
DBSerivce에 메소드에 대한 구현을 합니다.
async getData(key: string): Promise<T> {
return this.db.getData(key);
}
async saveData(key: string, data: T): Promise<void> {
await this.db.push(`/${key}`, data, true);
await this.db.save();
}
UserCreate 기능을 만들어 줍니다.
@Inject(UserEntity.name)
private readonly userJsonDBService: JsonDBService<UserEntity>,
...
createUser(){
const user = new UserEntity();
user.name = 'test';
user.age = 10;
this.userJsonDBService.saveData('user[]/', user);
}
테스트를 진행하여, 결과를 봅니다.
{
"user": [
{
"name": "test",
"age": 10
},
{
"name": "test",
"age": 10
}
]
}
잘 들어가는 것을 확인하였습니다.
출력도 확인을 해보겠습니다.
this.userJsonDBService.getData('user[]/').then((data) => {
console.log(data);
});
로 요청하여 아래 값을 받았습니다.
{ user: [ { name: 'test', age: 10 }, { name: 'test', age: 10 } ] }
각 필요한 부분에 interface를 만들어 추상화를 통하여 고도화를 진행할 수 있을 듯 합니다.
'Server 관련' 카테고리의 다른 글
S3 CloudFront에 의한 접근 정책 설정 (0) | 2023.06.21 |
---|---|
꺼진 Ec2도 다시보자. (0) | 2023.06.01 |
NestJs CacheModule 사용기 (0) | 2023.04.25 |
네트워크 CS ( OSI 7계층 / TCP,UDP,IP,패킷 / LAN ) (0) | 2023.03.19 |
[ Devops PT ] 클라우드, AWS의 다양한서비스, Terraform 관련 정리 (0) | 2022.08.28 |