ERP-node/docs/V2_COMPONENT_COUPLING_ANALY...

24 KiB

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 상세:

// 직접 의존
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 상세:

// 전역 상태 사용
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 상세:

// CustomEvent 수신
window.addEventListener("beforeFormSave", handleBeforeFormSave);
window.addEventListener("repeaterDataChange", handleDataChange);
window.addEventListener("tableListDataChange", handleDataChange);

v2-aggregation-widget 상세:

// 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 상세:

// 전역 상태 사용
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 상세:

// 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 사용 방법

// 이벤트 발행
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 컴포넌트 에러