refactor(numbering-rule): NumberingRule → Input canonical 흡수 + 채번 관리 페이지 분리

- 옛 registry/numbering-rule, registry/v2-numbering-rule, V2NumberingRuleConfigPanel,
  NumberingRuleTemplate 폐기 — InvFieldConfigPanel + InputComponent 로 통합
- input 에 numbering-picker / select-pickers 추가, autonum 타입 흡수
- 채번 관리 전용 admin 페이지(systemMng/numberingRuleList) + CreateDialog +
  SequenceManagementPanel 신설
- backend NumberingRule controller/service/mapper 갱신 (시퀀스 관리 엔드포인트)
- input canonical 진행 노트 + 채번 관리 mockup 추가

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 21:42:13 +09:00
parent 59f5cf22f0
commit a5bbd1eb7c
40 changed files with 3735 additions and 1247 deletions
@@ -0,0 +1,454 @@
# INVYONE Input 통합 — Canonical 마이그레이션 진행 노트
날짜: 2026-05-07 ~ 2026-05-11
작업자: gbpark
컨텍스트: 인비원스튜디오의 입력 컴포넌트 (input / v2-input / v2-select / 옛 6개) 가 분산되어 외형·모델 들쭉날쭉. **canonical 1안 (InvFieldConfigPanel + InputComponent)** 으로 통합 중.
---
## 0. 핵심 원칙
- INVYONE = VEX 의 2세대 리뉴얼. **운영 단계 아님** → 옛 키 fallback / 호환 부담 X. 깨끗한 canonical 1안.
- 옛것이 남아있으면 통합 아님. **1 패널 1 컴포넌트**.
- GPT-5.5 (codex:rescue) 와 단계마다 교차 검증.
## 1. 큰 그림 (목표 형태)
```
[8 통합 컴포넌트] = [8 단일 ConfigPanel + 단일 캔버스 컴포넌트]
input → InvFieldConfigPanel + InputComponent (text/number/money/date/single/multi/autonum/formula/audit/file)
table → InvTableConfigPanel
search · button · title · divider · stats · container
[옛 V2 / 옛 6개 컴포넌트] → 기능 이식 후 폐기
V2Input.tsx (1286줄)
V2Select.tsx (1350줄)
date-input / text-input / number-input / select-basic / checkbox-basic / textarea-basic 의 자체 캔버스 컴포넌트 6개
```
---
## 2. 완료 작업
### Phase 1 — ConfigPanel 통합 (이전 세션 + 일부 본 세션)
-`InvFieldConfigPanel` 의 brumb (4 kinds × 10 types × 32 formats) 그대로 canonical
-`resolveTriple` 에 옛 6개 컴포넌트 ID → triple default 분기 추가
- ✅ 옛 6개 `index.ts` — config_panel: InvFieldConfigPanel + default_config 에 `{kind, type, format}` triple 추가
- ✅ 신규 `input/index.ts` — config_panel: InvFieldConfigPanel + default_config triple
-`CONFIG_PANEL_MAP["input"]` → InvFieldConfigPanel
### Phase 2 — applyTriple canonical cleanup
-`TYPE_VOLATILE_FIELDS` 상수 + `clearVolatileFields` 함수
- ✅ text/number/money/date/choice 분기 자기 필드만 set, 잔재 일괄 reset
- ✅ formula `next.computed = prev.computed || ""` 패치 (분기 순환 시 사용자 수식 보존)
- ✅ auto/attach 분기 redundant `next.source = undefined` 제거
### Phase 3 — 캔버스 라우팅 통일
-`DynamicComponentRenderer.tsx:418-453` 의 fieldType / dbInputType → v2-input/v2-select **강제 swap 분기 통째 제거**
-`webTypeMapping.ts` 의 text 계열 9개 (text/email/password/tel/url/textarea/number/decimal/label) → `input`
- ✅ 기본 fallback 도 `v2-input``input`
### Phase 4 — InputComponent 외형 통일
- ✅ container 가 input box 역할 (border + radius + bg, padding 0)
-`baseInputStyle` 에서 자체 border 제거 + transparent (이중 박스 해소)
-`inputSlotStyle` (flex:1, width/height 100%) wrapper — 모든 type 의 위젯 박스 가득
- ✅ label position: absolute, top: -18 (박스 바깥 위 — V2 스타일)
- ✅ entity outer div / button height 100% + flexShrink:0
- ✅ text 분기에서 format 별 native input type 분기 (password/email/tel/url)
### Phase 5 — pickers 통일 (date 계열)
-`SingleDatePicker` / `DateTimePicker` / `TimePicker` / `RangeDatePicker` className prop 전파 → 자체 border 제거
-`DateTimePicker` 의 sub-picker (SingleDatePicker + TimePicker) 에 className 전파
-`DateTimePicker` gap-2 → 0 + 가운데 1px divider
-`TimePicker` 의 shadcn `Input` → raw `<input>` (default class 회피)
-`RangeCalendarPopover` className prop 받음 + RangeDatePicker 가 sub 에 전달
-`RangeDatePicker` gap-2 → 0
### Phase A.5 — 자동생성 hook
-`InputComponent` 에 useEffect — `autoGeneration.enabled``AutoGenerationUtils.generateValue` 호출
- ✅ 조건: 디자인 모드 X + 값 비어있을 때만 trigger
- ✅ 처리 type: uuid / current_user / current_time / sequence / random_string / random_number / company_code / department
-`numbering_rule` 은 별도 (Phase A.6 에서)
### Phase B.1 — select-pickers 모듈 시작
-`frontend/lib/registry/components/input/select-pickers.tsx` 신규
-`SingleSelectPicker` — Custom Popover dropdown + 검색 + allowClear + 외부 클릭 닫기 + ESC
- ✅ InputComponent select 분기 → SingleSelectPicker 사용 (native `<select>` 대체)
- ✅ webTypeMapping `select / dropdown``input` (single dropdown)
### Phase B.2 — MultiSelectPicker
-`MultiSelectPicker` 신규 (select-pickers.tsx) — 체크박스 list + maxSelect 차단 + 라벨 join 트리거
- ✅ InputComponent select 분기에 multi 분기 추가 (`type=multi` 또는 `config.multiple` 시)
- ✅ value 정규화 (string / string[] 둘 다 받음)
### Phase B.4 — displayMode (mode) 통합
- ✅ 처음 시도: `displayMode` 신규 prop + UI — **중복 발견 후 폐기** (기존 `config.mode` 와 동일)
- ✅ 정정: 기존 `config.mode` 사용 (dropdown/combobox/radio/check/tag/toggle 6 가지)
-`RadioPicker` 신규 — single + mode=radio (라디오 button list)
-`CheckboxListPicker` 신규 — multi + mode=check (체크박스 list, maxSelect 차단)
- ✅ InputComponent select 분기 — mode 따라 4 picker 분기 (Single/Multi/Radio/CheckboxList)
- ✅ ConfigPanel "선택 방식" 옵션을 multi prop 따라 분기 — single: dropdown/combobox/radio/toggle, multi: dropdown/combobox/check/tag
- ✅ TYPE_VOLATILE_FIELDS 에 `maxSelect` 추가 (`mode` 는 기존)
### Phase B.4 추가 정리 — 외각 box + 복수 선택 토글
- ✅ picker 4개 wrapper 의 `opacity-50` 제거 (시각 흐릿 해소)
- ✅ InputComponent select 분기 disabled 에 `isDesignMode` 빼기 — 디자인 모드에서도 클릭 가능 (시각만)
- ✅ container border/bg 분기 — `mode === "radio" || "check"` 시 외각 box 제거 (자체 visual element 가 표시)
- ✅ 고급 설정의 "복수 선택" CPSwitch 제거 — brumb 의 `단일/다중` 이 진실의 원천 (단일/다중 = 저장 형태 차이, UX 토글 아님)
- ✅ "최대 개수" 노출 조건 — `multi prop` 으로 분기 (config.multiple 의존 X)
- ✅ input default_size 높이 48 → 30 (사용자 의도)
### Phase B.4 의 알려진 이슈 (이후 해결됨)
-~~기본 선택값 (`config.defaultValue`) 동작 안 함~~ — 2026-05-11 해결 (BlockRenderer hijack 버그)
- ⚠️ dropdown 모드인데 박스 없음 사례 (사진 #32). 원인 가설: `config.mode` 잔재 ("radio"/"check"). 새 컴포넌트 끌어 놓으면 박스 정상
---
## 2026-05-11 진행 (이번 세션)
### Phase A.8 — 채번 admin 페이지 + 시퀀스 관리 (★ 별도 트랙)
**배경**: VEX 는 채번을 캔버스 컴포넌트로 처리 (잘못된 구조). 팀장 요구 — 별도 admin 페이지에서 채번 규칙 + 시퀀스 일원 관리. INVYONE 에는 그 기능 없음 → 신규 작성.
**작업 중 발견한 mismatch 들 (운영 전이라 모두 동시 fix)**:
1. **URL mismatch** — backend Controller `/api/numbering-rule` (단수) ↔ frontend `/numbering-rules` (복수)
- **fix**: backend `@RequestMapping` 복수로 변경
2. **응답 key mismatch** — backend `Map.of("code", ...)` ↔ frontend `data.generatedCode`
- **fix**: backend 4 endpoint (preview/test-preview/allocate/generate) 응답 key 모두 `generatedCode` 로 통일
3. **GREATEST 로직**`updateCurrentSequenceInRule` mapper 가 GREATEST 로 sequence 못 내림
- **fix**: admin 전용 `setCurrentSequenceInRule` SQL 신규 (GREATEST 없이 직접 SET). 기존 mapper 는 allocateCode 흐름용으로 유지
4. **두 테이블 ground truth 분리** — 실제 발번은 `numbering_rule_sequences` (prefix 별), `numbering_rules.current_sequence` 는 표시용
- **fix**: admin 의 reset/update 가 두 테이블 다 처리:
- `deleteSequencesByRuleId` (prefix sequences 비움)
- `setCurrentSequenceInRule` (numbering_rules 직접 set)
- 추가: `incrementSequenceForPrefix` 의 INSERT 분기에서 base 값을 `numbering_rules.current_sequence + 1` 로 변경 → admin set 값 N 이 다음 발번 N+1 로 정확히 반영
5. **권한 없음** — PUT /sequence + POST /reset 일반 사용자도 호출 가능
- **fix**: `@RequestAttribute("role")` 받아서 SUPER_ADMIN/ADMIN/COMPANY_ADMIN 만 허용. 403 으로 거부
6. **Designer wrong rule 위험**`NumberingRuleDesigner` lockedColumn 시 by-column 조회로 카테고리 분기 규칙에서 다른 rule 열림
- **fix**: 기존 `initialConfig` prop 활용. mount 시 setCurrentRule + setSelectedColumn 직접 set. lockedColumn effect 에 `if (initialConfig) return` 가드
7. **race condition** — SequenceManagementPanel 의 저장/리셋 동시 실행
- **fix**: 단일 `mutating: null | "save" | "reset"` state 로 통합. 양 버튼 disabled 가 `mutating !== null`
**신규 / 수정 파일**:
-`backend-spring/src/main/java/com/erp/controller/NumberingRuleController.java`
- `@RequestMapping` 단수 → 복수
- `PUT /{ruleId}/sequence` endpoint 신규 (권한 체크 포함)
- `POST /{ruleId}/reset` 에 권한 체크 추가
- preview/allocate/generate 응답 key 통일
- `isAdminRole` 헬퍼 추가
-`backend-spring/src/main/java/com/erp/service/NumberingRuleService.java`
- `updateRuleSequence` 메서드 신규 (두 테이블 처리)
- `resetSequence``setCurrentSequenceInRule` 사용으로 변경
- `incrementSequenceForPrefix` 의 INSERT 분기 base = numbering_rules.current_sequence + 1
-`backend-spring/src/main/resources/mapper/numberingRule.xml`
- `setCurrentSequenceInRule` SQL 신규 (admin 전용)
-`frontend/lib/api/numberingRule.ts`
- `updateRuleSequence(ruleId, newSequence)` 함수 신규
-`frontend/components/numbering-rule/SequenceManagementPanel.tsx` 신규
- 현재 시퀀스 + 직접 수정 + reset + 미리보기. mutation lock 적용
-`frontend/components/numbering-rule/NumberingRuleDesigner.tsx`
- `initialConfig` prop 활용 effect 추가 (by-column 우회)
- `selectedColumn` 초기 state 를 lockedColumn 으로
-`frontend/app/(main)/admin/systemMng/numberingRuleList/page.tsx` 신규
- 좌측 규칙 목록 + 우측 ① NumberingRuleDesigner + ② SequenceManagementPanel
- 신규 규칙 CTA → NumberingRuleCreateDialog
-`frontend/components/layout/AdminPageRenderer.tsx`
- `/admin/systemMng/numberingRuleList` 라우팅 등록
**남은 / 후속**:
- ✅ 사이드바 메뉴 DB INSERT (2026-05-11 완료)
- Flyway: `V017__register_numbering_rule_menu.sql` + `V018__assign_numbering_rule_menu_to_super_admin.sql`
- 즉시 운영 DB INSERT 실행: MENU_INFO row 등록 (OBJID=NUMBERING_RULE_LIST, SEQ=8, PARENT=시스템 관리 그룹)
- AUTHORITY_SUB_MENU 매핑: 운영 DB 에 SUPER_ADMIN AUTH_CODE 가 없어 INSERT 0 (다른 systemMng 메뉴도 매핑 없이 동작 중. 무해)
- ✅ v2-numbering-rule / numbering-rule 캔버스 컴포넌트 폐기 (2026-05-11 완료)
- `lib/registry/components/numbering-rule/` 폴더 통째 삭제
- `lib/registry/components/v2-numbering-rule/` 폴더 통째 삭제
- `components/v2/config-panels/V2NumberingRuleConfigPanel.tsx` 삭제
- `components/screen/templates/NumberingRuleTemplate.ts` 삭제 (dead code)
- `lib/registry/components/index.ts` 의 두 import 제거
- `lib/utils/getComponentConfigPanel.tsx` 의 두 import 제거
- `ComponentsPanel.tsx` 의 hidden list 에서 "v2-numbering-rule" / "numbering-rule" 제거
- ⏳ 운영 모드 동작 검증 (backend 재시작 필요 — URL mismatch + 응답 key + sequence 흐름 다 fix 됐는지 확인)
### Phase A.7 — 스튜디오 내 채번 규칙 생성 (CTA + Dialog)
사용자 요구: 운영에서 채번 규칙은 admin 페이지에서 사전 등록되어야 NumberingPicker 동작. 스튜디오에서도 새 규칙을 만들 수 있게 — InvFieldConfigPanel 의 채번 옵션에 "새 규칙 만들기" CTA + Modal.
-`NumberingRuleDesigner.tsx``lockedColumn?: { tableName, columnName }` prop 추가
- 좌측 컬럼 목록 UI hide (조건부 render)
- mount 시 자동 `handleSelectColumn` 호출
- selectedColumn 초기 state 를 lockedColumn 으로 (flash 방지)
- "컬럼을 선택하세요" → lockedColumn 시 "채번 규칙을 불러오는 중..." 로 분기
-`frontend/components/numbering-rule/NumberingRuleCreateDialog.tsx` 신규 — shadcn Dialog wrapper. NumberingRuleDesigner 를 lockedColumn 으로 띄움. onSave → onCreated callback + 자동 닫기
-`InvFieldConfigPanel.tsx` — NumberingOptions 에 "+ 새 규칙 만들기" 버튼 + Dialog 통합
- `rulesRefreshKey` state + numberingRules effect dep 추가
- 생성 후 새 ruleId 자동 선택 + rules 목록 refresh
- **canonical 위치 통일**: `autoGeneration.numberingRuleId` (옵션 밖) → `autoGeneration.options.numberingRuleId` (옵션 안)
- V2InputConfigPanel 도 옵션 안에 저장. autoGeneration.ts.generateValue 도 옵션 안 사용.
- InputComponent 의 NumberingPicker `numberingRuleId` prop 도 옵션 안에서 읽음 → 일관 ★
- 이전 옵션 밖 fallback 제거 (canonical 1안 원칙)
- ✅ Codex 검증 — 2 issue fix
- **MED**: applyRuleId 에 `enabled: true` + `options.numberingRuleId` 명시 (저장 시 무효 방지)
- **LOW**: selectedColumn 초기 state 를 lockedColumn 으로 (flash 방지)
- **OK** 항목 (3·5·6) 무수정
- **LOW** (4 토큰 컨벤션 raw hsl) — 기능 영향 없음, 후속 정리
### Phase A.6 — numbering API hook (완료)
-`frontend/lib/registry/components/input/numbering-picker.tsx` 신규
- V2Input.tsx:600~949, 1069~1144 의 채번 본체 추출 — useState/useRef + 3 useEffect (main / debounce / beforeFormSave) + 렌더 (readonly text vs prefix-input-suffix)
- `previewNumberingCode(ruleId, formData, manualInputValue)` API 사용
- ruleId 결정 흐름: `props.numberingRuleId` 우선 → 없으면 `by-column` API → fallback `getTableColumns.detailSettings.numberingRuleId`
- `____` 템플릿: 첫 생성 시 templateRef set + 부모 value, 카테고리 변경 시 manualInputValue 유지 + 새 조합값 onChange
- debounce (300ms) 디바운스로 manualInputValue 변경 시 suffix 동적 갱신
- beforeFormSave window listener — 저장 직전 조합값 formData 주입
- ✅ Codex 검증 (1차) — 6 issue 지적 → 전부 fix 적용
- cancellation flag (main + debounce) → race condition 방지
- `??``||` (tableName 폴백, 빈 문자열 통과 방지)
- `originalData || _originalData` 별칭 보강 (isEditMode)
- 카테고리 변경 + userEditedRef 시 onChange 호출 (parent value 누락 fix)
- propRuleId main effect dep 추가 + isDesignMode debounce dep 추가
- inputSlotStyle 에 `overflow: hidden + borderRadius: inherit` 이동 (NumberingPicker 모서리 처리)
- `_numberingRuleId` callback (EditModal/buttonActions 호환) — `onRuleIdResolved` prop 추가
- Legacy `inputType="numbering"` → "code" 매핑 추가 (V2 저장 데이터 호환)
- ✅ InputComponent.tsx — `case "code"` 분기 → NumberingPicker 호출. tableName 5경로 (props/config/component/overrides/screenInfo) + isEditMode 노출
- ✅ 타입체크 통과 (numbering-picker / InputComponent 에서 에러 0건)
### Phase B.4.4 — TogglePicker
-`TogglePicker` 신규 — boolean Y/N 토글 스위치 (truthy 자동 판정: bool/숫자/Y/N/true/false/1/0)
- ✅ InputComponent `case "checkbox"` 분기 — `mode=toggle` 시 TogglePicker, 아니면 기존 단일 체크박스
### Phase B.3 — TagPicker
-`TagPicker` 신규 — chip + 입력 (Enter / 구분자 추가, Backspace / ✕ 제거, maxSelect 차단, 중복 방지)
- ✅ InputComponent multi 분기 — `format=tags` 또는 `mode=tag` 시 TagPicker
### Phase B.4 추가 정리 (사용자 사진 검증 반영)
- ✅ Radio/Checkbox list 의 **외각 box 제거**`mode=radio||check` 시 container border/bg 없음 (자체 visual element 가 표시)
-**"복수 선택" CPSwitch 제거** (고급 설정) — brumb 의 `단일/다중` 이 진실의 원천. 단일/다중 = 저장 형태 차이 (값 cardinality), UX 토글 아님
- ✅ "최대 개수" 노출 조건 — `multi prop` 으로 분기 (config.multiple 의존 X)
- ✅ ConfigPanel "선택 방식" 옵션을 multi 따라 분기 — single: dropdown/combobox/radio/toggle, multi: dropdown/combobox/check/tag
-~~`displayMode` 신규 prop~~ 폐기 — 기존 `config.mode` 와 중복이라 정리. mode 만 사용
- ✅ Radio/Checkbox list — flex-col → flex-wrap 으로 변경 (디자인/운영 모드 외형 일관, 박스 사이즈에 맞게 자동 wrap)
- ✅ picker 4개 wrapper 의 `opacity-50` 제거 (시각 흐릿 해소)
- ✅ InputComponent select 분기 disabled 에 `isDesignMode` 빼기 — 디자인 모드에서도 클릭 가능
### Phase B.5 — CPSelect Portal 도입
-`CPSelect` 의 dropdown 을 React Portal 로 — `position: fixed` + `getBoundingClientRect()` 좌표 + scroll/resize 시 닫음
- ✅ ConfigPanel 의 `overflow:auto` 와 무관하게 dropdown 화면 끝까지 보임 (사진 #35 의 가려짐 해소)
### Phase B.6 — defaultValue 버그 (중요 발견)
**증상**: 사용자가 ConfigPanel 에 "기본 선택값: 저는 김민호" 설정해도 운영 (form-popup 수정 모달) 에서 default 안 적용. InputComponent props 의 `componentConfig.defaultValue: undefined`.
**진단 단계** (긴 디버그 흐름):
1. `[Input debug]` console.log 추가 → 스튜디오 OK, 운영 undefined 확인
2. `[saveTemplate]` 디버그 → edit 뷰 `overrides.defaultValue = "저는 김민호"` 저장 정상
3. `[loadTemplate]` 디버그 → editLegacy `componentConfig.defaultValue = "저는 김민호"` load 정상
4. `[Input render path]` 디버그 → `pageUrl: "/form-popup"` 확인, rawComponentConfig.defaultValue = undefined
5. → form-popup 페이지가 **BlockRenderer** 사용. 거기서 stripping
**진짜 원인**: `frontend/components/dash/BlockRenderer.tsx:54-57`
```ts
// BEFORE (버그)
const runtimeConfig =
resolvedColumnName != null
? { ...block.config, defaultValue: resolvedValue } // ← 무조건 덮어씀
: block.config;
```
→ columnName 있는 컴포넌트는 form data 의 그 컬럼 값 (`context.formRow?.[columnName]`) 으로 **defaultValue 를 무조건 hijack**. 신규/빈 row 시 `resolvedValue = undefined` → 사용자 설정 defaultValue 가 undefined 로 덮임.
**Fix**:
```ts
// AFTER
const runtimeConfig =
resolvedColumnName != null && resolvedValue !== undefined && resolvedValue !== null
? { ...block.config, defaultValue: resolvedValue }
: block.config; // ← formRow 값 없으면 사용자 설정 defaultValue 보존
```
### 잘못 들어간 경로 (rollback 완료)
-~~GPT 진단 — frontend `saveLayoutV2` 의 payload shape 와 backend `body.get("layout_data")` 불일치~~
- 처음 GPT 가 `saveLayoutV2` payload 를 `layout_data` 키로 감싸야 한다 제안
- 사용자 짚음: "그러면 옛 컴포넌트는 어떻게 저장됐냐" — 정확. 모순.
- **저장 자체는 정상**이었음. INVYONE 스튜디오는 `saveTemplate` 사용 (template_id 가 있을 때), saveLayoutV2 는 별개 경로
- rollback 완료. 정상 흐름 복원
### 임시 디버그 (잔존 — 정리 가능)
- `templateAdapter.ts:76``[saveTemplate] payload` 일반 진단용 로그 — 유지 (low-noise)
- 그 외 [Input debug] / [Save layout debug] / [loadTemplate] / [Input render path] 등 모두 제거 완료
---
## 3. 진행 중 / 남은 작업
### Phase A 잔여
-~~**A.6** numbering API hook~~ (2026-05-11 완료 — NumberingPicker 신규 + InputComponent 통합)
### Phase B 진행
-~~**B.2** MultiSelectPicker — multi / maxSelect~~ (완료)
-~~**B.3** TagPicker — tags (tagbox)~~ (완료)
-~~**B.4** radio / checkbox / toggle~~ (완료)
-**B.4.5** SwapPicker — multi + mode=swap (양쪽 list 간 이동, 큰 작업)
-**B.5** option loader — api / code / category / distinct / 계층 (apiClient 의존)
- ⏳ webTypeMapping 의 multi / checkbox / radio / boolean / code / category 매핑도 점진 input
### 디버그 (해결)
-~~`config.defaultValue` 동작 안 함~~ — BlockRenderer hijack 버그 (2026-05-11 해결)
### Phase C — entity
- ⏳ entity 검색팝업 + 다른 컬럼 auto-fill (V2Select.tsx:1007~)
- ⏳ 현재 InputComponent entity 분기는 placeholder 버튼만
### Phase D — V2 폐기
- ⏳ webTypeMapping 의 select 계열 (multi/category/entity/code/checkbox/radio/boolean) → input (B 단계 후)
- ⏳ webTypeMapping 의 file/image/img → input (file 통합 후)
-`ScreenSettingModal.tsx:2052-2066` 의 옛 컴포넌트 생성 경로 → input
- ⏳ DB 마이그 — `screens` 테이블의 `layout` JSON 안 componentType 변경 (사용자 승인 필요)
-`V2Input.tsx` / `V2Select.tsx` 파일 삭제
-`registerV2Components.ts` 의 v2-input / v2-select 등록 제거
-`V2InputConfigPanel.tsx` (832줄) 삭제 — 이전 세션의 폐기 보류
### Phase E — 옛 6개 폐기
- ⏳ date-input / text-input / number-input / select-basic / checkbox-basic / textarea-basic 의 캔버스 컴포넌트 (`DateInputComponent.tsx` 등) 폐기
- ⏳ 6 폴더 자체 삭제 (ScreenSettingModal 의 생성 경로 검증 후)
- ⏳ 부수 효과 — `DateInputComponent.tsx:214` 의 옛 TS 에러 자연 해소
---
## 4. 위험 영역 / 주의사항
1. **V2Input / V2Select 폐기 전 고유 기능 이식 검증 필수**
- V2Input: numbering API · mask · password · slider · color picker
- V2Select: radio/check/toggle/swap mode · entity FK 검색·auto-fill · option loader (api/code/category/distinct/계층)
2. **DB 마이그 (Phase D 4단계)**
- 화면 데이터는 `screens` 테이블의 `layout` JSON 안에 통째 저장 (별도 component 테이블 없음 — `screen_components` 같은 이름 X)
- JSON 안 `componentType` / `componentConfig` 변경 SQL 필요 (jsonb_set 또는 string replace)
- 운영 단계 아니라 데이터 마이그 부담 작음 — 단 UPDATE 사용자 승인 필요 (메모리)
3. **dev reload 캐시**
- webTypeMapping.ts 변경은 새로 끌어 놓는 컴포넌트만 반영
- 캐시 안 잡히면 hard reload (Cmd+Shift+R)
4. **canonical pattern — TYPE_VOLATILE_FIELDS**
- 새 type 추가 시 잔재 필드도 같이 등록할 것 (안 그러면 분기 간 잔재 → 옛 컴포넌트 분기 트리거 위험)
---
## 5. 관련 파일 맵
```
[Canonical (목표)]
frontend/components/v2/config-panels/InvFieldConfigPanel.tsx ← 단일 ConfigPanel (brumb)
frontend/lib/registry/components/input/InputComponent.tsx ← 단일 캔버스 컴포넌트
frontend/lib/registry/components/input/pickers.tsx ← date/datetime/time/daterange picker
frontend/lib/registry/components/input/select-pickers.tsx ← select picker (B.1 신규)
frontend/lib/registry/components/input/index.ts ← input 등록 (config_panel: InvFieldConfigPanel)
[라우팅]
frontend/lib/utils/webTypeMapping.ts ← web_type → componentType
frontend/lib/utils/getComponentConfigPanel.tsx ← componentId → ConfigPanel
frontend/lib/registry/DynamicComponentRenderer.tsx ← 캔버스 렌더 dispatch (fieldType swap 제거됨)
frontend/components/screen/panels/V2PropertiesPanel.tsx ← properties 패널 라우팅
[폐기 예정]
frontend/components/v2/V2Input.tsx ← 1286줄
frontend/components/v2/V2Select.tsx ← 1350줄
frontend/lib/registry/components/{date,text,number}-input/ ← 자체 캔버스 컴포넌트 폐기
frontend/lib/registry/components/{select,checkbox,textarea}-basic/
frontend/components/v2/config-panels/V2InputConfigPanel.tsx ← 832줄 (이전 세션 폐기 보류)
[유틸 / API]
frontend/lib/utils/autoGeneration.ts ← AutoGenerationUtils.generateValue (A.5 hook 연결됨)
frontend/lib/api/numberingRule.ts ← previewNumberingCode (A.6 이식 예정)
```
---
## 6. 핵심 의사결정 기록
| 결정 | 이유 |
|---|---|
| Canonical = InvFieldConfigPanel + InputComponent | brumb (kind/type/format) 풍부, FieldConfig spec 정합 |
| (가)/(다) 하이브리드 거부 → (나) 통합 추진 | canonical 1안 원칙 / 운영 단계 아님 |
| webTypeMapping → input 점진 변경 | V2Select 고유 기능 이식 후 매핑 변경 (안전) |
| native `<select>` → SingleSelectPicker | OS 기본 dropdown 통일 어려움 + V2Select 풍부 기능 흡수 |
| `TYPE_VOLATILE_FIELDS` reset 패턴 | 명시 cleanup 보다 유지보수성 + 새 type 자동 일관 |
| label position: absolute (박스 바깥) | V2 스타일 / 사용자 의도 |
| 옛 데이터 마이그 X | 운영 단계 아님 (사용자 명시) |
---
## 7. 다음 세션 진입점
### 우선순위
1. **B.4.5 — SwapPicker** (큰)
- multi + mode=swap
- 양쪽 list (선택 가능 / 선택됨) + 이동 버튼
- V2Select.tsx:530~ 참고
2. **B.5 — option loader** (중)
- api / code / category / distinct / 계층
- V2Select 의 option 로딩 로직 이식
- apiClient 의존
3. **C — entity 검색팝업 + auto-fill** (큰)
- V2Select.tsx:1007~ 의 entity FK 검색
- 다른 컬럼 auto-fill (조인)
- 현재 InputComponent entity 분기 = placeholder 버튼만
4. **D — V2 폐기** (위 1~3 완료 후)
- webTypeMapping 의 select 계열 (multi/category/entity/code/checkbox/radio/boolean) → input
- webTypeMapping 의 file/image/img → input (file 통합 후)
- `ScreenSettingModal.tsx:2052-2066` 의 옛 컴포넌트 생성 경로 → input
- DB 마이그 — `templates.views` JSON 안 componentType / url 변경 (사용자 승인 필요)
- `V2Input.tsx` / `V2Select.tsx` 파일 삭제
- `registerV2Components.ts` 의 v2-input / v2-select 등록 제거
- `V2InputConfigPanel.tsx` (832줄) 삭제
5. **E — 옛 6개 폐기**
- date-input / text-input / number-input / select-basic / checkbox-basic / textarea-basic 의 캔버스 컴포넌트 (`DateInputComponent.tsx` 등) 폐기
- 6 폴더 자체 삭제 (ScreenSettingModal 의 생성 경로 검증 후)
### 새 세션 진입 전 준비
- ✅ defaultValue 동작 검증 완료 — form-popup 수정 모달에서 default 적용 됨
- 이전에 한 변경들은 form-popup 의 BlockRenderer hijack 으로 동작 검증이 어려웠음. 이제 가능
- ⏳ Phase B.4.5 / B.5 / C 중 어디부터 진행할지 결정 — 사용자 의도 (사진 확인 가능한 것 우선) 따라
### 핵심 사실 (새 세션이 알아야 할 것)
1. **INVYONE 스튜디오 = templates 모드** (`template_id` 가 있는 경우 `saveTemplate` 호출, `saveLayoutV2` 아님)
2. **운영 모드 form-popup** (`/form-popup?templateId=...`) 가 `getTemplateInfo` → PopupTemplateRenderer → BlockRenderer → InputComponent 경로
3. **edit 뷰 컴포넌트** 가 운영에서 보임 (수정 모달)
4. **BlockRenderer.tsx:54-57** 의 runtimeConfig 가 columnName 기반 defaultValue hijack — fix 적용됨
5. 임시 console.log 모두 제거됨 (saveTemplate payload 의 일반 진단 로그만 1줄 유지)
File diff suppressed because it is too large Load Diff