410 lines
11 KiB
Markdown
410 lines
11 KiB
Markdown
|
|
# 📋 Phase 3.15: Batch Services Raw Query 전환 계획
|
||
|
|
|
||
|
|
## 📋 개요
|
||
|
|
|
||
|
|
배치 관련 서비스들은 총 **24개의 Prisma 호출**이 있으며, 배치 작업 실행 및 관리를 담당합니다.
|
||
|
|
|
||
|
|
### 📊 기본 정보
|
||
|
|
|
||
|
|
| 항목 | 내용 |
|
||
|
|
| --------------- | -------------------------------------------------------------- |
|
||
|
|
| 대상 서비스 | 4개 (BatchExternalDb, ExecutionLog, Management, Scheduler) |
|
||
|
|
| 파일 위치 | `backend-node/src/services/batch*.ts` |
|
||
|
|
| 총 파일 크기 | 2,161 라인 |
|
||
|
|
| Prisma 호출 | 24개 |
|
||
|
|
| **현재 진행률** | **0/24 (0%)** 🔄 **진행 예정** |
|
||
|
|
| 복잡도 | 높음 (외부 DB 연동, 스케줄링, 트랜잭션) |
|
||
|
|
| 우선순위 | 🔴 높음 (Phase 3.15) |
|
||
|
|
| **상태** | ⏳ **대기 중** |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔍 서비스별 상세 분석
|
||
|
|
|
||
|
|
### 1. BatchExternalDbService (8개 호출, 943 라인)
|
||
|
|
|
||
|
|
**주요 기능**:
|
||
|
|
- 외부 DB에서 배치 데이터 조회
|
||
|
|
- 외부 DB로 배치 데이터 저장
|
||
|
|
- 외부 DB 연결 관리
|
||
|
|
- 데이터 변환 및 매핑
|
||
|
|
|
||
|
|
**예상 Prisma 호출**:
|
||
|
|
- `getExternalDbConnection()` - 외부 DB 연결 정보 조회
|
||
|
|
- `fetchDataFromExternalDb()` - 외부 DB 데이터 조회
|
||
|
|
- `saveDataToExternalDb()` - 외부 DB 데이터 저장
|
||
|
|
- `validateExternalDbConnection()` - 연결 검증
|
||
|
|
- `getExternalDbTables()` - 테이블 목록 조회
|
||
|
|
- `getExternalDbColumns()` - 컬럼 정보 조회
|
||
|
|
- `executeBatchQuery()` - 배치 쿼리 실행
|
||
|
|
- `getBatchExecutionStatus()` - 실행 상태 조회
|
||
|
|
|
||
|
|
**기술적 고려사항**:
|
||
|
|
- 다양한 DB 타입 지원 (PostgreSQL, MySQL, Oracle, MSSQL)
|
||
|
|
- 연결 풀 관리
|
||
|
|
- 트랜잭션 처리
|
||
|
|
- 에러 핸들링 및 재시도
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2. BatchExecutionLogService (7개 호출, 299 라인)
|
||
|
|
|
||
|
|
**주요 기능**:
|
||
|
|
- 배치 실행 로그 생성
|
||
|
|
- 배치 실행 이력 조회
|
||
|
|
- 배치 실행 통계
|
||
|
|
- 로그 정리
|
||
|
|
|
||
|
|
**예상 Prisma 호출**:
|
||
|
|
- `createExecutionLog()` - 실행 로그 생성
|
||
|
|
- `updateExecutionLog()` - 실행 로그 업데이트
|
||
|
|
- `getExecutionLogs()` - 실행 로그 목록 조회
|
||
|
|
- `getExecutionLogById()` - 실행 로그 단건 조회
|
||
|
|
- `getExecutionStats()` - 실행 통계 조회
|
||
|
|
- `cleanupOldLogs()` - 오래된 로그 삭제
|
||
|
|
- `getFailedExecutions()` - 실패한 실행 조회
|
||
|
|
|
||
|
|
**기술적 고려사항**:
|
||
|
|
- 대용량 로그 처리
|
||
|
|
- 통계 쿼리 최적화
|
||
|
|
- 로그 보관 정책
|
||
|
|
- 페이징 및 필터링
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3. BatchManagementService (5개 호출, 373 라인)
|
||
|
|
|
||
|
|
**주요 기능**:
|
||
|
|
- 배치 작업 설정 관리
|
||
|
|
- 배치 작업 실행
|
||
|
|
- 배치 작업 중지
|
||
|
|
- 배치 작업 모니터링
|
||
|
|
|
||
|
|
**예상 Prisma 호출**:
|
||
|
|
- `getBatchJobs()` - 배치 작업 목록 조회
|
||
|
|
- `getBatchJob()` - 배치 작업 단건 조회
|
||
|
|
- `createBatchJob()` - 배치 작업 생성
|
||
|
|
- `updateBatchJob()` - 배치 작업 수정
|
||
|
|
- `deleteBatchJob()` - 배치 작업 삭제
|
||
|
|
|
||
|
|
**기술적 고려사항**:
|
||
|
|
- JSON 설정 필드 (job_config)
|
||
|
|
- 작업 상태 관리
|
||
|
|
- 동시 실행 제어
|
||
|
|
- 의존성 관리
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 4. BatchSchedulerService (4개 호출, 546 라인)
|
||
|
|
|
||
|
|
**주요 기능**:
|
||
|
|
- 배치 스케줄 설정
|
||
|
|
- Cron 표현식 관리
|
||
|
|
- 스케줄 실행
|
||
|
|
- 다음 실행 시간 계산
|
||
|
|
|
||
|
|
**예상 Prisma 호출**:
|
||
|
|
- `getScheduledBatches()` - 스케줄된 배치 조회
|
||
|
|
- `createSchedule()` - 스케줄 생성
|
||
|
|
- `updateSchedule()` - 스케줄 수정
|
||
|
|
- `deleteSchedule()` - 스케줄 삭제
|
||
|
|
|
||
|
|
**기술적 고려사항**:
|
||
|
|
- Cron 표현식 파싱
|
||
|
|
- 시간대 처리
|
||
|
|
- 실행 이력 추적
|
||
|
|
- 스케줄 충돌 방지
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 💡 통합 전환 전략
|
||
|
|
|
||
|
|
### Phase 1: 핵심 서비스 전환 (12개)
|
||
|
|
**BatchManagementService (5개) + BatchExecutionLogService (7개)**
|
||
|
|
- 배치 관리 및 로깅 기능 우선
|
||
|
|
- 상대적으로 단순한 CRUD
|
||
|
|
|
||
|
|
### Phase 2: 스케줄러 전환 (4개)
|
||
|
|
**BatchSchedulerService (4개)**
|
||
|
|
- 스케줄 관리
|
||
|
|
- Cron 표현식 처리
|
||
|
|
|
||
|
|
### Phase 3: 외부 DB 연동 전환 (8개)
|
||
|
|
**BatchExternalDbService (8개)**
|
||
|
|
- 가장 복잡한 서비스
|
||
|
|
- 외부 DB 연결 및 쿼리
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 💻 전환 예시
|
||
|
|
|
||
|
|
### 예시 1: 배치 실행 로그 생성
|
||
|
|
|
||
|
|
**변경 전**:
|
||
|
|
```typescript
|
||
|
|
const log = await prisma.batch_execution_logs.create({
|
||
|
|
data: {
|
||
|
|
batch_id: batchId,
|
||
|
|
status: "running",
|
||
|
|
started_at: new Date(),
|
||
|
|
execution_params: params,
|
||
|
|
company_code: companyCode,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
**변경 후**:
|
||
|
|
```typescript
|
||
|
|
const log = await queryOne<any>(
|
||
|
|
`INSERT INTO batch_execution_logs
|
||
|
|
(batch_id, status, started_at, execution_params, company_code)
|
||
|
|
VALUES ($1, $2, NOW(), $3, $4)
|
||
|
|
RETURNING *`,
|
||
|
|
[batchId, "running", JSON.stringify(params), companyCode]
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 예시 2: 배치 통계 조회
|
||
|
|
|
||
|
|
**변경 전**:
|
||
|
|
```typescript
|
||
|
|
const stats = await prisma.batch_execution_logs.groupBy({
|
||
|
|
by: ["status"],
|
||
|
|
where: {
|
||
|
|
batch_id: batchId,
|
||
|
|
started_at: { gte: startDate, lte: endDate },
|
||
|
|
},
|
||
|
|
_count: { id: true },
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
**변경 후**:
|
||
|
|
```typescript
|
||
|
|
const stats = await query<{ status: string; count: string }>(
|
||
|
|
`SELECT status, COUNT(*) as count
|
||
|
|
FROM batch_execution_logs
|
||
|
|
WHERE batch_id = $1
|
||
|
|
AND started_at >= $2
|
||
|
|
AND started_at <= $3
|
||
|
|
GROUP BY status`,
|
||
|
|
[batchId, startDate, endDate]
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 예시 3: 외부 DB 연결 및 쿼리
|
||
|
|
|
||
|
|
**변경 전**:
|
||
|
|
```typescript
|
||
|
|
// 연결 정보 조회
|
||
|
|
const connection = await prisma.external_db_connections.findUnique({
|
||
|
|
where: { id: connectionId },
|
||
|
|
});
|
||
|
|
|
||
|
|
// 외부 DB 쿼리 실행 (Prisma 사용 불가, 이미 Raw Query일 가능성)
|
||
|
|
const externalData = await externalDbClient.query(sql);
|
||
|
|
```
|
||
|
|
|
||
|
|
**변경 후**:
|
||
|
|
```typescript
|
||
|
|
// 연결 정보 조회
|
||
|
|
const connection = await queryOne<any>(
|
||
|
|
`SELECT * FROM external_db_connections WHERE id = $1`,
|
||
|
|
[connectionId]
|
||
|
|
);
|
||
|
|
|
||
|
|
// 외부 DB 쿼리 실행 (기존 로직 유지)
|
||
|
|
const externalData = await externalDbClient.query(sql);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 예시 4: 스케줄 관리
|
||
|
|
|
||
|
|
**변경 전**:
|
||
|
|
```typescript
|
||
|
|
const schedule = await prisma.batch_schedules.create({
|
||
|
|
data: {
|
||
|
|
batch_id: batchId,
|
||
|
|
cron_expression: cronExp,
|
||
|
|
is_active: true,
|
||
|
|
next_run_at: calculateNextRun(cronExp),
|
||
|
|
},
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
**변경 후**:
|
||
|
|
```typescript
|
||
|
|
const nextRun = calculateNextRun(cronExp);
|
||
|
|
|
||
|
|
const schedule = await queryOne<any>(
|
||
|
|
`INSERT INTO batch_schedules
|
||
|
|
(batch_id, cron_expression, is_active, next_run_at, created_at, updated_at)
|
||
|
|
VALUES ($1, $2, $3, $4, NOW(), NOW())
|
||
|
|
RETURNING *`,
|
||
|
|
[batchId, cronExp, true, nextRun]
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔧 기술적 고려사항
|
||
|
|
|
||
|
|
### 1. 외부 DB 연결 관리
|
||
|
|
```typescript
|
||
|
|
import { DatabaseConnectorFactory } from "../database/connectorFactory";
|
||
|
|
|
||
|
|
// 외부 DB 연결 생성
|
||
|
|
const connector = DatabaseConnectorFactory.create(connection);
|
||
|
|
const externalClient = await connector.connect();
|
||
|
|
|
||
|
|
try {
|
||
|
|
// 쿼리 실행
|
||
|
|
const result = await externalClient.query(sql, params);
|
||
|
|
} finally {
|
||
|
|
await connector.disconnect();
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. 트랜잭션 처리
|
||
|
|
```typescript
|
||
|
|
await transaction(async (client) => {
|
||
|
|
// 배치 상태 업데이트
|
||
|
|
await client.query(
|
||
|
|
`UPDATE batch_jobs SET status = $1 WHERE id = $2`,
|
||
|
|
["running", batchId]
|
||
|
|
);
|
||
|
|
|
||
|
|
// 실행 로그 생성
|
||
|
|
await client.query(
|
||
|
|
`INSERT INTO batch_execution_logs (batch_id, status, started_at)
|
||
|
|
VALUES ($1, $2, NOW())`,
|
||
|
|
[batchId, "running"]
|
||
|
|
);
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Cron 표현식 처리
|
||
|
|
```typescript
|
||
|
|
import cron from "node-cron";
|
||
|
|
|
||
|
|
// Cron 표현식 검증
|
||
|
|
const isValid = cron.validate(cronExpression);
|
||
|
|
|
||
|
|
// 다음 실행 시간 계산
|
||
|
|
function calculateNextRun(cronExp: string): Date {
|
||
|
|
// Cron 파서를 사용하여 다음 실행 시간 계산
|
||
|
|
// ...
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. 대용량 데이터 처리
|
||
|
|
```typescript
|
||
|
|
// 스트리밍 방식으로 대용량 데이터 처리
|
||
|
|
const stream = await query<any>(
|
||
|
|
`SELECT * FROM large_table WHERE batch_id = $1`,
|
||
|
|
[batchId]
|
||
|
|
);
|
||
|
|
|
||
|
|
for await (const row of stream) {
|
||
|
|
// 행 단위 처리
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📝 전환 체크리스트
|
||
|
|
|
||
|
|
### BatchExternalDbService (8개)
|
||
|
|
- [ ] `getExternalDbConnection()` - 연결 정보 조회
|
||
|
|
- [ ] `fetchDataFromExternalDb()` - 외부 DB 데이터 조회
|
||
|
|
- [ ] `saveDataToExternalDb()` - 외부 DB 데이터 저장
|
||
|
|
- [ ] `validateExternalDbConnection()` - 연결 검증
|
||
|
|
- [ ] `getExternalDbTables()` - 테이블 목록 조회
|
||
|
|
- [ ] `getExternalDbColumns()` - 컬럼 정보 조회
|
||
|
|
- [ ] `executeBatchQuery()` - 배치 쿼리 실행
|
||
|
|
- [ ] `getBatchExecutionStatus()` - 실행 상태 조회
|
||
|
|
|
||
|
|
### BatchExecutionLogService (7개)
|
||
|
|
- [ ] `createExecutionLog()` - 실행 로그 생성
|
||
|
|
- [ ] `updateExecutionLog()` - 실행 로그 업데이트
|
||
|
|
- [ ] `getExecutionLogs()` - 실행 로그 목록 조회
|
||
|
|
- [ ] `getExecutionLogById()` - 실행 로그 단건 조회
|
||
|
|
- [ ] `getExecutionStats()` - 실행 통계 조회
|
||
|
|
- [ ] `cleanupOldLogs()` - 오래된 로그 삭제
|
||
|
|
- [ ] `getFailedExecutions()` - 실패한 실행 조회
|
||
|
|
|
||
|
|
### BatchManagementService (5개)
|
||
|
|
- [ ] `getBatchJobs()` - 배치 작업 목록 조회
|
||
|
|
- [ ] `getBatchJob()` - 배치 작업 단건 조회
|
||
|
|
- [ ] `createBatchJob()` - 배치 작업 생성
|
||
|
|
- [ ] `updateBatchJob()` - 배치 작업 수정
|
||
|
|
- [ ] `deleteBatchJob()` - 배치 작업 삭제
|
||
|
|
|
||
|
|
### BatchSchedulerService (4개)
|
||
|
|
- [ ] `getScheduledBatches()` - 스케줄된 배치 조회
|
||
|
|
- [ ] `createSchedule()` - 스케줄 생성
|
||
|
|
- [ ] `updateSchedule()` - 스케줄 수정
|
||
|
|
- [ ] `deleteSchedule()` - 스케줄 삭제
|
||
|
|
|
||
|
|
### 공통 작업
|
||
|
|
- [ ] import 문 수정 (모든 서비스)
|
||
|
|
- [ ] Prisma import 완전 제거 (모든 서비스)
|
||
|
|
- [ ] 트랜잭션 로직 확인
|
||
|
|
- [ ] 에러 핸들링 검증
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🧪 테스트 계획
|
||
|
|
|
||
|
|
### 단위 테스트 (24개)
|
||
|
|
- 각 Prisma 호출별 1개씩
|
||
|
|
|
||
|
|
### 통합 테스트 (8개)
|
||
|
|
- BatchExternalDbService: 외부 DB 연동 테스트 (2개)
|
||
|
|
- BatchExecutionLogService: 로그 생성 및 조회 테스트 (2개)
|
||
|
|
- BatchManagementService: 배치 작업 실행 테스트 (2개)
|
||
|
|
- BatchSchedulerService: 스케줄 실행 테스트 (2개)
|
||
|
|
|
||
|
|
### 성능 테스트
|
||
|
|
- 대용량 데이터 처리 성능
|
||
|
|
- 동시 배치 실행 성능
|
||
|
|
- 외부 DB 연결 풀 성능
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🎯 예상 난이도 및 소요 시간
|
||
|
|
|
||
|
|
- **난이도**: ⭐⭐⭐⭐⭐ (매우 높음)
|
||
|
|
- 외부 DB 연동
|
||
|
|
- 트랜잭션 처리
|
||
|
|
- 스케줄링 로직
|
||
|
|
- 대용량 데이터 처리
|
||
|
|
|
||
|
|
- **예상 소요 시간**: 4~5시간
|
||
|
|
- Phase 1 (BatchManagement + ExecutionLog): 1.5시간
|
||
|
|
- Phase 2 (Scheduler): 1시간
|
||
|
|
- Phase 3 (ExternalDb): 2시간
|
||
|
|
- 테스트 및 문서화: 0.5시간
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ⚠️ 주의사항
|
||
|
|
|
||
|
|
### 중요 체크포인트
|
||
|
|
1. ✅ 외부 DB 연결은 반드시 try-finally에서 해제
|
||
|
|
2. ✅ 배치 실행 중 에러 시 롤백 처리
|
||
|
|
3. ✅ Cron 표현식 검증 필수
|
||
|
|
4. ✅ 대용량 데이터는 스트리밍 방식 사용
|
||
|
|
5. ✅ 동시 실행 제한 확인
|
||
|
|
|
||
|
|
### 성능 최적화
|
||
|
|
- 연결 풀 활용
|
||
|
|
- 배치 쿼리 최적화
|
||
|
|
- 인덱스 확인
|
||
|
|
- 불필요한 로그 제거
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**상태**: ⏳ **대기 중**
|
||
|
|
**특이사항**: 외부 DB 연동, 스케줄링, 트랜잭션 처리 포함
|
||
|
|
**⚠️ 주의**: 배치 시스템의 핵심 기능이므로 신중한 테스트 필수!
|
||
|
|
|