# V2 컴포넌트 및 V2 폼 컴포넌트 결합도 분석 보고서

> 작성일: 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-repeater | `v2-repeater/` | 통합 리피터 |

### 1.2 V2 폼 컴포넌트 (11개)

| # | 컴포넌트 | 파일 | 주요 용도 |
|---|---------|------|----------|
| 1 | V2Input | `V2Input.tsx` | 텍스트/숫자/이메일 등 입력 |
| 2 | V2Select | `V2Select.tsx` | 선택박스/라디오/체크박스 |
| 3 | V2Date | `V2Date.tsx` | 날짜/시간 입력 |
| 4 | V2Repeater | `V2Repeater.tsx` | 리피터 (테이블 형태) |
| 5 | V2Layout | `V2Layout.tsx` | 레이아웃 컨테이너 |
| 6 | V2Group | `V2Group.tsx` | 그룹 컨테이너 (카드/탭/접기) |
| 7 | V2Hierarchy | `V2Hierarchy.tsx` | 계층 구조 표시 |
| 8 | V2List | `V2List.tsx` | 리스트 표시 |
| 9 | V2Media | `V2Media.tsx` | 파일/이미지/비디오 업로드 |
| 10 | V2Biz | `V2Biz.tsx` | 비즈니스 컴포넌트 |
| 11 | V2FormContext | `V2FormContext.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-repeater | ❌ | 0개 | ❌ | 🟢 1/10 |

### 2.3 V2 폼 컴포넌트 결합도 상세

| 컴포넌트 | buttonActions Import | CustomEvent 사용 | window.__ 사용 | 결합도 점수 |
|---------|---------------------|------------------|----------------|------------|
| **V2Repeater** | ❌ | 7개 수신/발생 | 2개 사용 | 🔴 8/10 |
| **V2FormContext** | ❌ | 3개 발생 | ❌ | 🟠 4/10 |
| V2Input | ❌ | 0개 | ❌ | 🟢 1/10 |
| V2Select | ❌ | 0개 | ❌ | 🟢 1/10 |
| V2Date | ❌ | 0개 | ❌ | 🟢 1/10 |
| V2Layout | ❌ | 0개 | ❌ | 🟢 1/10 |
| V2Group | ❌ | 0개 | ❌ | 🟢 1/10 |
| V2Hierarchy | ❌ | 0개 | ❌ | 🟢 1/10 |
| V2List | ❌ | 0개 (TableList 래핑) | ❌ | 🟢 2/10 |
| V2Media | ❌ | 0개 | ❌ | 🟢 1/10 |
| V2Biz | ❌ | 0개 | ❌ | 🟢 1/10 |

**V2Repeater 상세:**
```typescript
// 전역 상태 사용
window.__v2RepeaterInstances = new Set();
window.__v2RepeaterInstances.add(targetTableName);

// CustomEvent 수신
window.addEventListener("repeaterSave", handleSaveEvent);
window.addEventListener("beforeFormSave", handleBeforeFormSave);
window.addEventListener("componentDataTransfer", handleComponentDataTransfer);
window.addEventListener("splitPanelDataTransfer", handleSplitPanelDataTransfer);
```

**V2FormContext 상세:**
```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     │
    └───────────┘     └───────────┘     └───────────────┘
          │                  │
          │                  │
          ▼                  ▼
    ┌───────────┐     ┌───────────┐
    │V2    │     │V2    │
    │Repeater   │     │FormContext│
    └───────────┘     └───────────┘
