# V2 컴포넌트 분석 가이드

## 개요

V2 컴포넌트는 **화면관리 시스템 전용**으로 개발된 컴포넌트 세트입니다. 기존 컴포넌트와의 충돌을 방지하고, 새로운 기능(엔티티 조인, 다국어 지원, 커스텀 테이블 등)을 지원합니다.

### 핵심 원칙

- 모든 V2 컴포넌트는 `v2-` 접두사를 사용
- 원본 컴포넌트는 기존 화면 호환성 유지용으로 보존
- 새로운 화면 개발 시 반드시 V2 컴포넌트만 사용
- Definition 이름에 `V2` 접두사 사용 (예: `V2TableListDefinition`)

### 파일 경로

```
frontend/lib/registry/components/
├── v2-button-primary/          ← V2 컴포넌트 (수정 대상)
├── v2-table-list/              ← V2 컴포넌트 (수정 대상)
├── v2-split-panel-layout/      ← V2 컴포넌트 (수정 대상)
├── ...
├── button-primary/             ← 원본 (수정 금지)
├── table-list/                 ← 원본 (수정 금지)
└── ...
```

---

## V2 컴포넌트 목록 (18개)

| 컴포넌트 ID | 이름 | 카테고리 | 용도 |
|------------|------|----------|------|
| `v2-table-list` | 테이블 리스트 | DISPLAY | 데이터 목록 표시 (테이블/카드 모드) |
| `v2-table-grouped` | 그룹화 테이블 | DISPLAY | **NEW** 그룹별 접기/펼치기 테이블 |
| `v2-split-panel-layout` | 분할 패널 | DISPLAY | 마스터-디테일 레이아웃 |
| `v2-unified-repeater` | 통합 리피터 | UNIFIED | 반복 데이터 관리 (인라인/모달/버튼) |
| `v2-pivot-grid` | 피벗 그리드 | DISPLAY | 다차원 데이터 분석 피벗 테이블 |
| `v2-button-primary` | 기본 버튼 | ACTION | 저장/삭제 등 액션 버튼 |
| `v2-text-display` | 텍스트 표시 | DISPLAY | 텍스트/라벨 표시 |
| `v2-divider-line` | 구분선 | DISPLAY | 시각적 구분선 |
| `v2-card-display` | 카드 디스플레이 | DISPLAY | 카드 형태 데이터 표시 |
| `v2-numbering-rule` | 채번 규칙 | DISPLAY | 코드 자동 채번 설정 |
| `v2-table-search-widget` | 검색 필터 | DISPLAY | 테이블 검색/필터 위젯 |
| `v2-section-paper` | 섹션 페이퍼 | LAYOUT | 섹션 구분 컨테이너 |
| `v2-section-card` | 섹션 카드 | LAYOUT | 카드형 섹션 컨테이너 |
| `v2-tabs-widget` | 탭 위젯 | LAYOUT | 탭 기반 콘텐츠 전환 |
| `v2-location-swap-selector` | 위치 선택 | INPUT | 출발지/도착지 스왑 선택 |
| `v2-rack-structure` | 렉 구조 | DISPLAY | 창고 렉 시각화 |
| `v2-aggregation-widget` | 집계 위젯 | DISPLAY | 데이터 집계 (합계/평균/개수) |
| `v2-repeat-container` | 리피터 컨테이너 | LAYOUT | 데이터 수만큼 반복 렌더링 |

---

## 주요 컴포넌트 상세 분석

### 1. v2-table-list (테이블 리스트)

**용도**: 데이터베이스 테이블 데이터를 테이블/카드 형태로 표시

#### 주요 특징

- 테이블 모드 / 카드 모드 전환 가능
- 페이지네이션, 정렬, 필터링 지원
- 체크박스 선택 (단일/다중)
- 가로 스크롤 및 컬럼 고정
- 엔티티 조인 컬럼 지원
- 인라인 편집 기능
- Excel 내보내기

#### 데이터 흐름

```
┌─────────────────────────────────────────────────────────────┐
│                    v2-table-list                            │
├─────────────────────────────────────────────────────────────┤
│  ① config.selectedTable / customTableName 확인              │
│           ↓                                                  │
│  ② tableTypeApi.getData() 호출                              │
│           ↓                                                  │
│  ③ entityJoinApi.getEntityJoinColumns() 조인 컬럼 로드      │
│           ↓                                                  │
│  ④ 데이터 + 조인 데이터 병합                                │
│           ↓                                                  │
│  ⑤ 테이블/카드 모드로 렌더링                                │
│           ↓                                                  │
│  ⑥ onRowClick / onSelectionChange 이벤트 발생              │
└─────────────────────────────────────────────────────────────┘
```

