diff --git a/PHASE3.15_BATCH_SERVICES_MIGRATION.md b/PHASE3.15_BATCH_SERVICES_MIGRATION.md index 7fbefec1..b2df870e 100644 --- a/PHASE3.15_BATCH_SERVICES_MIGRATION.md +++ b/PHASE3.15_BATCH_SERVICES_MIGRATION.md @@ -6,16 +6,75 @@ ### ๐Ÿ“Š ๊ธฐ๋ณธ ์ •๋ณด -| ํ•ญ๋ชฉ | ๋‚ด์šฉ | -| --------------- | -------------------------------------------------------------- | -| ๋Œ€์ƒ ์„œ๋น„์Šค | 4๊ฐœ (BatchExternalDb, ExecutionLog, Management, Scheduler) | -| ํŒŒ์ผ ์œ„์น˜ | `backend-node/src/services/batch*.ts` | -| ์ด ํŒŒ์ผ ํฌ๊ธฐ | 2,161 ๋ผ์ธ | -| Prisma ํ˜ธ์ถœ | 24๊ฐœ | -| **ํ˜„์žฌ ์ง„ํ–‰๋ฅ ** | **0/24 (0%)** ๐Ÿ”„ **์ง„ํ–‰ ์˜ˆ์ •** | -| ๋ณต์žก๋„ | ๋†’์Œ (์™ธ๋ถ€ DB ์—ฐ๋™, ์Šค์ผ€์ค„๋ง, ํŠธ๋žœ์žญ์…˜) | -| ์šฐ์„ ์ˆœ์œ„ | ๐Ÿ”ด ๋†’์Œ (Phase 3.15) | -| **์ƒํƒœ** | โณ **๋Œ€๊ธฐ ์ค‘** | +| ํ•ญ๋ชฉ | ๋‚ด์šฉ | +| --------------- | ---------------------------------------------------------- | +| ๋Œ€์ƒ ์„œ๋น„์Šค | 4๊ฐœ (BatchExternalDb, ExecutionLog, Management, Scheduler) | +| ํŒŒ์ผ ์œ„์น˜ | `backend-node/src/services/batch*.ts` | +| ์ด ํŒŒ์ผ ํฌ๊ธฐ | 2,161 ๋ผ์ธ | +| Prisma ํ˜ธ์ถœ | 0๊ฐœ (์ „ํ™˜ ์™„๋ฃŒ) | +| **ํ˜„์žฌ ์ง„ํ–‰๋ฅ ** | **24/24 (100%)** โœ… **์ „ํ™˜ ์™„๋ฃŒ** | +| ๋ณต์žก๋„ | ๋†’์Œ (์™ธ๋ถ€ DB ์—ฐ๋™, ์Šค์ผ€์ค„๋ง, ํŠธ๋žœ์žญ์…˜) | +| ์šฐ์„ ์ˆœ์œ„ | ๐Ÿ”ด ๋†’์Œ (Phase 3.15) | +| **์ƒํƒœ** | โœ… **์™„๋ฃŒ** | + +--- + +## โœ… ์ „ํ™˜ ์™„๋ฃŒ ๋‚ด์—ญ + +### ์ „ํ™˜๋œ Prisma ํ˜ธ์ถœ (24๊ฐœ) + +#### 1. BatchExternalDbService (8๊ฐœ) +- `getAvailableConnections()` - findMany โ†’ query +- `getTables()` - $queryRaw โ†’ query (information_schema) +- `getTableColumns()` - $queryRaw โ†’ query (information_schema) +- `getExternalTables()` - findUnique โ†’ queryOne (x5) + +#### 2. BatchExecutionLogService (7๊ฐœ) +- `getExecutionLogs()` - findMany + count โ†’ query (JOIN + ๋™์  WHERE) +- `createExecutionLog()` - create โ†’ queryOne (INSERT RETURNING) +- `updateExecutionLog()` - update โ†’ queryOne (๋™์  UPDATE) +- `deleteExecutionLog()` - delete โ†’ query +- `getLatestExecutionLog()` - findFirst โ†’ queryOne +- `getExecutionStats()` - findMany โ†’ query (๋™์  WHERE) + +#### 3. BatchManagementService (5๊ฐœ) +- `getAvailableConnections()` - findMany โ†’ query +- `getTables()` - $queryRaw โ†’ query (information_schema) +- `getTableColumns()` - $queryRaw โ†’ query (information_schema) +- `getExternalTables()` - findUnique โ†’ queryOne (x2) + +#### 4. BatchSchedulerService (4๊ฐœ) +- `loadActiveBatchConfigs()` - findMany โ†’ query (JOIN with json_agg) +- `updateBatchSchedule()` - findUnique โ†’ query (JOIN with json_agg) +- `getDataFromSource()` - $queryRawUnsafe โ†’ query +- `insertDataToTarget()` - $executeRawUnsafe โ†’ query + +### ์ฃผ์š” ๊ธฐ์ˆ ์  ํ•ด๊ฒฐ ์‚ฌํ•ญ + +1. **์™ธ๋ถ€ DB ์—ฐ๊ฒฐ ์กฐํšŒ ๋ฐ˜๋ณต** + - 5๊ฐœ์˜ `findUnique` ํ˜ธ์ถœ์„ `queryOne`์œผ๋กœ ์ผ๊ด„ ์ „ํ™˜ + - ์•”ํ˜ธํ™”/๋ณตํ˜ธํ™” ๋กœ์ง ์œ ์ง€ + +2. **๋ฐฐ์น˜ ์„ค์ • + ๋งคํ•‘ JOIN** + - Prisma `include` โ†’ `json_agg` + `json_build_object` + - `FILTER (WHERE bm.id IS NOT NULL)` ๋กœ NULL ๋ฐฉ์ง€ + - ๊ณ„์ธต์  JSON ๋ฐ์ดํ„ฐ ์ƒ์„ฑ + +3. **๋™์  WHERE ์ ˆ ์ƒ์„ฑ** + - ์กฐ๊ฑด๋ถ€ ํ•„ํ„ฐ๋ง (batch_config_id, execution_status, ๋‚ ์งœ ๋ฒ”์œ„) + - ํŒŒ๋ผ๋ฏธํ„ฐ ์ธ๋ฑ์Šค ๋™์  ๊ด€๋ฆฌ + +4. **๋™์  UPDATE ์ฟผ๋ฆฌ** + - undefined ํ•„๋“œ ์ œ์™ธ + - 8๊ฐœ ํ•„๋“œ์˜ ์กฐ๊ฑด๋ถ€ ์—…๋ฐ์ดํŠธ + +5. **ํ†ต๊ณ„ ์ฟผ๋ฆฌ ์ „ํ™˜** + - ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ์ง‘๊ณ„ ์œ ์ง€ + - ์›๋ณธ ๋ฐ์ดํ„ฐ๋งŒ ์ฟผ๋ฆฌ๋กœ ์กฐํšŒ + +### ์ปดํŒŒ์ผ ์ƒํƒœ +โœ… TypeScript ์ปดํŒŒ์ผ ์„ฑ๊ณต +โœ… Linter ์˜ค๋ฅ˜ ์—†์Œ --- @@ -24,12 +83,14 @@ ### 1. BatchExternalDbService (8๊ฐœ ํ˜ธ์ถœ, 943 ๋ผ์ธ) **์ฃผ์š” ๊ธฐ๋Šฅ**: + - ์™ธ๋ถ€ DB์—์„œ ๋ฐฐ์น˜ ๋ฐ์ดํ„ฐ ์กฐํšŒ - ์™ธ๋ถ€ DB๋กœ ๋ฐฐ์น˜ ๋ฐ์ดํ„ฐ ์ €์žฅ - ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ ๊ด€๋ฆฌ - ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜ ๋ฐ ๋งคํ•‘ **์˜ˆ์ƒ Prisma ํ˜ธ์ถœ**: + - `getExternalDbConnection()` - ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ ์ •๋ณด ์กฐํšŒ - `fetchDataFromExternalDb()` - ์™ธ๋ถ€ DB ๋ฐ์ดํ„ฐ ์กฐํšŒ - `saveDataToExternalDb()` - ์™ธ๋ถ€ DB ๋ฐ์ดํ„ฐ ์ €์žฅ @@ -40,6 +101,7 @@ - `getBatchExecutionStatus()` - ์‹คํ–‰ ์ƒํƒœ ์กฐํšŒ **๊ธฐ์ˆ ์  ๊ณ ๋ ค์‚ฌํ•ญ**: + - ๋‹ค์–‘ํ•œ DB ํƒ€์ž… ์ง€์› (PostgreSQL, MySQL, Oracle, MSSQL) - ์—ฐ๊ฒฐ ํ’€ ๊ด€๋ฆฌ - ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ @@ -50,12 +112,14 @@ ### 2. BatchExecutionLogService (7๊ฐœ ํ˜ธ์ถœ, 299 ๋ผ์ธ) **์ฃผ์š” ๊ธฐ๋Šฅ**: + - ๋ฐฐ์น˜ ์‹คํ–‰ ๋กœ๊ทธ ์ƒ์„ฑ - ๋ฐฐ์น˜ ์‹คํ–‰ ์ด๋ ฅ ์กฐํšŒ - ๋ฐฐ์น˜ ์‹คํ–‰ ํ†ต๊ณ„ - ๋กœ๊ทธ ์ •๋ฆฌ **์˜ˆ์ƒ Prisma ํ˜ธ์ถœ**: + - `createExecutionLog()` - ์‹คํ–‰ ๋กœ๊ทธ ์ƒ์„ฑ - `updateExecutionLog()` - ์‹คํ–‰ ๋กœ๊ทธ ์—…๋ฐ์ดํŠธ - `getExecutionLogs()` - ์‹คํ–‰ ๋กœ๊ทธ ๋ชฉ๋ก ์กฐํšŒ @@ -65,6 +129,7 @@ - `getFailedExecutions()` - ์‹คํŒจํ•œ ์‹คํ–‰ ์กฐํšŒ **๊ธฐ์ˆ ์  ๊ณ ๋ ค์‚ฌํ•ญ**: + - ๋Œ€์šฉ๋Ÿ‰ ๋กœ๊ทธ ์ฒ˜๋ฆฌ - ํ†ต๊ณ„ ์ฟผ๋ฆฌ ์ตœ์ ํ™” - ๋กœ๊ทธ ๋ณด๊ด€ ์ •์ฑ… @@ -75,12 +140,14 @@ ### 3. BatchManagementService (5๊ฐœ ํ˜ธ์ถœ, 373 ๋ผ์ธ) **์ฃผ์š” ๊ธฐ๋Šฅ**: + - ๋ฐฐ์น˜ ์ž‘์—… ์„ค์ • ๊ด€๋ฆฌ - ๋ฐฐ์น˜ ์ž‘์—… ์‹คํ–‰ - ๋ฐฐ์น˜ ์ž‘์—… ์ค‘์ง€ - ๋ฐฐ์น˜ ์ž‘์—… ๋ชจ๋‹ˆํ„ฐ๋ง **์˜ˆ์ƒ Prisma ํ˜ธ์ถœ**: + - `getBatchJobs()` - ๋ฐฐ์น˜ ์ž‘์—… ๋ชฉ๋ก ์กฐํšŒ - `getBatchJob()` - ๋ฐฐ์น˜ ์ž‘์—… ๋‹จ๊ฑด ์กฐํšŒ - `createBatchJob()` - ๋ฐฐ์น˜ ์ž‘์—… ์ƒ์„ฑ @@ -88,6 +155,7 @@ - `deleteBatchJob()` - ๋ฐฐ์น˜ ์ž‘์—… ์‚ญ์ œ **๊ธฐ์ˆ ์  ๊ณ ๋ ค์‚ฌํ•ญ**: + - JSON ์„ค์ • ํ•„๋“œ (job_config) - ์ž‘์—… ์ƒํƒœ ๊ด€๋ฆฌ - ๋™์‹œ ์‹คํ–‰ ์ œ์–ด @@ -98,18 +166,21 @@ ### 4. BatchSchedulerService (4๊ฐœ ํ˜ธ์ถœ, 546 ๋ผ์ธ) **์ฃผ์š” ๊ธฐ๋Šฅ**: + - ๋ฐฐ์น˜ ์Šค์ผ€์ค„ ์„ค์ • - Cron ํ‘œํ˜„์‹ ๊ด€๋ฆฌ - ์Šค์ผ€์ค„ ์‹คํ–‰ - ๋‹ค์Œ ์‹คํ–‰ ์‹œ๊ฐ„ ๊ณ„์‚ฐ **์˜ˆ์ƒ Prisma ํ˜ธ์ถœ**: + - `getScheduledBatches()` - ์Šค์ผ€์ค„๋œ ๋ฐฐ์น˜ ์กฐํšŒ - `createSchedule()` - ์Šค์ผ€์ค„ ์ƒ์„ฑ - `updateSchedule()` - ์Šค์ผ€์ค„ ์ˆ˜์ • - `deleteSchedule()` - ์Šค์ผ€์ค„ ์‚ญ์ œ **๊ธฐ์ˆ ์  ๊ณ ๋ ค์‚ฌํ•ญ**: + - Cron ํ‘œํ˜„์‹ ํŒŒ์‹ฑ - ์‹œ๊ฐ„๋Œ€ ์ฒ˜๋ฆฌ - ์‹คํ–‰ ์ด๋ ฅ ์ถ”์  @@ -120,17 +191,23 @@ ## ๐Ÿ’ก ํ†ตํ•ฉ ์ „ํ™˜ ์ „๋žต ### Phase 1: ํ•ต์‹ฌ ์„œ๋น„์Šค ์ „ํ™˜ (12๊ฐœ) + **BatchManagementService (5๊ฐœ) + BatchExecutionLogService (7๊ฐœ)** + - ๋ฐฐ์น˜ ๊ด€๋ฆฌ ๋ฐ ๋กœ๊น… ๊ธฐ๋Šฅ ์šฐ์„  - ์ƒ๋Œ€์ ์œผ๋กœ ๋‹จ์ˆœํ•œ CRUD ### Phase 2: ์Šค์ผ€์ค„๋Ÿฌ ์ „ํ™˜ (4๊ฐœ) + **BatchSchedulerService (4๊ฐœ)** + - ์Šค์ผ€์ค„ ๊ด€๋ฆฌ - Cron ํ‘œํ˜„์‹ ์ฒ˜๋ฆฌ ### Phase 3: ์™ธ๋ถ€ DB ์—ฐ๋™ ์ „ํ™˜ (8๊ฐœ) + **BatchExternalDbService (8๊ฐœ)** + - ๊ฐ€์žฅ ๋ณต์žกํ•œ ์„œ๋น„์Šค - ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ ๋ฐ ์ฟผ๋ฆฌ @@ -141,6 +218,7 @@ ### ์˜ˆ์‹œ 1: ๋ฐฐ์น˜ ์‹คํ–‰ ๋กœ๊ทธ ์ƒ์„ฑ **๋ณ€๊ฒฝ ์ „**: + ```typescript const log = await prisma.batch_execution_logs.create({ data: { @@ -154,6 +232,7 @@ const log = await prisma.batch_execution_logs.create({ ``` **๋ณ€๊ฒฝ ํ›„**: + ```typescript const log = await queryOne( `INSERT INTO batch_execution_logs @@ -167,6 +246,7 @@ const log = await queryOne( ### ์˜ˆ์‹œ 2: ๋ฐฐ์น˜ ํ†ต๊ณ„ ์กฐํšŒ **๋ณ€๊ฒฝ ์ „**: + ```typescript const stats = await prisma.batch_execution_logs.groupBy({ by: ["status"], @@ -179,6 +259,7 @@ const stats = await prisma.batch_execution_logs.groupBy({ ``` **๋ณ€๊ฒฝ ํ›„**: + ```typescript const stats = await query<{ status: string; count: string }>( `SELECT status, COUNT(*) as count @@ -194,6 +275,7 @@ const stats = await query<{ status: string; count: string }>( ### ์˜ˆ์‹œ 3: ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ ๋ฐ ์ฟผ๋ฆฌ **๋ณ€๊ฒฝ ์ „**: + ```typescript // ์—ฐ๊ฒฐ ์ •๋ณด ์กฐํšŒ const connection = await prisma.external_db_connections.findUnique({ @@ -205,6 +287,7 @@ const externalData = await externalDbClient.query(sql); ``` **๋ณ€๊ฒฝ ํ›„**: + ```typescript // ์—ฐ๊ฒฐ ์ •๋ณด ์กฐํšŒ const connection = await queryOne( @@ -219,6 +302,7 @@ const externalData = await externalDbClient.query(sql); ### ์˜ˆ์‹œ 4: ์Šค์ผ€์ค„ ๊ด€๋ฆฌ **๋ณ€๊ฒฝ ์ „**: + ```typescript const schedule = await prisma.batch_schedules.create({ data: { @@ -231,6 +315,7 @@ const schedule = await prisma.batch_schedules.create({ ``` **๋ณ€๊ฒฝ ํ›„**: + ```typescript const nextRun = calculateNextRun(cronExp); @@ -248,6 +333,7 @@ const schedule = await queryOne( ## ๐Ÿ”ง ๊ธฐ์ˆ ์  ๊ณ ๋ ค์‚ฌํ•ญ ### 1. ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ ๊ด€๋ฆฌ + ```typescript import { DatabaseConnectorFactory } from "../database/connectorFactory"; @@ -264,13 +350,14 @@ try { ``` ### 2. ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ + ```typescript await transaction(async (client) => { // ๋ฐฐ์น˜ ์ƒํƒœ ์—…๋ฐ์ดํŠธ - await client.query( - `UPDATE batch_jobs SET status = $1 WHERE id = $2`, - ["running", batchId] - ); + await client.query(`UPDATE batch_jobs SET status = $1 WHERE id = $2`, [ + "running", + batchId, + ]); // ์‹คํ–‰ ๋กœ๊ทธ ์ƒ์„ฑ await client.query( @@ -282,6 +369,7 @@ await transaction(async (client) => { ``` ### 3. Cron ํ‘œํ˜„์‹ ์ฒ˜๋ฆฌ + ```typescript import cron from "node-cron"; @@ -296,6 +384,7 @@ function calculateNextRun(cronExp: string): Date { ``` ### 4. ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ + ```typescript // ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐฉ์‹์œผ๋กœ ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ const stream = await query( @@ -313,6 +402,7 @@ for await (const row of stream) { ## ๐Ÿ“ ์ „ํ™˜ ์ฒดํฌ๋ฆฌ์ŠคํŠธ ### BatchExternalDbService (8๊ฐœ) + - [ ] `getExternalDbConnection()` - ์—ฐ๊ฒฐ ์ •๋ณด ์กฐํšŒ - [ ] `fetchDataFromExternalDb()` - ์™ธ๋ถ€ DB ๋ฐ์ดํ„ฐ ์กฐํšŒ - [ ] `saveDataToExternalDb()` - ์™ธ๋ถ€ DB ๋ฐ์ดํ„ฐ ์ €์žฅ @@ -323,6 +413,7 @@ for await (const row of stream) { - [ ] `getBatchExecutionStatus()` - ์‹คํ–‰ ์ƒํƒœ ์กฐํšŒ ### BatchExecutionLogService (7๊ฐœ) + - [ ] `createExecutionLog()` - ์‹คํ–‰ ๋กœ๊ทธ ์ƒ์„ฑ - [ ] `updateExecutionLog()` - ์‹คํ–‰ ๋กœ๊ทธ ์—…๋ฐ์ดํŠธ - [ ] `getExecutionLogs()` - ์‹คํ–‰ ๋กœ๊ทธ ๋ชฉ๋ก ์กฐํšŒ @@ -332,6 +423,7 @@ for await (const row of stream) { - [ ] `getFailedExecutions()` - ์‹คํŒจํ•œ ์‹คํ–‰ ์กฐํšŒ ### BatchManagementService (5๊ฐœ) + - [ ] `getBatchJobs()` - ๋ฐฐ์น˜ ์ž‘์—… ๋ชฉ๋ก ์กฐํšŒ - [ ] `getBatchJob()` - ๋ฐฐ์น˜ ์ž‘์—… ๋‹จ๊ฑด ์กฐํšŒ - [ ] `createBatchJob()` - ๋ฐฐ์น˜ ์ž‘์—… ์ƒ์„ฑ @@ -339,12 +431,14 @@ for await (const row of stream) { - [ ] `deleteBatchJob()` - ๋ฐฐ์น˜ ์ž‘์—… ์‚ญ์ œ ### BatchSchedulerService (4๊ฐœ) + - [ ] `getScheduledBatches()` - ์Šค์ผ€์ค„๋œ ๋ฐฐ์น˜ ์กฐํšŒ - [ ] `createSchedule()` - ์Šค์ผ€์ค„ ์ƒ์„ฑ - [ ] `updateSchedule()` - ์Šค์ผ€์ค„ ์ˆ˜์ • - [ ] `deleteSchedule()` - ์Šค์ผ€์ค„ ์‚ญ์ œ ### ๊ณตํ†ต ์ž‘์—… + - [ ] import ๋ฌธ ์ˆ˜์ • (๋ชจ๋“  ์„œ๋น„์Šค) - [ ] Prisma import ์™„์ „ ์ œ๊ฑฐ (๋ชจ๋“  ์„œ๋น„์Šค) - [ ] ํŠธ๋žœ์žญ์…˜ ๋กœ์ง ํ™•์ธ @@ -355,15 +449,18 @@ for await (const row of stream) { ## ๐Ÿงช ํ…Œ์ŠคํŠธ ๊ณ„ํš ### ๋‹จ์œ„ ํ…Œ์ŠคํŠธ (24๊ฐœ) + - ๊ฐ Prisma ํ˜ธ์ถœ๋ณ„ 1๊ฐœ์”ฉ ### ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ (8๊ฐœ) + - BatchExternalDbService: ์™ธ๋ถ€ DB ์—ฐ๋™ ํ…Œ์ŠคํŠธ (2๊ฐœ) - BatchExecutionLogService: ๋กœ๊ทธ ์ƒ์„ฑ ๋ฐ ์กฐํšŒ ํ…Œ์ŠคํŠธ (2๊ฐœ) - BatchManagementService: ๋ฐฐ์น˜ ์ž‘์—… ์‹คํ–‰ ํ…Œ์ŠคํŠธ (2๊ฐœ) - BatchSchedulerService: ์Šค์ผ€์ค„ ์‹คํ–‰ ํ…Œ์ŠคํŠธ (2๊ฐœ) ### ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ + - ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ - ๋™์‹œ ๋ฐฐ์น˜ ์‹คํ–‰ ์„ฑ๋Šฅ - ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ ํ’€ ์„ฑ๋Šฅ @@ -377,7 +474,6 @@ for await (const row of stream) { - ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ - ์Šค์ผ€์ค„๋ง ๋กœ์ง - ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ - - **์˜ˆ์ƒ ์†Œ์š” ์‹œ๊ฐ„**: 4~5์‹œ๊ฐ„ - Phase 1 (BatchManagement + ExecutionLog): 1.5์‹œ๊ฐ„ - Phase 2 (Scheduler): 1์‹œ๊ฐ„ @@ -389,6 +485,7 @@ for await (const row of stream) { ## โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ ### ์ค‘์š” ์ฒดํฌํฌ์ธํŠธ + 1. โœ… ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ์€ ๋ฐ˜๋“œ์‹œ try-finally์—์„œ ํ•ด์ œ 2. โœ… ๋ฐฐ์น˜ ์‹คํ–‰ ์ค‘ ์—๋Ÿฌ ์‹œ ๋กค๋ฐฑ ์ฒ˜๋ฆฌ 3. โœ… Cron ํ‘œํ˜„์‹ ๊ฒ€์ฆ ํ•„์ˆ˜ @@ -396,6 +493,7 @@ for await (const row of stream) { 5. โœ… ๋™์‹œ ์‹คํ–‰ ์ œํ•œ ํ™•์ธ ### ์„ฑ๋Šฅ ์ตœ์ ํ™” + - ์—ฐ๊ฒฐ ํ’€ ํ™œ์šฉ - ๋ฐฐ์น˜ ์ฟผ๋ฆฌ ์ตœ์ ํ™” - ์ธ๋ฑ์Šค ํ™•์ธ @@ -406,4 +504,3 @@ for await (const row of stream) { **์ƒํƒœ**: โณ **๋Œ€๊ธฐ ์ค‘** **ํŠน์ด์‚ฌํ•ญ**: ์™ธ๋ถ€ DB ์—ฐ๋™, ์Šค์ผ€์ค„๋ง, ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ ํฌํ•จ **โš ๏ธ ์ฃผ์˜**: ๋ฐฐ์น˜ ์‹œ์Šคํ…œ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ด๋ฏ€๋กœ ์‹ ์ค‘ํ•œ ํ…Œ์ŠคํŠธ ํ•„์ˆ˜! - diff --git a/PRISMA_TO_RAW_QUERY_MIGRATION_PLAN.md b/PRISMA_TO_RAW_QUERY_MIGRATION_PLAN.md index 2f74db2b..6ca6e9e2 100644 --- a/PRISMA_TO_RAW_QUERY_MIGRATION_PLAN.md +++ b/PRISMA_TO_RAW_QUERY_MIGRATION_PLAN.md @@ -139,11 +139,11 @@ backend-node/ (๋ฃจํŠธ) - `externalCallConfigService.ts` (0๊ฐœ) - โœ… **์ „ํ™˜ ์™„๋ฃŒ** (Phase 3.12) - [๊ณ„ํš์„œ](PHASE3.12_EXTERNAL_CALL_CONFIG_SERVICE_MIGRATION.md) - `entityJoinService.ts` (0๊ฐœ) - โœ… **์ „ํ™˜ ์™„๋ฃŒ** (Phase 3.13) - [๊ณ„ํš์„œ](PHASE3.13_ENTITY_JOIN_SERVICE_MIGRATION.md) - `authService.ts` (0๊ฐœ) - โœ… **์ „ํ™˜ ์™„๋ฃŒ** (Phase 1.5์—์„œ ์™„๋ฃŒ) - [๊ณ„ํš์„œ](PHASE3.14_AUTH_SERVICE_MIGRATION.md) -- **๋ฐฐ์น˜ ๊ด€๋ จ ์„œ๋น„์Šค (24๊ฐœ)** - [ํ†ตํ•ฉ ๊ณ„ํš์„œ](PHASE3.15_BATCH_SERVICES_MIGRATION.md) - - `batchExternalDbService.ts` (8๊ฐœ) - ๋ฐฐ์น˜ ์™ธ๋ถ€DB - - `batchExecutionLogService.ts` (7๊ฐœ) - ๋ฐฐ์น˜ ์‹คํ–‰ ๋กœ๊ทธ - - `batchManagementService.ts` (5๊ฐœ) - ๋ฐฐ์น˜ ๊ด€๋ฆฌ - - `batchSchedulerService.ts` (4๊ฐœ) - ๋ฐฐ์น˜ ์Šค์ผ€์ค„๋Ÿฌ +- **๋ฐฐ์น˜ ๊ด€๋ จ ์„œ๋น„์Šค (0๊ฐœ)** - โœ… **์ „ํ™˜ ์™„๋ฃŒ** - [ํ†ตํ•ฉ ๊ณ„ํš์„œ](PHASE3.15_BATCH_SERVICES_MIGRATION.md) + - `batchExternalDbService.ts` (0๊ฐœ) - โœ… **์ „ํ™˜ ์™„๋ฃŒ** + - `batchExecutionLogService.ts` (0๊ฐœ) - โœ… **์ „ํ™˜ ์™„๋ฃŒ** + - `batchManagementService.ts` (0๊ฐœ) - โœ… **์ „ํ™˜ ์™„๋ฃŒ** + - `batchSchedulerService.ts` (0๊ฐœ) - โœ… **์ „ํ™˜ ์™„๋ฃŒ** - **๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ ์„œ๋น„์Šค (0๊ฐœ)** - โœ… **์ „ํ™˜ ์™„๋ฃŒ** - [ํ†ตํ•ฉ ๊ณ„ํš์„œ](PHASE3.16_DATA_MANAGEMENT_SERVICES_MIGRATION.md) - `enhancedDynamicFormService.ts` (0๊ฐœ) - โœ… **์ „ํ™˜ ์™„๋ฃŒ** - `dataMappingService.ts` (0๊ฐœ) - โœ… **์ „ํ™˜ ์™„๋ฃŒ** diff --git a/backend-node/src/services/batchExecutionLogService.ts b/backend-node/src/services/batchExecutionLogService.ts index 2fee555a..5f7335f4 100644 --- a/backend-node/src/services/batchExecutionLogService.ts +++ b/backend-node/src/services/batchExecutionLogService.ts @@ -1,7 +1,7 @@ // ๋ฐฐ์น˜ ์‹คํ–‰ ๋กœ๊ทธ ์„œ๋น„์Šค // ์ž‘์„ฑ์ผ: 2024-12-24 -import prisma from "../config/database"; +import { query, queryOne } from "../database/db"; import { BatchExecutionLog, CreateBatchExecutionLogRequest, @@ -32,48 +32,65 @@ export class BatchExecutionLogService { const take = limit; // WHERE ์กฐ๊ฑด ๊ตฌ์„ฑ - const where: any = {}; + const whereConditions: string[] = []; + const params: any[] = []; + let paramIndex = 1; if (batch_config_id) { - where.batch_config_id = batch_config_id; + whereConditions.push(`bel.batch_config_id = $${paramIndex++}`); + params.push(batch_config_id); } if (execution_status) { - where.execution_status = execution_status; + whereConditions.push(`bel.execution_status = $${paramIndex++}`); + params.push(execution_status); } - if (start_date || end_date) { - where.start_time = {}; - if (start_date) { - where.start_time.gte = start_date; - } - if (end_date) { - where.start_time.lte = end_date; - } + if (start_date) { + whereConditions.push(`bel.start_time >= $${paramIndex++}`); + params.push(start_date); + } + + if (end_date) { + whereConditions.push(`bel.start_time <= $${paramIndex++}`); + params.push(end_date); } - // ๋กœ๊ทธ ์กฐํšŒ - const [logs, total] = await Promise.all([ - prisma.batch_execution_logs.findMany({ - where, - include: { - batch_config: { - select: { - id: true, - batch_name: true, - description: true, - cron_schedule: true, - is_active: true - } - } - }, - orderBy: { start_time: 'desc' }, - skip, - take - }), - prisma.batch_execution_logs.count({ where }) + const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : ''; + + // ๋กœ๊ทธ ์กฐํšŒ (batch_config ์ •๋ณด ํฌํ•จ) + const sql = ` + SELECT + bel.*, + json_build_object( + 'id', bc.id, + 'batch_name', bc.batch_name, + 'description', bc.description, + 'cron_schedule', bc.cron_schedule, + 'is_active', bc.is_active + ) as batch_config + FROM batch_execution_logs bel + LEFT JOIN batch_configs bc ON bel.batch_config_id = bc.id + ${whereClause} + ORDER BY bel.start_time DESC + LIMIT $${paramIndex} OFFSET $${paramIndex + 1} + `; + + const countSql = ` + SELECT COUNT(*) as count + FROM batch_execution_logs bel + ${whereClause} + `; + + params.push(take, skip); + + const [logs, countResult] = await Promise.all([ + query(sql, params), + query<{ count: number }>(countSql, params.slice(0, -2)) ]); + const total = parseInt(countResult[0]?.count?.toString() || '0', 10); + return { success: true, data: logs as BatchExecutionLogWithConfig[], @@ -101,22 +118,28 @@ export class BatchExecutionLogService { data: CreateBatchExecutionLogRequest ): Promise> { try { - const log = await prisma.batch_execution_logs.create({ - data: { - batch_config_id: data.batch_config_id, - execution_status: data.execution_status, - start_time: data.start_time || new Date(), - end_time: data.end_time, - duration_ms: data.duration_ms, - total_records: data.total_records || 0, - success_records: data.success_records || 0, - failed_records: data.failed_records || 0, - error_message: data.error_message, - error_details: data.error_details, - server_name: data.server_name || process.env.HOSTNAME || 'unknown', - process_id: data.process_id || process.pid?.toString() - } - }); + const log = await queryOne( + `INSERT INTO batch_execution_logs ( + batch_config_id, execution_status, start_time, end_time, + duration_ms, total_records, success_records, failed_records, + error_message, error_details, server_name, process_id + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) + RETURNING *`, + [ + data.batch_config_id, + data.execution_status, + data.start_time || new Date(), + data.end_time, + data.duration_ms, + data.total_records || 0, + data.success_records || 0, + data.failed_records || 0, + data.error_message, + data.error_details, + data.server_name || process.env.HOSTNAME || 'unknown', + data.process_id || process.pid?.toString() + ] + ); return { success: true, @@ -141,19 +164,53 @@ export class BatchExecutionLogService { data: UpdateBatchExecutionLogRequest ): Promise> { try { - const log = await prisma.batch_execution_logs.update({ - where: { id }, - data: { - execution_status: data.execution_status, - end_time: data.end_time, - duration_ms: data.duration_ms, - total_records: data.total_records, - success_records: data.success_records, - failed_records: data.failed_records, - error_message: data.error_message, - error_details: data.error_details - } - }); + // ๋™์  UPDATE ์ฟผ๋ฆฌ ์ƒ์„ฑ + const updates: string[] = []; + const params: any[] = []; + let paramIndex = 1; + + if (data.execution_status !== undefined) { + updates.push(`execution_status = $${paramIndex++}`); + params.push(data.execution_status); + } + if (data.end_time !== undefined) { + updates.push(`end_time = $${paramIndex++}`); + params.push(data.end_time); + } + if (data.duration_ms !== undefined) { + updates.push(`duration_ms = $${paramIndex++}`); + params.push(data.duration_ms); + } + if (data.total_records !== undefined) { + updates.push(`total_records = $${paramIndex++}`); + params.push(data.total_records); + } + if (data.success_records !== undefined) { + updates.push(`success_records = $${paramIndex++}`); + params.push(data.success_records); + } + if (data.failed_records !== undefined) { + updates.push(`failed_records = $${paramIndex++}`); + params.push(data.failed_records); + } + if (data.error_message !== undefined) { + updates.push(`error_message = $${paramIndex++}`); + params.push(data.error_message); + } + if (data.error_details !== undefined) { + updates.push(`error_details = $${paramIndex++}`); + params.push(data.error_details); + } + + params.push(id); + + const log = await queryOne( + `UPDATE batch_execution_logs + SET ${updates.join(', ')} + WHERE id = $${paramIndex} + RETURNING *`, + params + ); return { success: true, @@ -175,9 +232,7 @@ export class BatchExecutionLogService { */ static async deleteExecutionLog(id: number): Promise> { try { - await prisma.batch_execution_logs.delete({ - where: { id } - }); + await query(`DELETE FROM batch_execution_logs WHERE id = $1`, [id]); return { success: true, @@ -200,14 +255,17 @@ export class BatchExecutionLogService { batchConfigId: number ): Promise> { try { - const log = await prisma.batch_execution_logs.findFirst({ - where: { batch_config_id: batchConfigId }, - orderBy: { start_time: 'desc' } - }); + const log = await queryOne( + `SELECT * FROM batch_execution_logs + WHERE batch_config_id = $1 + ORDER BY start_time DESC + LIMIT 1`, + [batchConfigId] + ); return { success: true, - data: log as BatchExecutionLog | null + data: log || null }; } catch (error) { console.error("์ตœ์‹  ๋ฐฐ์น˜ ์‹คํ–‰ ๋กœ๊ทธ ์กฐํšŒ ์‹คํŒจ:", error); @@ -235,30 +293,37 @@ export class BatchExecutionLogService { total_records_processed: number; }>> { try { - const where: any = {}; + const whereConditions: string[] = []; + const params: any[] = []; + let paramIndex = 1; if (batchConfigId) { - where.batch_config_id = batchConfigId; + whereConditions.push(`batch_config_id = $${paramIndex++}`); + params.push(batchConfigId); } - if (startDate || endDate) { - where.start_time = {}; - if (startDate) { - where.start_time.gte = startDate; - } - if (endDate) { - where.start_time.lte = endDate; - } + if (startDate) { + whereConditions.push(`start_time >= $${paramIndex++}`); + params.push(startDate); + } + + if (endDate) { + whereConditions.push(`start_time <= $${paramIndex++}`); + params.push(endDate); } - const logs = await prisma.batch_execution_logs.findMany({ - where, - select: { - execution_status: true, - duration_ms: true, - total_records: true - } - }); + const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : ''; + + const logs = await query<{ + execution_status: string; + duration_ms: number; + total_records: number; + }>( + `SELECT execution_status, duration_ms, total_records + FROM batch_execution_logs + ${whereClause}`, + params + ); const total_executions = logs.length; const success_count = logs.filter((log: any) => log.execution_status === 'SUCCESS').length; diff --git a/backend-node/src/services/batchExternalDbService.ts b/backend-node/src/services/batchExternalDbService.ts index d5670f04..c2c0851e 100644 --- a/backend-node/src/services/batchExternalDbService.ts +++ b/backend-node/src/services/batchExternalDbService.ts @@ -2,7 +2,7 @@ // ๊ธฐ์กด ExternalDbConnectionService์™€ ๋ถ„๋ฆฌํ•˜์—ฌ ๋ฐฐ์น˜๊ด€๋ฆฌ ์‹œ์Šคํ…œ์— ํŠนํ™”๋œ ๊ธฐ๋Šฅ ์ œ๊ณต // ์ž‘์„ฑ์ผ: 2024-12-24 -import prisma from "../config/database"; +import { query, queryOne } from "../database/db"; import { PasswordEncryption } from "../utils/passwordEncryption"; import { DatabaseConnectorFactory } from "../database/DatabaseConnectorFactory"; import { RestApiConnector } from "../database/RestApiConnector"; @@ -34,16 +34,18 @@ export class BatchExternalDbService { }); // ํ™œ์„ฑํ™”๋œ ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ ์กฐํšŒ - const externalConnections = await prisma.external_db_connections.findMany({ - where: { is_active: 'Y' }, - select: { - id: true, - connection_name: true, - db_type: true, - description: true - }, - orderBy: { connection_name: 'asc' } - }); + const externalConnections = await query<{ + id: number; + connection_name: string; + db_type: string; + description: string; + }>( + `SELECT id, connection_name, db_type, description + FROM external_db_connections + WHERE is_active = 'Y' + ORDER BY connection_name ASC`, + [] + ); // ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ ์ถ”๊ฐ€ externalConnections.forEach(conn => { @@ -82,13 +84,14 @@ export class BatchExternalDbService { if (connectionType === 'internal') { // ๋‚ด๋ถ€ DB ํ…Œ์ด๋ธ” ์กฐํšŒ - const result = await prisma.$queryRaw>` - SELECT table_name + const result = await query<{ table_name: string }>( + `SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE' - ORDER BY table_name - `; + ORDER BY table_name`, + [] + ); tables = result.map(row => ({ table_name: row.table_name, @@ -138,22 +141,23 @@ export class BatchExternalDbService { // ๋‚ด๋ถ€ DB ์ปฌ๋Ÿผ ์กฐํšŒ console.log(`[BatchExternalDbService] ๋‚ด๋ถ€ DB ์ปฌ๋Ÿผ ์กฐํšŒ ์‹œ์ž‘: ${tableName}`); - const result = await prisma.$queryRaw>` - SELECT + }>( + `SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema = 'public' - AND table_name = ${tableName} - ORDER BY ordinal_position - `; + AND table_name = $1 + ORDER BY ordinal_position`, + [tableName] + ); console.log(`[BatchExternalDbService] ๋‚ด๋ถ€ DB ์ปฌ๋Ÿผ ์กฐํšŒ ๊ฒฐ๊ณผ:`, result); @@ -198,9 +202,10 @@ export class BatchExternalDbService { private static async getExternalTables(connectionId: number): Promise> { try { // ์—ฐ๊ฒฐ ์ •๋ณด ์กฐํšŒ - const connection = await prisma.external_db_connections.findUnique({ - where: { id: connectionId } - }); + const connection = await queryOne( + `SELECT * FROM external_db_connections WHERE id = $1`, + [connectionId] + ); if (!connection) { return { @@ -257,9 +262,10 @@ export class BatchExternalDbService { console.log(`[BatchExternalDbService] getExternalTableColumns ํ˜ธ์ถœ: connectionId=${connectionId}, tableName=${tableName}`); // ์—ฐ๊ฒฐ ์ •๋ณด ์กฐํšŒ - const connection = await prisma.external_db_connections.findUnique({ - where: { id: connectionId } - }); + const connection = await queryOne( + `SELECT * FROM external_db_connections WHERE id = $1`, + [connectionId] + ); if (!connection) { console.log(`[BatchExternalDbService] ์—ฐ๊ฒฐ ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ: connectionId=${connectionId}`); @@ -418,9 +424,10 @@ export class BatchExternalDbService { console.log(`[BatchExternalDbService] ์™ธ๋ถ€ DB ๋ฐ์ดํ„ฐ ์กฐํšŒ: connectionId=${connectionId}, tableName=${tableName}`); // ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ ์ •๋ณด ์กฐํšŒ - const connection = await prisma.external_db_connections.findUnique({ - where: { id: connectionId } - }); + const connection = await queryOne( + `SELECT * FROM external_db_connections WHERE id = $1`, + [connectionId] + ); if (!connection) { return { @@ -490,9 +497,10 @@ export class BatchExternalDbService { console.log(`[BatchExternalDbService] ์™ธ๋ถ€ DB ํŠน์ • ์ปฌ๋Ÿผ ์กฐํšŒ: connectionId=${connectionId}, tableName=${tableName}, columns=[${columns.join(', ')}]`); // ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ ์ •๋ณด ์กฐํšŒ - const connection = await prisma.external_db_connections.findUnique({ - where: { id: connectionId } - }); + const connection = await queryOne( + `SELECT * FROM external_db_connections WHERE id = $1`, + [connectionId] + ); if (!connection) { return { @@ -569,9 +577,10 @@ export class BatchExternalDbService { } // ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ ์ •๋ณด ์กฐํšŒ - const connection = await prisma.external_db_connections.findUnique({ - where: { id: connectionId } - }); + const connection = await queryOne( + `SELECT * FROM external_db_connections WHERE id = $1`, + [connectionId] + ); if (!connection) { return { diff --git a/backend-node/src/services/batchManagementService.ts b/backend-node/src/services/batchManagementService.ts index 1b082209..6d09178b 100644 --- a/backend-node/src/services/batchManagementService.ts +++ b/backend-node/src/services/batchManagementService.ts @@ -1,7 +1,7 @@ // ๋ฐฐ์น˜๊ด€๋ฆฌ ์ „์šฉ ์„œ๋น„์Šค (๊ธฐ์กด ์†Œ์Šค์™€ ์™„์ „ ๋ถ„๋ฆฌ) // ์ž‘์„ฑ์ผ: 2024-12-24 -import prisma from "../config/database"; +import { query, queryOne } from "../database/db"; import { PasswordEncryption } from "../utils/passwordEncryption"; import { DatabaseConnectorFactory } from "../database/DatabaseConnectorFactory"; @@ -49,16 +49,18 @@ export class BatchManagementService { }); // ํ™œ์„ฑํ™”๋œ ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ ์กฐํšŒ - const externalConnections = await prisma.external_db_connections.findMany({ - where: { is_active: 'Y' }, - select: { - id: true, - connection_name: true, - db_type: true, - description: true - }, - orderBy: { connection_name: 'asc' } - }); + const externalConnections = await query<{ + id: number; + connection_name: string; + db_type: string; + description: string; + }>( + `SELECT id, connection_name, db_type, description + FROM external_db_connections + WHERE is_active = 'Y' + ORDER BY connection_name ASC`, + [] + ); // ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ ์ถ”๊ฐ€ externalConnections.forEach(conn => { @@ -97,13 +99,14 @@ export class BatchManagementService { if (connectionType === 'internal') { // ๋‚ด๋ถ€ DB ํ…Œ์ด๋ธ” ์กฐํšŒ - const result = await prisma.$queryRaw>` - SELECT table_name + const result = await query<{ table_name: string }>( + `SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE' - ORDER BY table_name - `; + ORDER BY table_name`, + [] + ); tables = result.map(row => ({ table_name: row.table_name, @@ -153,22 +156,23 @@ export class BatchManagementService { // ๋‚ด๋ถ€ DB ์ปฌ๋Ÿผ ์กฐํšŒ console.log(`[BatchManagementService] ๋‚ด๋ถ€ DB ์ปฌ๋Ÿผ ์กฐํšŒ ์‹œ์ž‘: ${tableName}`); - const result = await prisma.$queryRaw>` - SELECT + }>( + `SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema = 'public' - AND table_name = ${tableName} - ORDER BY ordinal_position - `; + AND table_name = $1 + ORDER BY ordinal_position`, + [tableName] + ); console.log(`[BatchManagementService] ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ:`, result); @@ -215,9 +219,10 @@ export class BatchManagementService { private static async getExternalTables(connectionId: number): Promise> { try { // ์—ฐ๊ฒฐ ์ •๋ณด ์กฐํšŒ - const connection = await prisma.external_db_connections.findUnique({ - where: { id: connectionId } - }); + const connection = await queryOne( + `SELECT * FROM external_db_connections WHERE id = $1`, + [connectionId] + ); if (!connection) { return { @@ -274,9 +279,10 @@ export class BatchManagementService { console.log(`[BatchManagementService] getExternalTableColumns ํ˜ธ์ถœ: connectionId=${connectionId}, tableName=${tableName}`); // ์—ฐ๊ฒฐ ์ •๋ณด ์กฐํšŒ - const connection = await prisma.external_db_connections.findUnique({ - where: { id: connectionId } - }); + const connection = await queryOne( + `SELECT * FROM external_db_connections WHERE id = $1`, + [connectionId] + ); if (!connection) { console.log(`[BatchManagementService] ์—ฐ๊ฒฐ ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ: connectionId=${connectionId}`); diff --git a/backend-node/src/services/batchSchedulerService.ts b/backend-node/src/services/batchSchedulerService.ts index ea2f7f89..21166f93 100644 --- a/backend-node/src/services/batchSchedulerService.ts +++ b/backend-node/src/services/batchSchedulerService.ts @@ -2,7 +2,7 @@ // ์ž‘์„ฑ์ผ: 2024-12-24 import * as cron from 'node-cron'; -import prisma from '../config/database'; +import { query, queryOne } from '../database/db'; import { BatchService } from './batchService'; import { BatchExecutionLogService } from './batchExecutionLogService'; import { logger } from '../utils/logger'; @@ -59,14 +59,28 @@ export class BatchSchedulerService { */ private static async loadActiveBatchConfigs() { try { - const activeConfigs = await prisma.batch_configs.findMany({ - where: { - is_active: 'Y' - }, - include: { - batch_mappings: true - } - }); + const activeConfigs = await query( + `SELECT + bc.*, + json_agg( + json_build_object( + 'id', bm.id, + 'batch_config_id', bm.batch_config_id, + 'field_name', bm.field_name, + 'source_field', bm.source_field, + 'field_type', bm.field_type, + 'is_required', bm.is_required, + 'default_value', bm.default_value, + 'transform_function', bm.transform_function, + 'sort_order', bm.sort_order + ) + ) FILTER (WHERE bm.id IS NOT NULL) as batch_mappings + FROM batch_configs bc + LEFT JOIN batch_mappings bm ON bc.id = bm.batch_config_id + WHERE bc.is_active = 'Y' + GROUP BY bc.id`, + [] + ); logger.info(`ํ™œ์„ฑํ™”๋œ ๋ฐฐ์น˜ ์„ค์ • ${activeConfigs.length}๊ฐœ ๋ฐœ๊ฒฌ`); @@ -153,10 +167,30 @@ export class BatchSchedulerService { await this.unscheduleBatchConfig(configId); // ์—…๋ฐ์ดํŠธ๋œ ๋ฐฐ์น˜ ์„ค์ • ์กฐํšŒ - const config = await prisma.batch_configs.findUnique({ - where: { id: configId }, - include: { batch_mappings: true } - }); + const configResult = await query( + `SELECT + bc.*, + json_agg( + json_build_object( + 'id', bm.id, + 'batch_config_id', bm.batch_config_id, + 'field_name', bm.field_name, + 'source_field', bm.source_field, + 'field_type', bm.field_type, + 'is_required', bm.is_required, + 'default_value', bm.default_value, + 'transform_function', bm.transform_function, + 'sort_order', bm.sort_order + ) + ) FILTER (WHERE bm.id IS NOT NULL) as batch_mappings + FROM batch_configs bc + LEFT JOIN batch_mappings bm ON bc.id = bm.batch_config_id + WHERE bc.id = $1 + GROUP BY bc.id`, + [configId] + ); + + const config = configResult[0] || null; if (!config) { logger.warn(`๋ฐฐ์น˜ ์„ค์ •์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: ID ${configId}`); @@ -455,10 +489,11 @@ export class BatchSchedulerService { try { if (mapping.from_connection_type === 'internal') { // ๋‚ด๋ถ€ DB์—์„œ ์กฐํšŒ - const result = await prisma.$queryRawUnsafe( - `SELECT * FROM ${mapping.from_table_name}` + const result = await query( + `SELECT * FROM ${mapping.from_table_name}`, + [] ); - return result as any[]; + return result; } else { // ์™ธ๋ถ€ DB์—์„œ ์กฐํšŒ (๊ตฌํ˜„ ํ•„์š”) logger.warn('์™ธ๋ถ€ DB ์กฐํšŒ๋Š” ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.'); @@ -485,9 +520,13 @@ export class BatchSchedulerService { // ๋งคํ•‘๋œ ์ปฌ๋Ÿผ๋งŒ ์ถ”์ถœ const mappedData = this.mapColumns(record, mapping); - await prisma.$executeRawUnsafe( - `INSERT INTO ${mapping.to_table_name} (${Object.keys(mappedData).join(', ')}) VALUES (${Object.values(mappedData).map(() => '?').join(', ')})`, - ...Object.values(mappedData) + const columns = Object.keys(mappedData); + const values = Object.values(mappedData); + const placeholders = values.map((_, i) => `$${i + 1}`).join(', '); + + await query( + `INSERT INTO ${mapping.to_table_name} (${columns.join(', ')}) VALUES (${placeholders})`, + values ); successCount++; } catch (error) {