docs: Phase 3.15~3.16 통합 마이그레이션 계획서 작성
2개 주요 서비스 그룹에 대한 통합 전환 계획서 작성: 1. **Phase 3.15: Batch Services** (24개 호출) - 4개 배치 관련 서비스 통합 계획 - BatchExternalDbService (8개) - 외부 DB 연동 - BatchExecutionLogService (7개) - 실행 로그 - BatchManagementService (5개) - 배치 관리 - BatchSchedulerService (4개) - 스케줄러 주요 기술 요소: - 외부 DB 연결 및 쿼리 - 트랜잭션 처리 - Cron 표현식 스케줄링 - 대용량 데이터 처리 - 연결 풀 관리 2. **Phase 3.16: Data Management Services** (18개 호출) - 4개 데이터 관리 서비스 통합 계획 - EnhancedDynamicFormService (6개) - 고급 동적 폼 - DataMappingService (5개) - 데이터 매핑 - DataService (4개) - 동적 데이터 조회 - AdminService (3개) - 관리자 기능 주요 기술 요소: - 복잡한 JSON 필드 처리 - 동적 테이블 쿼리 (보안) - 재귀 CTE (계층 구조) - JSON 집계 쿼리 - SQL 인젝션 방지 각 통합 계획서 포함 내용: - 서비스별 상세 분석 - 통합 전환 전략 (Phase별) - 상세 전환 예시 (Before/After) - 기술적 고려사항 - 서비스별 체크리스트 - 통합 테스트 계획 - 예상 난이도 및 소요 시간 - 보안/성능 주의사항 메인 문서에 통합 계획서 링크 추가 서비스 그룹화로 가독성 향상
This commit is contained in:
parent
ce37626e49
commit
67b45ea699
|
|
@ -0,0 +1,409 @@
|
||||||
|
# 📋 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 연동, 스케줄링, 트랜잭션 처리 포함
|
||||||
|
**⚠️ 주의**: 배치 시스템의 핵심 기능이므로 신중한 테스트 필수!
|
||||||
|
|
||||||
|
|
@ -0,0 +1,438 @@
|
||||||
|
# 📋 Phase 3.16: Data Management Services Raw Query 전환 계획
|
||||||
|
|
||||||
|
## 📋 개요
|
||||||
|
|
||||||
|
데이터 관리 관련 서비스들은 총 **18개의 Prisma 호출**이 있으며, 동적 폼, 데이터 매핑, 데이터 서비스, 관리자 기능을 담당합니다.
|
||||||
|
|
||||||
|
### 📊 기본 정보
|
||||||
|
|
||||||
|
| 항목 | 내용 |
|
||||||
|
| --------------- | ------------------------------------------------------------------------- |
|
||||||
|
| 대상 서비스 | 4개 (EnhancedDynamicForm, DataMapping, Data, Admin) |
|
||||||
|
| 파일 위치 | `backend-node/src/services/{enhanced,data,admin}*.ts` |
|
||||||
|
| 총 파일 크기 | 2,062 라인 |
|
||||||
|
| Prisma 호출 | 18개 |
|
||||||
|
| **현재 진행률** | **0/18 (0%)** 🔄 **진행 예정** |
|
||||||
|
| 복잡도 | 중간 (동적 쿼리, JSON 필드, 관리자 기능) |
|
||||||
|
| 우선순위 | 🟡 중간 (Phase 3.16) |
|
||||||
|
| **상태** | ⏳ **대기 중** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 서비스별 상세 분석
|
||||||
|
|
||||||
|
### 1. EnhancedDynamicFormService (6개 호출, 786 라인)
|
||||||
|
|
||||||
|
**주요 기능**:
|
||||||
|
- 고급 동적 폼 관리
|
||||||
|
- 폼 검증 규칙
|
||||||
|
- 조건부 필드 표시
|
||||||
|
- 폼 템플릿 관리
|
||||||
|
|
||||||
|
**예상 Prisma 호출**:
|
||||||
|
- `getEnhancedForms()` - 고급 폼 목록 조회
|
||||||
|
- `getEnhancedForm()` - 고급 폼 단건 조회
|
||||||
|
- `createEnhancedForm()` - 고급 폼 생성
|
||||||
|
- `updateEnhancedForm()` - 고급 폼 수정
|
||||||
|
- `deleteEnhancedForm()` - 고급 폼 삭제
|
||||||
|
- `getFormValidationRules()` - 검증 규칙 조회
|
||||||
|
|
||||||
|
**기술적 고려사항**:
|
||||||
|
- JSON 필드 (validation_rules, conditional_logic, field_config)
|
||||||
|
- 복잡한 검증 규칙
|
||||||
|
- 동적 필드 생성
|
||||||
|
- 조건부 표시 로직
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. DataMappingService (5개 호출, 575 라인)
|
||||||
|
|
||||||
|
**주요 기능**:
|
||||||
|
- 데이터 매핑 설정 관리
|
||||||
|
- 소스-타겟 필드 매핑
|
||||||
|
- 데이터 변환 규칙
|
||||||
|
- 매핑 실행
|
||||||
|
|
||||||
|
**예상 Prisma 호출**:
|
||||||
|
- `getDataMappings()` - 매핑 설정 목록 조회
|
||||||
|
- `getDataMapping()` - 매핑 설정 단건 조회
|
||||||
|
- `createDataMapping()` - 매핑 설정 생성
|
||||||
|
- `updateDataMapping()` - 매핑 설정 수정
|
||||||
|
- `deleteDataMapping()` - 매핑 설정 삭제
|
||||||
|
|
||||||
|
**기술적 고려사항**:
|
||||||
|
- JSON 필드 (field_mappings, transformation_rules)
|
||||||
|
- 복잡한 변환 로직
|
||||||
|
- 매핑 검증
|
||||||
|
- 실행 이력 추적
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. DataService (4개 호출, 327 라인)
|
||||||
|
|
||||||
|
**주요 기능**:
|
||||||
|
- 동적 데이터 조회
|
||||||
|
- 데이터 필터링
|
||||||
|
- 데이터 정렬
|
||||||
|
- 데이터 집계
|
||||||
|
|
||||||
|
**예상 Prisma 호출**:
|
||||||
|
- `getDataByTable()` - 테이블별 데이터 조회
|
||||||
|
- `getDataById()` - 데이터 단건 조회
|
||||||
|
- `executeCustomQuery()` - 커스텀 쿼리 실행
|
||||||
|
- `getDataStatistics()` - 데이터 통계 조회
|
||||||
|
|
||||||
|
**기술적 고려사항**:
|
||||||
|
- 동적 테이블 쿼리
|
||||||
|
- SQL 인젝션 방지
|
||||||
|
- 동적 WHERE 조건
|
||||||
|
- 집계 쿼리
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. AdminService (3개 호출, 374 라인)
|
||||||
|
|
||||||
|
**주요 기능**:
|
||||||
|
- 관리자 메뉴 관리
|
||||||
|
- 시스템 설정
|
||||||
|
- 사용자 관리
|
||||||
|
- 로그 조회
|
||||||
|
|
||||||
|
**예상 Prisma 호출**:
|
||||||
|
- `getAdminMenus()` - 관리자 메뉴 조회
|
||||||
|
- `getSystemSettings()` - 시스템 설정 조회
|
||||||
|
- `updateSystemSettings()` - 시스템 설정 업데이트
|
||||||
|
|
||||||
|
**기술적 고려사항**:
|
||||||
|
- 메뉴 계층 구조
|
||||||
|
- 권한 기반 필터링
|
||||||
|
- JSON 설정 필드
|
||||||
|
- 캐싱
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 통합 전환 전략
|
||||||
|
|
||||||
|
### Phase 1: 단순 CRUD 전환 (12개)
|
||||||
|
**EnhancedDynamicFormService (6개) + DataMappingService (5개) + AdminService (1개)**
|
||||||
|
- 기본 CRUD 기능
|
||||||
|
- JSON 필드 처리
|
||||||
|
|
||||||
|
### Phase 2: 동적 쿼리 전환 (4개)
|
||||||
|
**DataService (4개)**
|
||||||
|
- 동적 테이블 쿼리
|
||||||
|
- 보안 검증
|
||||||
|
|
||||||
|
### Phase 3: 고급 기능 전환 (2개)
|
||||||
|
**AdminService (2개)**
|
||||||
|
- 시스템 설정
|
||||||
|
- 캐싱
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💻 전환 예시
|
||||||
|
|
||||||
|
### 예시 1: 고급 폼 생성 (JSON 필드)
|
||||||
|
|
||||||
|
**변경 전**:
|
||||||
|
```typescript
|
||||||
|
const form = await prisma.enhanced_forms.create({
|
||||||
|
data: {
|
||||||
|
form_code: formCode,
|
||||||
|
form_name: formName,
|
||||||
|
validation_rules: validationRules, // JSON
|
||||||
|
conditional_logic: conditionalLogic, // JSON
|
||||||
|
field_config: fieldConfig, // JSON
|
||||||
|
company_code: companyCode,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**변경 후**:
|
||||||
|
```typescript
|
||||||
|
const form = await queryOne<any>(
|
||||||
|
`INSERT INTO enhanced_forms
|
||||||
|
(form_code, form_name, validation_rules, conditional_logic,
|
||||||
|
field_config, company_code, created_at, updated_at)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW())
|
||||||
|
RETURNING *`,
|
||||||
|
[
|
||||||
|
formCode,
|
||||||
|
formName,
|
||||||
|
JSON.stringify(validationRules),
|
||||||
|
JSON.stringify(conditionalLogic),
|
||||||
|
JSON.stringify(fieldConfig),
|
||||||
|
companyCode,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 예시 2: 데이터 매핑 조회
|
||||||
|
|
||||||
|
**변경 전**:
|
||||||
|
```typescript
|
||||||
|
const mappings = await prisma.data_mappings.findMany({
|
||||||
|
where: {
|
||||||
|
source_table: sourceTable,
|
||||||
|
target_table: targetTable,
|
||||||
|
is_active: true,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
source_columns: true,
|
||||||
|
target_columns: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**변경 후**:
|
||||||
|
```typescript
|
||||||
|
const mappings = await query<any>(
|
||||||
|
`SELECT
|
||||||
|
dm.*,
|
||||||
|
json_agg(DISTINCT jsonb_build_object(
|
||||||
|
'column_id', sc.column_id,
|
||||||
|
'column_name', sc.column_name
|
||||||
|
)) FILTER (WHERE sc.column_id IS NOT NULL) as source_columns,
|
||||||
|
json_agg(DISTINCT jsonb_build_object(
|
||||||
|
'column_id', tc.column_id,
|
||||||
|
'column_name', tc.column_name
|
||||||
|
)) FILTER (WHERE tc.column_id IS NOT NULL) as target_columns
|
||||||
|
FROM data_mappings dm
|
||||||
|
LEFT JOIN columns sc ON dm.mapping_id = sc.mapping_id AND sc.type = 'source'
|
||||||
|
LEFT JOIN columns tc ON dm.mapping_id = tc.mapping_id AND tc.type = 'target'
|
||||||
|
WHERE dm.source_table = $1
|
||||||
|
AND dm.target_table = $2
|
||||||
|
AND dm.is_active = $3
|
||||||
|
GROUP BY dm.mapping_id`,
|
||||||
|
[sourceTable, targetTable, true]
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 예시 3: 동적 테이블 쿼리 (DataService)
|
||||||
|
|
||||||
|
**변경 전**:
|
||||||
|
```typescript
|
||||||
|
// Prisma로는 동적 테이블 쿼리 불가능
|
||||||
|
// 이미 $queryRawUnsafe 사용 중일 가능성
|
||||||
|
const data = await prisma.$queryRawUnsafe(
|
||||||
|
`SELECT * FROM ${tableName} WHERE ${whereClause}`,
|
||||||
|
...params
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**변경 후**:
|
||||||
|
```typescript
|
||||||
|
// SQL 인젝션 방지를 위한 테이블명 검증
|
||||||
|
const validTableName = validateTableName(tableName);
|
||||||
|
|
||||||
|
const data = await query<any>(
|
||||||
|
`SELECT * FROM ${validTableName} WHERE ${whereClause}`,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 예시 4: 관리자 메뉴 조회 (계층 구조)
|
||||||
|
|
||||||
|
**변경 전**:
|
||||||
|
```typescript
|
||||||
|
const menus = await prisma.admin_menus.findMany({
|
||||||
|
where: { is_active: true },
|
||||||
|
orderBy: { sort_order: "asc" },
|
||||||
|
include: {
|
||||||
|
children: {
|
||||||
|
orderBy: { sort_order: "asc" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**변경 후**:
|
||||||
|
```typescript
|
||||||
|
// 재귀 CTE를 사용한 계층 쿼리
|
||||||
|
const menus = await query<any>(
|
||||||
|
`WITH RECURSIVE menu_tree AS (
|
||||||
|
SELECT *, 0 as level, ARRAY[menu_id] as path
|
||||||
|
FROM admin_menus
|
||||||
|
WHERE parent_id IS NULL AND is_active = $1
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT m.*, mt.level + 1, mt.path || m.menu_id
|
||||||
|
FROM admin_menus m
|
||||||
|
JOIN menu_tree mt ON m.parent_id = mt.menu_id
|
||||||
|
WHERE m.is_active = $1
|
||||||
|
)
|
||||||
|
SELECT * FROM menu_tree
|
||||||
|
ORDER BY path, sort_order`,
|
||||||
|
[true]
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 기술적 고려사항
|
||||||
|
|
||||||
|
### 1. JSON 필드 처리
|
||||||
|
```typescript
|
||||||
|
// 복잡한 JSON 구조
|
||||||
|
interface ValidationRules {
|
||||||
|
required?: string[];
|
||||||
|
min?: Record<string, number>;
|
||||||
|
max?: Record<string, number>;
|
||||||
|
pattern?: Record<string, string>;
|
||||||
|
custom?: Array<{ field: string; rule: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 저장 시
|
||||||
|
JSON.stringify(validationRules);
|
||||||
|
|
||||||
|
// 조회 후
|
||||||
|
const parsed = typeof row.validation_rules === "string"
|
||||||
|
? JSON.parse(row.validation_rules)
|
||||||
|
: row.validation_rules;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 동적 테이블 쿼리 보안
|
||||||
|
```typescript
|
||||||
|
// 테이블명 화이트리스트
|
||||||
|
const ALLOWED_TABLES = ["users", "products", "orders"];
|
||||||
|
|
||||||
|
function validateTableName(tableName: string): string {
|
||||||
|
if (!ALLOWED_TABLES.includes(tableName)) {
|
||||||
|
throw new Error("Invalid table name");
|
||||||
|
}
|
||||||
|
return tableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 컬럼명 검증
|
||||||
|
function validateColumnName(columnName: string): string {
|
||||||
|
if (!/^[a-z_][a-z0-9_]*$/i.test(columnName)) {
|
||||||
|
throw new Error("Invalid column name");
|
||||||
|
}
|
||||||
|
return columnName;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 재귀 CTE (계층 구조)
|
||||||
|
```sql
|
||||||
|
WITH RECURSIVE hierarchy AS (
|
||||||
|
-- 최상위 노드
|
||||||
|
SELECT * FROM table WHERE parent_id IS NULL
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
-- 하위 노드
|
||||||
|
SELECT t.* FROM table t
|
||||||
|
JOIN hierarchy h ON t.parent_id = h.id
|
||||||
|
)
|
||||||
|
SELECT * FROM hierarchy
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. JSON 집계 (관계 데이터)
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
parent.*,
|
||||||
|
COALESCE(
|
||||||
|
json_agg(
|
||||||
|
jsonb_build_object('id', child.id, 'name', child.name)
|
||||||
|
) FILTER (WHERE child.id IS NOT NULL),
|
||||||
|
'[]'
|
||||||
|
) as children
|
||||||
|
FROM parent
|
||||||
|
LEFT JOIN child ON parent.id = child.parent_id
|
||||||
|
GROUP BY parent.id
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 전환 체크리스트
|
||||||
|
|
||||||
|
### EnhancedDynamicFormService (6개)
|
||||||
|
- [ ] `getEnhancedForms()` - 목록 조회
|
||||||
|
- [ ] `getEnhancedForm()` - 단건 조회
|
||||||
|
- [ ] `createEnhancedForm()` - 생성 (JSON 필드)
|
||||||
|
- [ ] `updateEnhancedForm()` - 수정 (JSON 필드)
|
||||||
|
- [ ] `deleteEnhancedForm()` - 삭제
|
||||||
|
- [ ] `getFormValidationRules()` - 검증 규칙 조회
|
||||||
|
|
||||||
|
### DataMappingService (5개)
|
||||||
|
- [ ] `getDataMappings()` - 목록 조회
|
||||||
|
- [ ] `getDataMapping()` - 단건 조회
|
||||||
|
- [ ] `createDataMapping()` - 생성
|
||||||
|
- [ ] `updateDataMapping()` - 수정
|
||||||
|
- [ ] `deleteDataMapping()` - 삭제
|
||||||
|
|
||||||
|
### DataService (4개)
|
||||||
|
- [ ] `getDataByTable()` - 동적 테이블 조회
|
||||||
|
- [ ] `getDataById()` - 단건 조회
|
||||||
|
- [ ] `executeCustomQuery()` - 커스텀 쿼리
|
||||||
|
- [ ] `getDataStatistics()` - 통계 조회
|
||||||
|
|
||||||
|
### AdminService (3개)
|
||||||
|
- [ ] `getAdminMenus()` - 메뉴 조회 (재귀 CTE)
|
||||||
|
- [ ] `getSystemSettings()` - 시스템 설정 조회
|
||||||
|
- [ ] `updateSystemSettings()` - 시스템 설정 업데이트
|
||||||
|
|
||||||
|
### 공통 작업
|
||||||
|
- [ ] import 문 수정 (모든 서비스)
|
||||||
|
- [ ] Prisma import 완전 제거
|
||||||
|
- [ ] JSON 필드 처리 확인
|
||||||
|
- [ ] 보안 검증 (SQL 인젝션)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 테스트 계획
|
||||||
|
|
||||||
|
### 단위 테스트 (18개)
|
||||||
|
- 각 Prisma 호출별 1개씩
|
||||||
|
|
||||||
|
### 통합 테스트 (6개)
|
||||||
|
- EnhancedDynamicFormService: 폼 생성 및 검증 테스트 (2개)
|
||||||
|
- DataMappingService: 매핑 설정 및 실행 테스트 (2개)
|
||||||
|
- DataService: 동적 쿼리 및 보안 테스트 (1개)
|
||||||
|
- AdminService: 메뉴 계층 구조 테스트 (1개)
|
||||||
|
|
||||||
|
### 보안 테스트
|
||||||
|
- SQL 인젝션 방지 테스트
|
||||||
|
- 테이블명 검증 테스트
|
||||||
|
- 컬럼명 검증 테스트
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 예상 난이도 및 소요 시간
|
||||||
|
|
||||||
|
- **난이도**: ⭐⭐⭐⭐ (높음)
|
||||||
|
- JSON 필드 처리
|
||||||
|
- 동적 쿼리 보안
|
||||||
|
- 재귀 CTE
|
||||||
|
- JSON 집계
|
||||||
|
|
||||||
|
- **예상 소요 시간**: 2.5~3시간
|
||||||
|
- Phase 1 (기본 CRUD): 1시간
|
||||||
|
- Phase 2 (동적 쿼리): 1시간
|
||||||
|
- Phase 3 (고급 기능): 0.5시간
|
||||||
|
- 테스트 및 문서화: 0.5시간
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 주의사항
|
||||||
|
|
||||||
|
### 보안 필수 체크리스트
|
||||||
|
1. ✅ 동적 테이블명은 반드시 화이트리스트 검증
|
||||||
|
2. ✅ 동적 컬럼명은 정규식으로 검증
|
||||||
|
3. ✅ WHERE 절 파라미터는 반드시 바인딩
|
||||||
|
4. ✅ JSON 필드는 파싱 에러 처리
|
||||||
|
5. ✅ 재귀 쿼리는 깊이 제한 설정
|
||||||
|
|
||||||
|
### 성능 최적화
|
||||||
|
- JSON 필드 인덱싱 (GIN 인덱스)
|
||||||
|
- 재귀 쿼리 깊이 제한
|
||||||
|
- 집계 쿼리 최적화
|
||||||
|
- 필요시 캐싱 적용
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**상태**: ⏳ **대기 중**
|
||||||
|
**특이사항**: JSON 필드, 동적 쿼리, 재귀 CTE, 보안 검증 포함
|
||||||
|
**⚠️ 주의**: 동적 쿼리는 SQL 인젝션 방지가 매우 중요!
|
||||||
|
|
||||||
|
|
@ -137,17 +137,19 @@ backend-node/ (루트)
|
||||||
|
|
||||||
- `ddlAuditLogger.ts` (8개) - DDL 감사 로그 - [계획서](PHASE3.11_DDL_AUDIT_LOGGER_MIGRATION.md)
|
- `ddlAuditLogger.ts` (8개) - DDL 감사 로그 - [계획서](PHASE3.11_DDL_AUDIT_LOGGER_MIGRATION.md)
|
||||||
- `externalCallConfigService.ts` (8개) - 외부 호출 설정 - [계획서](PHASE3.12_EXTERNAL_CALL_CONFIG_SERVICE_MIGRATION.md)
|
- `externalCallConfigService.ts` (8개) - 외부 호출 설정 - [계획서](PHASE3.12_EXTERNAL_CALL_CONFIG_SERVICE_MIGRATION.md)
|
||||||
- `batchExternalDbService.ts` (8개) - 배치 외부DB ⭐ 신규 발견
|
|
||||||
- `batchExecutionLogService.ts` (7개) - 배치 실행 로그 ⭐ 신규 발견
|
|
||||||
- `enhancedDynamicFormService.ts` (6개) - 확장 동적 폼 ⭐ 신규 발견
|
|
||||||
- `ddlExecutionService.ts` (6개) - DDL 실행 (이미 전환 완료?)
|
|
||||||
- `entityJoinService.ts` (5개) - 엔티티 조인 - [계획서](PHASE3.13_ENTITY_JOIN_SERVICE_MIGRATION.md)
|
- `entityJoinService.ts` (5개) - 엔티티 조인 - [계획서](PHASE3.13_ENTITY_JOIN_SERVICE_MIGRATION.md)
|
||||||
- `dataMappingService.ts` (5개) - 데이터 매핑 ⭐ 신규 발견
|
|
||||||
- `batchManagementService.ts` (5개) - 배치 관리 ⭐ 신규 발견
|
|
||||||
- `authService.ts` (5개) - 사용자 인증 - [계획서](PHASE3.14_AUTH_SERVICE_MIGRATION.md)
|
- `authService.ts` (5개) - 사용자 인증 - [계획서](PHASE3.14_AUTH_SERVICE_MIGRATION.md)
|
||||||
- `batchSchedulerService.ts` (4개) - 배치 스케줄러 ⭐ 신규 발견
|
- **배치 관련 서비스 (24개)** - [통합 계획서](PHASE3.15_BATCH_SERVICES_MIGRATION.md)
|
||||||
- `dataService.ts` (4개) - 데이터 서비스 ⭐ 신규 발견
|
- `batchExternalDbService.ts` (8개) - 배치 외부DB
|
||||||
- `adminService.ts` (3개) - 관리자 메뉴
|
- `batchExecutionLogService.ts` (7개) - 배치 실행 로그
|
||||||
|
- `batchManagementService.ts` (5개) - 배치 관리
|
||||||
|
- `batchSchedulerService.ts` (4개) - 배치 스케줄러
|
||||||
|
- **데이터 관리 서비스 (18개)** - [통합 계획서](PHASE3.16_DATA_MANAGEMENT_SERVICES_MIGRATION.md)
|
||||||
|
- `enhancedDynamicFormService.ts` (6개) - 확장 동적 폼
|
||||||
|
- `dataMappingService.ts` (5개) - 데이터 매핑
|
||||||
|
- `dataService.ts` (4개) - 데이터 서비스
|
||||||
|
- `adminService.ts` (3개) - 관리자 메뉴
|
||||||
|
- `ddlExecutionService.ts` (6개) - DDL 실행 (이미 전환 완료?)
|
||||||
- `referenceCacheService.ts` (3개) - 캐시 관리
|
- `referenceCacheService.ts` (3개) - 캐시 관리
|
||||||
|
|
||||||
#### 🟢 **단순 (컨트롤러 레이어) - 4순위**
|
#### 🟢 **단순 (컨트롤러 레이어) - 4순위**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue