refactor: ConfigPanel hook/helper 추출 + IconPicker cp+Portal
Build & Deploy to K8s / build-and-deploy (push) Successful in 5m32s

- useDbTables: search/table/stats 의 DB 테이블 로드 hook (3 패널 중복 제거)
- TableConnectSection + AutoLoadButton: search/table 의 테이블 연결 섹션 + 자동 로드 버튼
- row-helpers: RowNumberBadge / RowExpandChevron / RowDeleteBtn (4 패널 dense list helper)
- IconPicker: shadcn 톤 -> cp 톤 (28px 트리거, focus glow, cp 변수)
- IconPicker popover: React Portal + position:fixed (부모 overflow:hidden 우회)
- input X버튼은 hoverBg={false} 로 silent visual change 원복

Codex (GPT-5.5) 와 매 단계 교차검증 후 진행
미완 후속 사항 (auto-flip / 외부 클릭 닫기 / z-index 표준화 등) 노트에 기록

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
DDD1542
2026-04-29 11:25:06 +09:00
parent d0978f9398
commit e347a75953
9 changed files with 687 additions and 402 deletions
+111 -8
View File
@@ -7,7 +7,7 @@
## 1순위: 리팩토링 본체 (Codex 권장)
### 1.1 useDbTables() hook 추출 ★ 가장 안전
### 1.1 useDbTables() hook 추출 ★ 가장 안전 — ✅ 완료 (2026-04-29)
**현재 중복 위치**:
- `lib/registry/components/search/InvSearchConfigPanel.tsx`
@@ -49,7 +49,15 @@ export function useDbTables(opts?: {
- `tableName/table_name`, `display_name/tableLabel/table_label` normalization 을 hook 안에 고정
- table 의 connected column loading / autoload 로직은 **hook 에 섞지 말 것** (별개)
### 1.2 TableConnectSection 추출 (1.1 다음)
**적용 결과 (2026-04-29)**:
- 신규 파일: `frontend/lib/registry/components/common/useDbTables.ts`
- 적용 패널: search / table / stats (3개)
- input 은 패턴 없어서 적용 안 함
- normalize 는 hook 안 default + `opts.normalize` override 가능 (Codex 권장 그대로)
- stats 만 value 우선순위 살짝 달랐으나 (`table_name || tableName`) functional 동일이라 default 통일
- TS 검증: 변경 4 파일 새 에러 0건
### 1.2 TableConnectSection 추출 (1.1 다음) — ✅ 완료 (2026-04-29)
**현재 중복**:
- search / table 의 "테이블 연결 + 자동 로드 버튼" 섹션
@@ -60,7 +68,18 @@ export function useDbTables(opts?: {
- table: `[자동 로드]` = 컬럼 N개 자동 추출
- → autoload 콜백을 prop 으로 받음. label 도 prop.
### 1.3 DenseListRow / ExpandableRow 추출 (1.1, 1.2 다음)
**적용 결과 (2026-04-29)**:
- 신규 파일: `frontend/lib/registry/components/common/TableConnectSection.tsx`
- 두 컴포넌트 export:
1. `TableConnectSection` — CPSection + CPRow "테이블" + CPSelect (children 슬롯)
2. `AutoLoadButton` — 38줄 동일 inline 스타일 버튼 (label / onClick / iconSize prop)
- Codex 의 ★ 가이드 그대로: autoload 로직 (callback / 조건 / 컬럼 fetch) 호출처에 그대로 둠
- search 호출처는 `tableColumns.length > 0 && <AutoLoadButton ... />` + `!connectedTable && <Hint>` 직접 children 으로 주입
- table 호출처는 `<Hint>` (loading 상태) + `<AutoLoadButton ... />` 직접 children
- TS 검증: 변경 파일 새 에러 0건
- 변경 stat: 4 files, +81/-199 (호출처 순감 118줄, 신규 모듈 ~186줄, 총 net +68 — 가독성/유지보수 큰 이득)
### 1.3 DenseListRow / ExpandableRow 추출 (1.1, 1.2 다음) — ✅ helper-only 완료 (2026-04-29)
**현재 중복**:
- search 의 SearchFieldRow
@@ -70,6 +89,20 @@ export function useDbTables(opts?: {
→ 패턴 안정화 후 추출. 너무 일찍 하면 over-abstraction.
**적용 결과 (2026-04-29) — Codex 가이드 따라 helper-only 좁게 추출**:
- 신규 파일: `frontend/lib/registry/components/common/row-helpers.tsx`
- 3 helper export:
1. `RowNumberBadge` (n) — search/table/stats 의 #번호 (4 패널 중 3)
2. `RowExpandChevron` (expanded, onToggle, size?) — table/stats 의 ▸/▾ 펼침 버튼 (2 패널)
3. `RowDeleteBtn` (onClick, visible?, size?, children?, title?) — 4 패널 모두 사용
- ★ wrapper (DenseListRow / ExpandableRow) 는 **보류** — Codex 판단: row 별 expanded state 위치 / hover 표시 / row padding 회귀 위험 중간 이상
- silent visual change 1건: input 의 옵션 ×버튼 hover bg 추가됨 (이전 X) — Codex Y2 권고로 `hoverBg?: boolean` 옵션 추가 (기본 true), input 만 `hoverBg={false}` 로 원복
- Y1 ★ 경고 주석 반영: `RowDeleteBtn``visible` 기본값 true 위험 (호출처가 prop 생략 시 항상 노출) — 주석에 "dense list row 는 반드시 `visible={hover}` 명시" 명시
- 적용 패널: search (RowNumberBadge + RowDeleteBtn) / table + stats (3 helper 다) / input (RowDeleteBtn 만)
- TS 검증: 새 에러 0건
- 변경 stat 누적 (1.1+1.2+1.3): 5 files, +123 / -376 (호출처 -253 / 신규 3 모듈 +296 / net +43 — 가독성·유지보수 이득)
---
## 2순위: 잔여 V2* cp 마이그 (좌측 팔레트 외)
@@ -95,7 +128,7 @@ export function useDbTables(opts?: {
---
## 4순위: IconPicker 공용 cp 톤 마이그 (사용자 지적)
## 4순위: IconPicker 공용 cp 톤 마이그 (사용자 지적) — ✅ 완료 (2026-04-29)
**문제**: button / stats 등 패널의 "아이콘 선택" 셀렉트가 다른 select 와 사이즈 다름 (Image #9 참고). IconPicker 컴포넌트 자체 디자인이 cp 톤 아님.
@@ -103,6 +136,44 @@ export function useDbTables(opts?: {
**한 번 마이그하면 button / stats / 기타 IconPicker 사용 패널 일관성 회복.**
**적용 결과 (2026-04-29) — Codex Y4 권고 따라**:
- 위치: `frontend/lib/registry/components/common/IconPicker.tsx`
- 시그니처 / 동작 (외부 클릭 닫기 없음 등) **완전 동일**, 시각 톤만 마이그
- 트리거 버튼: 28px height, 12px font, cp-surface bg, cp-border + focus glow (primary 0.5/0.12), 6px radius
- 팝오버: cp-surface bg, cp-border, 6px radius, shadow
- 검색 input: cp-bg-subtle, cp-border, 4px radius, 11px font
- 그리드 버튼: cp hover (cp-surface-hover), primary 활성 tint (rgba(primary-rgb, 0.10) bg + primary text)
- shadcn 클래스 (`border-border bg-background text-xs` 등) 0건 → cp 변수 + inline style 전환
**영향 범위 검증**:
- 사용처 grep 결과 — IconPicker 본체는 button/stats 2 cp 패널만 사용
- 외부 7곳 (layout/admin/dash) 은 별개 `MenuIconPicker` 사용 → **영향 없음**
**검증**:
- TS 새 에러 0건
- diff stat: 1 file, +152 / -18 (cp inline style verbosity 로 size 증가, 시각 일관성 큰 이득)
**E3 후속 — popover Portal 도입 (2026-04-29)**:
- Codex 검증 시 ★ 발견: stats 의 list wrapper `overflow: hidden` (cp 표준 디자인) 안에서 IconPicker popover (absolute) 가 잘릴 위험
- search/table 도 동일 hidden 패턴 — cp 표준 깨면 일관성 깨짐
- 해결: IconPicker 의 popover 만 `React.createPortal(document.body)` + `position: fixed` + 좌표 (트리거 `getBoundingClientRect()`) 로 변경
- 부모 overflow 와 무관하게 항상 화면 위에 표시
- scroll/resize 발생 시 popover 자동 닫음 (좌표 안 따라감 — 단순)
- z-index 9999 로 최상위
- 다른 cp 패널 일관성 유지 (wrapper hidden 그대로)
**IconPicker 미완 후속 사항 (다음에 손볼 때 한꺼번에)** ★:
1. **viewport 하단 잘림 보정 (auto-flip)** — 트리거 아래 공간 부족 시 popover 를 트리거 위로 flip. 좌표 계산 시 `viewport.height - rect.bottom` 비교 후 결정.
2. **외부 클릭 닫기** — 현재 트리거 재클릭 / 그리드 선택 / 검색 후 선택 시만 닫힘. cp 표준 CPSelect 처럼 `mousedown` capture + Escape 키 처리 추가 (단 portal 안에서 처리 시 popover 자체 클릭은 닫지 않게 ref 비교).
3. **z-index 9999 표준화** — 향후 앱 모달/오버레이 (예: shadcn `Dialog`, `Sheet` 등) 와 충돌 가능. cp 표준 z-index 토큰 (`--cp-z-popover`) 정의 후 사용.
4. **viewport 우측 잘림 보정** — popover width 가 트리거 right 보다 클 때 좌측으로 shift. 현재 trigger.width 그대로 사용해 우측 잘림 가능성 낮지만, 동적 width 일 때 대비.
5. **키보드 네비** — ↑↓ 화살표 / Enter / Esc 등. 현재 마우스만 지원.
6. **카테고리 분류 / 더보기** — 현재 80개 cap. 검색 시에만 전체 매치. 카테고리 (액션/내비/IO 등) 추가 후보.
위 1~6 은 즉시 깨지는 결함은 아님. 다음에 IconPicker 본체 손볼 때 (예: button/stats 외 다른 패널 추가 또는 신규 기능 추가) 함께 처리.
---
## 안전 가이드
@@ -123,12 +194,44 @@ export function useDbTables(opts?: {
- ✅ getComponentConfigPanel.tsx stats key 중복 버그 수정
- ✅ ALIAS 충돌 수정 (옛 hidden 컴포넌트 → InvLegacy)
- ✅ input 통합 cp 톤 신규 (InvInputConfigPanel)
-**useDbTables hook 추출 (2026-04-29)**
-**TableConnectSection + AutoLoadButton 추출 (2026-04-29)**
-**Row helpers 추출 (RowNumberBadge / RowExpandChevron / RowDeleteBtn) — 2026-04-29**
-**IconPicker cp 톤 마이그 (2026-04-29)** — Codex Y4 권고
### 미완 (위 1~4 순위)
- ❌ useDbTables hook 추출
- ❌ TableConnectSection 추출
- ❌ DenseListRow 추출
- ❌ V2DateConfigPanel / V2BomTree / V2BomItemEditor cp
- ❌ InvDataConfigPanel 의 list/table 분기 cp (옛 화면 호환용)
- ❌ V2* 33개 dead code 정리
- ❌ IconPicker 공용 cp 톤
---
## Codex 검토 (2026-04-29) 핵심 반영
| 질문 | 판정 | 시급 액션 |
|---|---|---|
| Q1 todos 우선순위 | 수정필요 | useDbTables 만 먼저 (✅ 완료) + dead-code 스캔 기준 보강 |
| Q2 InvRepeater 신규 | 수정필요 | `import` type 보류, resolver/writer + 보존 키 목록 확정 |
| Q3 InvData 통합 | 수정필요 | taxonomy 하위 축 추가 + round-trip DoD |
| Q4 Inv* 네이밍 | OK | 옵션 B 유지, legacy 사용량 0 검증 전 삭제 금지 |
### ★ silent breakage 위험 (Codex 경고)
1. **TableConnectSection 추출 시** — search/table 의 자동 로드 의미 다름 (search: 검색 필드 8개 / table: 컬럼 N개). UI 만 공통화 + autoload 는 prop callback
2. **DenseListRow 추출 시** — row 별 key/펼침/onChange shape 다름. 너무 빨리 공통화하면 UI 는 떠도 저장 config 틀어질 수 있음
3. **dead code 33개 식별**`rg "V2XxxConfigPanel"` 만으로 부족
- `getComponentConfigPanel.tsx``CONFIG_PANEL_MAP` + `CONFIG_PANEL_ALIAS` (alias 가 먼저 해석됨, 예: `"v2-table-list": "table"`)
- `V2PropertiesPanel.tsx:215` hardcoded require
- dynamic import map 확인 필수
4. **InvRepeater config 보존 키**
- `source_detail_config` (table_name/foreign_key/parent_key 외 use_entity_join, column_mapping, additional_join_columns 도 보존)
- `column_mappings[]`, `calculation_rules[]`, `entity_joins[]` (modal 전용처럼 보여도 보존)
- `no_duplicate_pick` (현재 코드 L1335) 호환 체크 추가
5. **V2DateConfigPanel** — V2FieldConfigPanel 의 `input.date.*` 와 분리 결정 필요
6. **excludeFilter / linkedFilters[] / source_detail_config** — 통합 InvData 시 read/write round-trip 테스트 필수 (참조 테이블 변경 시 별도 컬럼 API 호출)
### 추가 권장사항
- **InvData 통합 패널 분리**: 3760줄 단일 파일 비추천. orchestrator / resolveAxis-applyAxis / read sections / write sections / API hooks / column editors 로 경계
- **InvRepeater resolver/writer 도입**: V2Field 의 resolveTriple/applyTriple 패턴 그대로 (InvRepeaterConfigPanel L124, L141 이미 같은 형태 사용 중)
- **InvRepeater type=import 보류**: 별도 reserved 또는 후속 컴포넌트 후보로