feat: V2 Core 및 이벤트 시스템 추가

- V2 Core 라이브러리를 추가하여 느슨한 결합 아키텍처를 지원합니다.
- V2 EventBus를 통해 타입 안전한 이벤트 발행 및 구독 기능을 구현하였습니다.
- V2ErrorBoundary 컴포넌트를 추가하여 각 컴포넌트의 에러를 격리하고, 사용자 정의 폴백 UI 및 재시도 기능을 제공합니다.
- UnifiedRepeater 및 ButtonPrimaryComponent에서 V2 EventBus를 활용하여 이벤트 처리 로직을 개선하였습니다.
- 레거시 이벤트와의 호환성을 위해 LegacyEventAdapter를 추가하여 점진적 마이그레이션을 지원합니다.
- V2 컴포넌트 간의 통신을 위한 이벤트 타입을 정의하였습니다.
This commit is contained in:
kjs
2026-01-26 11:34:31 +09:00
parent b1fba586cb
commit b39c98c73f
14 changed files with 2392 additions and 56 deletions

View File

@@ -27,6 +27,7 @@ import { useScreenContextOptional } from "@/contexts/ScreenContext";
import { useSplitPanelContext, SplitPanelPosition } from "@/contexts/SplitPanelContext";
import { applyMappingRules } from "@/lib/utils/dataMapping";
import { apiClient } from "@/lib/api/client";
import { V2ErrorBoundary, v2EventBus, V2_EVENTS } from "@/lib/v2-core";
export interface ButtonPrimaryComponentProps extends ComponentRendererProps {
config?: ButtonPrimaryConfig;
@@ -668,23 +669,32 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
// 저장/수정 성공 시 자동 처리
if (actionConfig.type === "save" || actionConfig.type === "edit") {
if (typeof window !== "undefined") {
// 1. 테이블 새로고침 이벤트 먼저 발송 (모달이 닫히기 전에)
window.dispatchEvent(new CustomEvent("refreshTable"));
// 1. 테이블 새로고침 이벤트 먼저 발송 (모달이 닫히기 전에)
// V2 EventBus 사용 (레거시 어댑터가 자동으로 window 이벤트로도 브릿지)
v2EventBus.emitSync(V2_EVENTS.TABLE_REFRESH, {
tableName: context.tableName,
target: "all",
});
// 2. 모달 닫기 (약간의 딜레이)
setTimeout(() => {
// EditModal 내부인지 확인 (isInModal prop 사용)
const isInEditModal = (props as any).isInModal;
// 2. 모달 닫기 (약간의 딜레이)
setTimeout(() => {
// EditModal 내부인지 확인 (isInModal prop 사용)
const isInEditModal = (props as any).isInModal;
if (isInEditModal) {
window.dispatchEvent(new CustomEvent("closeEditModal"));
}
if (isInEditModal) {
v2EventBus.emitSync(V2_EVENTS.MODAL_CLOSE, {
modalId: "edit-modal",
reason: "save",
});
}
// ScreenModal은 연속 등록 모드를 지원하므로 saveSuccessInModal 이벤트 발생
window.dispatchEvent(new CustomEvent("saveSuccessInModal"));
}, 100);
}
// ScreenModal은 연속 등록 모드를 지원하므로 saveSuccessInModal 이벤트 발생
v2EventBus.emitSync(V2_EVENTS.MODAL_SAVE_SUCCESS, {
modalId: "screen-modal",
savedData: context.formData || {},
tableName: context.tableName || "",
});
}, 100);
}
} catch (error) {
// 로딩 토스트 제거
@@ -1355,8 +1365,17 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
/**
* ButtonPrimary 래퍼 컴포넌트
* 추가적인 로직이나 상태 관리가 필요한 경우 사용
* V2 ErrorBoundary로 감싸서 에러 격리 제공
*/
export const ButtonPrimaryWrapper: React.FC<ButtonPrimaryComponentProps> = (props) => {
return <ButtonPrimaryComponent {...props} />;
return (
<V2ErrorBoundary
componentId={props.component?.id || `button-${Date.now()}`}
componentType="v2-button-primary"
fallbackStyle="minimal"
recoverable={true}
>
<ButtonPrimaryComponent {...props} />
</V2ErrorBoundary>
);
};