feat(button-actions): 저장 후 노드 플로우 실행 기능 추가 및 RepeatScreenModal props 수신 개선

- dataflowConfig.flowConfig 설정 시 저장 완료 후 노드 플로우 자동 실행
- executeNodeFlow API 동적 import로 번들 최적화
- RepeatScreenModal에서 _groupedData props 수신 지원 추가
- tiptap 라이브러리 버전 업그레이드 (2.11.5 → 2.27.1)
This commit is contained in:
SeongHyun Kim 2025-12-11 13:05:12 +09:00
parent 0e60f11084
commit 84095ace3b
4 changed files with 92 additions and 34 deletions

View File

@ -55,7 +55,8 @@ export function RepeatScreenModalComponent({
...props ...props
}: RepeatScreenModalComponentProps) { }: RepeatScreenModalComponentProps) {
// props에서도 groupedData를 추출 (DynamicWebTypeRenderer에서 전달될 수 있음) // props에서도 groupedData를 추출 (DynamicWebTypeRenderer에서 전달될 수 있음)
const groupedData = propsGroupedData || (props as any).groupedData; // DynamicComponentRenderer에서는 _groupedData로 전달됨
const groupedData = propsGroupedData || (props as any).groupedData || (props as any)._groupedData;
const componentConfig = { const componentConfig = {
...config, ...config,
...component?.config, ...component?.config,
@ -691,7 +692,7 @@ export function RepeatScreenModalComponent({
for (const tableRow of tableRowsWithExternalSource) { for (const tableRow of tableRowsWithExternalSource) {
const key = `${card._cardId}-${tableRow.id}`; const key = `${card._cardId}-${tableRow.id}`;
// 🆕 v3.7: 삭제된 행은 집계에서 제외 // 🆕 v3.7: 삭제된 행은 집계에서 제외
const rows = (extData[key] || []).filter((row) => !row._isDeleted); const rows = (extData[key] || []).filter((row) => !row._isDeleted);
externalRowsByTableId[tableRow.id] = rows; externalRowsByTableId[tableRow.id] = rows;
allExternalRows.push(...rows); allExternalRows.push(...rows);
@ -1183,19 +1184,19 @@ export function RepeatScreenModalComponent({
console.log(`[RepeatScreenModal] DELETE 성공: ${targetTable}, id=${targetRow._originalData.id}`); console.log(`[RepeatScreenModal] DELETE 성공: ${targetTable}, id=${targetRow._originalData.id}`);
// 성공 시 UI에서 완전히 제거 // 성공 시 UI에서 완전히 제거
setExternalTableData((prev) => { setExternalTableData((prev) => {
const newData = { const newData = {
...prev, ...prev,
[key]: prev[key].filter((row) => row._rowId !== rowId), [key]: prev[key].filter((row) => row._rowId !== rowId),
}; };
// 행 삭제 시 집계 재계산 // 행 삭제 시 집계 재계산
setTimeout(() => { setTimeout(() => {
recalculateAggregationsWithExternalData(newData); recalculateAggregationsWithExternalData(newData);
}, 0); }, 0);
return newData; return newData;
}); });
} catch (error: any) { } catch (error: any) {
console.error(`[RepeatScreenModal] DELETE 실패:`, error.response?.data || error.message); console.error(`[RepeatScreenModal] DELETE 실패:`, error.response?.data || error.message);
// 에러 시에도 다이얼로그 닫기 // 에러 시에도 다이얼로그 닫기

View File

@ -2651,6 +2651,55 @@ export class ButtonActionExecutor {
controlDataSource, controlDataSource,
}; };
// 노드 플로우 방식 실행 (flowConfig가 있는 경우)
const hasFlowConfig = config.dataflowConfig?.flowConfig && config.dataflowConfig.flowConfig.flowId;
if (hasFlowConfig) {
console.log("🎯 저장 후 노드 플로우 실행:", config.dataflowConfig.flowConfig);
const { flowId } = config.dataflowConfig.flowConfig;
try {
// 노드 플로우 실행 API 호출
const { executeNodeFlow } = await import("@/lib/api/nodeFlows");
// 데이터 소스 준비
let sourceData: any = context.formData || {};
// repeat-screen-modal 데이터가 있으면 병합
const repeatScreenModalKeys = Object.keys(context.formData || {}).filter((key) =>
key.startsWith("_repeatScreenModal_"),
);
if (repeatScreenModalKeys.length > 0) {
console.log("📦 repeat-screen-modal 데이터 발견:", repeatScreenModalKeys);
}
console.log("📦 노드 플로우에 전달할 데이터:", {
flowId,
dataSourceType: controlDataSource,
sourceData,
});
const result = await executeNodeFlow(flowId, {
dataSourceType: controlDataSource,
sourceData,
context: extendedContext,
});
if (result.success) {
console.log("✅ 저장 후 노드 플로우 실행 완료:", result);
toast.success("제어 로직 실행이 완료되었습니다.");
} else {
console.error("❌ 저장 후 노드 플로우 실행 실패:", result);
toast.error("저장은 완료되었으나 제어 실행 중 오류가 발생했습니다.");
}
} catch (error: any) {
console.error("❌ 저장 후 노드 플로우 실행 오류:", error);
toast.error(`제어 실행 오류: ${error.message || "알 수 없는 오류"}`);
}
return; // 노드 플로우 실행 후 종료
}
// 관계 기반 제어 실행 // 관계 기반 제어 실행
if (config.dataflowConfig?.controlMode === "relationship" && config.dataflowConfig?.relationshipConfig) { if (config.dataflowConfig?.controlMode === "relationship" && config.dataflowConfig?.relationshipConfig) {
console.log("🔗 저장 후 관계 기반 제어 실행:", config.dataflowConfig.relationshipConfig); console.log("🔗 저장 후 관계 기반 제어 실행:", config.dataflowConfig.relationshipConfig);

View File

@ -34,10 +34,11 @@
"@react-three/fiber": "^9.4.0", "@react-three/fiber": "^9.4.0",
"@tanstack/react-query": "^5.86.0", "@tanstack/react-query": "^5.86.0",
"@tanstack/react-table": "^8.21.3", "@tanstack/react-table": "^8.21.3",
"@tiptap/extension-placeholder": "^2.11.5", "@tiptap/core": "^3.13.0",
"@tiptap/extension-placeholder": "^2.27.1",
"@tiptap/pm": "^2.11.5", "@tiptap/pm": "^2.11.5",
"@tiptap/react": "^2.11.5", "@tiptap/react": "^2.27.1",
"@tiptap/starter-kit": "^2.11.5", "@tiptap/starter-kit": "^2.27.1",
"@turf/buffer": "^7.2.0", "@turf/buffer": "^7.2.0",
"@turf/helpers": "^7.2.0", "@turf/helpers": "^7.2.0",
"@turf/intersect": "^7.2.0", "@turf/intersect": "^7.2.0",
@ -3301,16 +3302,16 @@
} }
}, },
"node_modules/@tiptap/core": { "node_modules/@tiptap/core": {
"version": "2.27.1", "version": "3.13.0",
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.27.1.tgz", "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.13.0.tgz",
"integrity": "sha512-nkerkl8syHj44ZzAB7oA2GPmmZINKBKCa79FuNvmGJrJ4qyZwlkDzszud23YteFZEytbc87kVd/fP76ROS6sLg==", "integrity": "sha512-iUelgiTMgPVMpY5ZqASUpk8mC8HuR9FWKaDzK27w9oWip9tuB54Z8mePTxNcQaSPb6ErzEaC8x8egrRt7OsdGQ==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/pm": "^2.7.0" "@tiptap/pm": "^3.13.0"
} }
}, },
"node_modules/@tiptap/extension-blockquote": { "node_modules/@tiptap/extension-blockquote": {
@ -3699,6 +3700,19 @@
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
} }
}, },
"node_modules/@tiptap/starter-kit/node_modules/@tiptap/core": {
"version": "2.27.1",
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.27.1.tgz",
"integrity": "sha512-nkerkl8syHj44ZzAB7oA2GPmmZINKBKCa79FuNvmGJrJ4qyZwlkDzszud23YteFZEytbc87kVd/fP76ROS6sLg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/pm": "^2.7.0"
}
},
"node_modules/@turf/along": { "node_modules/@turf/along": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/@turf/along/-/along-7.2.0.tgz", "resolved": "https://registry.npmjs.org/@turf/along/-/along-7.2.0.tgz",
@ -6070,7 +6084,7 @@
"version": "20.19.24", "version": "20.19.24",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz",
"integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==", "integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==",
"devOptional": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.21.0" "undici-types": "~6.21.0"
@ -6108,7 +6122,7 @@
"version": "19.2.2", "version": "19.2.2",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz",
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==", "integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
"devOptional": true, "dev": true,
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
"@types/react": "^19.2.0" "@types/react": "^19.2.0"
@ -12524,13 +12538,6 @@
"react-dom": ">=16" "react-dom": ">=16"
} }
}, },
"node_modules/react-is": {
"version": "19.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.0.tgz",
"integrity": "sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA==",
"license": "MIT",
"peer": true
},
"node_modules/react-leaflet": { "node_modules/react-leaflet": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz", "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz",
@ -14190,7 +14197,7 @@
"version": "6.21.0", "version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"devOptional": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/unrs-resolver": { "node_modules/unrs-resolver": {

View File

@ -16,10 +16,6 @@
"test:dataflow": "jest lib/services/__tests__/buttonDataflowPerformance.test.ts" "test:dataflow": "jest lib/services/__tests__/buttonDataflowPerformance.test.ts"
}, },
"dependencies": { "dependencies": {
"@tiptap/extension-placeholder": "^2.11.5",
"@tiptap/pm": "^2.11.5",
"@tiptap/react": "^2.11.5",
"@tiptap/starter-kit": "^2.11.5",
"@dnd-kit/core": "^6.3.1", "@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0", "@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
@ -46,6 +42,11 @@
"@react-three/fiber": "^9.4.0", "@react-three/fiber": "^9.4.0",
"@tanstack/react-query": "^5.86.0", "@tanstack/react-query": "^5.86.0",
"@tanstack/react-table": "^8.21.3", "@tanstack/react-table": "^8.21.3",
"@tiptap/core": "^3.13.0",
"@tiptap/extension-placeholder": "^2.27.1",
"@tiptap/pm": "^2.11.5",
"@tiptap/react": "^2.27.1",
"@tiptap/starter-kit": "^2.27.1",
"@turf/buffer": "^7.2.0", "@turf/buffer": "^7.2.0",
"@turf/helpers": "^7.2.0", "@turf/helpers": "^7.2.0",
"@turf/intersect": "^7.2.0", "@turf/intersect": "^7.2.0",