# InvDataConfigPanel 통합 인벤토리

날짜: 2026-04-28
작성자: gbpark + Codex (GPT-5.5) + Claude
목적: V2TableListConfigPanel + V2RepeaterConfigPanel 통합 (`InvDataConfigPanel`) 설계의 기준 — 도주/regression 방지

---

## 0. 한줄 요약

두 패널 (1497 + 2263 = 3760줄) 의 **config 키 / API / state / mode 축** 인벤토리. **GPT-5.5 권장: 제한된 Option B** — 두 패널만 우선 통합, V2FieldConfigPanel 의 `resolveTriple/applyTriple` 패턴으로 legacy ↔ 새 taxonomy resolver/writer 작성 후 본체 마이그레이션.

---

## 1. V2TableListConfigPanel (Read 위주, 1497줄)

### 1.1 Props
| 필드 | 타입 |
|---|---|
| `config` | `TableListConfig` |
| `onChange` | `(config: Partial<TableListConfig>) => void` |
| `screenTableName?` | `string` |
| `tableColumns?` | `any[]` |
| `menuObjid?` | `number` |

### 1.2 Config 스키마 (TableListConfig — 핵심)

**기본**: `displayMode("table"|"card")`, `selectedTable`, `tableName`, `title`, `showHeader`, `showFooter`, `customTableName`, `useCustomTable`, `isReadOnly`

**카드 모드**: `cardConfig` { idColumn, titleColumn, subtitleColumn, descriptionColumn, imageColumn, cardsPerRow, cardSpacing, showActions, cardHeight }

**컬럼**: `columns: ColumnConfig[]` — { columnName, displayName, visible, sortable, searchable, width, align, format, order, dataType, isEntityJoin, entityJoinInfo, entityDisplayConfig, thousandSeparator, fixed("left"|"right"|false), fixedOrder, hidden, autoGeneration, editable, additionalJoinInfo }

**테이블 동작**: `autoWidth`, `stickyHeader`, `horizontalScroll{enabled, maxVisibleColumns, minColumnWidth, maxColumnWidth}`

**페이지네이션**: `pagination{enabled, pageSize, showSizeSelector, showPageInfo, pageSizeOptions, currentPage}`

**필터/액션/체크박스**: `filter{enabled, filters[], bottomSpacing}`, `actions{showActions, actions[], bulkActions, bulkActionList}`, `checkbox{enabled, multiple, position, selectAll}`

**스타일**: `tableStyle{theme, headerStyle, rowHeight, alternateRows, hoverEffect, borderStyle}`

**높이**: `height("auto"|"fixed"|"viewport")`, `fixedHeight`

**데이터 로딩**: `autoLoad`, `refreshInterval`

**고급**: `defaultSort{columnName, direction}`, `toolbar{showEditMode, showExcel, showPdf, showCopy, showSearch, showFilter, showRefresh, showPaginationRefresh}`, `dataFilter`, `linkedFilters[]`, `excludeFilter{enabled, referenceTable, referenceColumn, sourceColumn, filterColumn, filterValueSource("url"|"formData"|"parentData"), filterValueField}`

**이벤트**: `onRowClick`, `onRowDoubleClick`, `onSelectionChange`, `onPageChange`, `onSortChange`, `onFilterChange`, `onSelectedRowsChange`

### 1.3 Mode/Variant 축

| 축 | 값 | 분기 sub-config |
|---|---|---|
| `displayMode` | `"table"` / `"card"` | card 시 `cardConfig` 필수 |
| `useCustomTable` | `false` (메인) / `true` (커스텀) | true 시 `customTableName` 필수 |
| `pagination.enabled` | bool | true 시 `pageSize`/`pageSizeOptions` |
| `filter.enabled` | bool | true 시 `filters[]` |
| `checkbox.enabled` | bool | true 시 `position`/`selectAll` |
| `Column.isEntityJoin` | bool | true 시 `entityJoinInfo`/`entityDisplayConfig` |

### 1.4 API
- `tableTypeApi.getTables()` — 테이블 목록 (mount)
- `tableManagementApi.getColumnList(tableName)` — 컬럼 (targetTableName 변경)
- `entityJoinApi.getEntityJoinColumns(tableName)` — entity 조인 컬럼 (targetTableName 변경)
- `tableManagementApi.getColumnList(refTable)` — 참조 테이블 컬럼 (excludeFilter.referenceTable 변경)
- `tableTypeApi.getColumns(sourceTable)` — entityDisplay config 로드
- `entityJoinApi.getReferenceTableColumns(sourceTable|joinTable)` — ent 표시 설정

