231 lines
6.6 KiB
Markdown
231 lines
6.6 KiB
Markdown
# 📝 Phase 2.4: DynamicFormService Raw Query 전환 계획
|
|
|
|
## 📋 개요
|
|
|
|
DynamicFormService는 **13개의 Prisma 호출**이 있습니다. 대부분(약 11개)은 `$queryRaw`를 사용하고 있어 SQL은 이미 작성되어 있지만, **Prisma 클라이언트를 완전히 제거하려면 13개 모두를 `db.ts`의 `query` 함수로 교체**해야 합니다.
|
|
|
|
### 📊 기본 정보
|
|
|
|
| 항목 | 내용 |
|
|
| --------------- | ------------------------------------------------- |
|
|
| 파일 위치 | `backend-node/src/services/dynamicFormService.ts` |
|
|
| 파일 크기 | 1,213 라인 |
|
|
| Prisma 호출 | 0개 (전환 완료) |
|
|
| **현재 진행률** | **13/13 (100%)** ✅ **완료** |
|
|
| **전환 상태** | **Raw Query로 전환 완료** |
|
|
| 복잡도 | 낮음 (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<Array<{ column_name; data_type }>>`...`;
|
|
await prisma.$queryRawUnsafe(upsertQuery, ...values);
|
|
|
|
// 전환 후
|
|
import { query } from "../database/db";
|
|
await query<Array<{ column_name: string; data_type: string }>>(`...`);
|
|
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 () => { ... });
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 전환 완료 내역
|
|
|
|
### ✅ 전환된 함수들 (13개 Raw Query 호출)
|
|
|
|
1. **getTableColumnInfo()** - 컬럼 정보 조회
|
|
2. **getPrimaryKeyColumns()** - 기본 키 조회
|
|
3. **getNotNullColumns()** - NOT NULL 컬럼 조회
|
|
4. **upsertFormData()** - UPSERT 실행
|
|
5. **partialUpdateFormData()** - 부분 업데이트
|
|
6. **updateFormData()** - 전체 업데이트
|
|
7. **deleteFormData()** - 데이터 삭제
|
|
8. **getFormDataById()** - 폼 데이터 조회
|
|
9. **getTableColumns()** - 테이블 컬럼 조회
|
|
10. **getTablePrimaryKeys()** - 기본 키 조회
|
|
11. **getScreenLayoutsForControl()** - 화면 레이아웃 조회
|
|
|
|
### 🔧 주요 기술적 해결 사항
|
|
|
|
1. **Prisma import 완전 제거**: `import { query, queryOne } from "../database/db"`
|
|
2. **동적 UPSERT 쿼리**: PostgreSQL ON CONFLICT 구문 사용
|
|
3. **부분 업데이트**: 동적 SET 절 생성
|
|
4. **타입 변환**: PostgreSQL 타입 자동 변환 로직 유지
|
|
|
|
## 📋 체크리스트
|
|
|
|
### 1단계: ORM 호출 전환 ✅ **완료**
|
|
|
|
- [x] `getFormDataById()` - queryOne 전환
|
|
- [x] `getScreenLayoutsForControl()` - query 전환
|
|
- [x] 모든 Raw Query 함수 전환
|
|
|
|
### 2단계: 테스트 & 검증 ⏳ **진행 예정**
|
|
|
|
- [ ] 단위 테스트 작성 (5개)
|
|
- [ ] 통합 테스트 작성 (3개 시나리오)
|
|
- [x] Prisma import 완전 제거 확인 ✅
|
|
- [ ] 성능 테스트
|
|
|
|
---
|
|
|
|
## 🎯 완료 기준
|
|
|
|
- [x] **13개 모든 Prisma 호출을 Raw Query로 전환 완료** ✅
|
|
- [x] 11개 `$queryRaw` → `query()` 함수로 교체 ✅
|
|
- [x] 2개 ORM 메서드 → `query()` 함수로 전환 ✅
|
|
- [x] **모든 TypeScript 컴파일 오류 해결** ✅
|
|
- [x] **`import prisma` 완전 제거** ✅
|
|
- [ ] **모든 단위 테스트 통과 (5개)** ⏳
|
|
- [ ] **모든 통합 테스트 작성 완료 (3개 시나리오)** ⏳
|
|
- [ ] **성능 저하 없음** ⏳
|
|
|
|
---
|
|
|
|
**작성일**: 2025-09-30
|
|
**완료일**: 2025-10-01
|
|
**소요 시간**: 완료됨 (이전에 전환)
|
|
**담당자**: 백엔드 개발팀
|
|
**우선순위**: 🟢 낮음 (Phase 2.4)
|
|
**상태**: ✅ **전환 완료** (테스트 필요)
|
|
**특이사항**: SQL은 이미 작성되어 있었고, `query()` 함수로 교체 완료
|