From 67dced74bdd7d3611a8540de321821b2f798426e Mon Sep 17 00:00:00 2001 From: kjs Date: Tue, 30 Sep 2025 16:29:59 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20Phase=202.1=20Stage=202=20complete=20-?= =?UTF-8?q?=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EA=B4=80=EB=A6=AC=20?= =?UTF-8?q?=EC=A0=84=ED=99=98=20(12/46)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stage 2 완료: 레이아웃 관리 Raw Query 전환 ✅ 전환 완료 (4개 Prisma 호출): 9. saveLayout() - 레이아웃 저장 - 권한 확인 쿼리 - 기존 레이아웃 삭제 (DELETE) - 메타데이터 INSERT (격자 설정, 해상도) - 컴포넌트 루프 INSERT (JSON properties) 10. getLayout() - 레이아웃 조회 - 권한 확인 쿼리 - 레이아웃 조회 (ORDER BY display_order) 📊 진행률: 12/46 (26.1%) 🎯 다음: Stage 3 템플릿 & 메뉴 관리 전환 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../src/services/screenManagementService.ts | 107 ++++++++++-------- 1 file changed, 62 insertions(+), 45 deletions(-) diff --git a/backend-node/src/services/screenManagementService.ts b/backend-node/src/services/screenManagementService.ts index d15301c7..d11ed360 100644 --- a/backend-node/src/services/screenManagementService.ts +++ b/backend-node/src/services/screenManagementService.ts @@ -1116,7 +1116,7 @@ export class ScreenManagementService { // ======================================== /** - * 레이아웃 저장 + * 레이아웃 저장 (✅ Raw Query 전환 완료) */ async saveLayout( screenId: number, @@ -1130,22 +1130,26 @@ export class ScreenManagementService { console.log(`해상도 설정:`, layoutData.screenResolution); // 권한 확인 - const existingScreen = await prisma.screen_definitions.findUnique({ - where: { screen_id: screenId }, - }); + const screens = await query<{ company_code: string | null }>( + `SELECT company_code FROM screen_definitions WHERE screen_id = $1 LIMIT 1`, + [screenId] + ); - if (!existingScreen) { + if (screens.length === 0) { throw new Error("화면을 찾을 수 없습니다."); } + const existingScreen = screens[0]; + if (companyCode !== "*" && existingScreen.company_code !== companyCode) { throw new Error("이 화면의 레이아웃을 저장할 권한이 없습니다."); } // 기존 레이아웃 삭제 (컴포넌트와 메타데이터 모두) - await prisma.screen_layouts.deleteMany({ - where: { screen_id: screenId }, - }); + await query( + `DELETE FROM screen_layouts WHERE screen_id = $1`, + [screenId] + ); // 1. 메타데이터 저장 (격자 설정과 해상도 정보) if (layoutData.gridSettings || layoutData.screenResolution) { @@ -1154,20 +1158,24 @@ export class ScreenManagementService { screenResolution: layoutData.screenResolution, }; - await prisma.screen_layouts.create({ - data: { - screen_id: screenId, - component_type: "_metadata", // 특별한 타입으로 메타데이터 식별 - component_id: `_metadata_${screenId}`, - parent_id: null, - position_x: 0, - position_y: 0, - width: 0, - height: 0, - properties: metadata, - display_order: -1, // 메타데이터는 맨 앞에 배치 - }, - }); + await query( + `INSERT INTO screen_layouts ( + screen_id, component_type, component_id, parent_id, + position_x, position_y, width, height, properties, display_order + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`, + [ + screenId, + "_metadata", // 특별한 타입으로 메타데이터 식별 + `_metadata_${screenId}`, + null, + 0, + 0, + 0, + 0, + JSON.stringify(metadata), + -1, // 메타데이터는 맨 앞에 배치 + ] + ); console.log(`메타데이터 저장 완료:`, metadata); } @@ -1185,7 +1193,7 @@ export class ScreenManagementService { title: (component as any).title, }); - // Prisma JSON 필드에 맞는 타입으로 변환 + // JSON 필드에 맞는 타입으로 변환 const properties: any = { ...componentData, position: { @@ -1199,26 +1207,30 @@ export class ScreenManagementService { }, }; - await prisma.screen_layouts.create({ - data: { - screen_id: screenId, - component_type: component.type, - component_id: component.id, - parent_id: component.parentId || null, - position_x: component.position.x, - position_y: component.position.y, - width: component.size.width, - height: component.size.height, - properties: properties, - }, - }); + await query( + `INSERT INTO screen_layouts ( + screen_id, component_type, component_id, parent_id, + position_x, position_y, width, height, properties + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, + [ + screenId, + component.type, + component.id, + component.parentId || null, + component.position.x, + component.position.y, + component.size.width, + component.size.height, + JSON.stringify(properties), + ] + ); } console.log(`=== 레이아웃 저장 완료 ===`); } /** - * 레이아웃 조회 + * 레이아웃 조회 (✅ Raw Query 전환 완료) */ async getLayout( screenId: number, @@ -1228,22 +1240,27 @@ export class ScreenManagementService { console.log(`화면 ID: ${screenId}`); // 권한 확인 - const existingScreen = await prisma.screen_definitions.findUnique({ - where: { screen_id: screenId }, - }); + const screens = await query<{ company_code: string | null }>( + `SELECT company_code FROM screen_definitions WHERE screen_id = $1 LIMIT 1`, + [screenId] + ); - if (!existingScreen) { + if (screens.length === 0) { return null; } + const existingScreen = screens[0]; + if (companyCode !== "*" && existingScreen.company_code !== companyCode) { throw new Error("이 화면의 레이아웃을 조회할 권한이 없습니다."); } - const layouts = await prisma.screen_layouts.findMany({ - where: { screen_id: screenId }, - orderBy: { display_order: "asc" }, - }); + const layouts = await query( + `SELECT * FROM screen_layouts + WHERE screen_id = $1 + ORDER BY display_order ASC NULLS LAST, layout_id ASC`, + [screenId] + ); console.log(`DB에서 조회된 레이아웃 수: ${layouts.length}`);