ERP-node/docs/V2_컴포넌트_연동_가이드.md

74 KiB

V2 컴포넌트 연동 가이드

목차

  1. 개요
  2. V2 표준 이벤트 시스템
  3. 이벤트 시스템 상세
  4. Context 시스템
  5. 데이터 전달 인터페이스
  6. 컴포넌트별 연동 능력
  7. 연동 가능한 조합
  8. 연동 설정 방법

1. 개요

V2 컴포넌트들은 세 가지 메커니즘을 통해 상호 통신합니다:

메커니즘 용도 특징
이벤트 시스템 비동기 통신, 느슨한 결합 V2 표준 이벤트 타입 사용
Context 시스템 상태 공유, 동기 통신 React Context API
데이터 전달 인터페이스 명시적 데이터 전송 DataProvidable / DataReceivable

2. V2 표준 이벤트 시스템

2.1 이벤트 타입 정의 파일

파일 위치: frontend/types/component-events.ts

모든 V2 컴포넌트는 이 파일에 정의된 타입 안전한 이벤트 시스템을 사용해야 합니다.

2.2 이벤트 이름 상수

import { V2_EVENTS } from "@/types/component-events";

// 사용 가능한 이벤트
V2_EVENTS.TABLE_LIST_DATA_CHANGE    // "tableListDataChange"
V2_EVENTS.REPEATER_DATA_CHANGE      // "repeaterDataChange"
V2_EVENTS.BEFORE_FORM_SAVE          // "beforeFormSave"
V2_EVENTS.AFTER_FORM_SAVE           // "afterFormSave"
V2_EVENTS.REPEATER_SAVE             // "repeaterSave"
V2_EVENTS.REFRESH_TABLE             // "refreshTable"
V2_EVENTS.REFRESH_CARD_DISPLAY      // "refreshCardDisplay"
V2_EVENTS.COMPONENT_DATA_TRANSFER   // "componentDataTransfer"
V2_EVENTS.SPLIT_PANEL_DATA_TRANSFER // "splitPanelDataTransfer"

2.3 유틸리티 함수

타입 안전한 이벤트 발행

import { dispatchV2Event, V2_EVENTS } from "@/types/component-events";

// 올바른 방법 (타입 안전)
dispatchV2Event(V2_EVENTS.REPEATER_DATA_CHANGE, {
  componentId: "my-repeater",
  tableName: "order_detail",
  data: rows,
  selectedData: selectedRows,
});

// 잘못된 방법 (타입 오류 발생)
dispatchV2Event(V2_EVENTS.REPEATER_DATA_CHANGE, {
  wrongField: "value",  // 타입 에러!
});

타입 안전한 이벤트 구독

import { subscribeV2Event, V2_EVENTS, type RepeaterDataChangeDetail } from "@/types/component-events";

useEffect(() => {
  // 구독 (자동 cleanup 함수 반환)
  const unsubscribe = subscribeV2Event(
    V2_EVENTS.REPEATER_DATA_CHANGE,
    (event: CustomEvent<RepeaterDataChangeDetail>) => {
      const { componentId, data } = event.detail;
      // 타입 안전하게 데이터 접근
    }
  );

  return () => unsubscribe();
}, []);

2.4 이벤트 상세 타입

// 테이블 리스트 데이터 변경
interface TableListDataChangeDetail {
  componentId: string;
  tableName: string;
  data: any[];
  selectedRows: string[] | number[];
}

// 리피터 데이터 변경
interface RepeaterDataChangeDetail {
  componentId: string;
  tableName: string;
  data: any[];
  selectedData?: any[];
}

// 폼 저장 전
interface BeforeFormSaveDetail {
  formData: Record<string, any>;
  skipDefaultSave?: boolean;
}

// 리피터 저장 (마스터-디테일 FK 연결용)
interface RepeaterSaveDetail {
  parentId?: string | number;
  masterRecordId: string | number;
  mainFormData: Record<string, any>;
  tableName: string;
}

// 컴포넌트 간 데이터 전달
interface ComponentDataTransferDetail {
  sourceComponentId: string;
  targetComponentId: string;
  data: any[];
  mode: "append" | "replace" | "merge";
  mappingRules?: MappingRule[];
}

2.5 마이그레이션 가이드

이전 방식 (사용 금지):

// ❌ 타입 안전하지 않음
window.addEventListener("tableListDataChange" as any, handler);
window.dispatchEvent(new CustomEvent("repeaterDataChange", { detail }));

새로운 방식 (권장):

// ✅ 타입 안전함
import { subscribeV2Event, dispatchV2Event, V2_EVENTS } from "@/types/component-events";

const unsubscribe = subscribeV2Event(V2_EVENTS.TABLE_LIST_DATA_CHANGE, handler);
dispatchV2Event(V2_EVENTS.REPEATER_DATA_CHANGE, detail);

3. 이벤트 시스템 상세

3.1 저장 관련 이벤트

beforeFormSave

폼 저장 직전에 발생하여 각 컴포넌트가 데이터를 수집할 기회를 제공합니다.

항목 내용
발행자 buttonActions.ts, UnifiedFormContext.tsx
구독자 UnifiedRepeater, SimpleRepeaterTable, ModalRepeaterTable, SelectedItemsDetailInput, RepeatScreenModal, UniversalFormModal
데이터 구조 { formData: Record<string, any>, skipDefaultSave?: boolean }
// 발행 예시
window.dispatchEvent(new CustomEvent("beforeFormSave", {
  detail: { formData: {}, skipDefaultSave: false }
}));

