feat: Phase 2.1 - Start ScreenManagementService Raw Query migration (2/46)

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 <noreply@anthropic.com>
This commit is contained in:
kjs 2025-09-30 16:20:09 +09:00
parent ba10e7a12b
commit 95c9811c3b
1 changed files with 62 additions and 39 deletions

View File

@ -1,4 +1,5 @@
import prisma from "../config/database"; // ✅ Prisma → Raw Query 전환 (Phase 2.1)
import { query, transaction } from "../database/db";
import { import {
ScreenDefinition, ScreenDefinition,
CreateScreenRequest, CreateScreenRequest,
@ -39,7 +40,7 @@ export class ScreenManagementService {
// ======================================== // ========================================
/** /**
* * ( Raw Query )
*/ */
async createScreen( async createScreen(
screenData: CreateScreenRequest, screenData: CreateScreenRequest,
@ -49,75 +50,97 @@ export class ScreenManagementService {
console.log(`요청 데이터:`, screenData); console.log(`요청 데이터:`, screenData);
console.log(`사용자 회사 코드:`, userCompanyCode); console.log(`사용자 회사 코드:`, userCompanyCode);
// 화면 코드 중복 확인 // 화면 코드 중복 확인 (Raw Query)
const existingScreen = await prisma.screen_definitions.findFirst({ const existingResult = await query<{ screen_id: number }>(
where: { `SELECT screen_id FROM screen_definitions
screen_code: screenData.screenCode, WHERE screen_code = $1 AND is_active != 'D'
is_active: { not: "D" }, // 삭제되지 않은 화면만 중복 검사 LIMIT 1`,
}, [screenData.screenCode]
}); );
console.log( console.log(
`화면 코드 '${screenData.screenCode}' 중복 검사 결과:`, `화면 코드 '${screenData.screenCode}' 중복 검사 결과:`,
existingScreen ? "중복됨" : "사용 가능" existingResult.length > 0 ? "중복됨" : "사용 가능"
); );
if (existingScreen) { if (existingResult.length > 0) {
console.log(`기존 화면 정보:`, existingScreen); console.log(`기존 화면 정보:`, existingResult[0]);
throw new Error("이미 존재하는 화면 코드입니다."); throw new Error("이미 존재하는 화면 코드입니다.");
} }
const screen = await prisma.screen_definitions.create({ // 화면 생성 (Raw Query)
data: { const [screen] = await query<any>(
screen_name: screenData.screenName, `INSERT INTO screen_definitions (
screen_code: screenData.screenCode, screen_name, screen_code, table_name, company_code, description, created_by
table_name: screenData.tableName, ) VALUES ($1, $2, $3, $4, $5, $6)
company_code: screenData.companyCode, RETURNING *`,
description: screenData.description, [
created_by: screenData.createdBy, screenData.screenName,
}, screenData.screenCode,
}); screenData.tableName,
screenData.companyCode,
screenData.description || null,
screenData.createdBy,
]
);
return this.mapToScreenDefinition(screen); return this.mapToScreenDefinition(screen);
} }
/** /**
* ( ) - * ( ) - ( Raw Query )
*/ */
async getScreensByCompany( async getScreensByCompany(
companyCode: string, companyCode: string,
page: number = 1, page: number = 1,
size: number = 20 size: number = 20
): Promise<PaginatedResponse<ScreenDefinition>> { ): Promise<PaginatedResponse<ScreenDefinition>> {
const whereClause: any = { is_active: { not: "D" } }; // 삭제된 화면 제외 const offset = (page - 1) * size;
// WHERE 절 동적 생성
const whereConditions: string[] = ["is_active != 'D'"];
const params: any[] = [];
if (companyCode !== "*") { if (companyCode !== "*") {
whereClause.company_code = companyCode; whereConditions.push(`company_code = $${params.length + 1}`);
params.push(companyCode);
} }
const [screens, total] = await Promise.all([ const whereSQL = whereConditions.join(" AND ");
prisma.screen_definitions.findMany({
where: whereClause, // 페이징 쿼리 (Raw Query)
skip: (page - 1) * size, const [screens, totalResult] = await Promise.all([
take: size, query<any>(
orderBy: { created_date: "desc" }, `SELECT * FROM screen_definitions
}), WHERE ${whereSQL}
prisma.screen_definitions.count({ where: whereClause }), 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 = [ 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<string, string>(); let tableLabelMap = new Map<string, string>();
if (tableNames.length > 0) { if (tableNames.length > 0) {
try { try {
const tableLabels = await prisma.table_labels.findMany({ const placeholders = tableNames.map((_, i) => `$${i + 1}`).join(", ");
where: { table_name: { in: tableNames } }, const tableLabels = await query<{ table_name: string; table_label: string | null }>(
select: { table_name: true, table_label: true }, `SELECT table_name, table_label FROM table_labels
}); WHERE table_name IN (${placeholders})`,
tableNames
);
tableLabelMap = new Map( tableLabelMap = new Map(
tableLabels.map((tl) => [ tableLabels.map((tl) => [