= ({
// DOM 안전한 props만 필터링
const safeDomProps = filterDOMProps(domProps);
+ // 🆕 플로우 단계별 표시 제어
+ if (!shouldShowButton) {
+ // 레이아웃 동작에 따라 다르게 처리
+ if (flowConfig?.layoutBehavior === "preserve-position") {
+ // 위치 유지 (빈 공간, display: none)
+ return ;
+ } else {
+ // 완전히 렌더링하지 않음 (auto-compact, 빈 공간 제거)
+ return null;
+ }
+ }
+
return (
<>
diff --git a/frontend/stores/flowStepStore.ts b/frontend/stores/flowStepStore.ts
new file mode 100644
index 00000000..838baf6c
--- /dev/null
+++ b/frontend/stores/flowStepStore.ts
@@ -0,0 +1,135 @@
+"use client";
+
+import { create } from "zustand";
+import { devtools } from "zustand/middleware";
+
+/**
+ * 플로우 단계 전역 상태 관리
+ *
+ * 플로우 위젯에서 단계를 선택하면 이 스토어에 저장되고,
+ * 버튼 컴포넌트들이 현재 선택된 단계를 구독하여 표시/숨김을 결정합니다.
+ */
+interface FlowStepState {
+ /**
+ * 화면당 여러 플로우의 현재 선택된 단계
+ * key: flowComponentId (예: "component-123")
+ * value: stepId (플로우 단계 ID) 또는 null (선택 해제)
+ */
+ selectedSteps: Record;
+
+ /**
+ * 플로우 단계 선택
+ * @param flowComponentId 플로우 컴포넌트 고유 ID
+ * @param stepId 선택된 단계 ID (null이면 선택 해제)
+ */
+ setSelectedStep: (flowComponentId: string, stepId: number | null) => void;
+
+ /**
+ * 현재 단계 조회
+ * @param flowComponentId 플로우 컴포넌트 고유 ID
+ * @returns 현재 선택된 단계 ID 또는 null
+ */
+ getCurrentStep: (flowComponentId: string) => number | null;
+
+ /**
+ * 모든 플로우 초기화 (화면 전환 시 사용)
+ */
+ reset: () => void;
+
+ /**
+ * 특정 플로우만 초기화 (플로우 위젯 언마운트 시 사용)
+ * @param flowComponentId 플로우 컴포넌트 고유 ID
+ */
+ resetFlow: (flowComponentId: string) => void;
+}
+
+export const useFlowStepStore = create()(
+ devtools(
+ (set, get) => ({
+ selectedSteps: {},
+
+ setSelectedStep: (flowComponentId, stepId) => {
+ console.log("🔄 [FlowStepStore] 플로우 단계 변경:", {
+ flowComponentId,
+ stepId,
+ stepName: stepId ? `Step ${stepId}` : "선택 해제",
+ });
+
+ set((state) => ({
+ selectedSteps: {
+ ...state.selectedSteps,
+ [flowComponentId]: stepId,
+ },
+ }));
+
+ // 개발 모드에서 현재 상태 출력
+ if (process.env.NODE_ENV === "development") {
+ const currentState = get().selectedSteps;
+ console.log("📊 [FlowStepStore] 현재 상태:", currentState);
+ }
+ },
+
+ getCurrentStep: (flowComponentId) => {
+ const stepId = get().selectedSteps[flowComponentId] || null;
+
+ if (process.env.NODE_ENV === "development") {
+ console.log("🔍 [FlowStepStore] 현재 단계 조회:", {
+ flowComponentId,
+ stepId,
+ });
+ }
+
+ return stepId;
+ },
+
+ reset: () => {
+ console.log("🔄 [FlowStepStore] 모든 플로우 단계 초기화");
+ set({ selectedSteps: {} });
+ },
+
+ resetFlow: (flowComponentId) => {
+ console.log("🔄 [FlowStepStore] 플로우 단계 초기화:", flowComponentId);
+
+ set((state) => {
+ const { [flowComponentId]: _, ...rest } = state.selectedSteps;
+ return { selectedSteps: rest };
+ });
+ },
+ }),
+ { name: "FlowStepStore" }
+ )
+);
+
+/**
+ * 특정 플로우의 현재 단계를 구독하는 Hook
+ *
+ * @example
+ * const currentStep = useCurrentFlowStep("component-123");
+ * if (currentStep === null) {
+ * // 단계가 선택되지 않음
+ * }
+ */
+export const useCurrentFlowStep = (flowComponentId: string | null | undefined) => {
+ return useFlowStepStore((state) => {
+ if (!flowComponentId) return null;
+ return state.getCurrentStep(flowComponentId);
+ });
+};
+
+/**
+ * 여러 플로우의 현재 단계를 한 번에 구독하는 Hook
+ *
+ * @example
+ * const steps = useMultipleFlowSteps(["component-123", "component-456"]);
+ * // { "component-123": 1, "component-456": null }
+ */
+export const useMultipleFlowSteps = (flowComponentIds: string[]) => {
+ return useFlowStepStore((state) => {
+ const result: Record = {};
+ flowComponentIds.forEach((id) => {
+ result[id] = state.getCurrentStep(id);
+ });
+ return result;
+ });
+};
+
diff --git a/frontend/types/control-management.ts b/frontend/types/control-management.ts
index ca0e5c94..aba4c87e 100644
--- a/frontend/types/control-management.ts
+++ b/frontend/types/control-management.ts
@@ -49,12 +49,29 @@ export interface ExtendedButtonTypeConfig {
dataflowConfig?: ButtonDataflowConfig;
dataflowTiming?: "before" | "after" | "replace";
+ // 🆕 플로우 단계별 표시 제어
+ flowVisibilityConfig?: FlowVisibilityConfig;
+
// 스타일 설정
backgroundColor?: string;
textColor?: string;
borderColor?: string;
}
+/**
+ * 플로우 단계별 버튼 표시 설정
+ */
+export interface FlowVisibilityConfig {
+ enabled: boolean;
+ targetFlowComponentId: string;
+ targetFlowId?: number;
+ targetFlowName?: string;
+ mode: "whitelist" | "blacklist" | "all";
+ visibleSteps?: number[];
+ hiddenSteps?: number[];
+ layoutBehavior: "preserve-position" | "auto-compact";
+}
+
/**
* 🔥 단순화된 버튼 데이터플로우 설정
*/
diff --git a/frontend/types/screen-management.ts b/frontend/types/screen-management.ts
index 2d1e6015..cb9ec6af 100644
--- a/frontend/types/screen-management.ts
+++ b/frontend/types/screen-management.ts
@@ -289,6 +289,59 @@ export interface ButtonTypeConfig {
// ButtonActionType과 관련된 설정은 control-management.ts에서 정의
}
+/**
+ * 플로우 단계별 버튼 표시 설정
+ *
+ * 플로우 위젯과 버튼을 함께 사용할 때, 특정 플로우 단계에서만 버튼을 표시하거나 숨길 수 있습니다.
+ */
+export interface FlowVisibilityConfig {
+ /**
+ * 플로우 단계별 표시 제어 활성화 여부
+ */
+ enabled: boolean;
+
+ /**
+ * 대상 플로우 컴포넌트 ID
+ * 화면에 여러 플로우 위젯이 있을 경우, 어떤 플로우에 반응할지 지정
+ */
+ targetFlowComponentId: string;
+
+ /**
+ * 대상 플로우 정의 ID (선택사항, 검증용)
+ */
+ targetFlowId?: number;
+
+ /**
+ * 대상 플로우 이름 (표시용)
+ */
+ targetFlowName?: string;
+
+ /**
+ * 표시 조건 모드
+ * - whitelist: visibleSteps에 포함된 단계에서만 표시
+ * - blacklist: hiddenSteps에 포함된 단계에서 숨김
+ * - all: 모든 단계에서 표시 (기본값)
+ */
+ mode: "whitelist" | "blacklist" | "all";
+
+ /**
+ * 표시할 단계 ID 목록 (mode="whitelist"일 때 사용)
+ */
+ visibleSteps?: number[];
+
+ /**
+ * 숨길 단계 ID 목록 (mode="blacklist"일 때 사용)
+ */
+ hiddenSteps?: number[];
+
+ /**
+ * 레이아웃 동작 방식
+ * - preserve-position: 원래 위치 유지 (display: none, 빈 공간 유지)
+ * - auto-compact: 빈 공간 자동 제거 (Flexbox, 렌더링하지 않음)
+ */
+ layoutBehavior: "preserve-position" | "auto-compact";
+}
+
// ===== 데이터 테이블 관련 =====
/**