// 구독 예시
window.addEventListener("beforeFormSave", (event: CustomEvent) => {
  const { formData } = event.detail;
  formData["myField"] = myValue; // 데이터 추가
});

afterFormSave

폼 저장 완료 후 발생합니다.

항목 내용
발행자 UnifiedFormContext.tsx
데이터 구조 { success: boolean, data?: any }

repeaterSave

마스터 저장 후 리피터에 FK를 전달하기 위해 발생합니다.

항목 내용
발행자 InteractiveScreenViewerDynamic.tsx
구독자 UnifiedRepeater.tsx
데이터 구조 { parentId, masterRecordId, mainFormData, tableName }
// 마스터-디테일 저장 흐름
// 1. 마스터 저장 완료
// 2. repeaterSave 이벤트 발행
window.dispatchEvent(new CustomEvent("repeaterSave", {
  detail: {
    masterRecordId: savedId,  // 마스터 ID
    tableName: "receiving_mng",
    mainFormData: formData
  }
}));

// 3. UnifiedRepeater에서 수신
// → 모든 행의 foreignKeyColumn에 masterRecordId 설정
// → 디테일 테이블에 저장

3.2 데이터 변경 이벤트

tableListDataChange

테이블 리스트의 데이터가 변경될 때 발생합니다.

항목 내용
발행자 v2-table-list, table-list
구독자 v2-repeat-container, v2-aggregation-widget, repeat-container, aggregation-widget
데이터 구조 { componentId, tableName, data: any[], selectedRows: string[] }
// 테이블 리스트 → 집계 위젯 연동
// 테이블 데이터 변경 시 자동으로 집계 갱신

repeaterDataChange

리피터 컴포넌트의 데이터가 변경될 때 발생합니다.

항목 내용
구독자 v2-repeat-container, v2-aggregation-widget

3.3 UI 갱신 이벤트

refreshTable

테이블 데이터를 다시 로드합니다.

항목 내용
발행자 v2-button-primary, InteractiveScreenViewerDynamic, ScreenModal, buttonActions.ts
구독자 v2-table-list, v2-split-panel-layout, InteractiveDataTable
// 저장 후 테이블 새로고침
window.dispatchEvent(new CustomEvent("refreshTable"));

refreshCardDisplay

카드 디스플레이를 다시 로드합니다.

항목 내용
발행자 InteractiveScreenViewerDynamic, buttonActions.ts
구독자 v2-card-display, card-display

3.4 모달 제어 이벤트

openEditModal

편집 모달을 엽니다.

항목 내용
발행자 SplitPanelLayout2, InteractiveScreenViewer, InteractiveDataTable
구독자 EditModal.tsx, 화면 페이지

closeEditModal

편집 모달을 닫습니다.

항목 내용
발행자 v2-button-primary, buttonActions.ts
구독자 EditModal.tsx

saveSuccessInModal

모달 내 저장 성공 시 발생합니다.

항목 내용
발행자 v2-button-primary, buttonActions.ts
구독자 ScreenModal.tsx

3.5 데이터 전달 이벤트

componentDataTransfer

컴포넌트 간 데이터 전달 시 발생합니다.

항목 내용
발행자 buttonActions.ts
구독자 UnifiedRepeater.tsx

splitPanelDataTransfer

분할 패널 간 데이터 전달 시 발생합니다.

항목 내용
발행자 buttonActions.ts
구독자 UnifiedRepeater.tsx, RepeaterFieldGroupRenderer.tsx

screenDataTransfer

화면 간 데이터 전달 시 발생합니다.

항목 내용
발행자 buttonActions.ts, useScreenDataTransfer.ts
구독자 useScreenDataTransfer.ts

3.6 연관 데이터 버튼 이벤트

연관 데이터 버튼 클릭 시 발생합니다.

항목 내용
발행자 RelatedDataButtonsComponent.tsx
구독자 v2-table-list, table-list, InteractiveDataTable
데이터 구조 { targetTable, filterColumn, filterValue, selectedData }

연관 데이터 버튼이 대상 테이블을 등록/해제합니다.

항목 내용
발행자 RelatedDataButtonsComponent.tsx
구독자 v2-table-list, table-list

3.7 이벤트 흐름 다이어그램

┌─────────────────────────────────────────────────────────────────────────┐
│                           저장 플로우                                    │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  [저장 버튼 클릭]                                                        │
│        │                                                                 │
│        ▼                                                                 │
│  ┌─────────────────┐                                                    │
│  │ beforeFormSave  │ ────────────────────────────────────────────┐      │
│  └────────┬────────┘                                             │      │
│           │                                                      │      │
│           ▼                                                      ▼      │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐         │
│  │ UnifiedRepeater │  │ SimpleRepeater  │  │ ModalRepeater   │  ...    │
│  │ (데이터 수집)    │  │ (데이터 수집)    │  │ (데이터 수집)    │         │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘         │
│           │                                                             │
│           ▼                                                             │
│  ┌─────────────────┐                                                    │
│  │   API 저장      │                                                    │
│  └────────┬────────┘                                                    │
│           │                                                             │
│           ▼                                                             │
│  ┌─────────────────┐         ┌─────────────────┐                       │
│  │ afterFormSave   │         │  repeaterSave   │ (마스터-디테일 시)     │
│  └────────┬────────┘         └────────┬────────┘                       │
│           │                           │                                 │
│           ▼                           ▼                                 │
│  ┌─────────────────┐         ┌─────────────────┐                       │
│  │  refreshTable   │         │ UnifiedRepeater │                       │
│  └─────────────────┘         │ (FK 설정 후 저장)│                       │
│                              └─────────────────┘                       │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────┐
│                        데이터 변경 플로우                                │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌─────────────────┐                                                    │
│  │  v2-table-list  │                                                    │
│  │ (데이터 로드/변경)│                                                    │
│  └────────┬────────┘                                                    │
│           │                                                             │
│           ▼                                                             │
│  ┌─────────────────────┐                                                │
│  │ tableListDataChange │                                                │
│  └────────┬────────────┘                                                │
│           │                                                             │
│           ├─────────────────────┬─────────────────────┐                 │
│           ▼                     ▼                     ▼                 │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐         │
│  │v2-aggregation-  │  │v2-repeat-       │  │ 기타 구독자     │         │
│  │widget (집계갱신) │  │container        │  │                 │         │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘         │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

