From 95c9811c3b6a72c5b1f74c663456deb45481695e Mon Sep 17 00:00:00 2001 From: kjs Date: Tue, 30 Sep 2025 16:20:09 +0900 Subject: [PATCH] feat: Phase 2.1 - Start ScreenManagementService Raw Query migration (2/46) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1단계 기본 CRUD 전환 시작 (2/6 완료) ✅ 전환 완료 (2개): 1. createScreen() - 화면 생성 - 중복 확인: findFirst → Raw Query SELECT - 생성: create → Raw Query INSERT RETURNING - 파라미터 바인딩 적용 2. getScreensByCompany() - 화면 목록 조회 (페이징) - 동적 WHERE 절 생성 - Promise.all로 병렬 조회 (목록 + 총개수) - table_labels IN 쿼리 전환 🔧 주요 변경사항: - Prisma import 제거 → query, transaction import - 파라미터 바인딩으로 SQL Injection 방지 - COUNT 결과 문자열 → 숫자 변환 📊 진행률: - 전환 완료: 2/46 (4.3%) - 남은 작업: 44개 Prisma 호출 🎯 다음 작업: - getScreenByCode() - getScreenById() - updateScreen() - deleteScreen() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../src/services/screenManagementService.ts | 101 +++++++++++------- 1 file changed, 62 insertions(+), 39 deletions(-) diff --git a/backend-node/src/services/screenManagementService.ts b/backend-node/src/services/screenManagementService.ts index 3ae70d1f..3d05e253 100644 --- a/backend-node/src/services/screenManagementService.ts +++ b/backend-node/src/services/screenManagementService.ts @@ -1,4 +1,5 @@ -import prisma from "../config/database"; +// ✅ Prisma → Raw Query 전환 (Phase 2.1) +import { query, transaction } from "../database/db"; import { ScreenDefinition, CreateScreenRequest, @@ -39,7 +40,7 @@ export class ScreenManagementService { // ======================================== /** - * 화면 정의 생성 + * 화면 정의 생성 (✅ Raw Query 전환 완료) */ async createScreen( screenData: CreateScreenRequest, @@ -49,75 +50,97 @@ export class ScreenManagementService { console.log(`요청 데이터:`, screenData); console.log(`사용자 회사 코드:`, userCompanyCode); - // 화면 코드 중복 확인 - const existingScreen = await prisma.screen_definitions.findFirst({ - where: { - screen_code: screenData.screenCode, - is_active: { not: "D" }, // 삭제되지 않은 화면만 중복 검사 - }, - }); + // 화면 코드 중복 확인 (Raw Query) + const existingResult = await query<{ screen_id: number }>( + `SELECT screen_id FROM screen_definitions + WHERE screen_code = $1 AND is_active != 'D' + LIMIT 1`, + [screenData.screenCode] + ); console.log( `화면 코드 '${screenData.screenCode}' 중복 검사 결과:`, - existingScreen ? "중복됨" : "사용 가능" + existingResult.length > 0 ? "중복됨" : "사용 가능" ); - if (existingScreen) { - console.log(`기존 화면 정보:`, existingScreen); + if (existingResult.length > 0) { + console.log(`기존 화면 정보:`, existingResult[0]); throw new Error("이미 존재하는 화면 코드입니다."); } - const screen = await prisma.screen_definitions.create({ - data: { - screen_name: screenData.screenName, - screen_code: screenData.screenCode, - table_name: screenData.tableName, - company_code: screenData.companyCode, - description: screenData.description, - created_by: screenData.createdBy, - }, - }); + // 화면 생성 (Raw Query) + const [screen] = await query( + `INSERT INTO screen_definitions ( + screen_name, screen_code, table_name, company_code, description, created_by + ) VALUES ($1, $2, $3, $4, $5, $6) + RETURNING *`, + [ + screenData.screenName, + screenData.screenCode, + screenData.tableName, + screenData.companyCode, + screenData.description || null, + screenData.createdBy, + ] + ); return this.mapToScreenDefinition(screen); } /** - * 회사별 화면 목록 조회 (페이징 지원) - 활성 화면만 + * 회사별 화면 목록 조회 (페이징 지원) - 활성 화면만 (✅ Raw Query 전환 완료) */ async getScreensByCompany( companyCode: string, page: number = 1, size: number = 20 ): Promise> { - const whereClause: any = { is_active: { not: "D" } }; // 삭제된 화면 제외 + const offset = (page - 1) * size; + + // WHERE 절 동적 생성 + const whereConditions: string[] = ["is_active != 'D'"]; + const params: any[] = []; if (companyCode !== "*") { - whereClause.company_code = companyCode; + whereConditions.push(`company_code = $${params.length + 1}`); + params.push(companyCode); } - const [screens, total] = await Promise.all([ - prisma.screen_definitions.findMany({ - where: whereClause, - skip: (page - 1) * size, - take: size, - orderBy: { created_date: "desc" }, - }), - prisma.screen_definitions.count({ where: whereClause }), + const whereSQL = whereConditions.join(" AND "); + + // 페이징 쿼리 (Raw Query) + const [screens, totalResult] = await Promise.all([ + query( + `SELECT * FROM screen_definitions + WHERE ${whereSQL} + ORDER BY created_date DESC + LIMIT $${params.length + 1} OFFSET $${params.length + 2}`, + [...params, size, offset] + ), + query<{ count: string }>( + `SELECT COUNT(*)::text as count FROM screen_definitions + WHERE ${whereSQL}`, + params + ), ]); - // 테이블 라벨 정보를 한 번에 조회 + const total = parseInt(totalResult[0]?.count || "0", 10); + + // 테이블 라벨 정보를 한 번에 조회 (Raw Query) const tableNames = [ - ...new Set(screens.map((s) => s.table_name).filter(Boolean)), + ...new Set(screens.map((s: any) => s.table_name).filter(Boolean)), ]; let tableLabelMap = new Map(); if (tableNames.length > 0) { try { - const tableLabels = await prisma.table_labels.findMany({ - where: { table_name: { in: tableNames } }, - select: { table_name: true, table_label: true }, - }); + const placeholders = tableNames.map((_, i) => `$${i + 1}`).join(", "); + const tableLabels = await query<{ table_name: string; table_label: string | null }>( + `SELECT table_name, table_label FROM table_labels + WHERE table_name IN (${placeholders})`, + tableNames + ); tableLabelMap = new Map( tableLabels.map((tl) => [