#### 주요 설정 인터페이스

```typescript
interface TableListConfig {
  // 표시 모드
  displayMode: "table" | "card";
  
  // 커스텀 테이블 설정
  customTableName?: string;               // 커스텀 테이블
  useCustomTable?: boolean;               // 커스텀 테이블 사용 여부
  isReadOnly?: boolean;                   // 읽기전용
  
  // 컬럼 설정
  columns: ColumnConfig[];
  
  // 페이지네이션
  pagination: {
    enabled: boolean;
    pageSize: number;
    showSizeSelector: boolean;
    pageSizeOptions: number[];
  };
  
  // 체크박스
  checkbox: {
    enabled: boolean;
    multiple: boolean;                    // true: 체크박스, false: 라디오
    position: "left" | "right";
    selectAll: boolean;
  };
  
  // 필터
  filter: {
    enabled: boolean;
    filters: FilterConfig[];
  };
  
  // 연결된 필터 (다른 컴포넌트 값으로 필터링)
  linkedFilters?: LinkedFilterConfig[];
  
  // 제외 필터 (다른 테이블에 존재하는 데이터 제외)
  excludeFilter?: ExcludeFilterConfig;
  
  // 가로 스크롤 설정
  horizontalScroll: {
    enabled: boolean;
    maxVisibleColumns?: number;
    minColumnWidth?: number;
    maxColumnWidth?: number;
  };
}
```

#### 컬럼 설정

```typescript
interface ColumnConfig {
  columnName: string;                     // 컬럼명
  displayName: string;                    // 표시명
  visible: boolean;                       // 표시 여부
  sortable: boolean;                      // 정렬 가능
  searchable: boolean;                    // 검색 가능
  width?: number;                         // 너비
  align: "left" | "center" | "right";     // 정렬
  format?: "text" | "number" | "date" | "currency" | "boolean";
  
  // 엔티티 조인
  isEntityJoin?: boolean;                 // 조인 컬럼 여부
  entityJoinInfo?: {
    sourceTable: string;
    sourceColumn: string;
    joinAlias: string;
  };
  
  // 컬럼 고정
  fixed?: "left" | "right" | false;
  
  // 자동생성
  autoGeneration?: {
    type: "uuid" | "current_user" | "current_time" | "sequence" | "numbering_rule";
    enabled: boolean;
  };
  
  // 편집 가능 여부
  editable?: boolean;
}
```

---

### 2. v2-split-panel-layout (분할 패널)

**용도**: 마스터-디테일 패턴의 좌우 분할 레이아웃

#### 주요 특징

- 좌측: 마스터 목록 (리스트/테이블/커스텀 모드)
- 우측: 디테일 정보 (연관 데이터)
- 좌우 비율 조절 가능 (드래그 리사이즈)
- 다중 탭 지원 (우측 패널)
- N:M 관계 데이터 지원
- 중복 제거 기능
- **커스텀 모드 (NEW)**: 패널 내부에 자유롭게 컴포넌트 배치

#### 커스텀 모드 (displayMode: "custom")

패널 내부에 다양한 컴포넌트를 자유롭게 배치할 수 있는 모드입니다. **v2-tabs-widget과 동일한 구조**로 구현되어 있습니다.

**특징:**
- 패널 내부에 버튼, 테이블, 입력 필드 등 모든 V2 컴포넌트 배치 가능
- 드래그앤드롭으로 컴포넌트 이동
- 리사이즈 핸들로 크기 조절
- **컴포넌트 클릭 시 좌측 설정 패널에서 해당 컴포넌트 속성 편집**
- 디자인 모드에서 실제 컴포넌트가 렌더링되어 미리보기 가능

**사용 방법:**
1. 좌측/우측 패널의 `displayMode`를 `"custom"`으로 설정
2. 컴포넌트 팔레트에서 원하는 컴포넌트를 패널로 드래그
3. 배치된 컴포넌트 클릭 → 좌측 패널에서 속성 편집
4. 드래그 핸들(상단)로 이동, 모서리 핸들로 크기 조절

**컴포넌트 구조:**
```typescript
interface PanelInlineComponent {
  id: string;
  componentType: string;
  label?: string;
  position?: { x: number; y: number };
  size?: { width: number; height: number };
  componentConfig?: Record<string, any>;
  style?: Record<string, any>;
}
```

#### 데이터 흐름