4. Context 시스템

4.1 TableOptionsContext

역할: 화면 내 테이블 컴포넌트 등록/관리 및 필터링 연동

파일: frontend/contexts/TableOptionsContext.tsx

제공하는 기능

함수/상태 설명
registeredTables 등록된 테이블 Map
selectedTableId 현재 선택된 테이블 ID
registerTable(tableId, registration) 테이블 등록
unregisterTable(tableId) 테이블 해제
getTable(tableId) 테이블 조회
setSelectedTableId(id) 선택 테이블 설정
updateTableDataCount(tableId, count) 데이터 건수 업데이트
getActiveTabTables() 활성 탭의 테이블만 반환

TableRegistration 구조

interface TableRegistration {
  tableId: string;
  tableName: string;
  columns: ColumnInfo[];
  dataCount: number;
  parentTabId?: string;                    // 소속 탭 ID
  onFilterChange: (filters: TableFilter[]) => void;
  getColumnUniqueValues: (columnName: string) => Promise<SelectOption[]>;
}

연동 흐름

┌─────────────────────────────────────────────────────────────────────────┐
│                     TableOptionsContext 연동                            │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌─────────────────┐                                                    │
│  │  v2-table-list  │                                                    │
│  └────────┬────────┘                                                    │
│           │ registerTable()                                             │
│           ▼                                                             │
│  ┌─────────────────────────────────────┐                                │
│  │      TableOptionsContext            │                                │
│  │  ┌─────────────────────────────┐    │                                │
│  │  │     registeredTables        │    │                                │
│  │  │  - tableId                  │    │                                │
│  │  │  - onFilterChange()         │    │                                │
│  │  │  - columns                  │    │                                │
│  │  └─────────────────────────────┘    │                                │
│  └────────────────┬────────────────────┘                                │
│                   │                                                      │
│                   ▼                                                      │
│  ┌─────────────────────────────────────┐                                │
│  │    v2-table-search-widget           │                                │
│  │  - 등록된 테이블 목록 표시           │                                │
│  │  - 필터 입력                        │                                │
│  │  - currentTable.onFilterChange()    │                                │
│  └────────────────┬────────────────────┘                                │
│                   │                                                      │
│                   ▼                                                      │
│  ┌─────────────────────────────────────┐                                │
│  │    v2-table-list (자동 재조회)       │                                │
│  └─────────────────────────────────────┘                                │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

사용 컴포넌트

컴포넌트 사용 방식
v2-table-list 테이블 등록/해제, 데이터 건수 업데이트
v2-table-search-widget 등록된 테이블 목록 조회, 필터 적용
v2-split-panel-layout 내부 테이블 등록/해제
v2-card-display 테이블 등록 (선택적)

4.2 SplitPanelContext

역할: 좌우 분할 패널 간 데이터 전달 및 상태 관리

파일: frontend/contexts/SplitPanelContext.tsx

제공하는 기능

함수/상태 설명
splitPanelId 분할 패널 ID
leftScreenId, rightScreenId 좌우 화면 ID
selectedLeftData 좌측 선택 데이터
setSelectedLeftData(data) 좌측 선택 데이터 설정
addedItemIds 우측에 추가된 항목 ID Set
addItemIds(ids) 항목 ID 추가
registerReceiver(receiver) 데이터 수신자 등록
transferToOtherSide(data) 반대편으로 데이터 전달
linkedFilters 연결 필터 설정
parentDataMapping 부모 데이터 매핑 설정

연동 흐름

┌─────────────────────────────────────────────────────────────────────────┐
│                     SplitPanelContext 연동                              │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │                    v2-split-panel-layout                         │    │
│  │                    (SplitPanelProvider)                          │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│         │                                             │                  │
│         ▼                                             ▼                  │
│  ┌─────────────────┐                         ┌─────────────────┐        │
│  │    좌측 패널     │                         │    우측 패널     │        │
│  │  (CardDisplay)  │                         │  (TableList)    │        │
│  └────────┬────────┘                         └────────┬────────┘        │
│           │                                           │                  │
│           │ 행 클릭                                   │                  │
│           ▼                                           │                  │
│  setSelectedLeftData(rowData)                         │                  │
│           │                                           │                  │
│           └──────────────────────────────────────────▶│                  │
│                                                       │                  │
│                     relation 설정에 따라              │                  │
│                     자동 필터링 (FK 기반)             │                  │
│                                                       ▼                  │
│                                             ┌─────────────────┐         │
│                                             │ 필터링된 데이터  │         │
│                                             │ 표시            │         │
│                                             └─────────────────┘         │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

