0.1.5 • Published 5 months ago
@rstful/drizzle v0.1.5
Drizzle Module
이 모듈은 NestJS에서 Drizzle ORM을 사용하기 위한 동적 모듈입니다. 기본 Drizzle 기능과 더불어 CLS(Continuation Local Storage)와의 통합을 통한 고급 기능도 제공합니다.
설치
npm install drizzle-orm mysql2
# CLS 기능을 사용하려면 추가 패키지 설치
npm install nestjs-cls @nestjs-cls/transactional @nestjs-cls/transactional-adapter-drizzle-orm기본 사용법
1. 동기적 설정 (forRoot)
import { Module } from '@nestjs/common';
import { DrizzleModule } from '@rsnest/drizzle';
@Module({
imports: [
DrizzleModule.forRoot({
host: 'localhost',
port: 3306,
user: 'root',
password: 'password',
database: 'mydb',
connectTimeout: 30000,
}),
],
})
export class AppModule {}2. 비동기적 설정 (forRootAsync)
ConfigService를 사용하여 환경 변수에서 설정을 가져오는 경우:
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { DrizzleModule } from '@rsnest/drizzle';
@Module({
imports: [
ConfigModule.forRoot(),
DrizzleModule.forRootAsync({
useFactory: (configService: ConfigService) => ({
host: configService.get('DB_HOST'),
port: configService.get('DB_PORT'),
user: configService.get('DB_USER'),
password: configService.get('DB_PASSWORD'),
database: configService.get('DB_DATABASE'),
connectTimeout: 30000,
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}3. 서비스에서 사용
import { Injectable } from '@nestjs/common';
import { DrizzleService } from '@rsnest/drizzle';
@Injectable()
export class UserService {
constructor(private readonly drizzle: DrizzleService) {}
async getUsers() {
return this.drizzle.select().from(users);
}
}CLS (Continuation Local Storage) 통합
CLS 기능을 사용하면 다음과 같은 고급 기능을 이용할 수 있습니다:
- 자동 트랜잭션 관리:
@Transactional데코레이터를 통한 선언적 트랜잭션 - 요청 컨텍스트 관리: 요청 ID, 사용자 ID, 테넌트 ID 등의 자동 추적
- 멀티테넌시 지원: 테넌트별 데이터 분리
- 감사 로그: 자동 사용자 추적 및 메타데이터 생성
1. CLS 모듈 설정
import { Module } from '@nestjs/common';
import { DrizzleClsModule } from '@rsnest/drizzle';
@Module({
imports: [
DrizzleClsModule.forRoot({
host: 'localhost',
port: 3306,
user: 'root',
password: 'password',
database: 'mydb',
// CLS 특정 옵션
mountMiddleware: true, // 자동 미들웨어 마운트
enableTransactions: true, // 트랜잭션 기능 활성화
global: true, // 글로벌 모듈로 설정
}),
],
})
export class AppModule {}2. 트랜잭션 사용
데코레이터 방식
import { Injectable } from '@nestjs/common';
import { Transactional, DrizzleClsService } from '@rsnest/drizzle';
@Injectable()
export class UserService {
constructor(private readonly drizzleCls: DrizzleClsService) {}
@Transactional()
async createUserAndProfile(userData: any, profileData: any) {
// 트랜잭션 내에서 실행됨
const user = await this.drizzleCls.drizzle
.insert(users)
.values(userData)
.returning();
const profile = await this.drizzleCls.drizzle
.insert(profiles)
.values({ ...profileData, userId: user[0].id })
.returning();
// 에러가 발생하면 자동으로 롤백됨
if (Math.random() > 0.5) {
throw new Error('Random error for testing rollback');
}
return { user: user[0], profile: profile[0] };
}
}프로그래매틱 방식
import { Injectable } from '@nestjs/common';
import { DrizzleClsService } from '@rsnest/drizzle';
@Injectable()
export class UserService {
constructor(private readonly drizzleCls: DrizzleClsService) {}
async createUserAndProfile(userData: any, profileData: any) {
return this.drizzleCls.withTransaction(async () => {
const user = await this.drizzleCls.drizzle
.insert(users)
.values(userData)
.returning();
const profile = await this.drizzleCls.drizzle
.insert(profiles)
.values({ ...profileData, userId: user[0].id })
.returning();
return { user: user[0], profile: profile[0] };
});
}
}3. 컨텍스트 정보 활용
import { Injectable } from '@nestjs/common';
import { DrizzleClsService } from '@rsnest/drizzle';
@Injectable()
export class AuditService {
constructor(private readonly drizzleCls: DrizzleClsService) {}
async createAuditLog(action: string, entityId: string) {
const metadata = this.drizzleCls.getAuditMetadata();
return this.drizzleCls.drizzle
.insert(auditLogs)
.values({
action,
entityId,
userId: metadata.userId,
requestId: metadata.requestId,
timestamp: metadata.timestamp,
});
}
async getUserSpecificData() {
const userId = this.drizzleCls.getUserId();
const tenantId = this.drizzleCls.getTenantId();
// 현재 사용자의 데이터만 가져오기
return this.drizzleCls.drizzle
.select()
.from(userDocuments)
.where(and(
eq(userDocuments.userId, userId),
eq(userDocuments.tenantId, tenantId)
));
}
}4. 미들웨어를 통한 헤더 기반 컨텍스트 설정
CLS 모듈은 자동으로 다음 HTTP 헤더들을 CLS 컨텍스트에 저장합니다:
x-request-id: 요청 ID (없으면 자동 생성)x-user-id: 사용자 IDx-tenant-id: 테넌트 ID
# API 호출 예시
curl -H "x-user-id: user-123" \
-H "x-tenant-id: tenant-456" \
-H "x-request-id: req-789" \
http://localhost:3000/api/users5. 멀티테넌시 지원
import { Injectable } from '@nestjs/common';
import { DrizzleClsService } from '@rsnest/drizzle';
@Injectable()
export class MultiTenantService {
constructor(private readonly drizzleCls: DrizzleClsService) {}
async getTenantData() {
const schemaPrefix = this.drizzleCls.getSchemaPrefix();
const tenantId = this.drizzleCls.getTenantId();
// 테넌트별 데이터 조회
return this.drizzleCls.drizzle
.select()
.from(tenantData)
.where(eq(tenantData.tenantId, tenantId));
}
async getLogContext() {
// 로깅을 위한 전체 컨텍스트 정보
return this.drizzleCls.getLogContext();
// 결과: { requestId: '...', userId: '...', tenantId: '...', transactionActive: true }
}
}API 레퍼런스
DrizzleOptions
| 속성 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
| host | string | ✓ | - | 데이터베이스 호스트 |
| port | number | ✗ | 3306 | 데이터베이스 포트 |
| user | string | ✓ | - | 데이터베이스 사용자명 |
| password | string | ✓ | - | 데이터베이스 비밀번호 |
| database | string | ✓ | - | 데이터베이스 이름 |
| connectTimeout | number | ✗ | 30000 | 연결 타임아웃 (밀리초) |
| global | boolean | ✗ | false | 글로벌 모듈 여부 |
DrizzleClsOptions
DrizzleOptions의 모든 속성과 추가로:
| 속성 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
| mountMiddleware | boolean | ✗ | true | CLS 미들웨어 자동 마운트 |
| enableTransactions | boolean | ✗ | true | 트랜잭션 기능 활성화 |
| namespace | string | ✗ | 'drizzle' | CLS 네임스페이스 |
DrizzleClsService 메서드
컨텍스트 관리
getRequestId(): 현재 요청 ID 조회getUserId(): 현재 사용자 ID 조회getTenantId(): 현재 테넌트 ID 조회get<T>(key: string): 임의의 CLS 값 조회set<T>(key: string, value: T): 임의의 CLS 값 설정
트랜잭션 관리
isTransactionActive(): 트랜잭션 활성 상태 확인withTransaction<T>(callback): 트랜잭션 내에서 콜백 실행withoutTransaction<T>(callback): 트랜잭션 없이 콜백 실행drizzle: 현재 Drizzle 인스턴스 (트랜잭션 지원)
유틸리티
getLogContext(): 로깅용 컨텍스트 정보getSchemaPrefix(): 멀티테넌시용 스키마 접두사getAuditMetadata(): 감사 로그용 메타데이터
주의사항
- CLS 기능 사용 시: CLS 관련 패키지들이 설치되어 있어야 합니다.
- 트랜잭션 사용 시:
@Transactional데코레이터는 메서드에만 사용 가능합니다. - 컨텍스트 접근: CLS 컨텍스트는 요청 생명주기 내에서만 유효합니다.
- 멀티테넌시: 테넌트 ID는 HTTP 헤더 또는 수동으로 설정해야 합니다.
License
MIT