### 1.5 State (17개)
**Form 상태 (config 외)**: `entityDisplayConfigs`(컬럼별 entity 표시), `columnSearchText`

**API/Data**: `availableTables`, `availableColumns`, `entityJoinColumns`, `referenceTableColumns`

**Loading**: `loadingTables`, `loadingEntityJoins`, `loadingReferenceColumns`

**UI Open/Close**: `tableComboboxOpen`, `advancedOpen`, `entityDisplayOpen`, `columnSelectOpen`, `entityJoinOpen`, `displayColumnsOpen`, `entityJoinSubOpen{[i]}`

**Ref**: `prevColumnsLengthRef` (entity 자동 감지)

### 1.6 useEffect (7개)
1. `[screenTableName]` → selectedTable 자동 설정
2. `[]` → 테이블 목록 로드
3. `[targetTableName, useCustomTable, tableColumns]` → 컬럼 로드
4. `[targetTableName]` → entity 조인 컬럼 로드
5. `[excludeFilter.referenceTable]` → 참조 테이블 컬럼 로드
6. `[config.columns]` → entity display config 자동 로드
7. `[config.columns?.length, tableColumns, selectedTable]` → entity 컬럼 자동 감지

### 1.7 핵심 핸들러
`handleChange`, `updateField`, `updateNestedField`, `addColumn`, `addEntityColumn`, `removeColumn`, `updateColumn`, `handleTableChange`, `loadEntityDisplayConfig`, `toggleEntityDisplayColumn`, `updateEntityDisplaySeparator`

---

## 2. V2RepeaterConfigPanel (Write 위주, 2263줄)

### 2.1 Props
| 필드 | 타입 |
|---|---|
| `config` | `V2RepeaterConfig` |
| `onChange` | `(config: V2RepeaterConfig) => void` |
| `currentTableName?` | `string` |
| `screenTableName?` | `string` |
| `tableColumns?` | `any[]` |
| `menuObjid?` | `number \| string` |

### 2.2 Config 스키마 (V2RepeaterConfig — 핵심)

**렌더링**: `render_mode("inline"|"modal")`

**테이블 매핑**: `main_table_name`, `use_custom_table`, `foreign_key_column`, `foreign_key_source_column`

**소스 디테일**: `source_detail_config` { table_name, foreign_key, parent_key, use_entity_join, column_mapping, additional_join_columns[] }

**데이터 소스 (modal 모드)**: `data_source` { table_name, source_table, foreign_key, reference_key, display_column, filter{column, value} }

**컬럼**: `columns: RepeaterColumnConfig[]` — { key, title, width, visible, editable, hidden, is_join_column, source_table, is_source_display, source_key, input_type, auto_fill{ type, source_field, fixed_value, numbering_rule_id, selected_menu_objid, parent_field, separator, sequence_length }, detail_settings{ code_group, reference_table, reference_column, display_column, format, category_ref } }

**컬럼 매핑 (modal)**: `column_mappings[]` — { source_column, target_column, transform("copy"|"reference") }

**계산 규칙 (modal)**: `calculation_rules[]` — { id, target_column, formula, label }

**모달 설정**: `modal` { size, title, button_text, source_display_columns[], search_fields[], screen_id, title_template }

**Entity 조인**: `entity_joins[]` — { source_column, reference_table, columns[]{ reference_field, display_field } }

**기능 옵션**: `features` { show_add_button, show_delete_button, inline_edit, drag_sort, show_row_number, selectable, multi_select }

**스타일**: `style` { max_height, min_height, borderless, compact }

### 2.3 Mode/Variant 축

| 축 | 값 | 분기 sub-config |
|---|---|---|
| `render_mode` | `"inline"` (직접 입력) / `"modal"` (엔티티 검색) | modal 시 `data_source.source_table`, `modal`, `column_mappings`, `calculation_rules` |
| `use_custom_table` | bool | true 시 `main_table_name`, `foreign_key_column`, `foreign_key_source_column` 필수 |
| `source_detail_config` | undefined / defined | defined 시 `table_name`, `foreign_key`, `parent_key` 필수 |
| `Column.is_source_display` | bool | true 시 `editable: false` 강제 |
| `Column.is_join_column` | bool | true 시 FK 기반 자동 조회 (read only) |
| `auto_fill.type` | `"none"`/`"numbering"`/`"fromMainForm"`/`"fixed"`/`"parentSequence"` | type 별 sub-fields 다름 (numbering_rule_id, source_field, fixed_value, parent_field) |

