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:
@@ -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
Reference in New Issue
Block a user