feat: 멀티테넌시 지원을 위한 레이어 관리 기능 추가

- 레이어 목록 조회, 특정 레이어 레이아웃 조회, 레이어 삭제 및 조건 설정 업데이트 기능을 추가했습니다.
- 엔티티 참조 데이터 조회 및 공통 코드 데이터 조회에 멀티테넌시 필터를 적용하여 인증된 사용자의 회사 코드에 따라 데이터 접근을 제한했습니다.
- 레이어 관리 패널에서 기본 레이어와 조건부 레이어의 컴포넌트를 통합하여 조건부 영역의 표시를 개선했습니다.
- 레이아웃 저장 시 레이어 ID를 포함하여 레이어별로 저장할 수 있도록 변경했습니다.
This commit is contained in:
kjs
2026-02-09 13:21:56 +09:00
parent 84eb035069
commit 1c71b3aa83
14 changed files with 1571 additions and 598 deletions

View File

@@ -28,6 +28,7 @@ interface LegacyComponentData {
interface LegacyLayoutData {
components: LegacyComponentData[];
layers?: any[]; // 레이어 시스템
gridSettings?: any;
screenResolution?: any;
metadata?: any;
@@ -140,21 +141,22 @@ function applyDefaultsToSplitPanelComponents(mergedConfig: Record<string, any>):
// V2 → Legacy 변환 (로드 시)
// ============================================
export function convertV2ToLegacy(v2Layout: LayoutV2 | null): LegacyLayoutData | null {
if (!v2Layout || !v2Layout.components) {
if (!v2Layout) {
return null;
}
const components: LegacyComponentData[] = v2Layout.components.map((comp) => {
// V2 컴포넌트를 Legacy 컴포넌트로 변환하는 함수 (레이어 내 컴포넌트에도 재사용)
const convertV2Component = (comp: ComponentV2, layerId?: string): LegacyComponentData => {
const componentType = getComponentTypeFromUrl(comp.url);
const defaults = getDefaultsByUrl(comp.url);
let mergedConfig = mergeComponentConfig(defaults, comp.overrides);
// 🆕 분할 패널인 경우 내부 컴포넌트에도 기본값 적용
// 분할 패널인 경우 내부 컴포넌트에도 기본값 적용
if (componentType === "v2-split-panel-layout") {
mergedConfig = applyDefaultsToSplitPanelComponents(mergedConfig);
}
// 🆕 탭 위젯인 경우 탭 내부 컴포넌트에도 기본값 적용
// 탭 위젯인 경우 탭 내부 컴포넌트에도 기본값 적용
if (componentType === "v2-tabs-widget" && mergedConfig.tabs) {
mergedConfig = {
...mergedConfig,
@@ -170,7 +172,6 @@ export function convertV2ToLegacy(v2Layout: LayoutV2 | null): LegacyLayoutData |
};
}
// 🆕 overrides에서 상위 레벨 속성들 추출
const overrides = comp.overrides || {};
return {
@@ -181,45 +182,68 @@ export function convertV2ToLegacy(v2Layout: LayoutV2 | null): LegacyLayoutData |
position: comp.position,
size: comp.size,
componentConfig: mergedConfig,
// 🆕 상위 레벨 속성 복원 (테이블/컬럼 연결 정보)
// 상위 레벨 속성 복원
tableName: overrides.tableName,
columnName: overrides.columnName,
label: overrides.label || mergedConfig.label || "", // 라벨이 없으면 빈 문자열
label: overrides.label || mergedConfig.label || "",
required: overrides.required,
readonly: overrides.readonly,
hidden: overrides.hidden, // 🆕 숨김 설정 복원
hidden: overrides.hidden,
codeCategory: overrides.codeCategory,
inputType: overrides.inputType,
webType: overrides.webType,
// 🆕 autoFill 설정 복원 (자동 입력 기능)
autoFill: overrides.autoFill,
// 🆕 style 설정 복원 (라벨 텍스트, 라벨 스타일 등)
style: overrides.style || {},
// 🔧 webTypeConfig 복원 (버튼 제어기능, 플로우 가시성 등)
webTypeConfig: overrides.webTypeConfig || {},
// 기존 구조 호환을 위한 추가 필드
parentId: null,
gridColumns: 12,
gridRowIndex: 0,
// 🆕 레이어 ID 복원
...(layerId ? { layerId } : {}),
};
});
};
// 🆕 레이어 구조가 있는 경우 (v2.1)
const v2Layers = (v2Layout as any).layers;
if (v2Layers && Array.isArray(v2Layers) && v2Layers.length > 0) {
// 모든 레이어의 컴포넌트를 평탄화하여 layout.components에 저장 (layerId 포함)
const allComponents: LegacyComponentData[] = [];
const legacyLayers = v2Layers.map((layer: any) => {
const layerComponents = (layer.components || []).map((comp: any) =>
convertV2Component(comp, layer.id)
);
allComponents.push(...layerComponents);
return {
...layer,
// layer.components는 legacy 변환 시 빈 배열로 (layout.components + layerId 방식 사용)
components: [],
};
});
return {
components: allComponents,
layers: legacyLayers,
gridSettings: v2Layout.gridSettings || {
enabled: true, size: 20, color: "#d1d5db", opacity: 0.5,
snapToGrid: true, columns: 12, gap: 16, padding: 16,
},
screenResolution: v2Layout.screenResolution || { width: 1920, height: 1080 },
};
}
// 레이어 없는 기존 방식
if (!v2Layout.components) return null;
const components: LegacyComponentData[] = v2Layout.components.map((comp) => convertV2Component(comp));
return {
components,
gridSettings: v2Layout.gridSettings || {
enabled: true,
size: 20,
color: "#d1d5db",
opacity: 0.5,
snapToGrid: true,
columns: 12,
gap: 16,
padding: 16,
},
screenResolution: v2Layout.screenResolution || {
width: 1920,
height: 1080,
enabled: true, size: 20, color: "#d1d5db", opacity: 0.5,
snapToGrid: true, columns: 12, gap: 16, padding: 16,
},
screenResolution: v2Layout.screenResolution || { width: 1920, height: 1080 },
};
}
@@ -227,7 +251,8 @@ export function convertV2ToLegacy(v2Layout: LayoutV2 | null): LegacyLayoutData |
// Legacy → V2 변환 (저장 시)
// ============================================
export function convertLegacyToV2(legacyLayout: LegacyLayoutData): LayoutV2 {
const components: ComponentV2[] = legacyLayout.components.map((comp, index) => {
// 컴포넌트 변환 함수 (레이어 내 컴포넌트 변환에도 재사용)
const convertComponent = (comp: LegacyComponentData, index: number): ComponentV2 => {
// 컴포넌트 타입 결정
const componentType = comp.componentType || comp.widgetType || comp.type || "unknown";
const url = getComponentUrl(componentType);
@@ -301,12 +326,33 @@ export function convertLegacyToV2(legacyLayout: LegacyLayoutData): LayoutV2 {
displayOrder: index,
overrides: overrides,
};
});
};
// 🆕 레이어 정보 변환 (layers가 있으면 레이어 구조로 저장)
const legacyLayers = (legacyLayout as any).layers;
if (legacyLayers && Array.isArray(legacyLayers) && legacyLayers.length > 0) {
const v2Layers = legacyLayers.map((layer: any) => ({
...layer,
// 레이어 내 컴포넌트를 V2 형식으로 변환
components: (layer.components || []).map((comp: any, idx: number) => convertComponent(comp, idx)),
}));
return {
version: "2.1",
layers: v2Layers,
components: [], // 레이어 구조 사용 시 상위 components는 빈 배열
gridSettings: legacyLayout.gridSettings,
screenResolution: legacyLayout.screenResolution,
metadata: legacyLayout.metadata,
};
}
// 레이어 없으면 기존 방식 (컴포넌트만)
const components = legacyLayout.components.map((comp, index) => convertComponent(comp, index));
return {
version: "2.0",
components,
// 레이아웃 메타데이터 포함
gridSettings: legacyLayout.gridSettings,
screenResolution: legacyLayout.screenResolution,
metadata: legacyLayout.metadata,
@@ -317,7 +363,11 @@ export function convertLegacyToV2(legacyLayout: LegacyLayoutData): LayoutV2 {
// V2 레이아웃 유효성 검사
// ============================================
export function isValidV2Layout(data: any): data is LayoutV2 {
return data && typeof data === "object" && data.version === "2.0" && Array.isArray(data.components);
if (!data || typeof data !== "object") return false;
// v2.0: components 기반, v2.1: layers 기반
const isV2 = data.version === "2.0" && Array.isArray(data.components);
const isV21 = data.version === "2.1" && Array.isArray(data.layers);
return isV2 || isV21;
}
// ============================================