feat: Docker 및 컴포넌트 최적화
- Docker Compose 설정에서 Node.js 메모리 제한을 8192MB로 증가시키고, Next.js telemetry를 비활성화하여 성능을 개선하였습니다. - Next.js 구성에서 메모리 사용량 최적화를 위한 webpackMemoryOptimizations를 활성화하였습니다. - ScreenModal 컴포넌트에서 overflow 속성을 조정하여 라벨이 잘리지 않도록 개선하였습니다. - InteractiveScreenViewerDynamic 컴포넌트에서 라벨 표시 여부를 확인하는 로직을 추가하여 사용자 경험을 향상시켰습니다. - RealtimePreviewDynamic 컴포넌트에서 라벨 표시 및 디버깅 로그를 추가하여 렌더링 과정을 추적할 수 있도록 하였습니다. - ImprovedButtonControlConfigPanel에서 controlMode 설정을 추가하여 플로우 제어 기능을 개선하였습니다. - V2PropertiesPanel에서 라벨 텍스트 및 표시 상태 업데이트 로직을 개선하여 일관성을 높였습니다. - DynamicComponentRenderer에서 라벨 표시 로직을 개선하여 사용자 정의 스타일을 보다 효과적으로 적용할 수 있도록 하였습니다. - layoutV2Converter에서 webTypeConfig를 병합하여 버튼 제어 기능과 플로우 가시성을 보존하였습니다.
This commit is contained in:
@@ -43,13 +43,20 @@ export interface ButtonExecutionResult {
|
||||
}
|
||||
|
||||
interface ControlConfig {
|
||||
type: "relationship";
|
||||
relationshipConfig: {
|
||||
type: "relationship" | "flow";
|
||||
relationshipConfig?: {
|
||||
relationshipId: string;
|
||||
relationshipName: string;
|
||||
executionTiming: "before" | "after" | "replace";
|
||||
contextData?: Record<string, any>;
|
||||
};
|
||||
// 🆕 플로우 기반 제어 설정
|
||||
flowConfig?: {
|
||||
flowId: number;
|
||||
flowName: string;
|
||||
executionTiming: "before" | "after" | "replace";
|
||||
contextData?: Record<string, any>;
|
||||
};
|
||||
}
|
||||
|
||||
interface ExecutionPlan {
|
||||
@@ -163,15 +170,22 @@ export class ImprovedButtonActionExecutor {
|
||||
return plan;
|
||||
}
|
||||
|
||||
// enableDataflowControl 체크를 제거하고 dataflowConfig만 있으면 실행
|
||||
// 🔧 controlMode가 없으면 flowConfig/relationshipConfig 존재 여부로 자동 판단
|
||||
const effectiveControlMode = dataflowConfig.controlMode
|
||||
|| (dataflowConfig.flowConfig ? "flow" : null)
|
||||
|| (dataflowConfig.relationshipConfig ? "relationship" : null)
|
||||
|| "none";
|
||||
|
||||
console.log("📋 실행 계획 생성:", {
|
||||
controlMode: dataflowConfig.controlMode,
|
||||
effectiveControlMode,
|
||||
hasFlowConfig: !!dataflowConfig.flowConfig,
|
||||
hasRelationshipConfig: !!dataflowConfig.relationshipConfig,
|
||||
enableDataflowControl: buttonConfig.enableDataflowControl,
|
||||
});
|
||||
|
||||
// 관계 기반 제어만 지원
|
||||
if (dataflowConfig.controlMode === "relationship" && dataflowConfig.relationshipConfig) {
|
||||
// 관계 기반 제어
|
||||
if (effectiveControlMode === "relationship" && dataflowConfig.relationshipConfig) {
|
||||
const control: ControlConfig = {
|
||||
type: "relationship",
|
||||
relationshipConfig: dataflowConfig.relationshipConfig,
|
||||
@@ -191,11 +205,34 @@ export class ImprovedButtonActionExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
// 🆕 플로우 기반 제어
|
||||
if (effectiveControlMode === "flow" && dataflowConfig.flowConfig) {
|
||||
const control: ControlConfig = {
|
||||
type: "flow",
|
||||
flowConfig: dataflowConfig.flowConfig,
|
||||
};
|
||||
|
||||
console.log("📋 플로우 제어 설정:", dataflowConfig.flowConfig);
|
||||
|
||||
switch (dataflowConfig.flowConfig.executionTiming) {
|
||||
case "before":
|
||||
plan.beforeControls.push(control);
|
||||
break;
|
||||
case "after":
|
||||
plan.afterControls.push(control);
|
||||
break;
|
||||
case "replace":
|
||||
plan.afterControls.push(control);
|
||||
plan.hasReplaceControl = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return plan;
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔥 제어 실행 (관계 또는 외부호출)
|
||||
* 🔥 제어 실행 (관계 또는 플로우)
|
||||
*/
|
||||
private static async executeControls(
|
||||
controls: ControlConfig[],
|
||||
@@ -206,8 +243,16 @@ export class ImprovedButtonActionExecutor {
|
||||
|
||||
for (const control of controls) {
|
||||
try {
|
||||
// 관계 실행만 지원
|
||||
const result = await this.executeRelationship(control.relationshipConfig, formData, context);
|
||||
let result: ExecutionResult;
|
||||
|
||||
// 🆕 제어 타입에 따라 분기 처리
|
||||
if (control.type === "flow" && control.flowConfig) {
|
||||
result = await this.executeFlow(control.flowConfig, formData, context);
|
||||
} else if (control.type === "relationship" && control.relationshipConfig) {
|
||||
result = await this.executeRelationship(control.relationshipConfig, formData, context);
|
||||
} else {
|
||||
throw new Error(`지원하지 않는 제어 타입: ${control.type}`);
|
||||
}
|
||||
|
||||
results.push(result);
|
||||
|
||||
@@ -215,7 +260,7 @@ export class ImprovedButtonActionExecutor {
|
||||
if (!result.success) {
|
||||
throw new Error(result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error(`제어 실행 실패 (${control.type}):`, error);
|
||||
results.push({
|
||||
success: false,
|
||||
@@ -230,6 +275,61 @@ export class ImprovedButtonActionExecutor {
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 🆕 플로우 실행
|
||||
*/
|
||||
private static async executeFlow(
|
||||
config: {
|
||||
flowId: number;
|
||||
flowName: string;
|
||||
executionTiming: "before" | "after" | "replace";
|
||||
contextData?: Record<string, any>;
|
||||
},
|
||||
formData: Record<string, any>,
|
||||
context: ButtonExecutionContext,
|
||||
): Promise<ExecutionResult> {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
console.log(`🔄 플로우 실행 시작: ${config.flowName} (ID: ${config.flowId})`);
|
||||
|
||||
// 플로우 실행 API 호출
|
||||
const response = await apiClient.post(`/api/dataflow/node-flows/${config.flowId}/execute`, {
|
||||
formData,
|
||||
contextData: config.contextData || {},
|
||||
selectedRows: context.selectedRows || [],
|
||||
flowSelectedData: context.flowSelectedData || [],
|
||||
screenId: context.screenId,
|
||||
companyCode: context.companyCode,
|
||||
userId: context.userId,
|
||||
});
|
||||
|
||||
const executionTime = Date.now() - startTime;
|
||||
|
||||
if (response.data?.success) {
|
||||
console.log(`✅ 플로우 실행 성공: ${config.flowName}`, response.data);
|
||||
return {
|
||||
success: true,
|
||||
message: `플로우 "${config.flowName}" 실행 완료`,
|
||||
executionTime,
|
||||
data: response.data,
|
||||
};
|
||||
} else {
|
||||
throw new Error(response.data?.message || "플로우 실행 실패");
|
||||
}
|
||||
} catch (error: any) {
|
||||
const executionTime = Date.now() - startTime;
|
||||
console.error(`❌ 플로우 실행 실패: ${config.flowName}`, error);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: `플로우 "${config.flowName}" 실행 실패: ${error.message}`,
|
||||
executionTime,
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔥 관계 실행
|
||||
*/
|
||||
|
||||
@@ -191,6 +191,8 @@ export function convertV2ToLegacy(v2Layout: LayoutV2 | null): LegacyLayoutData |
|
||||
autoFill: overrides.autoFill,
|
||||
// 🆕 style 설정 복원 (라벨 텍스트, 라벨 스타일 등)
|
||||
style: overrides.style || {},
|
||||
// 🔧 webTypeConfig 복원 (버튼 제어기능, 플로우 가시성 등)
|
||||
webTypeConfig: overrides.webTypeConfig || {},
|
||||
// 기존 구조 호환을 위한 추가 필드
|
||||
parentId: null,
|
||||
gridColumns: 12,
|
||||
@@ -245,13 +247,47 @@ export function convertLegacyToV2(legacyLayout: LegacyLayoutData): LayoutV2 {
|
||||
if (comp.autoFill) topLevelProps.autoFill = comp.autoFill;
|
||||
// 🆕 style 설정 저장 (라벨 텍스트, 라벨 스타일 등)
|
||||
if (comp.style && Object.keys(comp.style).length > 0) topLevelProps.style = comp.style;
|
||||
// 🔧 webTypeConfig 저장 (버튼 제어기능, 플로우 가시성 등)
|
||||
if (comp.webTypeConfig && Object.keys(comp.webTypeConfig).length > 0) {
|
||||
topLevelProps.webTypeConfig = comp.webTypeConfig;
|
||||
// 🔍 디버그: webTypeConfig 저장 확인
|
||||
if (comp.webTypeConfig.dataflowConfig || comp.webTypeConfig.enableDataflowControl) {
|
||||
console.log("💾 webTypeConfig 저장:", {
|
||||
componentId: comp.id,
|
||||
enableDataflowControl: comp.webTypeConfig.enableDataflowControl,
|
||||
dataflowConfig: comp.webTypeConfig.dataflowConfig,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 현재 설정에서 차이값만 추출
|
||||
const fullConfig = comp.componentConfig || {};
|
||||
const configOverrides = extractCustomConfig(fullConfig, defaults);
|
||||
|
||||
// 🔧 디버그: style 저장 확인 (주석 처리)
|
||||
// if (comp.style?.labelDisplay !== undefined || configOverrides.style?.labelDisplay !== undefined) { console.log("💾 저장 시 style 변환:", { componentId: comp.id, "comp.style": comp.style, "configOverrides.style": configOverrides.style, "topLevelProps.style": topLevelProps.style }); }
|
||||
|
||||
// 상위 레벨 속성과 componentConfig 병합
|
||||
const overrides = { ...topLevelProps, ...configOverrides };
|
||||
// 🔧 style은 양쪽을 병합하되 comp.style(topLevelProps.style)을 우선시
|
||||
const mergedStyle = {
|
||||
...(configOverrides.style || {}),
|
||||
...(topLevelProps.style || {}),
|
||||
};
|
||||
|
||||
// 🔧 webTypeConfig도 병합 (topLevelProps가 우선, dataflowConfig 등 보존)
|
||||
const mergedWebTypeConfig = {
|
||||
...(configOverrides.webTypeConfig || {}),
|
||||
...(topLevelProps.webTypeConfig || {}),
|
||||
};
|
||||
|
||||
const overrides = {
|
||||
...topLevelProps,
|
||||
...configOverrides,
|
||||
// 🆕 병합된 style 사용 (comp.style 값이 최종 우선)
|
||||
...(Object.keys(mergedStyle).length > 0 ? { style: mergedStyle } : {}),
|
||||
// 🆕 병합된 webTypeConfig 사용 (comp.webTypeConfig가 최종 우선)
|
||||
...(Object.keys(mergedWebTypeConfig).length > 0 ? { webTypeConfig: mergedWebTypeConfig } : {}),
|
||||
};
|
||||
|
||||
return {
|
||||
id: comp.id,
|
||||
|
||||
Reference in New Issue
Block a user