사용 컴포넌트

컴포넌트 사용 방식
v2-split-panel-layout Provider 제공, 좌우 패널 관리
v2-table-list 분할 패널 데이터 수신자로 등록
v2-card-display 분할 패널 위치 확인, 데이터 수신
v2-button-primary 분할 패널 컨텍스트 확인

4.3 ScreenContext

역할: 같은 화면 내 컴포넌트 간 통신 (데이터 제공자/수신자 등록)

파일: frontend/contexts/ScreenContext.tsx

제공하는 기능

함수/상태 설명
screenId 화면 ID
tableName 테이블명
menuObjid 메뉴 OBJID (카테고리 값 조회용)
splitPanelPosition 분할 패널 위치 (left | right)
formData 폼 데이터
updateFormData(field, value) 폼 데이터 업데이트
registerDataProvider(provider) 데이터 제공자 등록
registerDataReceiver(receiver) 데이터 수신자 등록
getDataProvider(id) 데이터 제공자 조회
getDataReceiver(id) 데이터 수신자 조회
getAllDataProviders() 모든 데이터 제공자 조회

사용 컴포넌트

컴포넌트 사용 방식
v2-table-list 데이터 제공자/수신자로 등록
v2-card-display 화면 컨텍스트 확인
v2-button-primary 화면 컨텍스트 확인, 데이터 전달 실행
repeater-field-group 데이터 수신자 등록, formData 사용

4.4 UnifiedFormContext

역할: 폼 상태 관리, 조건부 로직, 저장/검증/초기화

파일: frontend/components/unified/UnifiedFormContext.tsx

제공하는 기능

함수/상태 설명
formData 폼 데이터
originalData 원본 데이터 (수정 모드)
status 폼 상태 (isSubmitting, isDirty, isValid 등)
errors 필드 에러 배열
getValue(field), setValue(field, value) 값 관리
submit(options) 폼 저장
reset() 폼 초기화
validate() 폼 검증
evaluateCondition(condition) 조건 평가
getRepeaterData(key), setRepeaterData(key, data) 리피터 데이터 관리

4.5 ActiveTabContext

역할: 탭 컴포넌트의 활성 탭 추적

파일: frontend/contexts/ActiveTabContext.tsx

제공하는 기능

함수/상태 설명
activeTabs 활성 탭 정보 Map
setActiveTab(tabsId, tabId) 활성 탭 설정
getActiveTabId(tabsId) 특정 탭 컴포넌트의 활성 탭 ID
getAllActiveTabIds() 전체 활성 탭 ID 목록

사용 컴포넌트

컴포넌트 사용 방식
v2-table-search-widget 활성 탭 기반 테이블 필터링
v2-tabs-widget 탭 활성화 관리

4.6 ScreenPreviewContext

역할: 디자이너 모드와 실제 화면 모드 구분

파일: frontend/contexts/ScreenPreviewContext.tsx

제공하는 기능

함수/상태 설명
isPreviewMode 미리보기 모드 여부

사용 컴포넌트

컴포넌트 사용 방식
v2-table-search-widget 미리보기 모드에서 설정 버튼 비활성화
v2-button-primary 프리뷰 모드 확인

4.7 Context 계층 구조

┌─────────────────────────────────────────────────────────────────────────┐
│                        Context 계층 구조                                 │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ScreenPreviewContext (최상위 - 디자이너/실제 화면 구분)                 │
│  │                                                                       │
│  └─── ScreenContext (화면 레벨)                                         │
│       │                                                                  │
│       ├─── TableOptionsContext (테이블 관리)                            │
│       │    │                                                             │
│       │    └─── ActiveTabContext (탭 필터링)                            │
│       │                                                                  │
│       └─── SplitPanelContext (분할 패널 - 선택적)                       │
│                                                                          │
│  UnifiedFormContext (폼 상태 관리 - 독립적, 선택적 사용)                │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

5. 데이터 전달 인터페이스

5.1 DataProvidable 인터페이스

데이터를 제공하는 컴포넌트가 구현하는 인터페이스

interface DataProvidable {
  componentId: string;
  componentType: string;
  
  // 선택된 데이터 반환
  getSelectedData(): any[];
  
  // 모든 데이터 반환
  getAllData(): any[];
  
  // 선택 초기화
  clearSelection(): void;
}

구현 컴포넌트

컴포넌트 제공 데이터
v2-table-list 선택된 행 데이터, 전체 데이터
v2-card-display 선택된 카드 데이터
select-basic 선택된 값
conditional-container 조건부 컨테이너의 선택 값

5.2 DataReceivable 인터페이스

데이터를 수신하는 컴포넌트가 구현하는 인터페이스

interface DataReceivable {
  componentId: string;
  componentType: DataReceivableComponentType;
  
  // 데이터 수신
  receiveData(data: any[], config: DataReceiverConfig): Promise<void>;
  
  // 현재 데이터 반환
  getData(): any;
}

type DataReceivableComponentType = 
  | "table-list" 
  | "unified-repeater" 
  | "repeater-field-group"
  | "simple-repeater-table";

구현 컴포넌트

컴포넌트 수신 모드
v2-table-list append, replace, merge
repeater-field-group append
embedded-screen 화면 임베딩 데이터 수신

5.3 DataReceiverConfig

데이터 전달 시 설정