```
┌─────────────────────────────────────────────────────────────┐
│                  v2-split-panel-layout                       │
├─────────────────────────────────────────────────────────────┤
│  ┌──────────────┐          ┌──────────────────────┐         │
│  │  좌측 패널    │ ───────→ │      우측 패널        │         │
│  │  (마스터)     │ 선택 이벤트│      (디테일)        │         │
│  └──────────────┘          └──────────────────────┘         │
│         │                            │                       │
│         ↓                            ↓                       │
│  leftPanel.tableName          rightPanel.tableName          │
│  leftPanel.columns            rightPanel.relation           │
│         │                            │                       │
│         ↓                            ↓                       │
│  좌측 데이터 조회 ─────────→ 관계 설정에 따라 우측 필터링      │
│  (독립 API 호출)              (FK/조인 키 기반)              │
└─────────────────────────────────────────────────────────────┘
```

#### 주요 설정 인터페이스

```typescript
interface SplitPanelLayoutConfig {
  // 좌측 패널
  leftPanel: {
    title: string;
    tableName?: string;
    useCustomTable?: boolean;
    customTableName?: string;
    displayMode?: "list" | "table" | "custom";  // 🆕 커스텀 모드 추가
    showSearch?: boolean;
    showAdd?: boolean;
    showEdit?: boolean;
    showDelete?: boolean;
    columns?: ColumnConfig[];
    tableConfig?: TableDisplayConfig;
    dataFilter?: DataFilterConfig;
    
    // 🆕 커스텀 모드 전용: 내부 컴포넌트 배열
    components?: PanelInlineComponent[];
  };
  
  // 우측 패널
  rightPanel: {
    title: string;
    tableName?: string;
    displayMode?: "list" | "table" | "custom";  // 🆕 커스텀 모드 추가
    columns?: ColumnConfig[];
    
    // 🆕 커스텀 모드 전용: 내부 컴포넌트 배열
    components?: PanelInlineComponent[];
    
    // 관계 설정
    relation?: {
      type?: "join" | "detail";
      leftColumn?: string;              // 좌측 조인 컬럼
      rightColumn?: string;             // 우측 조인 컬럼
      foreignKey?: string;              // FK 컬럼
      keys?: Array<{                    // 복합키 지원
        leftColumn: string;
        rightColumn: string;
      }>;
    };
    
    // 추가 설정 (N:M 관계)
    addConfig?: {
      targetTable?: string;             // 실제 INSERT 테이블
      autoFillColumns?: Record<string, any>;
      leftPanelColumn?: string;
      targetColumn?: string;
    };
    
    // 중복 제거
    deduplication?: {
      enabled: boolean;
      groupByColumn: string;
      keepStrategy: "latest" | "earliest" | "base_price" | "current_date";
    };
    
    // 추가 탭
    additionalTabs?: AdditionalTabConfig[];
  };
  
  // 레이아웃
  splitRatio?: number;                  // 좌우 비율 (0-100)
  resizable?: boolean;                  // 크기 조절 가능
  minLeftWidth?: number;
  minRightWidth?: number;
  
  // 동작
  autoLoad?: boolean;
  syncSelection?: boolean;
}
```

---

### 3. v2-unified-repeater (통합 리피터)

**용도**: 반복 데이터 관리 (기존 여러 리피터 통합)

#### 주요 특징

- 3가지 렌더링 모드: 인라인/모달/버튼
- 마스터-디테일 FK 자동 연결
- 저장 테이블 분리 가능
- 행 추가/삭제, 드래그 정렬
- 선택 기능 (단일/다중)

#### 데이터 흐름

```
┌─────────────────────────────────────────────────────────────┐
│                   v2-unified-repeater                        │
├─────────────────────────────────────────────────────────────┤
│  ① 마스터 저장 이벤트 수신 (repeaterSave)                   │
│           ↓                                                  │
│  ② masterRecordId 전달받음                                  │
│           ↓                                                  │
│  ③ foreignKeyColumn에 masterRecordId 자동 설정             │
│           ↓                                                  │
│  ④ dataSource.tableName으로 데이터 저장                     │
│           ↓                                                  │
│  ⑤ 저장 완료 후 onDataChange 이벤트 발생                   │
└─────────────────────────────────────────────────────────────┘
```

#### 주요 설정 인터페이스

```typescript
interface UnifiedRepeaterConfig {
  // 렌더링 모드
  renderMode: "inline" | "modal" | "button" | "mixed";
  
  // 데이터 소스
  dataSource: {
    tableName: string;                  // 저장 테이블
    foreignKey: string;                 // FK 컬럼
    referenceKey: string;               // 참조할 PK 컬럼
  };
  
  // 컬럼 설정
  columns: ColumnConfig[];
  
  // 모달 설정
  modal: {
    size: "sm" | "md" | "lg" | "xl";
  };
  
  // 버튼 설정
  button: {
    sourceType: "manual" | "auto";
    manualButtons: ButtonConfig[];
    layout: "horizontal" | "vertical";
    style: "outline" | "solid";
  };
  
  // 기능 설정
  features: {
    showAddButton: boolean;
    showDeleteButton: boolean;
    inlineEdit: boolean;
    dragSort: boolean;
    showRowNumber: boolean;
    selectable: boolean;
    multiSelect: boolean;
  };
}
```