### 2.4 API
- `apiClient.get("/admin/menus")` — 부모 메뉴 (mount)
- `getAvailableNumberingRules(menuObjid)` — 채번 규칙 (menuObjid 변경)
- `tableManagementApi.getTableList()` — 테이블 목록 (mount)
- `apiClient.get("/table-management/columns/{table}/referenced-by")` — FK 자동 설정용 (table 변경)
- `entityJoinApi.getEntityJoinColumns(targetTable)` — entity 조인 컬럼
- `tableTypeApi.getColumns(targetTable)` — 저장 테이블 컬럼
- `tableTypeApi.getColumns(sourceTable)` — modal 모드 소스 테이블 컬럼

### 2.5 State (24개)

**Form 상태 (config 외)**: `calculationRules` (덮어쓰기 가능), `selectedMenuObjid`

**API/Data**: `currentTableColumns`, `entityColumns`, `sourceTableColumns`, `allTables`, `relatedTables`, `entityJoinData`, `numberingRules`, `parentMenus`

**Loading**: `loadingColumns`, `loadingSourceColumns`, `loadingTables`, `loadingRelations`, `loadingEntityJoins`, `loadingNumberingRules`, `loadingMenus`

**UI Open/Close/Selected**: `tableComboboxOpen`, `expandedColumn`, `featureOptionsOpen`, `columnSelectOpen`, `selectedColumnsOpen`, `calcRulesOpen`, `entityJoinSubOpen{[i]}`, `configuredJoinsOpen`

### 2.6 useEffect (7개)
1. `[]` → 부모 메뉴 로드
2. `[selectedMenuObjid]` → 채번 규칙 로드
3. `[]` → 테이블 목록 로드
4. `[currentTableName, main_table_name]` → 연관 테이블 (FK 자동)
5. `[entityJoinTargetTable]` → entity 조인 컬럼
6. `[targetTableForColumns]` → 저장 테이블 컬럼
7. `[data_source?.source_table, render_mode]` → 소스 테이블 컬럼 (modal only)

### 2.7 핵심 핸들러
`updateConfig`, `updateDataSource`, `updateModal`, `updateFeatures`, `toggleEntityJoinColumn`, `isEntityJoinColumnActive`, `handleSaveTableSelect` (FK 자동), `toggleInputColumn`, `toggleSourceDisplayColumn`, `isColumnAdded`, `isSourceColumnSelected`, `updateColumnProp`, `handleEntityColumnSelect`, `addCalculationRule`, `removeCalculationRule`, `updateCalculationRule`, `insertColumnToFormula`, `formulaToKorean`

---

## 3. 공통 / 차이 정리

### 3.1 공통 개념 (통합 여지)

| 개념 | TableList | Repeater |
|---|---|---|
| 테이블 선택 | `selectedTable` | `main_table_name` |
| 커스텀 테이블 | `useCustomTable` + `customTableName` | `use_custom_table` + `main_table_name` |
| 컬럼 list | `columns: ColumnConfig[]` | `columns: RepeaterColumnConfig[]` |
| 컬럼 기본 키 | columnName, displayName, visible, width, format | key, title, visible, width, input_type |
| Entity 조인 | `isEntityJoin` + `entityJoinInfo` | `is_join_column` + `entity_joins` |
| 자동 입력 | `autoGeneration` | `auto_fill` |
| 컬럼 편집 | `editable` | `editable` |
| API set | tableTypeApi / tableManagementApi / entityJoinApi | + numberingRule API |

### 3.2 TableList 고유 (Repeater 에 없음)

- `displayMode` (table vs card) + `cardConfig`
- `filter` (UI + 조건)
- `actions` (행 단위)
- `pagination` (UI 제어)
- `linkedFilters[]` (다른 컴포넌트 ref)
- `excludeFilter` (다른 테이블 제외)
- 컬럼 `fixed` ("left"/"right"/false) + `fixedOrder`
- `defaultSort`
- `tableStyle` (theme/rowHeight/alternateRows/...)
- `height` 모드, `horizontalScroll`, `autoWidth`, `stickyHeader`
- `toolbar.show*` (excel/pdf/copy/search 등)
- 풍부한 이벤트 콜백

### 3.3 Repeater 고유 (TableList 에 없음)

- `render_mode` (`"inline"` 직접 입력 / `"modal"` 엔티티 검색)
- modal 모드: `data_source.source_table`, `column_mappings[]`, `calculation_rules[]`, `modal{}`
- `is_source_display` (소스 표시 컬럼 — 읽기전용)
- `source_detail_config` (소스 디테일 자동 조회)
- `auto_fill` 5가지 type (numbering / fromMainForm / fixed / parentSequence / none)
- 채번 규칙 (numberingRule API)
- FK 자동 감지 (`relatedTables`)
- `features{}` (add/delete/inline_edit/drag_sort/multi_select 등)
- 모달 설정 (`size`/`button_text`/`search_fields[]`)
- `entity_joins[]` (FK 기반 참조 데이터)