interface DataReceiverConfig {
  // 타겟 컴포넌트 정보
  targetComponentId: string;
  targetComponentType: DataReceivableComponentType;
  
  // 수신 모드
  mode: "append" | "replace" | "merge";
  
  // 필드 매핑 규칙
  mappingRules: Array<{
    sourceField: string;      // 소스 필드
    targetField: string;      // 타겟 필드
    transform?: string;       // 변환 함수 (선택)
    defaultValue?: any;       // 기본값 (선택)
  }>;
  
  // 조건부 전달
  condition?: {
    field: string;
    operator: "=" | "!=" | ">" | "<";
    value: any;
  };
  
  // 검증 규칙
  validation?: {
    required: string[];       // 필수 필드
    unique?: string[];        // 중복 불가 필드
  };
}

5.4 데이터 전달 흐름

┌─────────────────────────────────────────────────────────────────────────┐
│                     데이터 전달 흐름 (버튼 액션)                         │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌─────────────────┐                                                    │
│  │ v2-button-      │                                                    │
│  │ primary         │ action.type = "transferData"                       │
│  └────────┬────────┘                                                    │
│           │                                                              │
│           ▼                                                              │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │                 handleTransferDataAction()                       │    │
│  ├─────────────────────────────────────────────────────────────────┤    │
│  │  1. ScreenContext에서 소스 컴포넌트 조회                         │    │
│  │     getDataProvider(sourceComponentId)                           │    │
│  │                                                                  │    │
│  │  2. 소스에서 데이터 가져오기                                     │    │
│  │     source.getSelectedData() 또는 source.getAllData()            │    │
│  │                                                                  │    │
│  │  3. 매핑 규칙 적용                                               │    │
│  │     mappingRules.forEach(rule => ...)                            │    │
│  │                                                                  │    │
│  │  4. ScreenContext에서 타겟 컴포넌트 조회                         │    │
│  │     getDataReceiver(targetComponentId)                           │    │
│  │                                                                  │    │
│  │  5. 타겟에 데이터 전달                                           │    │
│  │     target.receiveData(mappedData, config)                       │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│           │                                                              │
│           ▼                                                              │
│  ┌─────────────────┐                                                    │
│  │ 타겟 컴포넌트    │                                                    │
│  │ (v2-table-list, │                                                    │
│  │  repeater 등)   │                                                    │
│  └─────────────────┘                                                    │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

6. 컴포넌트별 연동 능력

6.1 연동 능력 매트릭스

컴포넌트 이벤트 발행 이벤트 구독 DataProvider DataReceiver Context 사용
v2-table-list TableOptions, Screen, SplitPanel
v2-split-panel-layout TableOptions (Provider)
v2-unified-repeater Screen
v2-button-primary Screen, SplitPanel
v2-table-search-widget TableOptions, ActiveTab
v2-aggregation-widget -
v2-repeat-container -
v2-card-display TableOptions, Screen, SplitPanel
v2-pivot-grid -
v2-tabs-widget ActiveTab

6.2 컴포넌트별 상세

v2-table-list

발행 이벤트:

  • tableListDataChange - 데이터 로드/변경 시

구독 이벤트:

  • refreshTable - 테이블 새로고침
  • related-button-select - 연관 버튼 선택
  • related-button-register/unregister - 연관 버튼 등록/해제

DataProvidable 구현:

getSelectedData(): any[]    // 체크된 행 데이터
getAllData(): any[]         // 전체 데이터
clearSelection(): void      // 선택 초기화

DataReceivable 구현:

receiveData(data, config): Promise<void>
// mode: "append" - 기존 데이터에 추가
// mode: "replace" - 데이터 교체
// mode: "merge" - 키 기준 병합

v2-unified-repeater

발행 이벤트:

  • repeaterDataChange - 데이터 변경 시 (V2 표준 이벤트)

구독 이벤트:

  • beforeFormSave - 저장 전 데이터 수집
  • repeaterSave - 마스터 저장 후 FK 설정
  • componentDataTransfer - 컴포넌트 간 데이터 전달
  • splitPanelDataTransfer - 분할 패널 간 데이터 전달

DataProvidable 구현:

  • getSelectedData() - 선택된 행 데이터 반환
  • getAllData() - 전체 데이터 반환
  • clearSelection() - 선택 초기화

DataReceivable 구현:

  • receiveData(data, config) - 데이터 수신 (append, replace, merge 모드 지원)
  • getData() - 현재 데이터 반환

Context 등록:

  • ScreenContext에 DataProvider/DataReceiver 자동 등록

v2-button-primary

발행 이벤트:

  • refreshTable - 저장 후 테이블 갱신
  • closeEditModal - 모달 닫기
  • saveSuccessInModal - 모달 저장 성공

역할:

  • 저장, 삭제, 데이터 전달 등 액션 실행
  • buttonActions.ts의 함수들 호출

v2-table-search-widget

Context 의존:

  • TableOptionsContext - 등록된 테이블 조회, 필터 적용
  • ActiveTabContext - 활성 탭 기반 테이블 필터링

동작:

  1. TableOptionsContext.registeredTables에서 테이블 목록 조회
  2. 사용자가 필터 입력
  3. currentTable.onFilterChange(filters) 호출
  4. 해당 테이블이 자동으로 재조회

v2-aggregation-widget

구독 이벤트:

  • tableListDataChange - 테이블 데이터 변경 시 집계 갱신
  • repeaterDataChange - 리피터 데이터 변경 시 집계 갱신

v2-split-panel-layout