#### 데이터 전달 인터페이스

v2-unified-repeater는 **DataProvidable**과 **DataReceivable** 인터페이스를 구현하여 다른 컴포넌트와 데이터를 주고받을 수 있습니다.

**DataProvidable 구현**:

```typescript
// 다른 컴포넌트에서 이 리피터의 데이터를 가져갈 수 있음
const dataProvider: DataProvidable = {
  componentId: parentId || config.fieldName || "unified-repeater",
  componentType: "unified-repeater",
  
  // 선택된 행 데이터 반환
  getSelectedData: () => {
    return Array.from(selectedRows).map((idx) => data[idx]).filter(Boolean);
  },
  
  // 전체 데이터 반환
  getAllData: () => {
    return [...data];
  },
  
  // 선택 초기화
  clearSelection: () => {
    setSelectedRows(new Set());
  },
};
```

**DataReceivable 구현**:

```typescript
// 외부에서 이 리피터로 데이터를 전달받을 수 있음
const dataReceiver: DataReceivable = {
  componentId: parentId || config.fieldName || "unified-repeater",
  componentType: "repeater",
  
  // 데이터 수신 (append, replace, merge 모드 지원)
  receiveData: async (incomingData: any[], config: DataReceiverConfig) => {
    // 매핑 규칙 적용 후 모드에 따라 처리
    switch (config.mode) {
      case "replace": setData(mappedData); break;
      case "merge": /* 중복 제거 후 병합 */ break;
      case "append": /* 기존 데이터에 추가 */ break;
    }
  },
  
  // 현재 데이터 반환
  getData: () => [...data],
};
```

**ScreenContext 자동 등록**:

```typescript
// 컴포넌트 마운트 시 ScreenContext에 자동 등록
useEffect(() => {
  if (screenContext && componentId) {
    screenContext.registerDataProvider(componentId, dataProvider);
    screenContext.registerDataReceiver(componentId, dataReceiver);
    
    return () => {
      screenContext.unregisterDataProvider(componentId);
      screenContext.unregisterDataReceiver(componentId);
    };
  }
}, [screenContext, componentId]);
```

#### V2 이벤트 시스템

**발행 이벤트**:

| 이벤트 | 발행 시점 | 데이터 |
|--------|----------|--------|
| `repeaterDataChange` | 데이터 변경 시 | `{ componentId, tableName, data, selectedData }` |

```typescript
// 데이터 변경 시 V2 표준 이벤트 발행
import { V2_EVENTS, dispatchV2Event } from "@/types/component-events";

useEffect(() => {
  if (data.length !== prevDataLengthRef.current) {
    dispatchV2Event(V2_EVENTS.REPEATER_DATA_CHANGE, {
      componentId: parentId || config.fieldName || "unified-repeater",
      tableName: config.dataSource?.tableName || "",
      data: data,
      selectedData: Array.from(selectedRows).map((idx) => data[idx]).filter(Boolean),
    });
  }
}, [data, selectedRows]);
```

**구독 이벤트**:

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

---

### 4. v2-pivot-grid (피벗 그리드)

**용도**: 다차원 데이터 분석용 피벗 테이블

#### 주요 특징

- 행/열/데이터/필터 영역 드래그앤드롭
- 다양한 집계 함수 (합계, 평균, 개수, 최대, 최소, 고유 개수)
- 소계/총계 표시 (위치 설정 가능)
- 조건부 서식 (색상 스케일, 데이터 바, 아이콘)
- 차트 연동
- Excel 내보내기
- 날짜 그룹화 (연/분기/월/주/일)

#### 데이터 흐름

```
┌─────────────────────────────────────────────────────────────┐
│                     v2-pivot-grid                            │
├─────────────────────────────────────────────────────────────┤
│  ① dataSource 설정 (테이블/API/정적 데이터)                 │
│           ↓                                                  │
│  ② fields 설정 (행/열/데이터 필드 배치)                     │
│           ↓                                                  │
│  ③ processPivotData() 로 피벗 계산                         │
│           ↓                                                  │
│  ④ 집계 함수 적용 (sum, avg, count 등)                     │
│           ↓                                                  │
│  ⑤ PivotResult 생성 (rowHeaders, columnHeaders, dataMatrix)│
│           ↓                                                  │
│  ⑥ 조건부 서식 적용 후 렌더링                              │
└─────────────────────────────────────────────────────────────┘
```

