From 60b4bffdf93b673bb2bb1cdbacd0a4b8274998dd Mon Sep 17 00:00:00 2001 From: dohyeons Date: Wed, 17 Dec 2025 17:30:40 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5/=EB=A1=9C=EB=93=9C=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EA=B5=AC=EC=A1=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/controllers/reportController.ts | 35 ++++++++----- backend-node/src/services/reportService.ts | 49 ++++++++++++------- backend-node/src/types/report.ts | 32 +++++++++--- 3 files changed, 77 insertions(+), 39 deletions(-) diff --git a/backend-node/src/controllers/reportController.ts b/backend-node/src/controllers/reportController.ts index 62c1fd06..c6cd1ee7 100644 --- a/backend-node/src/controllers/reportController.ts +++ b/backend-node/src/controllers/reportController.ts @@ -223,11 +223,25 @@ export class ReportController { }); } - // components JSON 파싱 - const layoutData = { - ...layout, - components: layout.components ? JSON.parse(layout.components) : [], - }; + // components 컬럼에서 JSON 파싱 + const parsedComponents = layout.components ? JSON.parse(layout.components) : null; + + let layoutData; + // 새 구조 (layoutConfig.pages)인지 확인 + if (parsedComponents && parsedComponents.pages && Array.isArray(parsedComponents.pages)) { + // pages 배열을 직접 포함하여 반환 + layoutData = { + ...layout, + pages: parsedComponents.pages, + components: [], // 호환성을 위해 빈 배열 + }; + } else { + // 기존 구조: components 배열 + layoutData = { + ...layout, + components: parsedComponents || [], + }; + } return res.json({ success: true, @@ -248,16 +262,11 @@ export class ReportController { const data: SaveLayoutRequest = req.body; const userId = (req as any).user?.userId || "SYSTEM"; - // 필수 필드 검증 - if ( - !data.canvasWidth || - !data.canvasHeight || - !data.pageOrientation || - !data.components - ) { + // 필수 필드 검증 (페이지 기반 구조) + if (!data.layoutConfig || !data.layoutConfig.pages || data.layoutConfig.pages.length === 0) { return res.status(400).json({ success: false, - message: "필수 레이아웃 정보가 누락되었습니다.", + message: "레이아웃 설정이 필요합니다.", }); } diff --git a/backend-node/src/services/reportService.ts b/backend-node/src/services/reportService.ts index 77087f25..887985bf 100644 --- a/backend-node/src/services/reportService.ts +++ b/backend-node/src/services/reportService.ts @@ -561,7 +561,7 @@ export class ReportService { } /** - * 레이아웃 저장 (쿼리 포함) + * 레이아웃 저장 (쿼리 포함) - 페이지 기반 구조 */ async saveLayout( reportId: string, @@ -569,6 +569,19 @@ export class ReportService { userId: string ): Promise { return transaction(async (client) => { + // 첫 번째 페이지 정보를 기본 레이아웃으로 사용 + const firstPage = data.layoutConfig.pages[0]; + const canvasWidth = firstPage?.width || 210; + const canvasHeight = firstPage?.height || 297; + const pageOrientation = + canvasWidth > canvasHeight ? "landscape" : "portrait"; + const margins = firstPage?.margins || { + top: 20, + bottom: 20, + left: 20, + right: 20, + }; + // 1. 레이아웃 저장 const existingQuery = ` SELECT layout_id FROM report_layout WHERE report_id = $1 @@ -576,7 +589,7 @@ export class ReportService { const existing = await client.query(existingQuery, [reportId]); if (existing.rows.length > 0) { - // 업데이트 + // 업데이트 - components 컬럼에 전체 layoutConfig 저장 const updateQuery = ` UPDATE report_layout SET @@ -594,14 +607,14 @@ export class ReportService { `; await client.query(updateQuery, [ - data.canvasWidth, - data.canvasHeight, - data.pageOrientation, - data.marginTop, - data.marginBottom, - data.marginLeft, - data.marginRight, - JSON.stringify(data.components), + canvasWidth, + canvasHeight, + pageOrientation, + margins.top, + margins.bottom, + margins.left, + margins.right, + JSON.stringify(data.layoutConfig), // 전체 layoutConfig 저장 userId, reportId, ]); @@ -627,14 +640,14 @@ export class ReportService { await client.query(insertQuery, [ layoutId, reportId, - data.canvasWidth, - data.canvasHeight, - data.pageOrientation, - data.marginTop, - data.marginBottom, - data.marginLeft, - data.marginRight, - JSON.stringify(data.components), + canvasWidth, + canvasHeight, + pageOrientation, + margins.top, + margins.bottom, + margins.left, + margins.right, + JSON.stringify(data.layoutConfig), // 전체 layoutConfig 저장 userId, ]); } diff --git a/backend-node/src/types/report.ts b/backend-node/src/types/report.ts index 77cc35d7..2fe1cfd3 100644 --- a/backend-node/src/types/report.ts +++ b/backend-node/src/types/report.ts @@ -116,22 +116,38 @@ export interface UpdateReportRequest { useYn?: string; } +// 페이지 설정 +export interface PageConfig { + page_id: string; + page_name: string; + page_order: number; + width: number; + height: number; + background_color: string; + margins: { + top: number; + bottom: number; + left: number; + right: number; + }; + components: any[]; +} + +// 레이아웃 설정 +export interface ReportLayoutConfig { + pages: PageConfig[]; +} + // 레이아웃 저장 요청 export interface SaveLayoutRequest { - canvasWidth: number; - canvasHeight: number; - pageOrientation: string; - marginTop: number; - marginBottom: number; - marginLeft: number; - marginRight: number; - components: any[]; + layoutConfig: ReportLayoutConfig; queries?: Array<{ id: string; name: string; type: "MASTER" | "DETAIL"; sqlQuery: string; parameters: string[]; + externalConnectionId?: number; }>; }