Provider 제공:

  • SplitPanelContext - 좌우 패널 데이터 전달

발행 이벤트:

  • openScreenModal - 화면 모달 열기

구독 이벤트:

  • refreshTable - 내부 테이블 갱신

7. 연동 가능한 조합

7.1 검색/필터 연동

v2-table-search-widget ↔ v2-table-list

┌─────────────────────────────────────────────────────────────────────────┐
│                     검색 위젯 ↔ 테이블 리스트                           │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │              v2-table-search-widget                              │    │
│  │  ┌─────┐ ┌─────────┐ ┌──────────┐ ┌────────┐                    │    │
│  │  │ 이름 │ │ 날짜범위 │ │ 상태선택 │ │ 초기화 │                    │    │
│  │  └──┬──┘ └────┬────┘ └────┬─────┘ └────────┘                    │    │
│  └─────┼─────────┼───────────┼──────────────────────────────────────┘    │
│        │         │           │                                           │
│        └─────────┴───────────┘                                           │
│                  │ onFilterChange(filters)                               │
│                  ▼                                                       │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │              v2-table-list                                       │    │
│  │  ┌─────────────────────────────────────────────────────────┐    │    │
│  │  │ 필터링된 데이터 표시                                     │    │    │
│  │  └─────────────────────────────────────────────────────────┘    │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                          │
│  연결 방식: TableOptionsContext                                         │
│  설정: v2-table-search-widget의 targetPanelPosition 설정               │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

설정 방법:

  • v2-table-search-widgetfilterMode: "dynamic" 또는 "preset"
  • v2-table-search-widgettargetPanelPosition: "left", "right", "auto"
  • v2-table-list는 자동으로 TableOptionsContext에 등록됨

7.2 마스터-디테일 연동

v2-split-panel-layout (좌측 ↔ 우측)

┌─────────────────────────────────────────────────────────────────────────┐
│                     마스터-디테일 (분할 패널)                            │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌──────────────────────────────────────────────────────────────────┐   │
│  │                    v2-split-panel-layout                          │   │
│  │  ┌────────────────────┐  │  ┌────────────────────────────────┐   │   │
│  │  │    좌측 패널        │  │  │          우측 패널              │   │   │
│  │  │   (마스터 목록)     │  │  │         (디테일 정보)           │   │   │
│  │  │                    │  │  │                                │   │   │
│  │  │  - 부서 목록       │  │  │  - 선택된 부서의 직원 목록      │   │   │
│  │  │  - dept_info       │ ──▶ │  - user_info                   │   │   │
│  │  │                    │  │  │  - dept_code = 선택값          │   │   │
│  │  └────────────────────┘  │  └────────────────────────────────┘   │   │
│  └──────────────────────────────────────────────────────────────────┘   │
│                                                                          │
│  연결 방식: SplitPanelContext + relation 설정                           │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

설정 방법:

rightPanel: {
  tableName: "user_info",
  relation: {
    type: "detail",
    leftColumn: "dept_code",      // 좌측 테이블의 컬럼
    rightColumn: "dept_code",     // 우측 테이블의 필터 컬럼
    // 또는 복합키
    keys: [
      { leftColumn: "company_id", rightColumn: "company_id" },
      { leftColumn: "dept_code", rightColumn: "dept_code" }
    ]
  }
}

7.3 폼 저장 연동

v2-button-primary → v2-unified-repeater

┌─────────────────────────────────────────────────────────────────────────┐
│                     폼 저장 + 리피터 저장                                │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  [폼 입력 필드들]                                                │    │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐                         │    │
│  │  │ 입고번호  │ │ 입고일자  │ │ 거래처   │                         │    │
│  │  └──────────┘ └──────────┘ └──────────┘                         │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                          │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  v2-unified-repeater (입고 상세)                                 │    │
│  │  ┌─────────────────────────────────────────────────────────┐    │    │
│  │  │ 품목코드 │ 품목명 │ 수량 │ 단가 │ 금액                   │    │    │
│  │  ├─────────────────────────────────────────────────────────┤    │    │
│  │  │ ITEM001  │ ...    │ 10   │ 1000 │ 10000                 │    │    │
│  │  │ ITEM002  │ ...    │ 5    │ 2000 │ 10000                 │    │    │
│  │  └─────────────────────────────────────────────────────────┘    │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                          │
│  ┌─────────────────┐                                                    │
│  │   [저장 버튼]    │                                                    │
│  │  v2-button-     │                                                    │
│  │  primary        │                                                    │
│  └────────┬────────┘                                                    │
│           │                                                              │
│           ▼                                                              │
│  1. beforeFormSave 발행 → 리피터가 데이터 수집                          │
│  2. 마스터 테이블 저장 (receiving_mng)                                   │
│  3. repeaterSave 발행 → 리피터가 FK 설정 후 저장                        │
│  4. 디테일 테이블 저장 (receiving_detail)                               │
│  5. refreshTable 발행 → 테이블 갱신                                     │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

설정 방법:

// v2-button-primary
action: {
  type: "save",
  saveMode: "withRepeater",       // 리피터와 함께 저장
  tableName: "receiving_mng"
}

// v2-unified-repeater
dataSource: {
  tableName: "receiving_detail",
  foreignKey: "receiving_id",     // 마스터 FK 컬럼
  referenceKey: "id"              // 마스터 PK 컬럼
}

7.4 데이터 전달 연동

v2-table-list → v2-unified-repeater (버튼으로 데이터 추가)