#### 주요 설정 인터페이스

```typescript
interface PivotGridComponentConfig {
  // 데이터 소스
  dataSource?: {
    type: "table" | "api" | "static";
    tableName?: string;
    apiEndpoint?: string;
    staticData?: any[];
    filterConditions?: FilterCondition[];
    joinConfigs?: JoinConfig[];
  };
  
  // 필드 설정
  fields?: Array<{
    field: string;                      // 데이터 필드명
    caption: string;                    // 표시 라벨
    area: "row" | "column" | "data" | "filter";
    areaIndex?: number;                 // 영역 내 순서
    
    // 집계 (data 영역용)
    summaryType?: "sum" | "count" | "avg" | "min" | "max" | "countDistinct";
    
    // 날짜 그룹화
    groupInterval?: "year" | "quarter" | "month" | "week" | "day";
    
    // 포맷
    format?: {
      type: "number" | "currency" | "percent" | "date" | "text";
      precision?: number;
      thousandSeparator?: boolean;
      prefix?: string;
      suffix?: string;
    };
  }>;
  
  // 총합계 설정
  totals?: {
    showRowGrandTotals?: boolean;
    showRowTotals?: boolean;
    showColumnGrandTotals?: boolean;
    showColumnTotals?: boolean;
    rowGrandTotalPosition?: "top" | "bottom";
    columnGrandTotalPosition?: "left" | "right";
  };
  
  // 스타일
  style?: {
    theme: "default" | "compact" | "modern";
    alternateRowColors?: boolean;
    highlightTotals?: boolean;
    conditionalFormats?: ConditionalFormatRule[];
  };
  
  // 필드 선택기
  fieldChooser?: {
    enabled: boolean;
    allowSearch?: boolean;
  };
  
  // 차트 연동
  chart?: {
    enabled: boolean;
    type: "bar" | "line" | "area" | "pie" | "stackedBar";
    position: "top" | "bottom" | "left" | "right";
  };
}
```

---

### 5. v2-aggregation-widget (집계 위젯)

**용도**: 데이터 집계 결과 표시 (합계, 평균, 개수 등)

#### 주요 특징

- 다양한 집계 타입 (SUM, AVG, COUNT, MIN, MAX)
- 필터링 지원 (폼 데이터 연동)
- 가로/세로 레이아웃
- 아이콘 표시
- 폼 변경 시 자동 새로고침

#### 데이터 흐름

```
┌─────────────────────────────────────────────────────────────┐
│                  v2-aggregation-widget                       │
├─────────────────────────────────────────────────────────────┤
│  ① dataSourceType 확인 (table / repeater)                  │
│           ↓                                                  │
│  ② filters 적용 (필터 조건 구성)                           │
│           ↓                                                  │
│  ③ items 순회하며 각 집계 함수 실행                        │
│           ↓                                                  │
│  ④ 집계 결과 포맷팅 (천단위 구분, 접두사/접미사)           │
│           ↓                                                  │
│  ⑤ layout에 따라 렌더링 (horizontal / vertical)           │
└─────────────────────────────────────────────────────────────┘
```

#### 주요 설정 인터페이스

```typescript
interface AggregationWidgetConfig {
  // 데이터 소스
  dataSourceType: "table" | "repeater";
  
  // 집계 항목
  items: Array<{
    id: string;
    label: string;
    columnName: string;
    aggregationType: "sum" | "avg" | "count" | "min" | "max";
    format?: {
      prefix?: string;
      suffix?: string;
      thousandSeparator?: boolean;
      decimalPlaces?: number;
    };
    icon?: string;
    color?: string;
  }>;
  
  // 필터 조건
  filters: Array<{
    column: string;
    operator: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE" | "IN";
    value?: any;
    valueSource?: "static" | "formData" | "url";
    valueField?: string;
  }>;
  filterLogic: "AND" | "OR";
  
  // 레이아웃
  layout: "horizontal" | "vertical";
  gap: string;
  
  // 스타일
  showLabels: boolean;
  showIcons: boolean;
  backgroundColor: string;
  borderRadius: string;
  padding: string;
  
  // 동작
  autoRefresh: boolean;
  refreshOnFormChange: boolean;
}
```

#### V2 이벤트 시스템

v2-aggregation-widget은 V2 표준 이벤트 시스템을 사용하여 다른 컴포넌트의 데이터 변경을 감지합니다.

**구독 이벤트**:

