V2 이벤트 시스템 통합 및 데이터 전달 인터페이스 구현: UnifiedRepeater 컴포넌트에 데이터 제공 및 수신 인터페이스를 추가하여 다른 컴포넌트와의 데이터 연동을 개선하였습니다. 또한, AggregationWidgetComponent와 RepeatContainerComponent에서 V2 표준 이벤트를 구독하여 데이터 변경 이벤트를 효율적으로 처리하도록 수정하였습니다. 이를 통해 컴포넌트 간의 데이터 흐름과 사용자 경험을 향상시켰습니다.

This commit is contained in:
juseok2
2026-01-29 23:20:23 +09:00
parent 8cdb8a3047
commit 3803b7dce1
16 changed files with 4654 additions and 85 deletions
+241
View File
@@ -0,0 +1,241 @@
/**
* V2 컴포넌트 간 통신 이벤트 타입 정의
*
* 모든 V2 컴포넌트는 이 파일에 정의된 이벤트 타입을 사용해야 합니다.
* 이벤트 발행/구독 시 타입 안전성을 보장합니다.
*/
// ============================================================
// 이벤트 상세 데이터 타입 (event.detail)
// ============================================================
/**
* 테이블 리스트 데이터 변경 이벤트
* 발행: v2-table-list
* 구독: v2-aggregation-widget, v2-repeat-container
*/
export interface TableListDataChangeDetail {
componentId: string;
tableName: string;
data: any[];
selectedRows: string[] | number[];
}
/**
* 리피터 데이터 변경 이벤트
* 발행: v2-unified-repeater
* 구독: v2-aggregation-widget, v2-repeat-container
*/
export interface RepeaterDataChangeDetail {
componentId: string;
tableName: string;
data: any[];
selectedData?: any[];
}
/**
* 폼 저장 전 이벤트
* 발행: buttonActions, UnifiedFormContext
* 구독: v2-unified-repeater, simple-repeater-table, modal-repeater-table 등
*/
export interface BeforeFormSaveDetail {
formData: Record<string, any>;
skipDefaultSave?: boolean;
}
/**
* 폼 저장 후 이벤트
* 발행: UnifiedFormContext
* 구독: 저장 결과 처리 컴포넌트들
*/
export interface AfterFormSaveDetail {
success: boolean;
data?: any;
error?: string;
}
/**
* 리피터 저장 이벤트 (마스터-디테일 FK 연결용)
* 발행: InteractiveScreenViewerDynamic
* 구독: v2-unified-repeater
*/
export interface RepeaterSaveDetail {
parentId?: string | number;
masterRecordId: string | number;
mainFormData: Record<string, any>;
tableName: string;
}
/**
* 테이블 새로고침 이벤트
* 발행: v2-button-primary, buttonActions
* 구독: v2-table-list, v2-split-panel-layout
*/
export interface RefreshTableDetail {
tableName?: string;
componentId?: string;
}
/**
* 카드 디스플레이 새로고침 이벤트
* 발행: buttonActions, InteractiveScreenViewerDynamic
* 구독: v2-card-display
*/
export interface RefreshCardDisplayDetail {
componentId?: string;
}
/**
* 컴포넌트 간 데이터 전달 이벤트
* 발행: buttonActions
* 구독: v2-unified-repeater
*/
export interface ComponentDataTransferDetail {
sourceComponentId: string;
targetComponentId: string;
data: any[];
mode: "append" | "replace" | "merge";
mappingRules?: Array<{
sourceField: string;
targetField: string;
defaultValue?: any;
}>;
}
/**
* 분할 패널 간 데이터 전달 이벤트
* 발행: buttonActions
* 구독: v2-unified-repeater, repeater-field-group
*/
export interface SplitPanelDataTransferDetail {
sourcePosition: "left" | "right";
targetPosition: "left" | "right";
data: any[];
mode: "append" | "replace" | "merge";
mappingRules?: Array<{
sourceField: string;
targetField: string;
defaultValue?: any;
}>;
}
/**
* 연관 데이터 버튼 선택 이벤트
* 발행: related-data-buttons
* 구독: v2-table-list
*/
export interface RelatedButtonSelectDetail {
targetTable: string;
filterColumn: string;
filterValue: any;
selectedData?: any;
}
/**
* 모달 제어 이벤트
*/
export interface EditModalDetail {
screenId?: number;
recordId?: string | number;
data?: any;
}
// ============================================================
// 이벤트 이름 상수
// ============================================================
export const V2_EVENTS = {
// 데이터 변경 이벤트
TABLE_LIST_DATA_CHANGE: "tableListDataChange",
REPEATER_DATA_CHANGE: "repeaterDataChange",
// 폼 저장 이벤트
BEFORE_FORM_SAVE: "beforeFormSave",
AFTER_FORM_SAVE: "afterFormSave",
REPEATER_SAVE: "repeaterSave",
// UI 갱신 이벤트
REFRESH_TABLE: "refreshTable",
REFRESH_CARD_DISPLAY: "refreshCardDisplay",
// 데이터 전달 이벤트
COMPONENT_DATA_TRANSFER: "componentDataTransfer",
SPLIT_PANEL_DATA_TRANSFER: "splitPanelDataTransfer",
// 모달 제어 이벤트
OPEN_EDIT_MODAL: "openEditModal",
CLOSE_EDIT_MODAL: "closeEditModal",
SAVE_SUCCESS_IN_MODAL: "saveSuccessInModal",
// 연관 데이터 버튼 이벤트
RELATED_BUTTON_SELECT: "related-button-select",
RELATED_BUTTON_REGISTER: "related-button-register",
RELATED_BUTTON_UNREGISTER: "related-button-unregister",
} as const;
// ============================================================
// Window EventMap 확장 (타입 안전한 이벤트 리스너)
// ============================================================
declare global {
interface WindowEventMap {
// 데이터 변경 이벤트
[V2_EVENTS.TABLE_LIST_DATA_CHANGE]: CustomEvent<TableListDataChangeDetail>;
[V2_EVENTS.REPEATER_DATA_CHANGE]: CustomEvent<RepeaterDataChangeDetail>;
// 폼 저장 이벤트
[V2_EVENTS.BEFORE_FORM_SAVE]: CustomEvent<BeforeFormSaveDetail>;
[V2_EVENTS.AFTER_FORM_SAVE]: CustomEvent<AfterFormSaveDetail>;
[V2_EVENTS.REPEATER_SAVE]: CustomEvent<RepeaterSaveDetail>;
// UI 갱신 이벤트
[V2_EVENTS.REFRESH_TABLE]: CustomEvent<RefreshTableDetail>;
[V2_EVENTS.REFRESH_CARD_DISPLAY]: CustomEvent<RefreshCardDisplayDetail>;
// 데이터 전달 이벤트
[V2_EVENTS.COMPONENT_DATA_TRANSFER]: CustomEvent<ComponentDataTransferDetail>;
[V2_EVENTS.SPLIT_PANEL_DATA_TRANSFER]: CustomEvent<SplitPanelDataTransferDetail>;
// 연관 데이터 버튼 이벤트
[V2_EVENTS.RELATED_BUTTON_SELECT]: CustomEvent<RelatedButtonSelectDetail>;
}
}
// ============================================================
// 유틸리티 함수
// ============================================================
/**
* 타입 안전한 이벤트 발행 함수
*/
export function dispatchV2Event<K extends keyof WindowEventMap>(
eventName: K,
detail: WindowEventMap[K] extends CustomEvent<infer D> ? D : never
): void {
if (typeof window !== "undefined") {
window.dispatchEvent(new CustomEvent(eventName, { detail }));
}
}
/**
* 타입 안전한 이벤트 구독 함수
*/
export function subscribeV2Event<K extends keyof WindowEventMap>(
eventName: K,
handler: (event: WindowEventMap[K]) => void
): () => void {
if (typeof window === "undefined") {
return () => {};
}
window.addEventListener(eventName, handler as EventListener);
return () => {
window.removeEventListener(eventName, handler as EventListener);
};
}
// ============================================================
// 내보내기
// ============================================================
export type V2EventName = typeof V2_EVENTS[keyof typeof V2_EVENTS];