# ๐Ÿ“ Phase 2.4: DynamicFormService Raw Query ์ „ํ™˜ ๊ณ„ํš ## ๐Ÿ“‹ ๊ฐœ์š” DynamicFormService๋Š” **13๊ฐœ์˜ Prisma ํ˜ธ์ถœ**์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„(์•ฝ 11๊ฐœ)์€ `$queryRaw`๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด SQL์€ ์ด๋ฏธ ์ž‘์„ฑ๋˜์–ด ์žˆ์ง€๋งŒ, **Prisma ํด๋ผ์ด์–ธํŠธ๋ฅผ ์™„์ „ํžˆ ์ œ๊ฑฐํ•˜๋ ค๋ฉด 13๊ฐœ ๋ชจ๋‘๋ฅผ `db.ts`์˜ `query` ํ•จ์ˆ˜๋กœ ๊ต์ฒด**ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ### ๐Ÿ“Š ๊ธฐ๋ณธ ์ •๋ณด | ํ•ญ๋ชฉ | ๋‚ด์šฉ | | --------------- | ---------------------------------------------------- | | ํŒŒ์ผ ์œ„์น˜ | `backend-node/src/services/dynamicFormService.ts` | | ํŒŒ์ผ ํฌ๊ธฐ | 1,200+ ๋ผ์ธ | | Prisma ํ˜ธ์ถœ | 13๊ฐœ ($queryRaw: 11๊ฐœ, ORM: 2๊ฐœ) | | **ํ˜„์žฌ ์ง„ํ–‰๋ฅ ** | **0/13 (0%)** โณ **์ „ํ™˜ ํ•„์š”** | | **์ „ํ™˜ ํ•„์š”** | **13๊ฐœ ๋ชจ๋‘ ์ „ํ™˜ ํ•„์š”** (SQL์€ ์ด๋ฏธ ์ž‘์„ฑ๋˜์–ด ์žˆ์Œ) | | ๋ณต์žก๋„ | ๋‚ฎ์Œ (SQL ์ž‘์„ฑ์€ ์™„๋ฃŒ, `query()` ํ•จ์ˆ˜๋กœ ๊ต์ฒด๋งŒ ํ•„์š”) | | ์šฐ์„ ์ˆœ์œ„ | ๐ŸŸข ๋‚ฎ์Œ (Phase 2.4) | ### ๐ŸŽฏ ์ „ํ™˜ ๋ชฉํ‘œ - โœ… **13๊ฐœ ๋ชจ๋“  Prisma ํ˜ธ์ถœ์„ `db.ts`์˜ `query()` ํ•จ์ˆ˜๋กœ ๊ต์ฒด** - 11๊ฐœ `$queryRaw` โ†’ `query()` ํ•จ์ˆ˜๋กœ ๊ต์ฒด - 2๊ฐœ ORM ๋ฉ”์„œ๋“œ โ†’ `query()` (SQL ์ƒˆ๋กœ ์ž‘์„ฑ) - โœ… ๋ชจ๋“  ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ํ†ต๊ณผ - โœ… **Prisma import ์™„์ „ ์ œ๊ฑฐ** --- ## ๐Ÿ” Prisma ์‚ฌ์šฉ ํ˜„ํ™ฉ ๋ถ„์„ ### 1. `$queryRaw` / `$queryRawUnsafe` ์‚ฌ์šฉ (11๊ฐœ) **ํ˜„์žฌ ์ƒํƒœ**: SQL์€ ์ด๋ฏธ ์ž‘์„ฑ๋˜์–ด ์žˆ์Œ โœ… **์ „ํ™˜ ์ž‘์—…**: `prisma.$queryRaw` โ†’ `query()` ํ•จ์ˆ˜๋กœ ๊ต์ฒด๋งŒ ํ•˜๋ฉด ๋จ ```typescript // ๊ธฐ์กด await prisma.$queryRaw>`...`; await prisma.$queryRawUnsafe(upsertQuery, ...values); // ์ „ํ™˜ ํ›„ import { query } from "../database/db"; await query>(`...`); await query(upsertQuery, values); ``` ### 2. ORM ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ (2๊ฐœ) **ํ˜„์žฌ ์ƒํƒœ**: Prisma ORM ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ **์ „ํ™˜ ์ž‘์—…**: SQL ์ž‘์„ฑ ํ•„์š” #### 1. dynamic_form_data ์กฐํšŒ (1๊ฐœ) ```typescript // Line 867: ํผ ๋ฐ์ดํ„ฐ ์กฐํšŒ const result = await prisma.dynamic_form_data.findUnique({ where: { id }, select: { data: true }, }); ``` #### 2. screen_layouts ์กฐํšŒ (1๊ฐœ) ```typescript // Line 1101: ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ ์กฐํšŒ const screenLayouts = await prisma.screen_layouts.findMany({ where: { screen_id: screenId, component_type: "widget", }, select: { component_id: true, properties: true, }, }); ``` --- ## ๐Ÿ“ ์ „ํ™˜ ์˜ˆ์‹œ ### ์˜ˆ์‹œ 1: dynamic_form_data ์กฐํšŒ ์ „ํ™˜ **๊ธฐ์กด Prisma ์ฝ”๋“œ:** ```typescript const result = await prisma.dynamic_form_data.findUnique({ where: { id }, select: { data: true }, }); ``` **์ƒˆ๋กœ์šด Raw Query ์ฝ”๋“œ:** ```typescript import { queryOne } from "../database/db"; const result = await queryOne<{ data: any }>( `SELECT data FROM dynamic_form_data WHERE id = $1`, [id] ); ``` ### ์˜ˆ์‹œ 2: screen_layouts ์กฐํšŒ ์ „ํ™˜ **๊ธฐ์กด Prisma ์ฝ”๋“œ:** ```typescript const screenLayouts = await prisma.screen_layouts.findMany({ where: { screen_id: screenId, component_type: "widget", }, select: { component_id: true, properties: true, }, }); ``` **์ƒˆ๋กœ์šด Raw Query ์ฝ”๋“œ:** ```typescript import { query } from "../database/db"; const screenLayouts = await query<{ component_id: string; properties: any; }>( `SELECT component_id, properties FROM screen_layouts WHERE screen_id = $1 AND component_type = $2`, [screenId, "widget"] ); ``` --- ## ๐Ÿงช ํ…Œ์ŠคํŠธ ๊ณ„ํš ### ๋‹จ์œ„ ํ…Œ์ŠคํŠธ (5๊ฐœ) ```typescript describe("DynamicFormService Raw Query ์ „ํ™˜ ํ…Œ์ŠคํŠธ", () => { describe("getFormDataById", () => { test("ํผ ๋ฐ์ดํ„ฐ ์กฐํšŒ ์„ฑ๊ณต", async () => { ... }); test("์กด์žฌํ•˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ", async () => { ... }); }); describe("getScreenLayoutsForControl", () => { test("ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ ์กฐํšŒ ์„ฑ๊ณต", async () => { ... }); test("widget ํƒ€์ž…๋งŒ ํ•„ํ„ฐ๋ง", async () => { ... }); test("๋นˆ ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ", async () => { ... }); }); }); ``` ### ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ (3๊ฐœ ์‹œ๋‚˜๋ฆฌ์˜ค) ```typescript describe("๋™์  ํผ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ", () => { test("ํผ ๋ฐ์ดํ„ฐ UPSERT โ†’ ์กฐํšŒ", async () => { ... }); test("ํผ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ โ†’ ์กฐํšŒ", async () => { ... }); test("ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ ์กฐํšŒ โ†’ ์ œ์–ด ์„ค์ • ํ™•์ธ", async () => { ... }); }); ``` --- ## ๐Ÿ“‹ ์ฒดํฌ๋ฆฌ์ŠคํŠธ ### 1๋‹จ๊ณ„: ORM ํ˜ธ์ถœ ์ „ํ™˜ (2๊ฐœ ํ•จ์ˆ˜) โณ **์ง„ํ–‰ ์˜ˆ์ •** - [ ] `getFormDataById()` - dynamic_form_data.findUnique - [ ] `getScreenLayoutsForControl()` - screen_layouts.findMany ### 2๋‹จ๊ณ„: ํ…Œ์ŠคํŠธ & ๊ฒ€์ฆ โณ **์ง„ํ–‰ ์˜ˆ์ •** - [ ] ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ (5๊ฐœ) - [ ] ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ (3๊ฐœ ์‹œ๋‚˜๋ฆฌ์˜ค) - [ ] Prisma import ์™„์ „ ์ œ๊ฑฐ ํ™•์ธ - [ ] ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ --- ## ๐ŸŽฏ ์™„๋ฃŒ ๊ธฐ์ค€ - [ ] **13๊ฐœ ๋ชจ๋“  Prisma ํ˜ธ์ถœ์„ Raw Query๋กœ ์ „ํ™˜ ์™„๋ฃŒ** - [ ] 11๊ฐœ `$queryRaw` โ†’ `query()` ํ•จ์ˆ˜๋กœ ๊ต์ฒด - [ ] 2๊ฐœ ORM ๋ฉ”์„œ๋“œ โ†’ `query()` ํ•จ์ˆ˜๋กœ ์ „ํ™˜ (SQL ์ž‘์„ฑ) - [ ] **๋ชจ๋“  TypeScript ์ปดํŒŒ์ผ ์˜ค๋ฅ˜ ํ•ด๊ฒฐ** - [ ] **๋ชจ๋“  ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ํ†ต๊ณผ (5๊ฐœ)** - [ ] **๋ชจ๋“  ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ ์™„๋ฃŒ (3๊ฐœ ์‹œ๋‚˜๋ฆฌ์˜ค)** - [ ] **`import prisma` ์™„์ „ ์ œ๊ฑฐ ๋ฐ `import { query } from "../database/db"` ์‚ฌ์šฉ** - [ ] **์„ฑ๋Šฅ ์ €ํ•˜ ์—†์Œ** --- ## ๐Ÿ’ก ํŠน์ด์‚ฌํ•ญ ### SQL์€ ์ด๋ฏธ ๊ฑฐ์˜ ์ž‘์„ฑ๋˜์–ด ์žˆ์Œ ์ด ์„œ๋น„์Šค๋Š” ์ด๋ฏธ 85%๊ฐ€ `$queryRaw`๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด, **SQL ์ž‘์„ฑ์€ ๊ฑฐ์˜ ์™„๋ฃŒ**๋˜์—ˆ์Šต๋‹ˆ๋‹ค: - โœ… UPSERT ๋กœ์ง: SQL ์ž‘์„ฑ ์™„๋ฃŒ (`$queryRawUnsafe` ์‚ฌ์šฉ ์ค‘) - โœ… ์ปฌ๋Ÿผ ์ •๋ณด ์กฐํšŒ: SQL ์ž‘์„ฑ ์™„๋ฃŒ (`$queryRaw` ์‚ฌ์šฉ ์ค‘) - โœ… Primary Key ์กฐํšŒ: SQL ์ž‘์„ฑ ์™„๋ฃŒ (Raw Query ์‚ฌ์šฉ ์ค‘) - โณ **์ „ํ™˜ ์ž‘์—…**: `prisma.$queryRaw` โ†’ `query()` ํ•จ์ˆ˜๋กœ **๋‹จ์ˆœ ๊ต์ฒด๋งŒ ํ•„์š”** - โณ ๋‹จ์ˆœ ์กฐํšŒ: 2๊ฐœ๋งŒ SQL ์ƒˆ๋กœ ์ž‘์„ฑ ํ•„์š” (๋งค์šฐ ๊ฐ„๋‹จํ•œ SELECT ์ฟผ๋ฆฌ) --- **์ž‘์„ฑ์ผ**: 2025-09-30 **์˜ˆ์ƒ ์†Œ์š” ์‹œ๊ฐ„**: 0.5์ผ (SQL์€ 85% ์ž‘์„ฑ ์™„๋ฃŒ, ํ•จ์ˆ˜ ๊ต์ฒด ์ž‘์—… ํ•„์š”) **๋‹ด๋‹น์ž**: ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœํŒ€ **์šฐ์„ ์ˆœ์œ„**: ๐ŸŸข ๋‚ฎ์Œ (Phase 2.4) **์ƒํƒœ**: โณ **์ง„ํ–‰ ์˜ˆ์ •** **ํŠน์ด์‚ฌํ•ญ**: SQL์€ ๊ฑฐ์˜ ์ž‘์„ฑ๋˜์–ด ์žˆ์–ด `prisma.$queryRaw` โ†’ `query()` ๋‹จ์ˆœ ๊ต์ฒด ์ž‘์—…์ด ์ฃผ์š” ์ž‘์—