| 이벤트 | 용도 | 발행자 |
|--------|------|--------|
| `tableListDataChange` | 테이블 데이터 변경 시 집계 갱신 | v2-table-list |
| `repeaterDataChange` | 리피터 데이터 변경 시 집계 갱신 | v2-unified-repeater |

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

useEffect(() => {
  // 테이블 리스트 데이터 변경 이벤트 구독
  const unsubscribeTableList = subscribeV2Event(
    V2_EVENTS.TABLE_LIST_DATA_CHANGE,
    (event: CustomEvent<TableListDataChangeDetail>) => {
      const { data } = event.detail;
      // 필터 적용 후 집계 재계산
      const filteredData = applyFilters(data, filters, filterLogic, formData, selectedRows);
      setData(filteredData);
    }
  );

  // 리피터 데이터 변경 이벤트 구독
  const unsubscribeRepeater = subscribeV2Event(
    V2_EVENTS.REPEATER_DATA_CHANGE,
    (event: CustomEvent<RepeaterDataChangeDetail>) => {
      const { data, selectedData } = event.detail;
      const rows = selectedData || data || [];
      const filteredData = applyFilters(rows, filters, filterLogic, formData, selectedRows);
      setData(filteredData);
    }
  );

  return () => {
    unsubscribeTableList();
    unsubscribeRepeater();
  };
}, [dataSourceType, isDesignMode, filterLogic]);
```

**참고**: 이전에 사용하던 중복 이벤트(`selectionChange`, `tableSelectionChange`, `rowSelectionChange` 등)는 제거되었습니다. V2 표준 이벤트만 사용합니다.

---

### 6. v2-table-search-widget (검색 필터)

**용도**: 테이블 데이터 검색 및 필터링

#### 주요 특징

- 동적/고정 필터 모드
- 다중 테이블 지원
- 탭별 필터 값 저장
- 텍스트/숫자/날짜/셀렉트 필터 타입
- 다중선택 지원
- 대상 패널 지정 가능

#### 데이터 흐름

```
┌─────────────────────────────────────────────────────────────┐
│                 v2-table-search-widget                       │
├─────────────────────────────────────────────────────────────┤
│  ① TableOptionsContext에서 등록된 테이블 목록 조회          │
│           ↓                                                  │
│  ② targetPanelPosition에 따라 대상 테이블 필터링            │
│           ↓                                                  │
│  ③ 활성 필터 목록 로드 (localStorage에서 복원)              │
│           ↓                                                  │
│  ④ 필터 값 입력 → handleFilterChange()                     │
│           ↓                                                  │
│  ⑤ currentTable.onFilterChange(filters) 호출               │
│           ↓                                                  │
│  ⑥ 연결된 테이블이 자동으로 데이터 재조회                  │
└─────────────────────────────────────────────────────────────┘
```

#### 주요 설정 인터페이스

```typescript
interface TableSearchWidgetConfig {
  // 자동 선택
  autoSelectFirstTable?: boolean;
  showTableSelector?: boolean;
  
  // 필터 모드
  filterMode?: "dynamic" | "preset";
  
  // 고정 필터 (preset 모드)
  presetFilters?: Array<{
    id: string;
    columnName: string;
    columnLabel: string;
    filterType: "text" | "number" | "date" | "select";
    width?: number;
    multiSelect?: boolean;
  }>;
  
  // 대상 패널 위치
  targetPanelPosition?: "left" | "right" | "auto";
}
```

---

### 7. v2-repeat-container (리피터 컨테이너)

**용도**: 데이터 수만큼 내부 컴포넌트를 반복 렌더링

#### 주요 특징

- 수동/테이블/리피터 데이터 소스
- 세로/가로/그리드 레이아웃
- 페이징 지원
- 클릭 이벤트 (단일/다중 선택)
- 아이템 제목 템플릿

#### 데이터 흐름

```
┌─────────────────────────────────────────────────────────────┐
│                  v2-repeat-container                         │
├─────────────────────────────────────────────────────────────┤
│  ① dataSourceType에 따라 데이터 로드                        │
│     - manual: 수동 입력 데이터                              │
│     - table: DB 테이블에서 조회                             │
│     - repeater: 리피터 컴포넌트 데이터                      │
│           ↓                                                  │
│  ② layout에 따라 배치 (vertical / horizontal / grid)       │
│           ↓                                                  │
│  ③ 각 아이템에 대해 children 렌더링                        │
│     (RepeatItemContext로 현재 아이템 데이터 전달)           │
│           ↓                                                  │
│  ④ 클릭 시 선택 상태 관리 (selectionMode: single/multi)   │
└─────────────────────────────────────────────────────────────┘
```

#### 주요 설정 인터페이스

```typescript
interface RepeatContainerConfig {
  // 데이터 소스
  dataSourceType: "manual" | "table" | "repeater";
  tableName?: string;
  repeaterComponentId?: string;
  manualData?: any[];
  