---

## 4. InvDataConfigPanel 통합 설계 후보 (Step 2 제안)

### 4.1 brumb taxonomy (V2FieldConfigPanel 패턴 따름)

```
[모드 ▾]      [타입 ▾]                     [형식 ▾ — 모드별]
─────────────────────────────────────────────────
조회 (read)   테이블 (table)               기본 / 카드(card) / 페이지네이션 / 필터
              카드 그리드 (card-grid)       (cardConfig 사용)
              간이 list (simple-list)      (현재 미사용 — placeholder)

입력 (write)  인라인 (inline-rows)         FK 미사용
              모달 검색 (modal-pick)        FK + 소스 선택 + 추가 입력
              버튼 연결 (button-link)       (현재 V2Repeater 에서 미구현 — placeholder)
```

### 4.2 통합 후 Props 시그니처 후보

```ts
interface InvDataConfigPanelProps {
  config: any;                                       // legacy union (TableListConfig | V2RepeaterConfig)
  onChange: (config: any) => void;
  componentType: "v2-table-list" | "v2-repeater";    // runtime id (호환)
  screenTableName?: string;
  currentTableName?: string;
  tableColumns?: any[];
  menuObjid?: number | string;
}
```

### 4.3 resolver / writer (legacy ↔ taxonomy)

**resolveAxis(config, componentType) → { kind, type, format }** 우선순위:
1. componentType 으로 kind 결정 (`v2-table-list` → "조회", `v2-repeater` → "입력")
2. config.displayMode (TableList) 또는 config.render_mode (Repeater) 로 type 결정
3. 추가 sub-flag 로 format 결정 (예: pagination.enabled / source_detail_config 존재)

**applyAxis(prev, kind, type, format) → 새 config**:
- 각 (kind, type, format) 조합마다 displayMode / render_mode / cardConfig / data_source / modal 등 적절히 설정
- 기존 키는 유지 (불필요해도 보존 — DB 호환)

### 4.4 화면 흐름

```
[brumb mode▾ type▾ format▾]                       ← V2FieldConfigPanel 처럼
① 데이터 소속 (always open)                        ← 테이블 + FK + 화면 메인 vs 커스텀
② 컬럼 구성 (always open)                          ← 컬럼 list + entity 조인
③ 모드별 설정 (FormatBody 디스패처)                ← 카드 / 페이지 / 필터 / 모달 / auto_fill 등
④ 기능 옵션 (CPGroup default open)                ← features (Repeater) 또는 toolbar (TableList)
▾ 고급 설정                                        ← excludeFilter / linkedFilters / source_detail / 계산규칙
```

### 4.5 도주 방지 DoD 체크리스트

- [ ] config keys 누락 0 — 위 §1.2, §2.2 의 모든 키가 새 패널에서 read/write 가능
- [ ] API endpoint 호출 누락 0 — 위 §1.4, §2.4 의 모든 API 가 동일 시점에 호출
- [ ] state 누락 0 (UI / Loading / Form 다 보존)
- [ ] handler 누락 0 — 위 §1.7, §2.7 의 모든 핸들러 동작
- [ ] mode 분기 누락 0 — §1.3, §2.3 의 모든 축이 brumb 또는 sub-section 으로 노출
- [ ] resolver/writer 작성 — legacy config 로딩 시 새 taxonomy 로 mapping, 저장 시 legacy 키 보존
- [ ] shadcn import 0 — 마지막 grep 통과 조건

---

## 5. 다음 단계

- **Step 2 (다음 turn)**: 사용자와 §4 의 brumb taxonomy / 화면 흐름 합의
- **Step 3**: resolver/writer 구현 + InvDataConfigPanel 골격 (cp 마크업, 빈 분기)
- **Step 4**: 두 패널의 핸들러 / API call / state 이주
- **Step 5**: 호출처 갱신 (`registerV2Components.ts`) — `v2-table-list` / `v2-repeater` 모두 InvDataConfigPanel 로
- **Step 6**: V2TableListConfigPanel.tsx + V2RepeaterConfigPanel.tsx 삭제 → InvDataConfigPanel.tsx 만 남김
- **Step 7**: 검증 (TS / shadcn grep 0건 / 화면별 시각 확인)
