# V2 컴포넌트 및 Unified 폼 컴포넌트 결합도 분석 보고서 > 작성일: 2026-01-26 > 목적: 컴포넌트 간 결합도 분석 및 느슨한 결합 전환 가능성 평가 --- ## 1. 분석 대상 컴포넌트 목록 ### 1.1 V2 컴포넌트 (18개) | # | 컴포넌트 | 경로 | 주요 용도 | |---|---------|------|----------| | 1 | v2-aggregation-widget | `v2-aggregation-widget/` | 데이터 집계 표시 | | 2 | v2-button-primary | `v2-button-primary/` | 기본 버튼 (저장/삭제/모달 등) | | 3 | v2-card-display | `v2-card-display/` | 카드 형태 데이터 표시 | | 4 | v2-category-manager | `v2-category-manager/` | 카테고리 트리 관리 | | 5 | v2-divider-line | `v2-divider-line/` | 구분선 | | 6 | v2-location-swap-selector | `v2-location-swap-selector/` | 출발지/도착지 선택 | | 7 | v2-numbering-rule | `v2-numbering-rule/` | 채번 규칙 표시 | | 8 | v2-pivot-grid | `v2-pivot-grid/` | 피벗 테이블 | | 9 | v2-rack-structure | `v2-rack-structure/` | 렉 구조 표시 | | 10 | v2-repeat-container | `v2-repeat-container/` | 리피터 컨테이너 | | 11 | v2-repeat-screen-modal | `v2-repeat-screen-modal/` | 반복 화면 모달 | | 12 | v2-section-card | `v2-section-card/` | 섹션 카드 | | 13 | v2-section-paper | `v2-section-paper/` | 섹션 페이퍼 | | 14 | v2-split-panel-layout | `v2-split-panel-layout/` | 분할 패널 레이아웃 | | 15 | v2-table-list | `v2-table-list/` | 테이블 리스트 | | 16 | v2-table-search-widget | `v2-table-search-widget/` | 테이블 검색 위젯 | | 17 | v2-tabs-widget | `v2-tabs-widget/` | 탭 위젯 | | 18 | v2-text-display | `v2-text-display/` | 텍스트 표시 | | 19 | v2-unified-repeater | `v2-unified-repeater/` | 통합 리피터 | ### 1.2 Unified 폼 컴포넌트 (11개) | # | 컴포넌트 | 파일 | 주요 용도 | |---|---------|------|----------| | 1 | UnifiedInput | `UnifiedInput.tsx` | 텍스트/숫자/이메일 등 입력 | | 2 | UnifiedSelect | `UnifiedSelect.tsx` | 선택박스/라디오/체크박스 | | 3 | UnifiedDate | `UnifiedDate.tsx` | 날짜/시간 입력 | | 4 | UnifiedRepeater | `UnifiedRepeater.tsx` | 리피터 (테이블 형태) | | 5 | UnifiedLayout | `UnifiedLayout.tsx` | 레이아웃 컨테이너 | | 6 | UnifiedGroup | `UnifiedGroup.tsx` | 그룹 컨테이너 (카드/탭/접기) | | 7 | UnifiedHierarchy | `UnifiedHierarchy.tsx` | 계층 구조 표시 | | 8 | UnifiedList | `UnifiedList.tsx` | 리스트 표시 | | 9 | UnifiedMedia | `UnifiedMedia.tsx` | 파일/이미지/비디오 업로드 | | 10 | UnifiedBiz | `UnifiedBiz.tsx` | 비즈니스 컴포넌트 | | 11 | UnifiedFormContext | `UnifiedFormContext.tsx` | 폼 상태 관리 컨텍스트 | --- ## 2. 결합도 분석 결과 ### 2.1 결합도 유형 분류 | 유형 | 설명 | 문제점 | |------|------|--------| | **직접 Import** | 다른 모듈을 직접 import하여 사용 | 변경 시 영향 범위 큼 | | **CustomEvent** | window.dispatchEvent로 이벤트 발생/수신 | 암묵적 의존성, 타입 안전성 부족 | | **전역 상태 (window.__)** | window 객체에 전역 변수 저장 | 네임스페이스 충돌, 테스트 어려움 | | **Context API** | React Context로 상태 공유 | 상대적으로 안전하지만 범위 확장 시 주의 | ### 2.2 V2 컴포넌트 결합도 상세 #### 2.2.1 높은 결합도 (High Coupling) - 우선 개선 대상 | 컴포넌트 | buttonActions Import | CustomEvent 사용 | window.__ 사용 | 결합도 점수 | |---------|---------------------|------------------|----------------|------------| | **v2-button-primary** | ✅ 직접 Import | 4개 발생 | ❌ | 🔴 8/10 | | **v2-table-list** | ❌ | 16개 수신/발생 | 4개 사용 | 🔴 9/10 | **v2-button-primary 상세:** ```typescript // 직접 의존 import { ButtonActionExecutor, ButtonActionContext } from "@/lib/utils/buttonActions"; // CustomEvent 발생 window.dispatchEvent(new CustomEvent("refreshTable")); window.dispatchEvent(new CustomEvent("closeEditModal")); window.dispatchEvent(new CustomEvent("saveSuccessInModal")); ``` **v2-table-list 상세:** ```typescript // 전역 상태 사용 window.__relatedButtonsTargetTables window.__relatedButtonsSelectedData // CustomEvent 발생 window.dispatchEvent(new CustomEvent("tableListDataChange", { ... })); // CustomEvent 수신 window.addEventListener("refreshTable", handleRefreshTable); window.addEventListener("related-button-register", ...); window.addEventListener("related-button-unregister", ...); window.addEventListener("related-button-select", ...); ``` #### 2.2.2 중간 결합도 (Medium Coupling) | 컴포넌트 | buttonActions Import | CustomEvent 사용 | window.__ 사용 | 결합도 점수 | |---------|---------------------|------------------|----------------|------------| | **v2-repeat-container** | ❌ | 5개 수신/발생 | ❌ | 🟠 6/10 | | **v2-split-panel-layout** | ❌ | 3개 수신/발생 | ❌ | 🟠 5/10 | | **v2-aggregation-widget** | ❌ | 14개 수신 | ❌ | 🟠 6/10 | | **v2-tabs-widget** | ❌ | 2개 | ❌ | 🟠 4/10 | **v2-repeat-container 상세:** ```typescript // CustomEvent 수신 window.addEventListener("beforeFormSave", handleBeforeFormSave); window.addEventListener("repeaterDataChange", handleDataChange); window.addEventListener("tableListDataChange", handleDataChange); ``` **v2-aggregation-widget 상세:** ```typescript // CustomEvent 수신 (다수) window.addEventListener("tableListDataChange", handleTableListDataChange); window.addEventListener("repeaterDataChange", handleRepeaterDataChange); window.addEventListener("selectionChange", handleSelectionChange); window.addEventListener("tableSelectionChange", handleSelectionChange); window.addEventListener("rowSelectionChange", handleSelectionChange); window.addEventListener("checkboxSelectionChange", handleSelectionChange); ``` #### 2.2.3 낮은 결합도 (Low Coupling) - 독립적 | 컴포넌트 | buttonActions Import | CustomEvent 사용 | window.__ 사용 | 결합도 점수 | |---------|---------------------|------------------|----------------|------------| | v2-pivot-grid | ❌ | 0개 | window.open만 | 🟢 2/10 | | v2-card-display | ❌ | 1개 수신 | ❌ | 🟢 2/10 | | v2-category-manager | ❌ | 2개 (ConfigPanel) | ❌ | 🟢 2/10 | | v2-divider-line | ❌ | 0개 | ❌ | 🟢 1/10 | | v2-location-swap-selector | ❌ | 0개 | ❌ | 🟢 1/10 | | v2-numbering-rule | ❌ | 0개 | ❌ | 🟢 1/10 | | v2-rack-structure | ❌ | 0개 | ❌ | 🟢 1/10 | | v2-section-card | ❌ | 1개 (ConfigPanel) | ❌ | 🟢 1/10 | | v2-section-paper | ❌ | 1개 (ConfigPanel) | ❌ | 🟢 1/10 | | v2-table-search-widget | ❌ | 0개 | ❌ | 🟢 1/10 | | v2-text-display | ❌ | 0개 | ❌ | 🟢 1/10 | | v2-repeat-screen-modal | ❌ | 0개 | ❌ | 🟢 1/10 | | v2-unified-repeater | ❌ | 0개 | ❌ | 🟢 1/10 | ### 2.3 Unified 폼 컴포넌트 결합도 상세 | 컴포넌트 | buttonActions Import | CustomEvent 사용 | window.__ 사용 | 결합도 점수 | |---------|---------------------|------------------|----------------|------------| | **UnifiedRepeater** | ❌ | 7개 수신/발생 | 2개 사용 | 🔴 8/10 | | **UnifiedFormContext** | ❌ | 3개 발생 | ❌ | 🟠 4/10 | | UnifiedInput | ❌ | 0개 | ❌ | 🟢 1/10 | | UnifiedSelect | ❌ | 0개 | ❌ | 🟢 1/10 | | UnifiedDate | ❌ | 0개 | ❌ | 🟢 1/10 | | UnifiedLayout | ❌ | 0개 | ❌ | 🟢 1/10 | | UnifiedGroup | ❌ | 0개 | ❌ | 🟢 1/10 | | UnifiedHierarchy | ❌ | 0개 | ❌ | 🟢 1/10 | | UnifiedList | ❌ | 0개 (TableList 래핑) | ❌ | 🟢 2/10 | | UnifiedMedia | ❌ | 0개 | ❌ | 🟢 1/10 | | UnifiedBiz | ❌ | 0개 | ❌ | 🟢 1/10 | **UnifiedRepeater 상세:** ```typescript // 전역 상태 사용 window.__unifiedRepeaterInstances = new Set(); window.__unifiedRepeaterInstances.add(targetTableName); // CustomEvent 수신 window.addEventListener("repeaterSave", handleSaveEvent); window.addEventListener("beforeFormSave", handleBeforeFormSave); window.addEventListener("componentDataTransfer", handleComponentDataTransfer); window.addEventListener("splitPanelDataTransfer", handleSplitPanelDataTransfer); ``` **UnifiedFormContext 상세:** ```typescript // CustomEvent 발생 (레거시 호환) window.dispatchEvent(new CustomEvent("beforeFormSave", { detail: eventDetail })); window.dispatchEvent(new CustomEvent("afterFormSave", { detail: { ... } })); ``` --- ## 3. 주요 결합 지점 시각화 ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ buttonActions.ts (7,145줄) │ │ ⬇️ 직접 Import │ │ v2-button-primary ───────────────────────────────────────────────┐ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ CustomEvent ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ Event Bus (현재: window) │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ refreshTable │ │beforeFormSave│ │tableListData │ │ │ │ │ │ │ │ Change │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ │ │ └─────────│──────────────────│──────────────────│─────────────────────────┘ │ │ │ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────────┐ │v2-table │ │v2-repeat │ │v2-aggregation │ │ -list │ │-container │ │ -widget │ └───────────┘ └───────────┘ └───────────────┘ │ │ │ │ ▼ ▼ ┌───────────┐ ┌───────────┐ │Unified │ │Unified │ │Repeater │ │FormContext│ └───────────┘ └───────────┘ ``` --- ## 4. 이벤트 매트릭스 ### 4.1 이벤트 발생 컴포넌트 | 이벤트명 | 발생 컴포넌트 | 용도 | |---------|-------------|------| | `refreshTable` | v2-button-primary, buttonActions | 테이블 데이터 새로고침 | | `closeEditModal` | v2-button-primary, buttonActions | 수정 모달 닫기 | | `saveSuccessInModal` | v2-button-primary, buttonActions | 저장 성공 알림 (연속 등록) | | `beforeFormSave` | UnifiedFormContext, buttonActions | 저장 전 데이터 수집 | | `afterFormSave` | UnifiedFormContext | 저장 완료 알림 | | `tableListDataChange` | v2-table-list | 테이블 데이터 변경 알림 | | `repeaterDataChange` | UnifiedRepeater | 리피터 데이터 변경 알림 | | `repeaterSave` | buttonActions | 리피터 저장 요청 | | `openScreenModal` | v2-split-panel-layout | 화면 모달 열기 | | `refreshCardDisplay` | buttonActions | 카드 디스플레이 새로고침 | ### 4.2 이벤트 수신 컴포넌트 | 이벤트명 | 수신 컴포넌트 | 처리 내용 | |---------|-------------|----------| | `refreshTable` | v2-table-list, v2-split-panel-layout | 데이터 재조회 | | `beforeFormSave` | v2-repeat-container, UnifiedRepeater | formData에 섹션 데이터 추가 | | `tableListDataChange` | v2-aggregation-widget, v2-repeat-container | 집계 재계산, 데이터 동기화 | | `repeaterDataChange` | v2-aggregation-widget, v2-repeat-container | 집계 재계산, 데이터 동기화 | | `repeaterSave` | UnifiedRepeater | 리피터 데이터 저장 실행 | | `selectionChange` | v2-aggregation-widget | 선택 기반 집계 | | `componentDataTransfer` | UnifiedRepeater | 컴포넌트 간 데이터 전달 | | `splitPanelDataTransfer` | UnifiedRepeater | 분할 패널 데이터 전달 | | `refreshCardDisplay` | v2-card-display | 카드 데이터 재조회 | --- ## 5. 전역 상태 사용 현황 | 전역 변수 | 사용 컴포넌트 | 용도 | 위험도 | |----------|-------------|------|--------| | `window.__unifiedRepeaterInstances` | UnifiedRepeater, buttonActions | 리피터 인스턴스 추적 | 🟠 중간 | | `window.__relatedButtonsTargetTables` | v2-table-list | 관련 버튼 대상 테이블 | 🟠 중간 | | `window.__relatedButtonsSelectedData` | v2-table-list, buttonActions | 관련 버튼 선택 데이터 | 🟠 중간 | | `window.__dataRegistry` | v2-table-list (v1/v2) | 테이블 데이터 레지스트리 | 🟠 중간 | --- ## 6. 결합도 요약 점수 ### 6.1 V2 컴포넌트 (18개) | 결합도 수준 | 개수 | 컴포넌트 | |------------|------|---------| | 🔴 높음 (7-10점) | 2개 | v2-button-primary, v2-table-list | | 🟠 중간 (4-6점) | 4개 | v2-repeat-container, v2-split-panel-layout, v2-aggregation-widget, v2-tabs-widget | | 🟢 낮음 (1-3점) | 12개 | 나머지 | ### 6.2 Unified 컴포넌트 (11개) | 결합도 수준 | 개수 | 컴포넌트 | |------------|------|---------| | 🔴 높음 (7-10점) | 1개 | UnifiedRepeater | | 🟠 중간 (4-6점) | 1개 | UnifiedFormContext | | 🟢 낮음 (1-3점) | 9개 | 나머지 | ### 6.3 전체 결합도 분포 ``` 전체 29개 컴포넌트 높은 결합도 (🔴): 3개 (10.3%) ├── v2-button-primary ├── v2-table-list └── UnifiedRepeater 중간 결합도 (🟠): 5개 (17.2%) ├── v2-repeat-container ├── v2-split-panel-layout ├── v2-aggregation-widget ├── v2-tabs-widget └── UnifiedFormContext 낮은 결합도 (🟢): 21개 (72.5%) └── 나머지 모든 컴포넌트 ``` --- ## 7. 장애 영향 분석 ### 7.1 현재 구조에서의 장애 전파 경로 ``` v2-button-primary 오류 발생 시: ├── buttonActions.ts 영향 → 모든 저장/삭제 기능 중단 ├── refreshTable 이벤트 미발생 → 테이블 갱신 안됨 └── closeEditModal 이벤트 미발생 → 모달 닫기 안됨 v2-table-list 오류 발생 시: ├── tableListDataChange 미발생 → 집계 위젯 업데이트 안됨 ├── related-button 이벤트 미발생 → 관련 버튼 비활성화 └── 전역 상태 오염 가능성 UnifiedRepeater 오류 발생 시: ├── beforeFormSave 처리 실패 → 리피터 데이터 저장 누락 ├── repeaterSave 수신 실패 → 저장 요청 무시 └── 전역 인스턴스 레지스트리 오류 ``` ### 7.2 장애 격리 현황 | 컴포넌트 | 장애 시 영향 범위 | 격리 수준 | |---------|-----------------|----------| | v2-button-primary | 저장/삭제 전체 | ❌ 격리 안됨 | | v2-table-list | 집계/관련버튼 | ❌ 격리 안됨 | | UnifiedRepeater | 리피터 저장 | ❌ 격리 안됨 | | v2-aggregation-widget | 자신만 | ✅ 부분 격리 | | v2-repeat-container | 자신만 | ✅ 부분 격리 | | 나머지 21개 | 자신만 | ✅ 완전 격리 | --- ## 8. 느슨한 결합 전환 권장사항 ### 8.1 1단계: 인프라 구축 (1-2일) 1. **V2 EventBus 생성** - 타입 안전한 이벤트 시스템 - 에러 격리 (Promise.allSettled) - 구독/발행 패턴 2. **V2 ErrorBoundary 생성** - 컴포넌트별 장애 격리 - 폴백 UI 제공 - 재시도 기능 ### 8.2 2단계: 핵심 컴포넌트 분리 (3-4일) | 우선순위 | 컴포넌트 | 작업 내용 | |---------|---------|----------| | 1 | v2-button-primary | buttonActions 의존성 제거, 독립 저장 서비스 | | 2 | v2-table-list | 전역 상태 제거, EventBus 전환 | | 3 | UnifiedRepeater | 전역 상태 제거, EventBus 전환 | ### 8.3 3단계: 이벤트 통합 (2-3일) | 기존 이벤트 | 신규 이벤트 | 변환 방식 | |------------|------------|----------| | `refreshTable` | `v2:table:refresh` | EventBus 발행 | | `beforeFormSave` | `v2:form:save:before` | EventBus 발행 | | `tableListDataChange` | `v2:table:data:change` | EventBus 발행 | | `repeaterSave` | `v2:repeater:save` | EventBus 발행 | ### 8.4 4단계: 레거시 제거 (1-2일) - `window.__` 전역 변수 → Context API 또는 Zustand - 기존 CustomEvent → V2 EventBus로 완전 전환 - buttonActions.ts 경량화 (7,145줄 → 분할) --- ## 9. 예상 효과 ### 9.1 장애 격리 | 현재 | 전환 후 | |------|--------| | 한 컴포넌트 오류 → 연쇄 실패 | 한 컴포넌트 오류 → 해당만 실패 표시 | | 저장 실패 → 전체 중단 | 저장 실패 → 부분 저장 + 에러 표시 | ### 9.2 유지보수성 | 현재 | 전환 후 | |------|--------| | buttonActions.ts 7,145줄 | 여러 서비스로 분리 (각 500줄 이하) | | 암묵적 이벤트 계약 | 타입 정의된 이벤트 | | 전역 상태 오염 위험 | Context/Store로 관리 | ### 9.3 테스트 용이성 | 현재 | 전환 후 | |------|--------| | 통합 테스트만 가능 | 단위 테스트 가능 | | 모킹 어려움 | EventBus 모킹 용이 | --- ## 10. 구현 현황 (2026-01-26 업데이트) ### 10.1 V2 Core 인프라 (✅ 완료) 다음 핵심 인프라가 구현되었습니다: | 모듈 | 경로 | 설명 | 상태 | |------|------|------|------| | **V2 EventBus** | `lib/v2-core/events/EventBus.ts` | 타입 안전한 이벤트 시스템 | ✅ 완료 | | **V2 이벤트 타입** | `lib/v2-core/events/types.ts` | 모든 이벤트 타입 정의 | ✅ 완료 | | **V2 ErrorBoundary** | `lib/v2-core/components/V2ErrorBoundary.tsx` | 컴포넌트별 에러 격리 | ✅ 완료 | | **레거시 어댑터** | `lib/v2-core/adapters/LegacyEventAdapter.ts` | CustomEvent ↔ EventBus 브릿지 | ✅ 완료 | | **V2 Core 초기화** | `lib/v2-core/init.ts` | 앱 시작 시 초기화 | ✅ 완료 | ### 10.2 컴포넌트 마이그레이션 현황 | 컴포넌트 | V2 EventBus 적용 | ErrorBoundary 적용 | 레거시 지원 | 상태 | |---------|-----------------|-------------------|-------------|------| | **v2-button-primary** | ✅ | ✅ | ✅ | 완료 | | **v2-table-list** | ✅ | - | ✅ | 완료 | | **UnifiedRepeater** | ✅ | - | ✅ | 완료 | ### 10.3 아키텍처 특징 **점진적 마이그레이션 지원:** - 레거시 `window.dispatchEvent` 이벤트와 V2 EventBus 이벤트가 **양방향 브릿지**로 연결됨 - 기존 코드 수정 없이 새 시스템 도입 가능 - 모든 V2 이벤트는 자동으로 레거시 CustomEvent로도 발행됨 **에러 격리:** - V2ErrorBoundary로 감싼 컴포넌트는 에러 발생 시 해당 컴포넌트만 에러 UI 표시 - 다른 컴포넌트는 정상 작동 유지 - 재시도 버튼으로 복구 가능 ### 10.4 사용 방법 ```typescript // 이벤트 발행 import { v2EventBus, V2_EVENTS } from "@/lib/v2-core"; v2EventBus.emit(V2_EVENTS.TABLE_REFRESH, { tableName: "item_info", target: "single", }); // 이벤트 구독 const unsubscribe = v2EventBus.subscribe( V2_EVENTS.TABLE_REFRESH, (payload) => { console.log("테이블 새로고침:", payload.tableName); }, { componentId: "my-component" } ); // 정리 useEffect(() => { return () => unsubscribe(); }, []); ``` --- ## 11. 결론 ### 11.1 현재 상태 요약 - **전체 29개 컴포넌트 중 72.5%(21개)는 이미 낮은 결합도**를 가지고 있어 독립적으로 동작 - **핵심 문제 컴포넌트 3개 (v2-button-primary, v2-table-list, UnifiedRepeater) 마이그레이션 완료** - **buttonActions.ts (7,145줄)**는 추후 분할 예정 (현재는 동작 유지) ### 11.2 달성 목표 ✅ **V2 Core 인프라 구축 완료** - 타입 안전한 EventBus - 컴포넌트별 ErrorBoundary - 레거시 호환 어댑터 - 앱 초기화 연동 ### 11.3 다음 단계 1. **buttonActions.ts 분할** - 서비스별 모듈 분리 2. **나머지 중간 결합도 컴포넌트 마이그레이션** (v2-repeat-container, v2-split-panel-layout 등) 3. **전역 상태 (window.__) 제거** - Context API 또는 Zustand로 전환 --- ## 부록 A: 파일 위치 참조 ``` frontend/ ├── lib/ │ ├── registry/ │ │ └── components/ │ │ ├── v2-aggregation-widget/ │ │ ├── v2-button-primary/ │ │ ├── v2-card-display/ │ │ ├── v2-category-manager/ │ │ ├── v2-divider-line/ │ │ ├── v2-location-swap-selector/ │ │ ├── v2-numbering-rule/ │ │ ├── v2-pivot-grid/ │ │ ├── v2-rack-structure/ │ │ ├── v2-repeat-container/ │ │ ├── v2-repeat-screen-modal/ │ │ ├── v2-section-card/ │ │ ├── v2-section-paper/ │ │ ├── v2-split-panel-layout/ │ │ ├── v2-table-list/ │ │ ├── v2-table-search-widget/ │ │ ├── v2-tabs-widget/ │ │ ├── v2-text-display/ │ │ └── v2-unified-repeater/ │ └── utils/ │ └── buttonActions.ts (7,145줄) └── components/ └── unified/ ├── UnifiedInput.tsx ├── UnifiedSelect.tsx ├── UnifiedDate.tsx ├── UnifiedRepeater.tsx ├── UnifiedLayout.tsx ├── UnifiedGroup.tsx ├── UnifiedHierarchy.tsx ├── UnifiedList.tsx ├── UnifiedMedia.tsx ├── UnifiedBiz.tsx └── UnifiedFormContext.tsx ``` ## 부록 B: V2 Core 파일 구조 (구현됨) ``` frontend/lib/v2-core/ ├── index.ts # 메인 내보내기 ├── init.ts # 앱 초기화 ├── events/ │ ├── index.ts │ ├── types.ts # 이벤트 타입 정의 │ └── EventBus.ts # 이벤트 버스 구현 ├── components/ │ ├── index.ts │ └── V2ErrorBoundary.tsx # 에러 바운더리 └── adapters/ ├── index.ts └── LegacyEventAdapter.ts # 레거시 브릿지 ``` ## 부록 C: 이벤트 타입 정의 (구현됨) 전체 이벤트 타입은 `frontend/lib/v2-core/events/types.ts`에 정의되어 있습니다. 주요 이벤트: | 이벤트 | 설명 | |--------|------| | `v2:table:refresh` | 테이블 새로고침 | | `v2:table:data:change` | 테이블 데이터 변경 | | `v2:form:save:collect` | 폼 저장 전 데이터 수집 | | `v2:modal:close` | 모달 닫기 | | `v2:modal:save:success` | 모달 저장 성공 | | `v2:repeater:save` | 리피터 저장 | | `v2:component:error` | 컴포넌트 에러 |