  // 레이아웃
  layout: "vertical" | "horizontal" | "grid";
  gridColumns: number;
  gap: string;
  
  // 스타일
  showBorder: boolean;
  showShadow: boolean;
  borderRadius: string;
  backgroundColor: string;
  padding: string;
  
  // 아이템 제목
  showItemTitle: boolean;
  itemTitleTemplate: string;              // 예: "${name} - ${code}"
  titleFontSize: string;
  titleColor: string;
  titleFontWeight: string;
  
  // 빈 상태
  emptyMessage: string;
  
  // 페이징
  usePaging: boolean;
  pageSize: number;
  
  // 선택
  clickable: boolean;
  showSelectedState: boolean;
  selectionMode: "single" | "multi";
}
```

#### V2 이벤트 시스템

v2-repeat-container는 V2 표준 이벤트 시스템을 사용하여 다른 컴포넌트의 데이터 변경을 감지하고 반복 렌더링합니다.

**구독 이벤트**:

| 이벤트 | 용도 | 발행자 |
|--------|------|--------|
| `tableListDataChange` | 테이블 데이터 변경 시 반복 항목 갱신 | v2-table-list |
| `repeaterDataChange` | 리피터 데이터 변경 시 반복 항목 갱신 | v2-unified-repeater |

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

useEffect(() => {
  // 공통 데이터 처리 함수
  const processIncomingData = (componentId: string | undefined, tableName: string | undefined, eventData: any[]) => {
    // dataSourceComponentId가 설정된 경우 해당 컴포넌트만 매칭
    if (dataSourceComponentId && componentId === dataSourceComponentId) {
      setData(eventData);
      setCurrentPage(1);
      setSelectedIndices([]);
    }
    // 테이블명으로 매칭
    else if (effectiveTableName && tableName === effectiveTableName) {
      setData(eventData);
      setCurrentPage(1);
      setSelectedIndices([]);
    }
  };

  // V2 표준 이벤트 구독
  const unsubscribeTableList = subscribeV2Event(V2_EVENTS.TABLE_LIST_DATA_CHANGE, (event) => {
    const { componentId, tableName, data } = event.detail;
    processIncomingData(componentId, tableName, data);
  });

  const unsubscribeRepeater = subscribeV2Event(V2_EVENTS.REPEATER_DATA_CHANGE, (event) => {
    const { componentId, tableName, data } = event.detail;
    processIncomingData(componentId, tableName, data);
  });

  return () => {
    unsubscribeTableList();
    unsubscribeRepeater();
  };
}, [dataSourceComponentId, effectiveTableName, isDesignMode]);
```

---

## 공통 데이터 흐름 패턴

### 1. 엔티티 조인 데이터 로드

모든 테이블 기반 V2 컴포넌트는 엔티티 조인을 지원합니다.

```
┌─────────────────────────────────────────────────────────────┐
│                    엔티티 조인 흐름                          │
├─────────────────────────────────────────────────────────────┤
│  ① entityJoinApi.getEntityJoinColumns(tableName)           │
│           ↓                                                  │
│  ② 응답: { joinTables, availableColumns }                  │
│           ↓                                                  │
│  ③ ConfigPanel에서 조인 컬럼 선택                          │
│           ↓                                                  │
│  ④ entityJoinApi.getTableDataWithJoins() 데이터 조회       │
│           ↓                                                  │
│  ⑤ "테이블명.컬럼명" 형식으로 조인 데이터 포함             │
└─────────────────────────────────────────────────────────────┘
```

### 2. 폼 데이터 관리

V2 컴포넌트는 통합 폼 시스템(UnifiedFormContext)을 사용합니다.

```
┌─────────────────────────────────────────────────────────────┐
│                    폼 데이터 흐름                            │
├─────────────────────────────────────────────────────────────┤
│  ① 컴포넌트에서 useFormCompatibility() 훅 사용             │
│           ↓                                                  │
│  ② getValue(fieldName) - 값 읽기                           │
│  ③ setValue(fieldName, value) - 값 설정                    │
│           ↓                                                  │
│  ④ 값 변경이 전체 폼 시스템에 전파                         │
│           ↓                                                  │
│  ⑤ 저장 버튼 클릭 시 beforeFormSave 이벤트 발생            │
│           ↓                                                  │
│  ⑥ 모든 컴포넌트가 현재 값을 formData에 추가               │
│           ↓                                                  │
│  ⑦ API 호출하여 저장                                       │
└─────────────────────────────────────────────────────────────┘
```