```

---

## 4. 이벤트 매트릭스

### 4.1 이벤트 발생 컴포넌트

| 이벤트명 | 발생 컴포넌트 | 용도 |
|---------|-------------|------|
| `refreshTable` | v2-button-primary, buttonActions | 테이블 데이터 새로고침 |
| `closeEditModal` | v2-button-primary, buttonActions | 수정 모달 닫기 |
| `saveSuccessInModal` | v2-button-primary, buttonActions | 저장 성공 알림 (연속 등록) |
| `beforeFormSave` | V2FormContext, buttonActions | 저장 전 데이터 수집 |
| `afterFormSave` | V2FormContext | 저장 완료 알림 |
| `tableListDataChange` | v2-table-list | 테이블 데이터 변경 알림 |
| `repeaterDataChange` | V2Repeater | 리피터 데이터 변경 알림 |
| `repeaterSave` | buttonActions | 리피터 저장 요청 |
| `openScreenModal` | v2-split-panel-layout | 화면 모달 열기 |
| `refreshCardDisplay` | buttonActions | 카드 디스플레이 새로고침 |

### 4.2 이벤트 수신 컴포넌트

| 이벤트명 | 수신 컴포넌트 | 처리 내용 |
|---------|-------------|----------|
| `refreshTable` | v2-table-list, v2-split-panel-layout | 데이터 재조회 |
| `beforeFormSave` | v2-repeat-container, V2Repeater | formData에 섹션 데이터 추가 |
| `tableListDataChange` | v2-aggregation-widget, v2-repeat-container | 집계 재계산, 데이터 동기화 |
| `repeaterDataChange` | v2-aggregation-widget, v2-repeat-container | 집계 재계산, 데이터 동기화 |
| `repeaterSave` | V2Repeater | 리피터 데이터 저장 실행 |
| `selectionChange` | v2-aggregation-widget | 선택 기반 집계 |
| `componentDataTransfer` | V2Repeater | 컴포넌트 간 데이터 전달 |
| `splitPanelDataTransfer` | V2Repeater | 분할 패널 데이터 전달 |
| `refreshCardDisplay` | v2-card-display | 카드 데이터 재조회 |

---

## 5. 전역 상태 사용 현황

| 전역 변수 | 사용 컴포넌트 | 용도 | 위험도 |
|----------|-------------|------|--------|
| `window.__v2RepeaterInstances` | V2Repeater, 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 V2 컴포넌트 (11개)

| 결합도 수준 | 개수 | 컴포넌트 |
|------------|------|---------|
| 🔴 높음 (7-10점) | 1개 | V2Repeater |
| 🟠 중간 (4-6점) | 1개 | V2FormContext |
| 🟢 낮음 (1-3점) | 9개 | 나머지 |

### 6.3 전체 결합도 분포

```
전체 29개 컴포넌트

높은 결합도 (🔴): 3개 (10.3%)
├── v2-button-primary
├── v2-table-list
└── V2Repeater

중간 결합도 (🟠): 5개 (17.2%)
├── v2-repeat-container
├── v2-split-panel-layout
├── v2-aggregation-widget
├── v2-tabs-widget
└── V2FormContext

낮은 결합도 (🟢): 21개 (72.5%)
└── 나머지 모든 컴포넌트
```

---

## 7. 장애 영향 분석

### 7.1 현재 구조에서의 장애 전파 경로

```
v2-button-primary 오류 발생 시:
├── buttonActions.ts 영향 → 모든 저장/삭제 기능 중단
├── refreshTable 이벤트 미발생 → 테이블 갱신 안됨
└── closeEditModal 이벤트 미발생 → 모달 닫기 안됨

v2-table-list 오류 발생 시:
├── tableListDataChange 미발생 → 집계 위젯 업데이트 안됨
├── related-button 이벤트 미발생 → 관련 버튼 비활성화
└── 전역 상태 오염 가능성

V2Repeater 오류 발생 시:
├── beforeFormSave 처리 실패 → 리피터 데이터 저장 누락
├── repeaterSave 수신 실패 → 저장 요청 무시
└── 전역 인스턴스 레지스트리 오류
```

### 7.2 장애 격리 현황

| 컴포넌트 | 장애 시 영향 범위 | 격리 수준 |
|---------|-----------------|----------|
| v2-button-primary | 저장/삭제 전체 | ❌ 격리 안됨 |
| v2-table-list | 집계/관련버튼 | ❌ 격리 안됨 |
| V2Repeater | 리피터 저장 | ❌ 격리 안됨 |
| 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 | V2Repeater | 전역 상태 제거, 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** | ✅ | - | ✅ | 완료 |
| **V2Repeater** | ✅ | - | ✅ | 완료 |

### 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, V2Repeater) 마이그레이션 완료**
- **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-repeater/
│   └── utils/
│       └── buttonActions.ts (7,145줄)
└── components/
    └── v2/
        ├── V2Input.tsx
        ├── V2Select.tsx
        ├── V2Date.tsx
        ├── V2Repeater.tsx
        ├── V2Layout.tsx
        ├── V2Group.tsx
        ├── V2Hierarchy.tsx
        ├── V2List.tsx
        ├── V2Media.tsx
        ├── V2Biz.tsx
        └── V2FormContext.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` | 컴포넌트 에러 |

