diff --git a/PHASE3.15_BATCH_SERVICES_MIGRATION.md b/PHASE3.15_BATCH_SERVICES_MIGRATION.md new file mode 100644 index 00000000..7fbefec1 --- /dev/null +++ b/PHASE3.15_BATCH_SERVICES_MIGRATION.md @@ -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( + `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( + `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( + `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( + `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 ์—ฐ๋™, ์Šค์ผ€์ค„๋ง, ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ ํฌํ•จ +**โš ๏ธ ์ฃผ์˜**: ๋ฐฐ์น˜ ์‹œ์Šคํ…œ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ด๋ฏ€๋กœ ์‹ ์ค‘ํ•œ ํ…Œ์ŠคํŠธ ํ•„์ˆ˜! + diff --git a/PHASE3.16_DATA_MANAGEMENT_SERVICES_MIGRATION.md b/PHASE3.16_DATA_MANAGEMENT_SERVICES_MIGRATION.md new file mode 100644 index 00000000..457e0dd4 --- /dev/null +++ b/PHASE3.16_DATA_MANAGEMENT_SERVICES_MIGRATION.md @@ -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( + `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( + `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( + `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( + `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; + max?: Record; + pattern?: Record; + 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 ์ธ์ ์…˜ ๋ฐฉ์ง€๊ฐ€ ๋งค์šฐ ์ค‘์š”! + diff --git a/PRISMA_TO_RAW_QUERY_MIGRATION_PLAN.md b/PRISMA_TO_RAW_QUERY_MIGRATION_PLAN.md index 7d0e3176..e458899a 100644 --- a/PRISMA_TO_RAW_QUERY_MIGRATION_PLAN.md +++ b/PRISMA_TO_RAW_QUERY_MIGRATION_PLAN.md @@ -137,17 +137,19 @@ backend-node/ (๋ฃจํŠธ) - `ddlAuditLogger.ts` (8๊ฐœ) - DDL ๊ฐ์‚ฌ ๋กœ๊ทธ - [๊ณ„ํš์„œ](PHASE3.11_DDL_AUDIT_LOGGER_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) -- `dataMappingService.ts` (5๊ฐœ) - ๋ฐ์ดํ„ฐ ๋งคํ•‘ โญ ์‹ ๊ทœ ๋ฐœ๊ฒฌ -- `batchManagementService.ts` (5๊ฐœ) - ๋ฐฐ์น˜ ๊ด€๋ฆฌ โญ ์‹ ๊ทœ ๋ฐœ๊ฒฌ - `authService.ts` (5๊ฐœ) - ์‚ฌ์šฉ์ž ์ธ์ฆ - [๊ณ„ํš์„œ](PHASE3.14_AUTH_SERVICE_MIGRATION.md) -- `batchSchedulerService.ts` (4๊ฐœ) - ๋ฐฐ์น˜ ์Šค์ผ€์ค„๋Ÿฌ โญ ์‹ ๊ทœ ๋ฐœ๊ฒฌ -- `dataService.ts` (4๊ฐœ) - ๋ฐ์ดํ„ฐ ์„œ๋น„์Šค โญ ์‹ ๊ทœ ๋ฐœ๊ฒฌ -- `adminService.ts` (3๊ฐœ) - ๊ด€๋ฆฌ์ž ๋ฉ”๋‰ด +- **๋ฐฐ์น˜ ๊ด€๋ จ ์„œ๋น„์Šค (24๊ฐœ)** - [ํ†ตํ•ฉ ๊ณ„ํš์„œ](PHASE3.15_BATCH_SERVICES_MIGRATION.md) + - `batchExternalDbService.ts` (8๊ฐœ) - ๋ฐฐ์น˜ ์™ธ๋ถ€DB + - `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๊ฐœ) - ์บ์‹œ ๊ด€๋ฆฌ #### ๐ŸŸข **๋‹จ์ˆœ (์ปจํŠธ๋กค๋Ÿฌ ๋ ˆ์ด์–ด) - 4์ˆœ์œ„**