┌─────────────────────────────────────────────────────────────────────────┐
│                     테이블 선택 → 리피터 추가                            │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  v2-table-list (품목 선택)                                       │    │
│  │  ┌─────────────────────────────────────────────────────────┐    │    │
│  │  │ ☑ │ 품목코드 │ 품목명         │ 단가                    │    │    │
│  │  ├─────────────────────────────────────────────────────────┤    │    │
│  │  │ ☑ │ ITEM001  │ 노트북         │ 1,000,000              │    │    │
│  │  │ ☑ │ ITEM002  │ 마우스         │ 50,000                 │    │    │
│  │  │ ☐ │ ITEM003  │ 키보드         │ 100,000                │    │    │
│  │  └─────────────────────────────────────────────────────────┘    │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                          │
│  ┌─────────────────┐                                                    │
│  │   [추가 버튼]    │ action.type = "transferData"                      │
│  │  v2-button-     │                                                    │
│  │  primary        │                                                    │
│  └────────┬────────┘                                                    │
│           │                                                              │
│           ▼                                                              │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  v2-unified-repeater (주문 상세)                                 │    │
│  │  ┌─────────────────────────────────────────────────────────┐    │    │
│  │  │ 품목코드 │ 품목명         │ 수량 │ 단가      │ 금액      │    │    │
│  │  ├─────────────────────────────────────────────────────────┤    │    │
│  │  │ ITEM001  │ 노트북         │ 1    │ 1,000,000 │ 1,000,000│    │    │
│  │  │ ITEM002  │ 마우스         │ 1    │ 50,000    │ 50,000   │    │    │
│  │  └─────────────────────────────────────────────────────────┘    │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

설정 방법:

// v2-button-primary
action: {
  type: "transferData",
  sourceComponentId: "item-table-list",
  targetComponentId: "order-detail-repeater",
  mappingRules: [
    { sourceField: "item_code", targetField: "item_code" },
    { sourceField: "item_name", targetField: "item_name" },
    { sourceField: "unit_price", targetField: "unit_price" },
    { sourceField: "", targetField: "quantity", defaultValue: 1 }
  ],
  mode: "append"
}

7.5 데이터 집계 연동

v2-table-list → v2-aggregation-widget

┌─────────────────────────────────────────────────────────────────────────┐
│                     테이블 데이터 → 집계 위젯                            │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  v2-aggregation-widget                                           │    │
│  │  ┌───────────────┐ ┌───────────────┐ ┌───────────────┐          │    │
│  │  │ 📊 총 건수     │ │ 💰 총 금액    │ │ 📈 평균 단가   │          │    │
│  │  │    15건       │ │ ₩3,500,000   │ │ ₩233,333     │          │    │
│  │  └───────────────┘ └───────────────┘ └───────────────┘          │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                              ▲                                           │
│                              │ tableListDataChange 이벤트               │
│                              │                                           │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  v2-table-list                                                   │    │
│  │  (데이터 변경 시 자동으로 이벤트 발행)                           │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                          │
│  연결 방식: tableListDataChange 이벤트 자동 구독                        │
│  설정: v2-aggregation-widget의 dataSourceType = "table"                 │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

설정 방법:

// v2-aggregation-widget
{
  dataSourceType: "table",
  items: [
    { columnName: "id", aggregationType: "count", label: "총 건수" },
    { columnName: "amount", aggregationType: "sum", label: "총 금액" },
    { columnName: "unit_price", aggregationType: "avg", label: "평균 단가" }
  ]
}

7.6 연관 데이터 버튼 연동

┌─────────────────────────────────────────────────────────────────────────┐
│                     연관 데이터 버튼 ↔ 테이블                           │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  좌측 패널 (거래처 선택)                                                │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  v2-card-display (거래처 목록)                                   │    │
│  │  ┌─────────────────────────────────────────────────────────┐    │    │
│  │  │ [선택됨] ABC상사                                          │    │    │
│  │  │          DEF물산                                          │    │    │
│  │  └─────────────────────────────────────────────────────────┘    │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                              │                                           │
│                              ▼ 선택 데이터 전달                          │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  related-data-buttons                                            │    │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐                         │    │
│  │  │ 품목정보  │ │ 단가정보  │ │ 거래내역  │                         │    │
│  │  │   (5)    │ │   (3)    │ │   (12)   │                         │    │
│  │  └────┬─────┘ └──────────┘ └──────────┘                         │    │
│  └───────┼─────────────────────────────────────────────────────────┘    │
│          │ related-button-select 이벤트                                 │
│          ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  v2-table-list (품목정보)                                        │    │
│  │  - customer_code = "ABC상사" 조건으로 필터링                     │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

7.7 탭 기반 테이블 필터링

v2-tabs-widget → v2-table-search-widget → v2-table-list

┌─────────────────────────────────────────────────────────────────────────┐
│                     탭 기반 테이블 필터링                                │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  v2-tabs-widget                                                  │    │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐                         │    │
│  │  │ [주문]    │ │  입고    │ │  재고    │                         │    │
│  │  └──────────┘ └──────────┘ └──────────┘                         │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                              │                                           │
│                              ▼ ActiveTabContext                         │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  v2-table-search-widget                                          │    │
│  │  (활성 탭에 해당하는 테이블만 대상으로 표시)                      │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                              │                                           │
│                              ▼ onFilterChange                           │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  v2-table-list (주문 탭 내)                                      │    │
│  │  - parentTabId가 활성 탭과 일치하는 테이블만 필터 적용           │    │
│  └─────────────────────────────────────────────────────────────────┘    │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