### 3. 컴포넌트 간 통신

```
┌─────────────────────────────────────────────────────────────┐
│                   컴포넌트 간 통신                           │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌──────────────┐         ┌──────────────┐                  │
│  │ 버튼 컴포넌트 │ ──────→ │ 리피터 컴포넌트│                  │
│  │              │ repeater│              │                  │
│  │ v2-button    │ Save    │ v2-unified-  │                  │
│  │ -primary     │ 이벤트   │ repeater     │                  │
│  └──────────────┘         └──────────────┘                  │
│         │                        │                          │
│         │  masterRecordId        │                          │
│         └────────────────────────┘                          │
│                                                              │
│  ┌──────────────┐         ┌──────────────┐                  │
│  │ 검색 위젯    │ ──────→ │ 테이블 리스트 │                  │
│  │              │ onFilter│              │                  │
│  │ v2-table-    │ Change  │ v2-table-    │                  │
│  │ search-widget│         │ list         │                  │
│  └──────────────┘         └──────────────┘                  │
│                                                              │
└─────────────────────────────────────────────────────────────┘
```

---

## 컴포넌트 등록 구조

```typescript
// frontend/lib/registry/components/index.ts

// V2 컴포넌트들 (화면관리 전용)
import "./v2-unified-repeater/UnifiedRepeaterRenderer";
import "./v2-button-primary/ButtonPrimaryRenderer";
import "./v2-split-panel-layout/SplitPanelLayoutRenderer";
import "./v2-aggregation-widget/AggregationWidgetRenderer";
import "./v2-card-display/CardDisplayRenderer";
import "./v2-numbering-rule/NumberingRuleRenderer";
import "./v2-table-list/TableListRenderer";
import "./v2-text-display/TextDisplayRenderer";
import "./v2-pivot-grid/PivotGridRenderer";
import "./v2-divider-line/DividerLineRenderer";
import "./v2-repeat-container/RepeatContainerRenderer";
import "./v2-section-card/SectionCardRenderer";
import "./v2-section-paper/SectionPaperRenderer";
import "./v2-rack-structure/RackStructureRenderer";
import "./v2-location-swap-selector/LocationSwapSelectorRenderer";
import "./v2-table-search-widget";
import "./v2-tabs-widget/tabs-component";
```

---

## 파일 구조 표준

각 V2 컴포넌트 폴더는 다음 구조를 따릅니다:

```
v2-{component-name}/
├── index.ts                    # 컴포넌트 Definition (V2 접두사)
├── types.ts                    # TypeScript 타입 정의
├── {Component}Component.tsx    # 실제 컴포넌트 구현
├── {Component}ConfigPanel.tsx  # 설정 패널
├── {Component}Renderer.tsx     # 레지스트리 등록 및 래퍼
├── config.ts                   # 기본 설정값 (선택)
└── README.md                   # 사용 가이드 (선택)
```

---

## 개발 가이드라인

### 새 V2 컴포넌트 생성 시

1. `v2-` 접두사로 폴더 생성
2. Definition 이름에 `V2` 접두사 사용 (예: `V2NewComponentDefinition`)
3. `index.ts`에서 import 추가
4. 엔티티 조인 지원 필수 구현
5. 다국어 키 필드 추가 (`langKeyId`, `langKey`)

### 체크리스트

- [ ] V2 폴더에서 작업 중인지 확인
- [ ] 원본 폴더는 수정하지 않음
- [ ] 컴포넌트 ID에 `v2-` 접두사 사용
- [ ] Definition 이름에 `V2` 접두사 사용
- [ ] 엔티티 조인 컬럼 지원
- [ ] 커스텀 테이블 설정 지원
- [ ] 다국어 필드 추가

---

## 관련 파일 목록

| 파일 | 역할 |
|------|------|
| `frontend/lib/api/entityJoin.ts` | 엔티티 조인 API |
| `frontend/hooks/useFormCompatibility.ts` | 폼 호환성 브릿지 |
| `frontend/components/unified/UnifiedFormContext.tsx` | 통합 폼 Context |
| `frontend/lib/utils/multilangLabelExtractor.ts` | 다국어 라벨 추출/매핑 |
| `frontend/contexts/ScreenMultiLangContext.tsx` | 다국어 번역 Context |
| `frontend/lib/registry/components/index.ts` | 컴포넌트 등록 |

---

## 참고 문서

- [component-development-guide.mdc](.cursor/rules/component-development-guide.mdc) - 컴포넌트 개발 상세 가이드
- [table-list-component-guide.mdc](.cursor/rules/table-list-component-guide.mdc) - 테이블 리스트 가이드
