feat: V2/Unified 컴포넌트 설정 스키마 정비 및 레거시 컴포넌트 제거

- 레거시 컴포넌트를 제거하고, V2 및 Unified 컴포넌트 전용 Zod 스키마와 기본값 레지스트리를 통합 관리합니다.
- V2 컴포넌트와 Unified 컴포넌트의 overrides 스키마를 정의하고, 기본값과의 병합 로직을 추가하였습니다.
- 레이아웃 조회 시 V2 테이블을 우선적으로 조회하고, 없을 경우 V1 테이블을 조회하도록 개선하였습니다.
- 관련된 테스트 계획 및 에러 처리 계획을 수립하여 안정성을 높였습니다.
This commit is contained in:
kjs
2026-01-28 16:08:19 +09:00
parent 7a7d06e785
commit 4fe512aeda
11 changed files with 910 additions and 207 deletions

View File

@@ -1586,6 +1586,7 @@ export class ScreenManagementService {
/**
* 레이아웃 조회 (✅ Raw Query 전환 완료)
* V2 테이블 우선 조회 → 없으면 V1 테이블 조회
*/
async getLayout(
screenId: number,
@@ -1610,6 +1611,76 @@ export class ScreenManagementService {
throw new Error("이 화면의 레이아웃을 조회할 권한이 없습니다.");
}
// 🆕 V2 테이블 우선 조회 (회사별 → 공통(*))
let v2Layout = await queryOne<{ layout_data: any }>(
`SELECT layout_data FROM screen_layouts_v2
WHERE screen_id = $1 AND company_code = $2`,
[screenId, companyCode]
);
// 회사별 레이아웃 없으면 공통(*) 조회
if (!v2Layout && companyCode !== "*") {
v2Layout = await queryOne<{ layout_data: any }>(
`SELECT layout_data FROM screen_layouts_v2
WHERE screen_id = $1 AND company_code = '*'`,
[screenId]
);
}
// V2 레이아웃이 있으면 V2 형식으로 반환
if (v2Layout && v2Layout.layout_data) {
console.log(`V2 레이아웃 발견, V2 형식으로 반환`);
const layoutData = v2Layout.layout_data;
// V2 형식의 components를 LayoutData 형식으로 변환
const components = (layoutData.components || []).map((comp: any) => ({
id: comp.id,
type: comp.overrides?.type || "component",
position: comp.position || { x: 0, y: 0, z: 1 },
size: comp.size || { width: 200, height: 100 },
componentUrl: comp.url,
componentType: comp.overrides?.type,
componentConfig: comp.overrides || {},
displayOrder: comp.displayOrder || 0,
...comp.overrides,
}));
// screenResolution이 없으면 컴포넌트 위치 기반으로 자동 계산
let screenResolution = layoutData.screenResolution;
if (!screenResolution && components.length > 0) {
let maxRight = 0;
let maxBottom = 0;
for (const comp of layoutData.components || []) {
const right = (comp.position?.x || 0) + (comp.size?.width || 200);
const bottom = (comp.position?.y || 0) + (comp.size?.height || 100);
maxRight = Math.max(maxRight, right);
maxBottom = Math.max(maxBottom, bottom);
}
// 여백 100px 추가, 최소 1200x800 보장
screenResolution = {
width: Math.max(1200, maxRight + 100),
height: Math.max(800, maxBottom + 100),
};
console.log(`screenResolution 자동 계산:`, screenResolution);
}
return {
components,
gridSettings: layoutData.gridSettings || {
columns: 12,
gap: 16,
padding: 16,
snapToGrid: true,
showGrid: true,
},
screenResolution,
};
}
console.log(`V2 레이아웃 없음, V1 테이블 조회`);
const layouts = await query<any>(
`SELECT * FROM screen_layouts
WHERE screen_id = $1