From faf4f566f79ff81160131b4afe747828bbb404b7 Mon Sep 17 00:00:00 2001 From: DDD1542 Date: Wed, 4 Feb 2026 10:33:51 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8-?= =?UTF-8?q?=EC=BB=AC=EB=9F=BC=20=EC=97=B0=EA=B2=B0=20=EC=A3=BC=EC=9D=98?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EB=B0=8F=20=EC=9D=BC=EA=B4=84=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20SQL=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 컴포넌트 배치 시 "component"로 표시되는 문제와 그 해결 방법을 문서화하였습니다. - `overrides.type` 필드 누락 문제를 진단하고, 이를 해결하기 위한 일괄 수정 SQL 쿼리를 추가하였습니다. - V2 컴포넌트의 자동 매핑 규칙과 관련된 내용을 명확히 하여, 개발자들이 올바른 컴포넌트를 생성할 수 있도록 안내하였습니다. - 수정된 화면 및 컴포넌트 수에 대한 통계를 포함하여, 일괄 수정 실행 결과를 기록하였습니다. --- .../V2_마이그레이션_학습노트_DDD1542.md | 399 ++++++++++++++++++ .../본서버_개발서버_마이그레이션_가이드.md | 128 ++++++ .../screen/panels/V2PropertiesPanel.tsx | 16 +- frontend/components/v2/V2Biz.tsx | 9 +- frontend/components/v2/V2Date.tsx | 11 +- frontend/components/v2/V2Hierarchy.tsx | 9 +- frontend/components/v2/V2Input.tsx | 11 +- frontend/components/v2/V2Media.tsx | 11 +- frontend/components/v2/V2Select.tsx | 9 +- frontend/lib/utils/layoutV2Converter.ts | 10 +- 10 files changed, 592 insertions(+), 21 deletions(-) create mode 100644 docs/DDD1542/V2_마이그레이션_학습노트_DDD1542.md diff --git a/docs/DDD1542/V2_마이그레이션_학습노트_DDD1542.md b/docs/DDD1542/V2_마이그레이션_학습노트_DDD1542.md new file mode 100644 index 00000000..801fb213 --- /dev/null +++ b/docs/DDD1542/V2_마이그레이션_학습노트_DDD1542.md @@ -0,0 +1,399 @@ +# V2 마이그레이션 학습노트 (DDD1542 전용) + +> **목적**: 마이그레이션 작업 전 완벽한 이해를 위한 개인 학습노트 +> **작성일**: 2026-02-03 +> **절대 규칙**: 모르면 물어보기, 추측 금지 + +--- + +## 1. 가장 중요한 핵심 (이전 신하가 실패한 이유) + +### 1.1 "component" vs "v2-input" 차이 + +``` +[잘못된 상태] [올바른 상태] +┌──────────────────┐ ┌──────────────────┐ +│ component │ │ v2-input │ +│ 업체코드 │ │ 업체코드 │ +│ "자동 생성됩니다" │ │ "자동 생성됩니다" │ +└──────────────────┘ └──────────────────┘ + ↑ ↑ + 테이블-컬럼 연결 없음 table_name + column_name 연결됨 +``` + +**핵심**: 컬럼을 왼쪽 패널에서 **드래그**해야 올바른 연결이 생성됨 + +### 1.2 올바른 컴포넌트 생성 방법 + +``` +[왼쪽 패널: 테이블 컬럼 목록] +운송업체 (8개) +├── 업체코드 [numbering] ─드래그→ 화면 캔버스 → v2-numbering-rule (또는 v2-input) +├── 업체명 [text] ─드래그→ 화면 캔버스 → v2-input +├── 유형 [category] ─드래그→ 화면 캔버스 → v2-select +├── 연락처 [text] ─드래그→ 화면 캔버스 → v2-input +└── ... +``` + +### 1.3 input_type → V2 컴포넌트 매핑 + +| table_type_columns.input_type | V2 컴포넌트 | 연동 테이블 | +|-------------------------------|-------------|-------------| +| text | v2-input | - | +| number | v2-input (type=number) | - | +| date | v2-date | - | +| category | v2-select | category_values | +| numbering | v2-numbering-rule 또는 v2-input | numbering_rules | +| entity | v2-entity-search | 엔티티 조인 | + +--- + +## 2. V1 vs V2 구조 차이 + +### 2.1 테이블 구조 + +``` +V1 (본서버: screen_layouts) V2 (개발서버: screen_layouts_v2) +────────────────────────────────────────────────────────────────── +- 컴포넌트별 1개 레코드 - 화면당 1개 레코드 +- properties JSONB - layout_data JSONB +- component_type VARCHAR - url (컴포넌트 경로) +- menu_objid 기반 채번/카테고리 - table_name + column_name 기반 +``` + +### 2.2 V2 layout_data 구조 + +```json +{ + "version": "2.0", + "components": [ + { + "id": "comp_xxx", + "url": "@/lib/registry/components/v2-table-list", + "position": { "x": 0, "y": 0 }, + "size": { "width": 100, "height": 50 }, + "displayOrder": 0, + "overrides": { + "tableName": "inspection_standard", + "columns": ["id", "name", "status"] + } + } + ], + "updatedAt": "2026-02-03T12:00:00Z" +} +``` + +### 2.3 컴포넌트 URL 매핑 + +```typescript +const V1_TO_V2_URL_MAPPING = { + 'table-list': '@/lib/registry/components/v2-table-list', + 'button-primary': '@/lib/registry/components/v2-button-primary', + 'text-input': '@/lib/registry/components/v2-input', + 'select-basic': '@/lib/registry/components/v2-select', + 'date-input': '@/lib/registry/components/v2-date', + 'entity-search-input': '@/lib/registry/components/v2-entity-search', + 'category-manager': '@/lib/registry/components/v2-category-manager', + 'numbering-rule': '@/lib/registry/components/v2-numbering-rule', + 'tabs-widget': '@/lib/registry/components/v2-tabs-widget', + 'split-panel-layout': '@/lib/registry/components/v2-split-panel-layout', +}; +``` + +--- + +## 3. 데이터 타입 관리 (V2) + +### 3.1 핵심 테이블 관계 + +``` +table_type_columns (컬럼 타입 정의) +├── input_type = 'category' → category_values (table_name + column_name) +├── input_type = 'numbering' → numbering_rules (detail_settings.numberingRuleId) +├── input_type = 'entity' → 엔티티 조인 +└── input_type = 'text', 'number', 'date', etc. +``` + +### 3.2 category_values 조회 쿼리 + +```sql +-- 특정 테이블.컬럼의 카테고리 값 조회 +SELECT value_id, value_code, value_label, parent_value_id, depth +FROM category_values +WHERE table_name = '테이블명' + AND column_name = '컬럼명' + AND company_code = 'COMPANY_7' +ORDER BY value_order; +``` + +### 3.3 numbering_rules 연결 방식 + +```json +// table_type_columns.detail_settings +{ + "numberingRuleId": "rule-xxx" +} + +// numbering_rules에서 해당 rule 조회 +SELECT * FROM numbering_rules WHERE rule_id = 'rule-xxx'; +``` + +--- + +## 4. V2 컴포넌트 목록 (23개) + +### 4.1 입력 컴포넌트 + +| ID | 이름 | 용도 | +|----|------|------| +| v2-input | 입력 | 텍스트, 숫자, 비밀번호, 이메일 | +| v2-select | 선택 | 드롭다운, 라디오, 체크박스 | +| v2-date | 날짜 | 날짜, 시간, 날짜범위 | + +### 4.2 표시 컴포넌트 + +| ID | 이름 | 용도 | +|----|------|------| +| v2-text-display | 텍스트 표시 | 라벨, 제목 | +| v2-card-display | 카드 디스플레이 | 카드 형태 데이터 | +| v2-aggregation-widget | 집계 위젯 | 합계, 평균, 개수 | + +### 4.3 테이블/데이터 컴포넌트 + +| ID | 이름 | 용도 | +|----|------|------| +| v2-table-list | 테이블 리스트 | 데이터 그리드 | +| v2-table-search-widget | 검색 필터 | 테이블 검색 | +| v2-pivot-grid | 피벗 그리드 | 다차원 분석 | +| v2-table-grouped | 그룹화 테이블 | 그룹별 접기/펼치기 | + +### 4.4 레이아웃 컴포넌트 + +| ID | 이름 | 용도 | +|----|------|------| +| v2-split-panel-layout | 분할 패널 | 마스터-디테일 | +| v2-tabs-widget | 탭 위젯 | 탭 전환 | +| v2-section-card | 섹션 카드 | 제목+테두리 그룹 | +| v2-section-paper | 섹션 페이퍼 | 배경색 그룹 | +| v2-divider-line | 구분선 | 영역 구분 | +| v2-repeat-container | 리피터 컨테이너 | 데이터 반복 | +| v2-unified-repeater | 통합 리피터 | 인라인/모달/버튼 | + +### 4.5 액션/특수 컴포넌트 + +| ID | 이름 | 용도 | +|----|------|------| +| v2-button-primary | 기본 버튼 | 저장, 삭제 등 | +| v2-numbering-rule | 채번 규칙 | 자동 코드 생성 | +| v2-category-manager | 카테고리 관리자 | 카테고리 관리 | +| v2-location-swap-selector | 위치 교환 | 위치 선택 | +| v2-rack-structure | 랙 구조 | 창고 랙 시각화 | + +--- + +## 5. 화면 패턴 (5가지) + +### 5.1 패턴 A: 기본 마스터 화면 + +``` +사용 조건: 단일 테이블 CRUD + +┌─────────────────────────────────────────────────┐ +│ v2-table-search-widget │ +├─────────────────────────────────────────────────┤ +│ v2-table-list │ +│ [신규] [삭제] v2-button-primary │ +└─────────────────────────────────────────────────┘ +``` + +### 5.2 패턴 B: 마스터-디테일 화면 + +``` +사용 조건: 마스터 선택 → 디테일 표시 + +┌──────────────────┬──────────────────────────────┐ +│ 마스터 리스트 │ 디테일 리스트 │ +│ v2-table-list │ v2-table-list │ +│ │ (relation: foreignKey) │ +└──────────────────┴──────────────────────────────┘ + v2-split-panel-layout +``` + +**필수 설정:** +```json +{ + "leftPanel": { "tableName": "master_table" }, + "rightPanel": { + "tableName": "detail_table", + "relation": { "type": "detail", "foreignKey": "master_id" } + }, + "splitRatio": 30 +} +``` + +### 5.3 패턴 C: 마스터-디테일 + 탭 + +``` +┌──────────────────┬──────────────────────────────┐ +│ 마스터 리스트 │ v2-tabs-widget │ +│ v2-table-list │ ├─ 탭1: v2-table-list │ +│ │ ├─ 탭2: v2-table-list │ +│ │ └─ 탭3: 폼 컴포넌트들 │ +└──────────────────┴──────────────────────────────┘ + v2-split-panel-layout +``` + +--- + +## 6. 모달 처리 방식 변경 + +### 6.1 V1 (본서버) + +``` +화면 A (screen_id: 142) - 검사장비관리 + └── 버튼 클릭 → 화면 B (screen_id: 143) - 검사장비 등록모달 (별도 screen_id) +``` + +### 6.2 V2 (개발서버) + +``` +화면 A (screen_id: 142) - 검사장비관리 + └── layout_data.components[] 내에 v2-dialog-form 또는 overlay 포함 +``` + +**핵심**: V2에서는 모달을 별도 화면이 아닌, 부모 화면의 컴포넌트로 통합 + +--- + +## 7. 마이그레이션 절차 (Step by Step) + +### Step 1: 사전 분석 + +```sql +-- 본서버 화면 목록 확인 +SELECT sd.screen_id, sd.screen_code, sd.screen_name, sd.table_name, + COUNT(sl.layout_id) as component_count +FROM screen_definitions sd +LEFT JOIN screen_layouts sl ON sd.screen_id = sl.screen_id +WHERE sd.screen_code LIKE 'COMPANY_7_%' +GROUP BY sd.screen_id, sd.screen_code, sd.screen_name, sd.table_name; + +-- 개발서버 V2 현황 확인 +SELECT sd.screen_id, sd.screen_code, sd.screen_name, + sv2.layout_data IS NOT NULL as has_v2_layout +FROM screen_definitions sd +LEFT JOIN screen_layouts_v2 sv2 ON sd.screen_id = sv2.screen_id +WHERE sd.company_code = 'COMPANY_7'; +``` + +### Step 2: table_type_columns 확인 + +```sql +-- 해당 테이블의 컬럼 타입 확인 +SELECT column_name, column_label, input_type, detail_settings +FROM table_type_columns +WHERE table_name = '대상테이블명' + AND company_code = 'COMPANY_7'; +``` + +### Step 3: V2 layout_data 생성 + +```json +{ + "version": "2.0", + "components": [ + { + "id": "생성된ID", + "url": "@/lib/registry/components/v2-컴포넌트타입", + "position": { "x": 0, "y": 0 }, + "size": { "width": 100, "height": 50 }, + "displayOrder": 0, + "overrides": { + "tableName": "테이블명", + "fieldName": "컬럼명" + } + } + ], + "migratedFrom": "V1", + "migratedAt": "2026-02-03T00:00:00Z" +} +``` + +### Step 4: screen_layouts_v2 INSERT + +```sql +INSERT INTO screen_layouts_v2 (screen_id, company_code, layout_data) +VALUES ($1, $2, $3::jsonb) +ON CONFLICT (screen_id, company_code) +DO UPDATE SET layout_data = $3::jsonb, updated_at = NOW(); +``` + +### Step 5: 검증 + +- [ ] 화면 렌더링 확인 (component가 아닌 v2-xxx로 표시되는지) +- [ ] 컴포넌트별 테이블-컬럼 연결 확인 +- [ ] 카테고리 드롭다운 동작 확인 +- [ ] 채번 규칙 동작 확인 +- [ ] 저장/수정/삭제 테스트 + +--- + +## 8. 품질관리 메뉴 마이그레이션 현황 + +| 본서버 코드 | 화면명 | 테이블 | 상태 | 비고 | +|-------------|--------|--------|------|------| +| COMPANY_7_126 | 검사정보 관리 | inspection_standard | ✅ V2 존재 | 컴포넌트 검증 필요 | +| COMPANY_7_127 | 품목옵션 설정 | - | ✅ V2 존재 | v2-category-manager | +| COMPANY_7_138 | 카테고리 설정 | inspection_standard | ❌ 누락 | table_name 기반 | +| COMPANY_7_139 | 코드 설정 | inspection_standard | ❌ 누락 | table_name 기반 | +| COMPANY_7_142 | 검사장비 관리 | inspection_equipment_mng | ❌ 누락 | 모달 통합 | +| COMPANY_7_143 | 검사장비 등록모달 | inspection_equipment_mng | ❌ 누락 | → 142 통합 | +| COMPANY_7_144 | 불량기준 정보 | defect_standard_mng | ❌ 누락 | 모달 통합 | +| COMPANY_7_145 | 불량기준 등록모달 | defect_standard_mng | ❌ 누락 | → 144 통합 | + +--- + +## 9. 관련 코드 파일 경로 + +| 항목 | 경로 | +|------|------| +| V2 컴포넌트 폴더 | `frontend/lib/registry/components/v2-xxx/` | +| 컴포넌트 등록 | `frontend/lib/registry/components/index.ts` | +| 카테고리 서비스 | `backend-node/src/services/categoryTreeService.ts` | +| 채번 서비스 | `backend-node/src/services/numberingRuleService.ts` | +| 엔티티 조인 API | `frontend/lib/api/entityJoin.ts` | +| 폼 호환성 훅 | `frontend/hooks/useFormCompatibility.ts` | + +--- + +## 10. 절대 하지 말 것 + +1. ❌ **테이블-컬럼 연결 없이 컴포넌트 배치** → "component"로 표시됨 +2. ❌ **menu_objid 기반 카테고리/채번 사용** → V2는 table_name + column_name 기반 +3. ❌ **모달을 별도 screen_id로 생성** → V2는 부모 화면에 통합 +4. ❌ **V1 컴포넌트 타입 사용** → 반드시 v2- 접두사 컴포넌트 사용 +5. ❌ **company_code 필터링 누락** → 멀티테넌시 필수 + +--- + +## 11. 모르면 확인할 곳 + +1. **컴포넌트 구조**: `docs/V2_컴포넌트_분석_가이드.md` +2. **화면 개발 표준**: `docs/screen-implementation-guide/화면개발_표준_가이드.md` +3. **마이그레이션 절차**: `docs/DDD1542/본서버_개발서버_마이그레이션_상세가이드.md` +4. **탑실 디자인 명세**: `/Users/gbpark/Downloads/화면개발 8/` +5. **실제 코드**: 위 경로의 소스 파일들 + +--- + +## 12. 왕의 훈계 + +> **"항상 애매한 거는 md파일 보거나 물어볼 것. 코드에는 전부 정답이 있음. 만약 모른다면 너 잘못. 실수해도 너 잘못."** + +--- + +## 변경 이력 + +| 날짜 | 작성자 | 내용 | +|------|--------|------| +| 2026-02-03 | DDD1542 | 초안 작성 (문서 4개 정독 후) | diff --git a/docs/DDD1542/본서버_개발서버_마이그레이션_가이드.md b/docs/DDD1542/본서버_개발서버_마이그레이션_가이드.md index e8f7b39e..b7a0e353 100644 --- a/docs/DDD1542/본서버_개발서버_마이그레이션_가이드.md +++ b/docs/DDD1542/본서버_개발서버_마이그레이션_가이드.md @@ -42,6 +42,53 @@ const pool = new Pool({ --- +## 절대 주의: 컴포넌트-컬럼 연결 (이전 실패 원인) + +### "component" vs "v2-input" 구분 + +``` +❌ 잘못된 상태 ✅ 올바른 상태 +┌──────────────────┐ ┌──────────────────┐ +│ component │ │ v2-input │ +│ 업체코드 │ │ 업체코드 │ +└──────────────────┘ └──────────────────┘ + ↑ ↑ + overrides.type 없음 overrides.type = "v2-input" +``` + +**핵심 원인**: 컴포넌트를 그냥 배치하면 "component"로 표시됨. 반드시 왼쪽 패널에서 테이블 컬럼을 **드래그**해야 올바른 v2-xxx 컴포넌트가 생성됨. + +### 🔥 핵심 발견: overrides.type 필수 (2026-02-04 발견) + +**"component"로 표시되는 근본 원인:** + +| 항목 | 드래그로 배치 | 마이그레이션 (잘못된) | +|------|---------------|----------------------| +| `overrides.type` | **"v2-input"** ✅ | **없음** ❌ | +| `overrides.webType` | "text" 등 | 없음 | +| `overrides.tableName` | "carrier_mng" 등 | 없음 | + +**프론트엔드가 컴포넌트 타입을 인식하는 방법:** +1. `overrides.type` 확인 → 있으면 해당 값 사용 (예: "v2-input") +2. 없으면 → 기본값 "component"로 폴백 + +**결론**: 마이그레이션 시 `overrides.type` 필드를 반드시 설정해야 함! + +### input_type → V2 컴포넌트 자동 매핑 + +| table_type_columns.input_type | 드래그 시 생성되는 V2 컴포넌트 | +|-------------------------------|-------------------------------| +| text | v2-input | +| number | v2-input (type=number) | +| date | v2-date | +| category | v2-select (category_values 연동) | +| numbering | v2-numbering-rule 또는 v2-input | +| entity | v2-entity-search | + +**절대 규칙**: 컴포넌트가 "component"로 표시되면 연결 실패 상태. 반드시 "v2-xxx"로 표시되어야 함. + +--- + ## 핵심 개념 ### V1 vs V2 구조 차이 @@ -283,6 +330,8 @@ detail_settings, company_code 1. **개발서버 → 본서버 마이그레이션** (반대 방향) 2. **본서버 데이터 직접 수정** (SELECT만 허용) 3. **company_code 누락** (멀티테넌시 필수) +4. **테이블-컬럼 연결 없이 컴포넌트 배치** ("component"로 표시되면 실패) +5. **menu_objid 기반 카테고리/채번 사용** (V2는 table_name + column_name 기반) ### 반드시 할 것 @@ -290,6 +339,77 @@ detail_settings, company_code 2. 컴포넌트 변환 시 **V2 컴포넌트만 사용** (v2- prefix) 3. 모달 화면은 **부모 화면에 통합** 4. 카테고리/채번은 **table_name + column_name 기반** +5. 컴포넌트 배치 후 **"v2-xxx"로 표시되는지 반드시 확인** + +### 실패 사례 (이전 작업자) + +**물류정보관리 → 운송업체 관리 마이그레이션 실패** + +- **원인**: 컴포넌트를 직접 배치하여 "component"로 생성됨 +- **증상**: 화면에 "component" 라벨 표시, 데이터 바인딩 실패 +- **해결**: 왼쪽 패널에서 테이블 컬럼을 드래그하여 "v2-input" 등으로 생성 + +--- + +## 🔧 일괄 수정 SQL (overrides.type 누락 문제) + +### 문제 진단 쿼리 + +```sql +-- overrides.type이 없는 컴포넌트 수 확인 +SELECT + COUNT(DISTINCT sv2.screen_id) as affected_screens, + COUNT(*) as affected_components +FROM screen_layouts_v2 sv2, + jsonb_array_elements(sv2.layout_data->'components') as comp +WHERE (comp->>'url' LIKE '%/v2-input' + OR comp->>'url' LIKE '%/v2-select' + OR comp->>'url' LIKE '%/v2-date') + AND NOT (comp->'overrides' ? 'type'); +``` + +### 일괄 수정 쿼리 (개발서버에서만!) + +```sql +UPDATE screen_layouts_v2 +SET layout_data = jsonb_set( + layout_data, + '{components}', + ( + SELECT jsonb_agg( + CASE + WHEN comp->>'url' LIKE '%/v2-input' AND NOT (comp->'overrides' ? 'type') + THEN jsonb_set(comp, '{overrides,type}', '"v2-input"') + WHEN comp->>'url' LIKE '%/v2-select' AND NOT (comp->'overrides' ? 'type') + THEN jsonb_set(comp, '{overrides,type}', '"v2-select"') + WHEN comp->>'url' LIKE '%/v2-date' AND NOT (comp->'overrides' ? 'type') + THEN jsonb_set(comp, '{overrides,type}', '"v2-date"') + WHEN comp->>'url' LIKE '%/v2-textarea' AND NOT (comp->'overrides' ? 'type') + THEN jsonb_set(comp, '{overrides,type}', '"v2-textarea"') + ELSE comp + END + ) + FROM jsonb_array_elements(layout_data->'components') comp + ) +), +updated_at = NOW() +WHERE EXISTS ( + SELECT 1 FROM jsonb_array_elements(layout_data->'components') c + WHERE (c->>'url' LIKE '%/v2-input' OR c->>'url' LIKE '%/v2-select' + OR c->>'url' LIKE '%/v2-date' OR c->>'url' LIKE '%/v2-textarea') + AND NOT (c->'overrides' ? 'type') +); +``` + +### 2026-02-04 일괄 수정 실행 결과 + +| 항목 | 수량 | +|------|------| +| 수정된 화면 | 397개 | +| 수정된 컴포넌트 | 2,455개 | +| v2-input | 1,983개 | +| v2-select | 336개 | +| v2-date | 136개 | --- @@ -298,6 +418,10 @@ detail_settings, company_code | 날짜 | 메뉴 | 담당 | 상태 | 비고 | |------|------|------|------|------| | 2026-02-03 | 품질관리 | DDD1542 | 분석 완료 | 마이그레이션 대기 | +| 2026-02-03 | 물류관리 (운송업체) | 이전 신하 | ❌ 실패 | component 연결 오류 | +| 2026-02-03 | 문서 학습 | DDD1542 | ✅ 완료 | 핵심 4개 문서 정독, 학습노트 작성 | +| **2026-02-04** | **overrides.type 원인 분석** | **AI** | **✅ 완료** | **핵심 원인 발견: overrides.type 누락** | +| **2026-02-04** | **전체 입력폼 일괄 수정** | **AI** | **✅ 완료** | **397개 화면, 2,455개 컴포넌트 수정** | | | 물류관리 | - | 미시작 | | | | 생산관리 | - | 미시작 | | | | 영업관리 | - | 미시작 | | @@ -323,3 +447,7 @@ detail_settings, company_code | 날짜 | 작성자 | 내용 | |------|--------|------| | 2026-02-03 | DDD1542 | 초안 작성 | +| 2026-02-03 | DDD1542 | 컴포넌트-컬럼 연결 주의사항 추가 (이전 실패 원인) | +| 2026-02-03 | DDD1542 | 개인 학습노트 작성 (V2_마이그레이션_학습노트_DDD1542.md) | +| **2026-02-04** | **AI** | **핵심 원인 발견: overrides.type 필드 누락 문제** | +| **2026-02-04** | **AI** | **일괄 수정 SQL 추가 및 397개 화면 수정 완료** | diff --git a/frontend/components/screen/panels/V2PropertiesPanel.tsx b/frontend/components/screen/panels/V2PropertiesPanel.tsx index be0d265b..a843d710 100644 --- a/frontend/components/screen/panels/V2PropertiesPanel.tsx +++ b/frontend/components/screen/panels/V2PropertiesPanel.tsx @@ -822,8 +822,12 @@ export const V2PropertiesPanel: React.FC = ({
handleUpdate("style.labelText", e.target.value)} + value={selectedComponent.style?.labelText !== undefined ? selectedComponent.style.labelText : (selectedComponent.label || "")} + onChange={(e) => { + handleUpdate("style.labelText", e.target.value); + handleUpdate("label", e.target.value); // label도 함께 업데이트 + }} + placeholder="라벨을 입력하세요 (비우면 라벨 없음)" className="h-6 w-full px-2 py-0 text-xs" />
@@ -868,9 +872,9 @@ export const V2PropertiesPanel: React.FC = ({ )} - {/* 옵션 */} + {/* 옵션 - 입력 필드에서는 항상 표시, 기타 컴포넌트는 속성이 정의된 경우만 표시 */}
- {widget.required !== undefined && ( + {(isInputField || widget.required !== undefined) && (
= ({
)} - {widget.readonly !== undefined && ( + {(isInputField || widget.readonly !== undefined) && (
= ({
)} - {/* 숨김 옵션 */} + {/* 숨김 옵션 - 모든 컴포넌트에서 표시 */}
( className="flex flex-col" style={{ width: componentWidth, - height: componentHeight, + // 🔧 높이는 컨테이너가 아닌 입력 필드에만 적용 (라벨 높이는 별도) }} > {showLabel && ( @@ -335,7 +335,12 @@ export const V2Biz = forwardRef( {label} )} -
+
{renderBiz()}
diff --git a/frontend/components/v2/V2Date.tsx b/frontend/components/v2/V2Date.tsx index c825b1bf..f3102d5a 100644 --- a/frontend/components/v2/V2Date.tsx +++ b/frontend/components/v2/V2Date.tsx @@ -460,7 +460,7 @@ export const V2Date = forwardRef((props, ref) => { className="flex flex-col" style={{ width: componentWidth, - height: componentHeight, + // 🔧 높이는 컨테이너가 아닌 입력 필드에만 적용 (라벨 높이는 별도) }} > {showLabel && ( @@ -478,7 +478,14 @@ export const V2Date = forwardRef((props, ref) => { {required && *} )} -
{renderDatePicker()}
+
+ {renderDatePicker()} +
); }); diff --git a/frontend/components/v2/V2Hierarchy.tsx b/frontend/components/v2/V2Hierarchy.tsx index 17a3e141..23e4fd85 100644 --- a/frontend/components/v2/V2Hierarchy.tsx +++ b/frontend/components/v2/V2Hierarchy.tsx @@ -469,7 +469,7 @@ export const V2Hierarchy = forwardRef( className="flex flex-col" style={{ width: componentWidth, - height: componentHeight, + // 🔧 높이는 컨테이너가 아닌 입력 필드에만 적용 (라벨 높이는 별도) }} > {showLabel && ( @@ -487,7 +487,12 @@ export const V2Hierarchy = forwardRef( {required && *} )} -
+
{renderHierarchy()}
diff --git a/frontend/components/v2/V2Input.tsx b/frontend/components/v2/V2Input.tsx index edaefffb..10be3bf2 100644 --- a/frontend/components/v2/V2Input.tsx +++ b/frontend/components/v2/V2Input.tsx @@ -799,7 +799,7 @@ export const V2Input = forwardRef((props, ref) => className="flex flex-col" style={{ width: componentWidth, - height: componentHeight, + // 🔧 높이는 컨테이너가 아닌 입력 필드에만 적용 (라벨 높이는 별도) }} > {showLabel && ( @@ -817,7 +817,14 @@ export const V2Input = forwardRef((props, ref) => {required && *} )} -
{renderInput()}
+
+ {renderInput()} +
); }); diff --git a/frontend/components/v2/V2Media.tsx b/frontend/components/v2/V2Media.tsx index f7359962..c457b3f7 100644 --- a/frontend/components/v2/V2Media.tsx +++ b/frontend/components/v2/V2Media.tsx @@ -540,10 +540,10 @@ export const V2Media = forwardRef(
{showLabel && ( @@ -561,7 +561,12 @@ export const V2Media = forwardRef( {required && *} )} -
+
{renderMedia()}
diff --git a/frontend/components/v2/V2Select.tsx b/frontend/components/v2/V2Select.tsx index 872945f9..1aeac80d 100644 --- a/frontend/components/v2/V2Select.tsx +++ b/frontend/components/v2/V2Select.tsx @@ -751,7 +751,7 @@ export const V2Select = forwardRef( className="flex flex-col" style={{ width: componentWidth, - height: componentHeight, + // 🔧 높이는 컨테이너가 아닌 입력 필드에만 적용 (라벨 높이는 별도) }} > {showLabel && ( @@ -769,7 +769,12 @@ export const V2Select = forwardRef( {required && *} )} -
+
{renderSelect()}
diff --git a/frontend/lib/utils/layoutV2Converter.ts b/frontend/lib/utils/layoutV2Converter.ts index fa7606b6..32360a73 100644 --- a/frontend/lib/utils/layoutV2Converter.ts +++ b/frontend/lib/utils/layoutV2Converter.ts @@ -183,13 +183,15 @@ export function convertV2ToLegacy(v2Layout: LayoutV2 | null): LegacyLayoutData | label: overrides.label || mergedConfig.label || "", // 라벨이 없으면 빈 문자열 required: overrides.required, readonly: overrides.readonly, + hidden: overrides.hidden, // 🆕 숨김 설정 복원 codeCategory: overrides.codeCategory, inputType: overrides.inputType, webType: overrides.webType, // 🆕 autoFill 설정 복원 (자동 입력 기능) autoFill: overrides.autoFill, + // 🆕 style 설정 복원 (라벨 텍스트, 라벨 스타일 등) + style: overrides.style || {}, // 기존 구조 호환을 위한 추가 필드 - style: {}, parentId: null, gridColumns: 12, gridRowIndex: 0, @@ -231,14 +233,18 @@ export function convertLegacyToV2(legacyLayout: LegacyLayoutData): LayoutV2 { const topLevelProps: Record = {}; if (comp.tableName) topLevelProps.tableName = comp.tableName; if (comp.columnName) topLevelProps.columnName = comp.columnName; - if (comp.label) topLevelProps.label = comp.label; + // 🔧 label은 빈 문자열도 저장 (라벨 삭제 지원) + if (comp.label !== undefined) topLevelProps.label = comp.label; if (comp.required !== undefined) topLevelProps.required = comp.required; if (comp.readonly !== undefined) topLevelProps.readonly = comp.readonly; + if (comp.hidden !== undefined) topLevelProps.hidden = comp.hidden; // 🆕 숨김 설정 저장 if (comp.codeCategory) topLevelProps.codeCategory = comp.codeCategory; if (comp.inputType) topLevelProps.inputType = comp.inputType; if (comp.webType) topLevelProps.webType = comp.webType; // 🆕 autoFill 설정 저장 (자동 입력 기능) if (comp.autoFill) topLevelProps.autoFill = comp.autoFill; + // 🆕 style 설정 저장 (라벨 텍스트, 라벨 스타일 등) + if (comp.style && Object.keys(comp.style).length > 0) topLevelProps.style = comp.style; // 현재 설정에서 차이값만 추출 const fullConfig = comp.componentConfig || {};