8. 연동 설정 방법

8.1 검색 위젯 + 테이블 리스트

필요한 컴포넌트:

  • v2-table-search-widget
  • v2-table-list

설정:

// v2-table-search-widget 설정
{
  filterMode: "preset",                 // 또는 "dynamic"
  presetFilters: [
    { columnName: "name", filterType: "text", columnLabel: "이름" },
    { columnName: "created_at", filterType: "date", columnLabel: "등록일" },
    { columnName: "status", filterType: "select", columnLabel: "상태" }
  ],
  targetPanelPosition: "auto"           // "left" | "right" | "auto"
}

// v2-table-list는 특별한 설정 불필요 (자동 등록)

8.2 분할 패널 마스터-디테일

필요한 컴포넌트:

  • v2-split-panel-layout

설정:

{
  leftPanel: {
    title: "부서 목록",
    tableName: "dept_info",
    displayMode: "list",
    columns: [
      { name: "dept_code", label: "부서코드" },
      { name: "dept_name", label: "부서명" }
    ]
  },
  rightPanel: {
    title: "직원 목록",
    tableName: "user_info",
    displayMode: "table",
    columns: [
      { name: "user_id", label: "사번" },
      { name: "user_name", label: "이름" },
      { name: "position", label: "직책" }
    ],
    relation: {
      type: "detail",
      leftColumn: "dept_code",
      rightColumn: "dept_code"
    }
  },
  splitRatio: 30,
  resizable: true
}

8.3 폼 + 리피터 저장

필요한 컴포넌트:

  • 입력 컴포넌트들 (text-input, date-input 등)
  • v2-unified-repeater
  • v2-button-primary

설정:

// v2-unified-repeater 설정
{
  renderMode: "inline",
  dataSource: {
    tableName: "order_detail",
    foreignKey: "order_id",           // 마스터 테이블의 FK
    referenceKey: "id"                // 마스터 테이블의 PK
  },
  columns: [
    { name: "item_code", label: "품목코드" },
    { name: "quantity", label: "수량" },
    { name: "unit_price", label: "단가" }
  ],
  features: {
    showAddButton: true,
    showDeleteButton: true,
    inlineEdit: true
  }
}

// v2-button-primary 설정
{
  text: "저장",
  action: {
    type: "save",
    saveMode: "withRepeater"
  }
}

8.4 데이터 전달 (테이블 → 리피터)

필요한 컴포넌트:

  • v2-table-list (소스)
  • v2-button-primary (전달 트리거)
  • v2-unified-repeater (타겟)

설정:

// v2-button-primary 설정
{
  text: "추가",
  action: {
    type: "transferData",
    config: {
      sourceComponentId: "item-selection-table",
      targetComponentId: "order-detail-repeater",
      mode: "append",
      mappingRules: [
        { sourceField: "item_code", targetField: "item_code" },
        { sourceField: "item_name", targetField: "item_name" },
        { sourceField: "unit_price", targetField: "unit_price" },
        { sourceField: "", targetField: "quantity", defaultValue: 1 }
      ],
      validation: {
        unique: ["item_code"]           // 중복 방지
      }
    }
  }
}

8.5 집계 위젯 연동

필요한 컴포넌트:

  • v2-table-list 또는 v2-unified-repeater
  • v2-aggregation-widget

설정:

// v2-aggregation-widget 설정
{
  dataSourceType: "table",              // 또는 "repeater"
  // sourceComponentId는 자동 감지 (같은 화면의 첫 번째 테이블)
  items: [
    {
      id: "total-count",
      label: "총 건수",
      columnName: "id",
      aggregationType: "count",
      icon: "FileText"
    },
    {
      id: "total-amount",
      label: "총 금액",
      columnName: "amount",
      aggregationType: "sum",
      format: {
        prefix: "₩",
        thousandSeparator: true
      }
    }
  ],
  layout: "horizontal",
  refreshOnFormChange: true
}

연동 조합 요약표

소스 컴포넌트 타겟 컴포넌트 연동 방식 용도
v2-table-search-widget v2-table-list TableOptionsContext 검색/필터
v2-split-panel-layout v2-split-panel-layout SplitPanelContext 마스터-디테일
v2-button-primary v2-unified-repeater beforeFormSave/repeaterSave 저장
v2-table-list v2-unified-repeater DataProvidable/DataReceivable 데이터 전달
v2-table-list v2-aggregation-widget tableListDataChange 집계
v2-unified-repeater v2-aggregation-widget repeaterDataChange 집계
v2-tabs-widget v2-table-search-widget ActiveTabContext 탭 필터링
related-data-buttons v2-table-list related-button-select 연관 데이터
v2-button-primary v2-table-list refreshTable 새로고침
v2-card-display v2-table-list SplitPanelContext 선택 연동

관련 파일 참조

파일 역할
frontend/lib/utils/buttonActions.ts 버튼 액션 실행, 이벤트 발행
frontend/contexts/TableOptionsContext.tsx 테이블 관리 Context
frontend/contexts/SplitPanelContext.tsx 분할 패널 Context
frontend/contexts/ScreenContext.tsx 화면 Context
frontend/contexts/ActiveTabContext.tsx 활성 탭 Context
frontend/components/unified/UnifiedFormContext.tsx 폼 Context
frontend/types/data-transfer.ts 데이터 전달 타입