From 95bef976a5b1bf8b210672fd3aed5036f0d9c062 Mon Sep 17 00:00:00 2001 From: kjs Date: Wed, 28 Jan 2026 17:36:19 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20=EB=8B=A4=EC=96=91=ED=95=9C=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=20=EB=B0=8F=20=EA=B0=80=EC=9D=B4=EB=93=9C=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 여러 문서의 내용을 업데이트하여 최신 정보를 반영하였습니다. - 컴포넌트 개발 가이드와 관련된 문서의 목차를 재구성하고, V2 및 Zod 레이아웃 시스템에 대한 내용을 추가하였습니다. - 화면 컴포넌트 개발 가이드를 개선하여 핵심 원칙과 패턴을 명확히 설명하였습니다. - 불필요한 문서 및 가이드를 삭제하고, 통합된 가이드를 통해 개발자들이 쉽게 참고할 수 있도록 하였습니다. --- .cursor/rules/component-development-guide.mdc | 62 +- PLAN.MD | 20 +- backend-node/src/app.ts | 2 +- .../src/controllers/adminController.ts | 2 +- .../src/controllers/entitySearchController.ts | 2 +- backend-node/src/routes/entitySearchRoutes.ts | 2 +- .../services/enhancedDynamicFormService.ts | 2 +- .../src/services/numberingRuleService.ts | 2 +- .../src/services/screenManagementService.ts | 16 +- .../src/services/tableManagementService.ts | 4 +- backend-node/src/types/screen.ts | 2 +- .../{unified-web-types.ts => v2-web-types.ts} | 2 +- backend-node/src/utils/componentDefaults.ts | 10 +- .../CATEGORY_TREE_CONTROLLER_ANALYSIS.md | 2 +- .../COLUMN_LABELS_MIGRATION_COMPLETE.md | 0 .../COMPONENT_JSON_MANAGEMENT_ANALYSIS.md | 0 .../COMPONENT_LAYOUT_V2_ARCHITECTURE.md | 2 +- .../COMPONENT_MANAGEMENT_FINAL_DESIGN.md | 0 ...MPONENT_MANAGEMENT_REFACTORING_PROPOSAL.md | 0 .../{ => DDD1542}/COMPONENT_MIGRATION_PLAN.md | 4 +- .../COMPONENT_URL_SYSTEM_IMPLEMENTATION.md | 0 ...COMPONENT_URL_ZOD_ARCHITECTURE_ANALYSIS.md | 0 docs/{ => DDD1542}/DB_CLEANUP_LOG_20260120.md | 0 .../{ => DDD1542}/DB_INEFFICIENCY_ANALYSIS.md | 0 docs/{ => DDD1542}/DB_STRUCTURE_DIAGRAM.md | 0 .../화면관계_시각화_개선_보고서.md | 0 .../화면설정모달_개선_완료_보고서.md | 0 .../dohyeons/DEPLOYMENT_GUIDE_KPSLP.md | 0 .../dohyeons/PROJECT_STATUS_2025_11_20.md | 0 .../report-grid-system-implementation-plan.md | 0 .../리포트_관리_시스템_구현_완료_기능.md | 0 .../리포트_관리_시스템_구현_진행상황.md | 0 .../{ => dohyeons}/리포트_관리_시스템_설계.md | 0 .../리포트_문서번호_채번_시스템_설계.md | 0 .../리포트_페이지_관리_시스템_설계.md | 0 .../dohyeons/테이블_변경_이력_로그_시스템_구현_계획서.md | 0 docs/etc/v2-components-implementation.md | 192 +++++++ DOCKER.md => docs/hyeonsu/DOCKER.md | 0 .../external-call-implementation-plan.md | 0 .../external-connection-management-plan.md | 0 .../공통코드_관리_시스템_설계.md | 0 docs/{ => hyeonsu}/조건부_연결_구현_계획.md | 0 .../화면간_데이터_관계_설정_시스템_설계.md | 0 docs/{ => kjs}/ADMIN_STYLE_GUIDE_EXAMPLE.md | 0 .../COLUMN_LABELS_MIGRATION_ANALYSIS.md | 2 +- .../kjs/DETAILED_FILE_MIGRATION_PLAN.md | 0 docs/{ => kjs}/Database_Schema_Collection.md | 0 .../kjs/Entity_조인_기능_개발계획서.md | 0 .../{ => kjs}/FINAL_GRID_MIGRATION_ROADMAP.md | 0 docs/{ => kjs}/FLOW_DATA_STRUCTURE_GUIDE.md | 0 .../FLOW_EXTERNAL_INTEGRATION_PLAN.md | 0 .../{ => kjs}/FLOW_HYBRID_MODE_USAGE_GUIDE.md | 0 docs/{ => kjs}/FLOW_MANAGEMENT_UI_DESIGN.md | 0 docs/{ => kjs}/GRID_SYSTEM_REDESIGN_PLAN.md | 0 docs/{ => kjs}/NodeJS_Refactoring_Rules.md | 0 .../PHASE1_FLOW_IMPLEMENTATION_SUMMARY.md | 0 PLAN_RENEWAL.md => docs/kjs/PLAN_RENEWAL.md | 170 +++--- .../kjs/README-WINDOWS.md | 0 docs/{ => kjs}/TODO.md | 0 .../V2_COMPONENT_COUPLING_ANALYSIS.md | 126 ++--- docs/{ => kjs}/V2_COMPONENT_GUIDE.md | 46 +- .../{ => kjs}/WIDTH_REMOVAL_MIGRATION_PLAN.md | 0 .../input-type-detail-type-system.md | 0 .../node-action-target-selection-plan.md | 0 .../phase0-component-usage-analysis.md | 78 +-- docs/{ => kjs}/phase0-migration-strategy.md | 84 +-- .../screen-management-dynamic-system-plan.md | 0 .../shadcn-ui-레이아웃-패턴-분석-보고서.md | 2 +- docs/{ => kjs}/shadcn-ui-완전가이드.md | 0 .../shadcn-ui-적용-상태-분석-보고서.md | 0 docs/{ => kjs}/권한_그룹_관리_상세_가이드.md | 0 .../{ => kjs}/권한_그룹_메뉴_필터링_가이드.md | 0 docs/{ => kjs}/권한_그룹_시스템_설계.md | 0 .../권한_시스템_마이그레이션_완료.md | 0 docs/{ => kjs}/권한_체계_가이드.md | 0 docs/{ => kjs}/그리드_컬럼수_옵션_통합.md | 0 docs/{ => kjs}/기간별_단가_설정_가이드.md | 0 docs/{ => kjs}/노드_구조_개선안.md | 0 .../노드_기반_제어_시스템_개선_계획.md | 0 docs/{ => kjs}/노드_시스템_버튼_통합_분석.md | 0 docs/{ => kjs}/노드_실행_엔진_설계.md | 0 docs/{ => kjs}/노드_연결_규칙_설계.md | 0 .../kjs/노드_플로우_데이터소스_설정_가이드.md | 0 docs/{ => kjs}/노드플로우_개선사항.md | 0 .../다국어_관리_시스템_개선_계획서.md | 0 docs/{ => kjs}/다국어_시스템_가이드.md | 0 .../kjs/데이터소스_일관성_개선_완료.md | 0 .../kjs/동적_테이블_접근_시스템_개선_완료.md | 0 docs/{ => kjs}/레벨기반_연쇄드롭다운_설계.md | 0 .../리소스_기반_권한_시스템_가이드.md | 0 .../멀티테넌시_구현_현황_분석_보고서.md | 0 .../{ => kjs}/메뉴_기반_권한_시스템_가이드.md | 0 docs/{ => kjs}/메뉴_복사_기능_구현_계획서.md | 0 .../{ => kjs}/메뉴_회사별_필터링_개선_완료.md | 0 .../{ => kjs}/메뉴_회사별_필터링_구현_완료.md | 0 docs/{ => kjs}/메일발송_기능_사용_가이드.md | 0 .../kjs/버튼_제어관리_기능_통합_계획서.md | 0 .../kjs/버튼_제어관리_기능_통합_잠재적_문제점_분석.md | 0 .../kjs/버튼_제어관리_성능_최적화_전략.md | 0 .../kjs/선택항목_상세입력_완전_자동화_가이드.md | 0 .../kjs/선택항목_상세입력_컴포넌트_완성_가이드.md | 0 .../kjs/수주등록_화면_개발_계획서.md | 0 .../kjs/스크롤_문제_해결_가이드.md | 0 docs/{ => kjs}/시스템_강점_어필_문서.md | 0 시연_시나리오.md => docs/kjs/시연_시나리오.md | 0 docs/{ => kjs}/엑셀_다운로드_개선_계획.md | 0 docs/{ => kjs}/엑셀_다운로드_개선_계획_v2.md | 0 docs/{ => kjs}/영업_계약_수정.md | 0 docs/{ => kjs}/외부_DB_연결_풀_가이드.md | 0 .../kjs/외부호출_데이터_매핑_시스템_설계서.md | 0 .../kjs/제어관리_데이터소스_확장_가이드.md | 0 .../kjs/제어관리_시스템_개선_계획서.md | 0 .../kjs/제어관리_외부커넥션_통합_개선_계획서.md | 0 .../kjs/제어관리_외부커넥션_통합_기능_가이드.md | 0 .../kjs/제어관리_외부호출_REST_API_구현_계획서.md | 0 .../kjs/제어관리_트랜잭션_및_조건부실행_개선방안.md | 0 .../즉시저장_버튼_액션_구현_계획서.md | 0 docs/{ => kjs}/집계위젯_개발진행상황.md | 6 +- .../채번규칙_멀티테넌시_버그_수정_완료.md | 0 docs/{ => kjs}/채번규칙_컴포넌트_구현_완료.md | 0 .../kjs/채번규칙_테이블기반_자동감지_구현_완료.md | 0 .../kjs/채번규칙_테이블기반_필터링_구현_계획서.md | 0 .../kjs/채번규칙_테이블기반_필터링_구현_완료_보고서.md | 0 .../kjs/카테고리_관리_컴포넌트_구현_계획서.md | 0 .../카테고리_멀티테넌시_버그_분석.md | 0 .../카테고리_멀티테넌시_버그_수정_완료.md | 0 .../kjs/카테고리_메뉴기반_전환_계획서.md | 0 ...테고리_메뉴별_컬럼_분리_구현_완료_보고서.md | 0 .../카테고리_메뉴별_컬럼_분리_전략.md | 0 .../카테고리_메뉴스코프_개선_계획서.md | 0 .../kjs/카테고리_시스템_구현_계획서.md | 0 .../kjs/카테고리_시스템_재구현_계획서.md | 0 .../kjs/카테고리_시스템_재구현_완료_보고서.md | 0 .../kjs/카테고리_시스템_최종_완료_보고서.md | 4 +- .../kjs/카테고리_채번_메뉴스코프_전환_통합_계획서.md | 0 .../kjs/카테고리_컴포넌트_DB_호환성_분석.md | 0 .../kjs/카테고리_컴포넌트_구현_완료.md | 0 .../kjs/카테고리_타입_구현_완료.md | 0 .../컴포넌트_기본_너비_설정_가이드.md | 0 docs/{ => kjs}/컴포넌트_분석_및_통합_계획.md | 74 +-- .../kjs/코드_채번_규칙_컴포넌트_구현_계획서.md | 2 +- .../테이블_검색필터_컴포넌트_분리_계획서.md | 0 .../kjs/테이블_그룹핑_기능_구현_계획서.md | 0 .../kjs/테이블_동적_생성_기능_개발_계획서.md | 0 .../kjs/테이블_복제_기능_구현_계획서.md | 0 ...블_컬럼_타입_멀티테넌시_구조적_문제_분석.md | 0 .../테이블_컬럼_타입_멀티테넌시_수정_완료.md | 0 .../kjs/테이블_타입_관리_개선_계획서.md | 2 +- .../kjs/테이블_타입_관리_개선_사용_가이드.md | 0 .../테이블_타입관리_성능최적화_결과.md | 0 .../테이블_패널_컴포넌트_기본_너비_설정.md | 0 .../kjs/플로우_위젯_컬럼_표시_설정_구현_완료.md | 0 .../kjs/화면_임베딩_및_데이터_전달_시스템_구현_계획서.md | 0 .../kjs/화면_임베딩_시스템_Phase1-4_구현_완료.md | 0 .../kjs/화면_임베딩_시스템_충돌_분석_보고서.md | 0 .../kjs/화면관리_검증_시스템_사용_가이드.md | 8 +- .../kjs/화면관리_및_테이블관리_개선사항_목록.md | 0 docs/{ => kjs}/화면관리_시스템_설계.md | 0 .../kjs/화면관리_타입_문제_분석_및_해결방안.md | 8 +- .../AI_비용_및_하드웨어_요구사항_분석.md | 0 .../AI_어시스턴트_사용가이드.md | 0 .../GroupBy_컴포넌트_적용완료.md | 0 .../GroupBy_컴포넌트화_완료.md | 0 docs/{ => leeheejin}/OCR_문자인식_통합완료.md | 0 .../PanelResize_컴포넌트_적용완료.md | 0 .../TableActionBar_컴포넌트_완성.md | 0 .../leeheejin/UI_개선사항_문서.md | 0 .../shadcn-ui_디자인_시스템_가이드.md | 0 ...hadcn-ui_디자인_시스템_적용_완료_보고서.md | 0 docs/{ => leeheejin}/공정관리_방법론.md | 0 .../그룹화_옵션_저장_가이드.md | 0 .../기상청_API키_발급가이드.md | 0 .../날씨위젯_API키_설정가이드.md | 0 .../리스크알림_API키_발급가이드.md | 0 .../leeheejin/메일관리_기능_리스트.md | 0 .../leeheejin/메일관리_시스템_구현_계획서.md | 0 .../leeheejin/메일시스템_검증_보고서.md | 0 .../생산계획_수량조정_분할_기능_안내.md | 0 .../외부_DB_연결_관리_기능_가이드.md | 0 .../외부_DB_연결_관리_기능_개선_계획.md | 0 .../leeheejin/외부_데이터베이스_제어관리_시스템_계획서.md | 0 docs/{ => leeheejin}/위젯_승격_완료_보고서.md | 0 .../{ => leeheejin}/창고관리_개발자_가이드.md | 0 .../창고관리_모바일_사용가이드.md | 0 .../창고관리_시스템_완성_보고서.md | 0 docs/{ => leeheejin}/컬럼_매핑_사용_가이드.md | 0 .../컴포넌트화_최종_완료_보고서.md | 0 .../테스트_위젯_누락_기능_분석_보고서.md | 0 docs/unified-components-implementation.md | 192 ------- .../admin/screenMng/screenMngList/page.tsx | 16 +- .../app/(main)/admin/validation-demo/page.tsx | 2 +- frontend/components/admin/UserToolbar.tsx | 4 +- .../widget-sections/ListWidgetSection.tsx | 4 +- ...iedColumnEditor.tsx => V2ColumnEditor.tsx} | 4 +- .../dataflow/node-editor/FlowEditor.tsx | 4 +- frontend/components/screen/EditModal.tsx | 4 +- .../screen/InteractiveScreenViewer.tsx | 6 +- .../screen/InteractiveScreenViewerDynamic.tsx | 4 +- .../components/screen/NodeSettingModal.tsx | 34 +- frontend/components/screen/SaveModal.tsx | 4 +- frontend/components/screen/ScreenDesigner.tsx | 66 +-- .../config-panels/ButtonConfigPanel.tsx | 2 +- .../config-panels/DataFilterConfigPanel.tsx | 4 +- .../screen/panels/ComponentsPanel.tsx | 60 +- .../screen/panels/DetailSettingsPanel.tsx | 4 +- ...pertiesPanel.tsx => V2PropertiesPanel.tsx} | 84 +-- ...ftUnifiedToolbar.tsx => LeftV2Toolbar.tsx} | 8 +- .../unified/UnifiedComponentRenderer.tsx | 111 ---- .../components/unified/config-panels/index.ts | 15 - frontend/components/unified/index.ts | 125 ---- .../ConditionalConfigPanel.tsx | 2 +- .../{unified => v2}/DynamicConfigPanel.tsx | 20 +- .../{unified/UnifiedBiz.tsx => v2/V2Biz.tsx} | 12 +- .../components/v2/V2ComponentRenderer.tsx | 111 ++++ .../V2ComponentsDemo.tsx} | 246 ++++---- .../UnifiedDate.tsx => v2/V2Date.tsx} | 12 +- .../V2FormContext.tsx} | 52 +- .../UnifiedGroup.tsx => v2/V2Group.tsx} | 12 +- .../V2Hierarchy.tsx} | 12 +- .../UnifiedInput.tsx => v2/V2Input.tsx} | 18 +- .../UnifiedLayout.tsx => v2/V2Layout.tsx} | 12 +- .../UnifiedList.tsx => v2/V2List.tsx} | 14 +- .../UnifiedMedia.tsx => v2/V2Media.tsx} | 12 +- .../UnifiedRepeater.tsx => v2/V2Repeater.tsx} | 60 +- .../UnifiedSelect.tsx => v2/V2Select.tsx} | 18 +- .../config-panels/V2BizConfigPanel.tsx} | 10 +- .../config-panels/V2DateConfigPanel.tsx} | 10 +- .../config-panels/V2GroupConfigPanel.tsx} | 10 +- .../config-panels/V2HierarchyConfigPanel.tsx} | 10 +- .../config-panels/V2InputConfigPanel.tsx} | 10 +- .../config-panels/V2LayoutConfigPanel.tsx} | 10 +- .../config-panels/V2ListConfigPanel.tsx} | 24 +- .../config-panels/V2MediaConfigPanel.tsx} | 10 +- .../config-panels/V2RepeaterConfigPanel.tsx} | 22 +- .../config-panels/V2SelectConfigPanel.tsx} | 10 +- frontend/components/v2/config-panels/index.ts | 15 + frontend/components/v2/index.ts | 125 ++++ .../registerV2Components.ts} | 144 ++--- frontend/hooks/useFormCompatibility.ts | 84 +-- frontend/hooks/useScreenDataTransfer.ts | 2 +- frontend/lib/api/user.ts | 2 +- .../lib/registry/DynamicComponentRenderer.tsx | 142 ++--- frontend/lib/registry/components/index.ts | 4 +- .../components/repeat-container/types.ts | 2 +- .../components/unified-repeater/index.ts | 101 ---- .../AggregationWidgetConfigPanel.tsx | 18 +- .../lib/registry/components/v2-date/index.ts | 77 +++ .../lib/registry/components/v2-input/index.ts | 78 +++ .../components/v2-repeat-container/types.ts | 2 +- .../V2RepeaterRenderer.tsx} | 26 +- .../index.ts | 23 +- .../registry/components/v2-select/index.ts | 84 +++ .../UnifiedRepeaterRenderer.tsx | 115 ---- frontend/lib/registry/init.ts | 8 +- frontend/lib/schemas/componentConfig.ts | 98 ++-- frontend/lib/utils/buttonActions.ts | 40 +- frontend/lib/utils/dbTypeMapping.ts | 4 +- frontend/lib/utils/formValidation.ts | 2 +- .../lib/utils/getComponentConfigPanel.tsx | 50 +- .../lib/utils/improvedButtonActionExecutor.ts | 2 +- frontend/lib/utils/webTypeMapping.ts | 162 +++--- .../test-scenarios/api-integration-tests.ts | 18 +- frontend/test-scenarios/type-safety-tests.ts | 14 +- frontend/types/component.ts | 4 +- frontend/types/control-management.ts | 2 +- frontend/types/index.ts | 20 +- frontend/types/input-type-mapping.ts | 2 +- frontend/types/screen-management.ts | 2 +- frontend/types/screen.ts | 6 +- frontend/types/table-management.ts | 60 +- frontend/types/unified-components.ts | 512 ----------------- frontend/types/v2-components.ts | 532 ++++++++++++++++++ .../types/{unified-core.ts => v2-core.ts} | 0 .../types/{unified-form.ts => v2-form.ts} | 10 +- .../{unified-repeater.ts => v2-repeater.ts} | 11 +- .../{unified-web-types.ts => v2-web-types.ts} | 0 276 files changed, 2544 insertions(+), 2495 deletions(-) rename backend-node/src/types/{unified-web-types.ts => v2-web-types.ts} (99%) rename docs/{ => DDD1542}/CATEGORY_TREE_CONTROLLER_ANALYSIS.md (99%) rename docs/{ => DDD1542}/COLUMN_LABELS_MIGRATION_COMPLETE.md (100%) rename docs/{ => DDD1542}/COMPONENT_JSON_MANAGEMENT_ANALYSIS.md (100%) rename docs/{ => DDD1542}/COMPONENT_LAYOUT_V2_ARCHITECTURE.md (99%) rename docs/{ => DDD1542}/COMPONENT_MANAGEMENT_FINAL_DESIGN.md (100%) rename docs/{ => DDD1542}/COMPONENT_MANAGEMENT_REFACTORING_PROPOSAL.md (100%) rename docs/{ => DDD1542}/COMPONENT_MIGRATION_PLAN.md (99%) rename docs/{ => DDD1542}/COMPONENT_URL_SYSTEM_IMPLEMENTATION.md (100%) rename docs/{ => DDD1542}/COMPONENT_URL_ZOD_ARCHITECTURE_ANALYSIS.md (100%) rename docs/{ => DDD1542}/DB_CLEANUP_LOG_20260120.md (100%) rename docs/{ => DDD1542}/DB_INEFFICIENCY_ANALYSIS.md (100%) rename docs/{ => DDD1542}/DB_STRUCTURE_DIAGRAM.md (100%) rename docs/{ => DDD1542}/화면관계_시각화_개선_보고서.md (100%) rename docs/{ => DDD1542}/화면설정모달_개선_완료_보고서.md (100%) rename DEPLOYMENT_GUIDE_KPSLP.md => docs/dohyeons/DEPLOYMENT_GUIDE_KPSLP.md (100%) rename PROJECT_STATUS_2025_11_20.md => docs/dohyeons/PROJECT_STATUS_2025_11_20.md (100%) rename docs/{ => dohyeons}/report-grid-system-implementation-plan.md (100%) rename docs/{ => dohyeons}/리포트_관리_시스템_구현_완료_기능.md (100%) rename docs/{ => dohyeons}/리포트_관리_시스템_구현_진행상황.md (100%) rename docs/{ => dohyeons}/리포트_관리_시스템_설계.md (100%) rename docs/{ => dohyeons}/리포트_문서번호_채번_시스템_설계.md (100%) rename docs/{ => dohyeons}/리포트_페이지_관리_시스템_설계.md (100%) rename 테이블_변경_이력_로그_시스템_구현_계획서.md => docs/dohyeons/테이블_변경_이력_로그_시스템_구현_계획서.md (100%) create mode 100644 docs/etc/v2-components-implementation.md rename DOCKER.md => docs/hyeonsu/DOCKER.md (100%) rename docs/{ => hyeonsu}/external-call-implementation-plan.md (100%) rename docs/{ => hyeonsu}/external-connection-management-plan.md (100%) rename docs/{ => hyeonsu}/공통코드_관리_시스템_설계.md (100%) rename docs/{ => hyeonsu}/조건부_연결_구현_계획.md (100%) rename docs/{ => hyeonsu}/화면간_데이터_관계_설정_시스템_설계.md (100%) rename docs/{ => kjs}/ADMIN_STYLE_GUIDE_EXAMPLE.md (100%) rename docs/{ => kjs}/COLUMN_LABELS_MIGRATION_ANALYSIS.md (99%) rename DETAILED_FILE_MIGRATION_PLAN.md => docs/kjs/DETAILED_FILE_MIGRATION_PLAN.md (100%) rename docs/{ => kjs}/Database_Schema_Collection.md (100%) rename Entity_조인_기능_개발계획서.md => docs/kjs/Entity_조인_기능_개발계획서.md (100%) rename docs/{ => kjs}/FINAL_GRID_MIGRATION_ROADMAP.md (100%) rename docs/{ => kjs}/FLOW_DATA_STRUCTURE_GUIDE.md (100%) rename docs/{ => kjs}/FLOW_EXTERNAL_INTEGRATION_PLAN.md (100%) rename docs/{ => kjs}/FLOW_HYBRID_MODE_USAGE_GUIDE.md (100%) rename docs/{ => kjs}/FLOW_MANAGEMENT_UI_DESIGN.md (100%) rename docs/{ => kjs}/GRID_SYSTEM_REDESIGN_PLAN.md (100%) rename docs/{ => kjs}/NodeJS_Refactoring_Rules.md (100%) rename docs/{ => kjs}/PHASE1_FLOW_IMPLEMENTATION_SUMMARY.md (100%) rename PLAN_RENEWAL.md => docs/kjs/PLAN_RENEWAL.md (76%) rename README-WINDOWS.md => docs/kjs/README-WINDOWS.md (100%) rename docs/{ => kjs}/TODO.md (100%) rename docs/{ => kjs}/V2_COMPONENT_COUPLING_ANALYSIS.md (85%) rename docs/{ => kjs}/V2_COMPONENT_GUIDE.md (92%) rename docs/{ => kjs}/WIDTH_REMOVAL_MIGRATION_PLAN.md (100%) rename docs/{ => kjs}/input-type-detail-type-system.md (100%) rename docs/{ => kjs}/node-action-target-selection-plan.md (100%) rename docs/{ => kjs}/phase0-component-usage-analysis.md (71%) rename docs/{ => kjs}/phase0-migration-strategy.md (80%) rename docs/{ => kjs}/screen-management-dynamic-system-plan.md (100%) rename docs/{ => kjs}/shadcn-ui-레이아웃-패턴-분석-보고서.md (99%) rename docs/{ => kjs}/shadcn-ui-완전가이드.md (100%) rename docs/{ => kjs}/shadcn-ui-적용-상태-분석-보고서.md (100%) rename docs/{ => kjs}/권한_그룹_관리_상세_가이드.md (100%) rename docs/{ => kjs}/권한_그룹_메뉴_필터링_가이드.md (100%) rename docs/{ => kjs}/권한_그룹_시스템_설계.md (100%) rename docs/{ => kjs}/권한_시스템_마이그레이션_완료.md (100%) rename docs/{ => kjs}/권한_체계_가이드.md (100%) rename docs/{ => kjs}/그리드_컬럼수_옵션_통합.md (100%) rename docs/{ => kjs}/기간별_단가_설정_가이드.md (100%) rename docs/{ => kjs}/노드_구조_개선안.md (100%) rename docs/{ => kjs}/노드_기반_제어_시스템_개선_계획.md (100%) rename docs/{ => kjs}/노드_시스템_버튼_통합_분석.md (100%) rename docs/{ => kjs}/노드_실행_엔진_설계.md (100%) rename docs/{ => kjs}/노드_연결_규칙_설계.md (100%) rename 노드_플로우_데이터소스_설정_가이드.md => docs/kjs/노드_플로우_데이터소스_설정_가이드.md (100%) rename docs/{ => kjs}/노드플로우_개선사항.md (100%) rename docs/{ => kjs}/다국어_관리_시스템_개선_계획서.md (100%) rename docs/{ => kjs}/다국어_시스템_가이드.md (100%) rename 데이터소스_일관성_개선_완료.md => docs/kjs/데이터소스_일관성_개선_완료.md (100%) rename 동적_테이블_접근_시스템_개선_완료.md => docs/kjs/동적_테이블_접근_시스템_개선_완료.md (100%) rename docs/{ => kjs}/레벨기반_연쇄드롭다운_설계.md (100%) rename docs/{ => kjs}/리소스_기반_권한_시스템_가이드.md (100%) rename docs/{ => kjs}/멀티테넌시_구현_현황_분석_보고서.md (100%) rename docs/{ => kjs}/메뉴_기반_권한_시스템_가이드.md (100%) rename docs/{ => kjs}/메뉴_복사_기능_구현_계획서.md (100%) rename docs/{ => kjs}/메뉴_회사별_필터링_개선_완료.md (100%) rename docs/{ => kjs}/메뉴_회사별_필터링_구현_완료.md (100%) rename docs/{ => kjs}/메일발송_기능_사용_가이드.md (100%) rename 버튼_제어관리_기능_통합_계획서.md => docs/kjs/버튼_제어관리_기능_통합_계획서.md (100%) rename 버튼_제어관리_기능_통합_잠재적_문제점_분석.md => docs/kjs/버튼_제어관리_기능_통합_잠재적_문제점_분석.md (100%) rename 버튼_제어관리_성능_최적화_전략.md => docs/kjs/버튼_제어관리_성능_최적화_전략.md (100%) rename 선택항목_상세입력_완전_자동화_가이드.md => docs/kjs/선택항목_상세입력_완전_자동화_가이드.md (100%) rename 선택항목_상세입력_컴포넌트_완성_가이드.md => docs/kjs/선택항목_상세입력_컴포넌트_완성_가이드.md (100%) rename 수주등록_화면_개발_계획서.md => docs/kjs/수주등록_화면_개발_계획서.md (100%) rename 스크롤_문제_해결_가이드.md => docs/kjs/스크롤_문제_해결_가이드.md (100%) rename docs/{ => kjs}/시스템_강점_어필_문서.md (100%) rename 시연_시나리오.md => docs/kjs/시연_시나리오.md (100%) rename docs/{ => kjs}/엑셀_다운로드_개선_계획.md (100%) rename docs/{ => kjs}/엑셀_다운로드_개선_계획_v2.md (100%) rename docs/{ => kjs}/영업_계약_수정.md (100%) rename docs/{ => kjs}/외부_DB_연결_풀_가이드.md (100%) rename 외부호출_데이터_매핑_시스템_설계서.md => docs/kjs/외부호출_데이터_매핑_시스템_설계서.md (100%) rename 제어관리_데이터소스_확장_가이드.md => docs/kjs/제어관리_데이터소스_확장_가이드.md (100%) rename 제어관리_시스템_개선_계획서.md => docs/kjs/제어관리_시스템_개선_계획서.md (100%) rename 제어관리_외부커넥션_통합_개선_계획서.md => docs/kjs/제어관리_외부커넥션_통합_개선_계획서.md (100%) rename 제어관리_외부커넥션_통합_기능_가이드.md => docs/kjs/제어관리_외부커넥션_통합_기능_가이드.md (100%) rename 제어관리_외부호출_REST_API_구현_계획서.md => docs/kjs/제어관리_외부호출_REST_API_구현_계획서.md (100%) rename 제어관리_트랜잭션_및_조건부실행_개선방안.md => docs/kjs/제어관리_트랜잭션_및_조건부실행_개선방안.md (100%) rename docs/{ => kjs}/즉시저장_버튼_액션_구현_계획서.md (100%) rename docs/{ => kjs}/집계위젯_개발진행상황.md (96%) rename docs/{ => kjs}/채번규칙_멀티테넌시_버그_수정_완료.md (100%) rename docs/{ => kjs}/채번규칙_컴포넌트_구현_완료.md (100%) rename 채번규칙_테이블기반_자동감지_구현_완료.md => docs/kjs/채번규칙_테이블기반_자동감지_구현_완료.md (100%) rename 채번규칙_테이블기반_필터링_구현_계획서.md => docs/kjs/채번규칙_테이블기반_필터링_구현_계획서.md (100%) rename 채번규칙_테이블기반_필터링_구현_완료_보고서.md => docs/kjs/채번규칙_테이블기반_필터링_구현_완료_보고서.md (100%) rename 카테고리_관리_컴포넌트_구현_계획서.md => docs/kjs/카테고리_관리_컴포넌트_구현_계획서.md (100%) rename docs/{ => kjs}/카테고리_멀티테넌시_버그_분석.md (100%) rename docs/{ => kjs}/카테고리_멀티테넌시_버그_수정_완료.md (100%) rename 카테고리_메뉴기반_전환_계획서.md => docs/kjs/카테고리_메뉴기반_전환_계획서.md (100%) rename docs/{ => kjs}/카테고리_메뉴별_컬럼_분리_구현_완료_보고서.md (100%) rename docs/{ => kjs}/카테고리_메뉴별_컬럼_분리_전략.md (100%) rename docs/{ => kjs}/카테고리_메뉴스코프_개선_계획서.md (100%) rename 카테고리_시스템_구현_계획서.md => docs/kjs/카테고리_시스템_구현_계획서.md (100%) rename 카테고리_시스템_재구현_계획서.md => docs/kjs/카테고리_시스템_재구현_계획서.md (100%) rename 카테고리_시스템_재구현_완료_보고서.md => docs/kjs/카테고리_시스템_재구현_완료_보고서.md (100%) rename 카테고리_시스템_최종_완료_보고서.md => docs/kjs/카테고리_시스템_최종_완료_보고서.md (99%) rename 카테고리_채번_메뉴스코프_전환_통합_계획서.md => docs/kjs/카테고리_채번_메뉴스코프_전환_통합_계획서.md (100%) rename 카테고리_컴포넌트_DB_호환성_분석.md => docs/kjs/카테고리_컴포넌트_DB_호환성_분석.md (100%) rename 카테고리_컴포넌트_구현_완료.md => docs/kjs/카테고리_컴포넌트_구현_완료.md (100%) rename 카테고리_타입_구현_완료.md => docs/kjs/카테고리_타입_구현_완료.md (100%) rename docs/{ => kjs}/컴포넌트_기본_너비_설정_가이드.md (100%) rename docs/{ => kjs}/컴포넌트_분석_및_통합_계획.md (78%) rename 코드_채번_규칙_컴포넌트_구현_계획서.md => docs/kjs/코드_채번_규칙_컴포넌트_구현_계획서.md (99%) rename docs/{ => kjs}/테이블_검색필터_컴포넌트_분리_계획서.md (100%) rename 테이블_그룹핑_기능_구현_계획서.md => docs/kjs/테이블_그룹핑_기능_구현_계획서.md (100%) rename 테이블_동적_생성_기능_개발_계획서.md => docs/kjs/테이블_동적_생성_기능_개발_계획서.md (100%) rename 테이블_복제_기능_구현_계획서.md => docs/kjs/테이블_복제_기능_구현_계획서.md (100%) rename docs/{ => kjs}/테이블_컬럼_타입_멀티테넌시_구조적_문제_분석.md (100%) rename docs/{ => kjs}/테이블_컬럼_타입_멀티테넌시_수정_완료.md (100%) rename 테이블_타입_관리_개선_계획서.md => docs/kjs/테이블_타입_관리_개선_계획서.md (99%) rename 테이블_타입_관리_개선_사용_가이드.md => docs/kjs/테이블_타입_관리_개선_사용_가이드.md (100%) rename docs/{ => kjs}/테이블_타입관리_성능최적화_결과.md (100%) rename docs/{ => kjs}/테이블_패널_컴포넌트_기본_너비_설정.md (100%) rename 플로우_위젯_컬럼_표시_설정_구현_완료.md => docs/kjs/플로우_위젯_컬럼_표시_설정_구현_완료.md (100%) rename 화면_임베딩_및_데이터_전달_시스템_구현_계획서.md => docs/kjs/화면_임베딩_및_데이터_전달_시스템_구현_계획서.md (100%) rename 화면_임베딩_시스템_Phase1-4_구현_완료.md => docs/kjs/화면_임베딩_시스템_Phase1-4_구현_완료.md (100%) rename 화면_임베딩_시스템_충돌_분석_보고서.md => docs/kjs/화면_임베딩_시스템_충돌_분석_보고서.md (100%) rename 화면관리_검증_시스템_사용_가이드.md => docs/kjs/화면관리_검증_시스템_사용_가이드.md (97%) rename 화면관리_및_테이블관리_개선사항_목록.md => docs/kjs/화면관리_및_테이블관리_개선사항_목록.md (100%) rename docs/{ => kjs}/화면관리_시스템_설계.md (100%) rename 화면관리_타입_문제_분석_및_해결방안.md => docs/kjs/화면관리_타입_문제_분석_및_해결방안.md (98%) rename docs/{ => leeheejin}/AI_비용_및_하드웨어_요구사항_분석.md (100%) rename docs/{ => leeheejin}/AI_어시스턴트_사용가이드.md (100%) rename docs/{ => leeheejin}/GroupBy_컴포넌트_적용완료.md (100%) rename docs/{ => leeheejin}/GroupBy_컴포넌트화_완료.md (100%) rename docs/{ => leeheejin}/OCR_문자인식_통합완료.md (100%) rename docs/{ => leeheejin}/PanelResize_컴포넌트_적용완료.md (100%) rename docs/{ => leeheejin}/TableActionBar_컴포넌트_완성.md (100%) rename UI_개선사항_문서.md => docs/leeheejin/UI_개선사항_문서.md (100%) rename docs/{ => leeheejin}/shadcn-ui_디자인_시스템_가이드.md (100%) rename docs/{ => leeheejin}/shadcn-ui_디자인_시스템_적용_완료_보고서.md (100%) rename docs/{ => leeheejin}/공정관리_방법론.md (100%) rename docs/{ => leeheejin}/그룹화_옵션_저장_가이드.md (100%) rename docs/{ => leeheejin}/기상청_API키_발급가이드.md (100%) rename docs/{ => leeheejin}/날씨위젯_API키_설정가이드.md (100%) rename docs/{ => leeheejin}/리스크알림_API키_발급가이드.md (100%) rename 메일관리_기능_리스트.md => docs/leeheejin/메일관리_기능_리스트.md (100%) rename 메일관리_시스템_구현_계획서.md => docs/leeheejin/메일관리_시스템_구현_계획서.md (100%) rename 메일시스템_검증_보고서.md => docs/leeheejin/메일시스템_검증_보고서.md (100%) rename docs/{ => leeheejin}/생산계획_수량조정_분할_기능_안내.md (100%) rename docs/{ => leeheejin}/외부_DB_연결_관리_기능_가이드.md (100%) rename docs/{ => leeheejin}/외부_DB_연결_관리_기능_개선_계획.md (100%) rename 외부_데이터베이스_제어관리_시스템_계획서.md => docs/leeheejin/외부_데이터베이스_제어관리_시스템_계획서.md (100%) rename docs/{ => leeheejin}/위젯_승격_완료_보고서.md (100%) rename docs/{ => leeheejin}/창고관리_개발자_가이드.md (100%) rename docs/{ => leeheejin}/창고관리_모바일_사용가이드.md (100%) rename docs/{ => leeheejin}/창고관리_시스템_완성_보고서.md (100%) rename docs/{ => leeheejin}/컬럼_매핑_사용_가이드.md (100%) rename docs/{ => leeheejin}/컴포넌트화_최종_완료_보고서.md (100%) rename docs/{ => leeheejin}/테스트_위젯_누락_기능_분석_보고서.md (100%) delete mode 100644 docs/unified-components-implementation.md rename frontend/components/admin/dashboard/widgets/list-widget/{UnifiedColumnEditor.tsx => V2ColumnEditor.tsx} (98%) rename frontend/components/screen/panels/{UnifiedPropertiesPanel.tsx => V2PropertiesPanel.tsx} (95%) rename frontend/components/screen/toolbar/{LeftUnifiedToolbar.tsx => LeftV2Toolbar.tsx} (93%) delete mode 100644 frontend/components/unified/UnifiedComponentRenderer.tsx delete mode 100644 frontend/components/unified/config-panels/index.ts delete mode 100644 frontend/components/unified/index.ts rename frontend/components/{unified => v2}/ConditionalConfigPanel.tsx (99%) rename frontend/components/{unified => v2}/DynamicConfigPanel.tsx (96%) rename frontend/components/{unified/UnifiedBiz.tsx => v2/V2Biz.tsx} (97%) create mode 100644 frontend/components/v2/V2ComponentRenderer.tsx rename frontend/components/{unified/UnifiedComponentsDemo.tsx => v2/V2ComponentsDemo.tsx} (87%) rename frontend/components/{unified/UnifiedDate.tsx => v2/V2Date.tsx} (98%) rename frontend/components/{unified/UnifiedFormContext.tsx => v2/V2FormContext.tsx} (93%) rename frontend/components/{unified/UnifiedGroup.tsx => v2/V2Group.tsx} (97%) rename frontend/components/{unified/UnifiedHierarchy.tsx => v2/V2Hierarchy.tsx} (97%) rename frontend/components/{unified/UnifiedInput.tsx => v2/V2Input.tsx} (98%) rename frontend/components/{unified/UnifiedLayout.tsx => v2/V2Layout.tsx} (97%) rename frontend/components/{unified/UnifiedList.tsx => v2/V2List.tsx} (93%) rename frontend/components/{unified/UnifiedMedia.tsx => v2/V2Media.tsx} (98%) rename frontend/components/{unified/UnifiedRepeater.tsx => v2/V2Repeater.tsx} (95%) rename frontend/components/{unified/UnifiedSelect.tsx => v2/V2Select.tsx} (97%) rename frontend/components/{unified/config-panels/UnifiedBizConfigPanel.tsx => v2/config-panels/V2BizConfigPanel.tsx} (98%) rename frontend/components/{unified/config-panels/UnifiedDateConfigPanel.tsx => v2/config-panels/V2DateConfigPanel.tsx} (95%) rename frontend/components/{unified/config-panels/UnifiedGroupConfigPanel.tsx => v2/config-panels/V2GroupConfigPanel.tsx} (96%) rename frontend/components/{unified/config-panels/UnifiedHierarchyConfigPanel.tsx => v2/config-panels/V2HierarchyConfigPanel.tsx} (98%) rename frontend/components/{unified/config-panels/UnifiedInputConfigPanel.tsx => v2/config-panels/V2InputConfigPanel.tsx} (98%) rename frontend/components/{unified/config-panels/UnifiedLayoutConfigPanel.tsx => v2/config-panels/V2LayoutConfigPanel.tsx} (97%) rename frontend/components/{unified/config-panels/UnifiedListConfigPanel.tsx => v2/config-panels/V2ListConfigPanel.tsx} (86%) rename frontend/components/{unified/config-panels/UnifiedMediaConfigPanel.tsx => v2/config-panels/V2MediaConfigPanel.tsx} (96%) rename frontend/components/{unified/config-panels/UnifiedRepeaterConfigPanel.tsx => v2/config-panels/V2RepeaterConfigPanel.tsx} (99%) rename frontend/components/{unified/config-panels/UnifiedSelectConfigPanel.tsx => v2/config-panels/V2SelectConfigPanel.tsx} (98%) create mode 100644 frontend/components/v2/config-panels/index.ts create mode 100644 frontend/components/v2/index.ts rename frontend/components/{unified/registerUnifiedComponents.ts => v2/registerV2Components.ts} (53%) delete mode 100644 frontend/lib/registry/components/unified-repeater/index.ts create mode 100644 frontend/lib/registry/components/v2-date/index.ts create mode 100644 frontend/lib/registry/components/v2-input/index.ts rename frontend/lib/registry/components/{unified-repeater/UnifiedRepeaterRenderer.tsx => v2-repeater/V2RepeaterRenderer.tsx} (78%) rename frontend/lib/registry/components/{v2-unified-repeater => v2-repeater}/index.ts (79%) create mode 100644 frontend/lib/registry/components/v2-select/index.ts delete mode 100644 frontend/lib/registry/components/v2-unified-repeater/UnifiedRepeaterRenderer.tsx delete mode 100644 frontend/types/unified-components.ts create mode 100644 frontend/types/v2-components.ts rename frontend/types/{unified-core.ts => v2-core.ts} (100%) rename frontend/types/{unified-form.ts => v2-form.ts} (96%) rename frontend/types/{unified-repeater.ts => v2-repeater.ts} (96%) rename frontend/types/{unified-web-types.ts => v2-web-types.ts} (100%) diff --git a/.cursor/rules/component-development-guide.mdc b/.cursor/rules/component-development-guide.mdc index 91611e51..511ae9b6 100644 --- a/.cursor/rules/component-development-guide.mdc +++ b/.cursor/rules/component-development-guide.mdc @@ -51,7 +51,7 @@ alwaysApply: false | `v2-split-panel-layout` | 분할 패널 | 좌우/상하 분할 | | `v2-numbering-rule` | 채번 규칙 | 자동 채번 생성 | | `v2-tabs-widget` | 탭 위젯 | 탭 레이아웃 | -| `v2-unified-repeater` | 통합 리피터 | 행 단위 입력/저장 | +| `v2-repeater` | 통합 리피터 | 행 단위 입력/저장 | | `v2-rack-structure` | 렉 구조 | 창고 렉 위치 생성 | | `v2-section-paper` | 섹션 페이퍼 | 섹션 컨테이너 | | `v2-section-card` | 섹션 카드 | 카드 컨테이너 | @@ -118,7 +118,7 @@ export const V2TableListDefinition = createComponentDefinition({ "components": [ { "id": "comp_xxx", - "url": "@/lib/registry/components/unified-select", + "url": "@/lib/registry/components/v2-select", "position": { "x": 100, "y": 50 }, "size": { "width": 180, "height": 30 }, "displayOrder": 0, @@ -226,7 +226,7 @@ export function convertV2ToLegacy(v2Layout: LayoutV2): LegacyLayoutData { // frontend/lib/schemas/componentConfig.ts // 컴포넌트별 overrides 스키마 -export const unifiedSelectOverridesSchema = z.object({ +export const v2SelectOverridesSchema = z.object({ mode: z.enum(["dropdown", "combobox", "radio", "checkbox"]).default("dropdown"), source: z.enum(["static", "code", "entity", "db", "distinct"]).default("distinct"), multiple: z.boolean().default(false), @@ -236,15 +236,15 @@ export const unifiedSelectOverridesSchema = z.object({ // 스키마 레지스트리 export const componentOverridesSchemaRegistry: Record> = { - "unified-select": unifiedSelectOverridesSchema, - "unified-input": unifiedInputOverridesSchema, + "v2-select": v2SelectOverridesSchema, + "v2-input": v2InputOverridesSchema, "v2-table-list": v2TableListOverridesSchema, // ... }; // 기본값 레지스트리 export const componentDefaultsRegistry: Record = { - "unified-select": { + "v2-select": { mode: "dropdown", source: "distinct", // 기본: 테이블 컬럼에서 자동 로드 multiple: false, @@ -254,7 +254,7 @@ export const componentDefaultsRegistry: Record = { }; ``` -### unified-select 자동 옵션 로드 +### v2-select 자동 옵션 로드 `webType`이 `"select"`인 컬럼을 드래그하면: @@ -265,9 +265,9 @@ export const componentDefaultsRegistry: Record = { ```typescript // DynamicComponentRenderer.tsx -case "unified-select": +case "v2-select": return ( - { ## 10. 폼 데이터 관리 -### 통합 폼 시스템 (UnifiedFormContext) +### 통합 폼 시스템 (V2FormContext) ```typescript import { useFormCompatibility } from "@/hooks/useFormCompatibility"; @@ -949,15 +949,15 @@ const MyComponent = ({ onFormDataChange, formData }) => { ```typescript const handleChange = useCallback((value: any) => { - // 1. UnifiedFormContext - unifiedContext?.setValue(fieldName, value); + // 1. V2FormContext + v2Context?.setValue(fieldName, value); // 2. ScreenContext screenContext?.updateFormData?.(fieldName, value); // 3. 레거시 콜백 onFormDataChange?.(fieldName, value); -}, [fieldName, unifiedContext, screenContext, onFormDataChange]); +}, [fieldName, v2Context, screenContext, onFormDataChange]); ``` --- @@ -1120,7 +1120,7 @@ const MyFormComponent = ({ formData, onFormDataChange }) => { ## 13. 표준 코드 스타일 가이드 -**`v2-unified-repeater`** 컴포넌트를 표준으로 삼아 동일한 구조로 작성합니다. +**`v2-repeater`** 컴포넌트를 표준으로 삼아 동일한 구조로 작성합니다. ### 핵심 원칙: 느슨한 결합도 (Loose Coupling) @@ -1675,7 +1675,7 @@ const derivedValue = useMemo(() => data.map(x => x.value), [data]); - [ ] 테이블 컬럼 드래그 시 `tableName`, `columnName` 저장 확인 - [ ] `convertLegacyToV2`에서 상위 레벨 속성 포함 확인 - [ ] `convertV2ToLegacy`에서 상위 레벨 속성 복원 확인 -- [ ] unified-select는 `source: "distinct"` 기본값 확인 +- [ ] v2-select는 `source: "distinct"` 기본값 확인 ### 표준 Props @@ -1749,7 +1749,7 @@ const derivedValue = useMemo(() => data.map(x => x.value), [data]); ### 코드 스타일 -- [ ] `v2-unified-repeater` 구조 참고 +- [ ] `v2-repeater` 구조 참고 - [ ] 느슨한 결합도 유지 (이벤트 기반 통신) ### 성능 최적화 @@ -1769,9 +1769,9 @@ const derivedValue = useMemo(() => data.map(x => x.value), [data]); | 파일 | 역할 | |------|------| -| `components/unified/UnifiedInput.tsx` | text, number 입력 | -| `components/unified/UnifiedSelect.tsx` | code, entity 선택 | -| `components/unified/UnifiedDate.tsx` | date, datetime 선택 | +| `components/v2/V2Input.tsx` | text, number 입력 | +| `components/v2/V2Select.tsx` | code, entity 선택 | +| `components/v2/V2Date.tsx` | date, datetime 선택 | | `lib/registry/components/v2-*/` | V2 컴포넌트 폴더 | | `lib/api/entityJoin.ts` | 엔티티 조인 API | | `hooks/useFormCompatibility.ts` | 폼 호환성 브릿지 | @@ -1783,6 +1783,6 @@ const derivedValue = useMemo(() => data.map(x => x.value), [data]); | 컴포넌트 | 경로 | 참고 사항 | |----------|------|-----------| -| `v2-unified-repeater` | `lib/registry/components/v2-unified-repeater/` | **표준 참조 컴포넌트** | +| `v2-repeater` | `lib/registry/components/v2-repeater/` | **표준 참조 컴포넌트** | | `v2-table-list` | `lib/registry/components/v2-table-list/` | 조회 컴포넌트 참조 | | `v2-table-search-widget` | `lib/registry/components/v2-table-search-widget/` | 검색 필터 참조 | diff --git a/PLAN.MD b/PLAN.MD index f41ab11b..d4ecf2a6 100644 --- a/PLAN.MD +++ b/PLAN.MD @@ -1,12 +1,12 @@ -# 프로젝트: V2/Unified 컴포넌트 설정 스키마 정비 +# 프로젝트: V2/V2 컴포넌트 설정 스키마 정비 ## 개요 -레거시 컴포넌트를 제거하고, V2/Unified 컴포넌트 전용 Zod 스키마와 기본값 레지스트리를 한 곳에서 관리한다. +레거시 컴포넌트를 제거하고, V2/V2 컴포넌트 전용 Zod 스키마와 기본값 레지스트리를 한 곳에서 관리한다. ## 핵심 기능 1. [x] 레거시 컴포넌트 스키마 제거 2. [x] V2 컴포넌트 overrides 스키마 정의 (16개) -3. [x] Unified 컴포넌트 overrides 스키마 정의 (9개) +3. [x] V2 컴포넌트 overrides 스키마 정의 (9개) 4. [x] componentConfig.ts 한 파일에서 통합 관리 ## 정의된 V2 컴포넌트 (18개) @@ -16,17 +16,17 @@ - v2-numbering-rule, v2-category-manager, v2-pivot-grid - v2-location-swap-selector, v2-aggregation-widget - v2-card-display, v2-table-search-widget, v2-tabs-widget -- v2-unified-repeater +- v2-v2-repeater -## 정의된 Unified 컴포넌트 (9개) -- unified-input, unified-select, unified-date -- unified-list, unified-layout, unified-group -- unified-media, unified-biz, unified-hierarchy +## 정의된 V2 컴포넌트 (9개) +- v2-input, v2-select, v2-date +- v2-list, v2-layout, v2-group +- v2-media, v2-biz, v2-hierarchy ## 테스트 계획 ### 1단계: 기본 기능 - [x] V2 레이아웃 저장 시 컴포넌트별 overrides 스키마 검증 통과 -- [x] Unified 컴포넌트 기본값과 스키마가 매칭됨 +- [x] V2 컴포넌트 기본값과 스키마가 매칭됨 ### 2단계: 에러 케이스 - [x] 잘못된 overrides 입력 시 Zod 검증 실패 처리 (safeParse + console.warn + graceful fallback) @@ -38,7 +38,7 @@ ## 진행 상태 - [x] 레거시 컴포넌트 제거 완료 -- [x] V2/Unified 스키마 정의 완료 +- [x] V2/V2 스키마 정의 완료 - [x] 한 파일 통합 관리 완료 # 프로젝트: 화면 복제 기능 개선 (DB 구조 개편 후) diff --git a/backend-node/src/app.ts b/backend-node/src/app.ts index daf9e344..771ab80d 100644 --- a/backend-node/src/app.ts +++ b/backend-node/src/app.ts @@ -254,7 +254,7 @@ app.use("/api/table-categories", tableCategoryValueRoutes); // 카테고리 값 app.use("/api/code-merge", codeMergeRoutes); // 코드 병합 app.use("/api/numbering-rules", numberingRuleRoutes); // 채번 규칙 관리 app.use("/api/entity-search", entitySearchRoutes); // 엔티티 검색 -app.use("/api/entity", entityOptionsRouter); // 엔티티 옵션 (UnifiedSelect용) +app.use("/api/entity", entityOptionsRouter); // 엔티티 옵션 (V2Select용) app.use("/api/driver", driverRoutes); // 공차중계 운전자 관리 app.use("/api/tax-invoice", taxInvoiceRoutes); // 세금계산서 관리 app.use("/api/cascading-relations", cascadingRelationRoutes); // 연쇄 드롭다운 관계 관리 diff --git a/backend-node/src/controllers/adminController.ts b/backend-node/src/controllers/adminController.ts index 16a87c3e..a530cf15 100644 --- a/backend-node/src/controllers/adminController.ts +++ b/backend-node/src/controllers/adminController.ts @@ -244,7 +244,7 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => { // 검색 조건 처리 if (search && typeof search === "string" && search.trim()) { // 통합 검색 - searchType = "unified"; + searchType = "v2"; const searchTerm = search.trim(); whereConditions.push(`( diff --git a/backend-node/src/controllers/entitySearchController.ts b/backend-node/src/controllers/entitySearchController.ts index 29170e9f..2e850a03 100644 --- a/backend-node/src/controllers/entitySearchController.ts +++ b/backend-node/src/controllers/entitySearchController.ts @@ -105,7 +105,7 @@ export async function getDistinctColumnValues(req: AuthenticatedRequest, res: Re } /** - * 엔티티 옵션 조회 API (UnifiedSelect용) + * 엔티티 옵션 조회 API (V2Select용) * GET /api/entity/:tableName/options * * Query Params: diff --git a/backend-node/src/routes/entitySearchRoutes.ts b/backend-node/src/routes/entitySearchRoutes.ts index b3b11e5c..cddae443 100644 --- a/backend-node/src/routes/entitySearchRoutes.ts +++ b/backend-node/src/routes/entitySearchRoutes.ts @@ -12,7 +12,7 @@ router.get("/:tableName", authenticateToken, searchEntity); export default router; -// 엔티티 옵션 라우터 (UnifiedSelect용) +// 엔티티 옵션 라우터 (V2Select용) export const entityOptionsRouter = Router(); /** diff --git a/backend-node/src/services/enhancedDynamicFormService.ts b/backend-node/src/services/enhancedDynamicFormService.ts index d9670db4..b78bc069 100644 --- a/backend-node/src/services/enhancedDynamicFormService.ts +++ b/backend-node/src/services/enhancedDynamicFormService.ts @@ -11,7 +11,7 @@ import { isValidWebType, WEB_TYPE_TO_POSTGRES_CONVERTER, WEB_TYPE_VALIDATION_PATTERNS, -} from "../types/unified-web-types"; +} from "../types/v2-web-types"; import { DataflowControlService } from "./dataflowControlService"; // 테이블 컬럼 정보 diff --git a/backend-node/src/services/numberingRuleService.ts b/backend-node/src/services/numberingRuleService.ts index 397a402d..83e9b705 100644 --- a/backend-node/src/services/numberingRuleService.ts +++ b/backend-node/src/services/numberingRuleService.ts @@ -987,7 +987,7 @@ class NumberingRuleService { } // 카테고리 매핑에서 해당 값에 대한 형식 찾기 - // selectedValue는 valueCode일 수 있음 (UnifiedSelect에서 valueCode를 value로 사용) + // selectedValue는 valueCode일 수 있음 (V2Select에서 valueCode를 value로 사용) const selectedValueStr = String(selectedValue); const mapping = categoryMappings.find( (m: any) => { diff --git a/backend-node/src/services/screenManagementService.ts b/backend-node/src/services/screenManagementService.ts index afca0251..9338188f 100644 --- a/backend-node/src/services/screenManagementService.ts +++ b/backend-node/src/services/screenManagementService.ts @@ -1406,7 +1406,7 @@ export class ScreenManagementService { */ private inferWebType(dataType: string): WebType { // 통합 타입 매핑에서 import - const { DB_TYPE_TO_WEB_TYPE } = require("../types/unified-web-types"); + const { DB_TYPE_TO_WEB_TYPE } = require("../types/v2-web-types"); const lowerType = dataType.toLowerCase(); @@ -1741,15 +1741,15 @@ export class ScreenManagementService { ? inputTypeMap.get(`${tableName}.${columnName}`) : null; - // 🆕 Unified 컴포넌트는 덮어쓰지 않음 (새로운 컴포넌트 시스템 보호) + // 🆕 V2 컴포넌트는 덮어쓰지 않음 (새로운 컴포넌트 시스템 보호) const savedComponentType = properties?.componentType; - const isUnifiedComponent = savedComponentType?.startsWith("unified-"); + const isV2Component = savedComponentType?.startsWith("v2-"); const component = { id: layout.component_id, - // 🔥 최신 componentType이 있으면 type 덮어쓰기 (단, Unified 컴포넌트는 제외) - type: isUnifiedComponent - ? layout.component_type as any // Unified는 저장된 값 유지 + // 🔥 최신 componentType이 있으면 type 덮어쓰기 (단, V2 컴포넌트는 제외) + type: isV2Component + ? layout.component_type as any // V2는 저장된 값 유지 : (latestTypeInfo?.componentType || layout.component_type as any), position: { x: layout.position_x, @@ -1759,8 +1759,8 @@ export class ScreenManagementService { size: { width: layout.width, height: layout.height }, parentId: layout.parent_id, ...properties, - // 🔥 최신 inputType이 있으면 widgetType, componentType 덮어쓰기 (단, Unified 컴포넌트는 제외) - ...(!isUnifiedComponent && latestTypeInfo && { + // 🔥 최신 inputType이 있으면 widgetType, componentType 덮어쓰기 (단, V2 컴포넌트는 제외) + ...(!isV2Component && latestTypeInfo && { widgetType: latestTypeInfo.inputType, inputType: latestTypeInfo.inputType, componentType: latestTypeInfo.componentType, diff --git a/backend-node/src/services/tableManagementService.ts b/backend-node/src/services/tableManagementService.ts index c0d7e6d1..c8196235 100644 --- a/backend-node/src/services/tableManagementService.ts +++ b/backend-node/src/services/tableManagementService.ts @@ -10,7 +10,7 @@ import { EntityJoinResponse, EntityJoinConfig, } from "../types/tableManagement"; -import { WebType } from "../types/unified-web-types"; +import { WebType } from "../types/v2-web-types"; import { entityJoinService } from "./entityJoinService"; import { referenceCacheService } from "./referenceCacheService"; @@ -4301,7 +4301,7 @@ export class TableManagementService { */ private inferWebType(dataType: string): WebType { // 통합 타입 매핑에서 import - const { DB_TYPE_TO_WEB_TYPE } = require("../types/unified-web-types"); + const { DB_TYPE_TO_WEB_TYPE } = require("../types/v2-web-types"); const lowerType = dataType.toLowerCase(); diff --git a/backend-node/src/types/screen.ts b/backend-node/src/types/screen.ts index 8260f3c6..2cf57d5d 100644 --- a/backend-node/src/types/screen.ts +++ b/backend-node/src/types/screen.ts @@ -5,7 +5,7 @@ export type ComponentType = "container" | "row" | "column" | "widget" | "group"; // 웹 타입 정의 // WebType은 통합 타입에서 import (중복 정의 제거) -import { WebType } from "./unified-web-types"; +import { WebType } from "./v2-web-types"; export { WebType }; // 위치 정보 diff --git a/backend-node/src/types/unified-web-types.ts b/backend-node/src/types/v2-web-types.ts similarity index 99% rename from backend-node/src/types/unified-web-types.ts rename to backend-node/src/types/v2-web-types.ts index 9ac51e57..843fbfaf 100644 --- a/backend-node/src/types/unified-web-types.ts +++ b/backend-node/src/types/v2-web-types.ts @@ -264,7 +264,7 @@ export const WEB_TYPE_VALIDATION_PATTERNS: Record = { }; // 업데이트된 웹 타입 옵션 (기존 WEB_TYPE_OPTIONS 대체) -export const UNIFIED_WEB_TYPE_OPTIONS = [ +export const V2_WEB_TYPE_OPTIONS = [ { value: "text", label: "text", diff --git a/backend-node/src/utils/componentDefaults.ts b/backend-node/src/utils/componentDefaults.ts index 590cea14..9a3e7d35 100644 --- a/backend-node/src/utils/componentDefaults.ts +++ b/backend-node/src/utils/componentDefaults.ts @@ -134,7 +134,7 @@ export const componentDefaults: Record = { "flow-widget": { type: "flow-widget", webType: "text", displayMode: "horizontal", allowDataMove: false, showStepCount: true }, "entity-search-input": { type: "entity-search-input", webType: "entity" }, "autocomplete-search-input": { type: "autocomplete-search-input", webType: "entity" }, - "unified-list": { type: "unified-list", webType: "table" }, + "v2-list": { type: "v2-list", webType: "table" }, "modal-repeater-table": { type: "modal-repeater-table", webType: "table", columns: [], multiSelect: true }, "category-manager": { type: "category-manager", webType: "custom" }, "numbering-rule": { type: "numbering-rule", webType: "text" }, @@ -159,10 +159,10 @@ export const componentDefaults: Record = { "repeat-screen-modal": { type: "repeat-screen-modal", webType: "custom" }, "related-data-buttons": { type: "related-data-buttons", webType: "custom" }, "split-panel-layout2": { type: "split-panel-layout2", webType: "custom" }, - "unified-input": { type: "unified-input", webType: "text" }, - "unified-select": { type: "unified-select", webType: "select" }, - "unified-date": { type: "unified-date", webType: "date" }, - "unified-repeater": { type: "unified-repeater", webType: "custom" }, + "v2-input": { type: "v2-input", webType: "text" }, + "v2-select": { type: "v2-select", webType: "select" }, + "v2-date": { type: "v2-date", webType: "date" }, + "v2-repeater": { type: "v2-repeater", webType: "custom" }, "v2-repeat-container": { type: "v2-repeat-container", webType: "custom" }, }; diff --git a/docs/CATEGORY_TREE_CONTROLLER_ANALYSIS.md b/docs/DDD1542/CATEGORY_TREE_CONTROLLER_ANALYSIS.md similarity index 99% rename from docs/CATEGORY_TREE_CONTROLLER_ANALYSIS.md rename to docs/DDD1542/CATEGORY_TREE_CONTROLLER_ANALYSIS.md index 65615cc4..45e33756 100644 --- a/docs/CATEGORY_TREE_CONTROLLER_ANALYSIS.md +++ b/docs/DDD1542/CATEGORY_TREE_CONTROLLER_ANALYSIS.md @@ -527,7 +527,7 @@ flowchart TB subgraph Usage["사용처"] U1[NumberingRuleDesigner.tsx] - U2[UnifiedSelect.tsx] + U2[V2Select.tsx] U3[screenManagementService.ts] end diff --git a/docs/COLUMN_LABELS_MIGRATION_COMPLETE.md b/docs/DDD1542/COLUMN_LABELS_MIGRATION_COMPLETE.md similarity index 100% rename from docs/COLUMN_LABELS_MIGRATION_COMPLETE.md rename to docs/DDD1542/COLUMN_LABELS_MIGRATION_COMPLETE.md diff --git a/docs/COMPONENT_JSON_MANAGEMENT_ANALYSIS.md b/docs/DDD1542/COMPONENT_JSON_MANAGEMENT_ANALYSIS.md similarity index 100% rename from docs/COMPONENT_JSON_MANAGEMENT_ANALYSIS.md rename to docs/DDD1542/COMPONENT_JSON_MANAGEMENT_ANALYSIS.md diff --git a/docs/COMPONENT_LAYOUT_V2_ARCHITECTURE.md b/docs/DDD1542/COMPONENT_LAYOUT_V2_ARCHITECTURE.md similarity index 99% rename from docs/COMPONENT_LAYOUT_V2_ARCHITECTURE.md rename to docs/DDD1542/COMPONENT_LAYOUT_V2_ARCHITECTURE.md index 023acd08..77d1f4e8 100644 --- a/docs/COMPONENT_LAYOUT_V2_ARCHITECTURE.md +++ b/docs/DDD1542/COMPONENT_LAYOUT_V2_ARCHITECTURE.md @@ -185,7 +185,7 @@ POST /api/screen-management/screens/:screenId/layout-v2 | `@/lib/registry/components/flow-widget` | 플로우 위젯 | | `@/lib/registry/components/category-management` | 카테고리 관리 | | `@/lib/registry/components/pivot-table` | 피벗 테이블 | -| `@/lib/registry/components/unified-grid` | 통합 그리드 | +| `@/lib/registry/components/v2-grid` | 통합 그리드 | --- diff --git a/docs/COMPONENT_MANAGEMENT_FINAL_DESIGN.md b/docs/DDD1542/COMPONENT_MANAGEMENT_FINAL_DESIGN.md similarity index 100% rename from docs/COMPONENT_MANAGEMENT_FINAL_DESIGN.md rename to docs/DDD1542/COMPONENT_MANAGEMENT_FINAL_DESIGN.md diff --git a/docs/COMPONENT_MANAGEMENT_REFACTORING_PROPOSAL.md b/docs/DDD1542/COMPONENT_MANAGEMENT_REFACTORING_PROPOSAL.md similarity index 100% rename from docs/COMPONENT_MANAGEMENT_REFACTORING_PROPOSAL.md rename to docs/DDD1542/COMPONENT_MANAGEMENT_REFACTORING_PROPOSAL.md diff --git a/docs/COMPONENT_MIGRATION_PLAN.md b/docs/DDD1542/COMPONENT_MIGRATION_PLAN.md similarity index 99% rename from docs/COMPONENT_MIGRATION_PLAN.md rename to docs/DDD1542/COMPONENT_MIGRATION_PLAN.md index eb6670b2..c2512521 100644 --- a/docs/COMPONENT_MIGRATION_PLAN.md +++ b/docs/DDD1542/COMPONENT_MIGRATION_PLAN.md @@ -192,7 +192,7 @@ async function verifyRenderingEquality(layoutId: number) { | 6 | select-basic | 129 | 100% | 낮음 | | 7 | split-panel-layout | 129 | 100% | 높음 | | 8 | date-input | 116 | 100% | 낮음 | -| 9 | unified-list | 97 | 100% | 높음 | +| 9 | v2-list | 97 | 100% | 높음 | | 10 | number-input | 87 | 100% | 낮음 | ### 4.2 발견된 문제점 @@ -433,7 +433,7 @@ DROP TABLE screen_layouts_v2; - [ ] select-basic - [ ] split-panel-layout - [ ] date-input -- [ ] unified-list +- [ ] v2-list - [ ] number-input ### Step 3: 마이그레이션 스크립트 diff --git a/docs/COMPONENT_URL_SYSTEM_IMPLEMENTATION.md b/docs/DDD1542/COMPONENT_URL_SYSTEM_IMPLEMENTATION.md similarity index 100% rename from docs/COMPONENT_URL_SYSTEM_IMPLEMENTATION.md rename to docs/DDD1542/COMPONENT_URL_SYSTEM_IMPLEMENTATION.md diff --git a/docs/COMPONENT_URL_ZOD_ARCHITECTURE_ANALYSIS.md b/docs/DDD1542/COMPONENT_URL_ZOD_ARCHITECTURE_ANALYSIS.md similarity index 100% rename from docs/COMPONENT_URL_ZOD_ARCHITECTURE_ANALYSIS.md rename to docs/DDD1542/COMPONENT_URL_ZOD_ARCHITECTURE_ANALYSIS.md diff --git a/docs/DB_CLEANUP_LOG_20260120.md b/docs/DDD1542/DB_CLEANUP_LOG_20260120.md similarity index 100% rename from docs/DB_CLEANUP_LOG_20260120.md rename to docs/DDD1542/DB_CLEANUP_LOG_20260120.md diff --git a/docs/DB_INEFFICIENCY_ANALYSIS.md b/docs/DDD1542/DB_INEFFICIENCY_ANALYSIS.md similarity index 100% rename from docs/DB_INEFFICIENCY_ANALYSIS.md rename to docs/DDD1542/DB_INEFFICIENCY_ANALYSIS.md diff --git a/docs/DB_STRUCTURE_DIAGRAM.md b/docs/DDD1542/DB_STRUCTURE_DIAGRAM.md similarity index 100% rename from docs/DB_STRUCTURE_DIAGRAM.md rename to docs/DDD1542/DB_STRUCTURE_DIAGRAM.md diff --git a/docs/화면관계_시각화_개선_보고서.md b/docs/DDD1542/화면관계_시각화_개선_보고서.md similarity index 100% rename from docs/화면관계_시각화_개선_보고서.md rename to docs/DDD1542/화면관계_시각화_개선_보고서.md diff --git a/docs/화면설정모달_개선_완료_보고서.md b/docs/DDD1542/화면설정모달_개선_완료_보고서.md similarity index 100% rename from docs/화면설정모달_개선_완료_보고서.md rename to docs/DDD1542/화면설정모달_개선_완료_보고서.md diff --git a/DEPLOYMENT_GUIDE_KPSLP.md b/docs/dohyeons/DEPLOYMENT_GUIDE_KPSLP.md similarity index 100% rename from DEPLOYMENT_GUIDE_KPSLP.md rename to docs/dohyeons/DEPLOYMENT_GUIDE_KPSLP.md diff --git a/PROJECT_STATUS_2025_11_20.md b/docs/dohyeons/PROJECT_STATUS_2025_11_20.md similarity index 100% rename from PROJECT_STATUS_2025_11_20.md rename to docs/dohyeons/PROJECT_STATUS_2025_11_20.md diff --git a/docs/report-grid-system-implementation-plan.md b/docs/dohyeons/report-grid-system-implementation-plan.md similarity index 100% rename from docs/report-grid-system-implementation-plan.md rename to docs/dohyeons/report-grid-system-implementation-plan.md diff --git a/docs/리포트_관리_시스템_구현_완료_기능.md b/docs/dohyeons/리포트_관리_시스템_구현_완료_기능.md similarity index 100% rename from docs/리포트_관리_시스템_구현_완료_기능.md rename to docs/dohyeons/리포트_관리_시스템_구현_완료_기능.md diff --git a/docs/리포트_관리_시스템_구현_진행상황.md b/docs/dohyeons/리포트_관리_시스템_구현_진행상황.md similarity index 100% rename from docs/리포트_관리_시스템_구현_진행상황.md rename to docs/dohyeons/리포트_관리_시스템_구현_진행상황.md diff --git a/docs/리포트_관리_시스템_설계.md b/docs/dohyeons/리포트_관리_시스템_설계.md similarity index 100% rename from docs/리포트_관리_시스템_설계.md rename to docs/dohyeons/리포트_관리_시스템_설계.md diff --git a/docs/리포트_문서번호_채번_시스템_설계.md b/docs/dohyeons/리포트_문서번호_채번_시스템_설계.md similarity index 100% rename from docs/리포트_문서번호_채번_시스템_설계.md rename to docs/dohyeons/리포트_문서번호_채번_시스템_설계.md diff --git a/docs/리포트_페이지_관리_시스템_설계.md b/docs/dohyeons/리포트_페이지_관리_시스템_설계.md similarity index 100% rename from docs/리포트_페이지_관리_시스템_설계.md rename to docs/dohyeons/리포트_페이지_관리_시스템_설계.md diff --git a/테이블_변경_이력_로그_시스템_구현_계획서.md b/docs/dohyeons/테이블_변경_이력_로그_시스템_구현_계획서.md similarity index 100% rename from 테이블_변경_이력_로그_시스템_구현_계획서.md rename to docs/dohyeons/테이블_변경_이력_로그_시스템_구현_계획서.md diff --git a/docs/etc/v2-components-implementation.md b/docs/etc/v2-components-implementation.md new file mode 100644 index 00000000..18f51099 --- /dev/null +++ b/docs/etc/v2-components-implementation.md @@ -0,0 +1,192 @@ +# V2 Components 구현 완료 보고서 + +## 구현 일시 + +2024-12-19 + +## 구현된 컴포넌트 목록 (10개) + +### Phase 1: 핵심 입력 컴포넌트 + +| 컴포넌트 | 파일 | 모드/타입 | 설명 | +| :---------------- | :------------------ | :-------------------------------------------- | :---------------------- | +| **V2Input** | `V2Input.tsx` | text, number, password, slider, color, button | 통합 입력 컴포넌트 | +| **V2Select** | `V2Select.tsx` | dropdown, radio, check, tag, toggle, swap | 통합 선택 컴포넌트 | +| **V2Date** | `V2Date.tsx` | date, time, datetime + range | 통합 날짜/시간 컴포넌트 | + +### Phase 2: 레이아웃 및 그룹 컴포넌트 + +| 컴포넌트 | 파일 | 모드/타입 | 설명 | +| :---------------- | :------------------ | :-------------------------------------------------------- | :--------------------- | +| **V2List** | `V2List.tsx` | table, card, kanban, list | 통합 리스트 컴포넌트 | +| **V2Layout** | `V2Layout.tsx` | grid, split, flex, divider, screen-embed | 통합 레이아웃 컴포넌트 | +| **V2Group** | `V2Group.tsx` | tabs, accordion, section, card-section, modal, form-modal | 통합 그룹 컴포넌트 | + +### Phase 3: 미디어 및 비즈니스 컴포넌트 + +| 컴포넌트 | 파일 | 모드/타입 | 설명 | +| :------------------- | :--------------------- | :------------------------------------------------------------- | :---------------------- | +| **V2Media** | `V2Media.tsx` | file, image, video, audio | 통합 미디어 컴포넌트 | +| **V2Biz** | `V2Biz.tsx` | flow, rack, map, numbering, category, mapping, related-buttons | 통합 비즈니스 컴포넌트 | +| **V2Hierarchy** | `V2Hierarchy.tsx` | tree, org, bom, cascading | 통합 계층 구조 컴포넌트 | + +--- + +## 공통 인프라 + +### 설정 패널 + +- **DynamicConfigPanel**: JSON Schema 기반 동적 설정 UI 생성 + +### 렌더러 + +- **V2ComponentRenderer**: v2Type에 따른 동적 컴포넌트 렌더링 + +--- + +## 파일 구조 + +``` +frontend/components/v2/ +├── index.ts # 모듈 인덱스 +├── V2ComponentRenderer.tsx # 동적 렌더러 +├── DynamicConfigPanel.tsx # JSON Schema 설정 패널 +├── V2Input.tsx # 통합 입력 +├── V2Select.tsx # 통합 선택 +├── V2Date.tsx # 통합 날짜 +├── V2List.tsx # 통합 리스트 +├── V2Layout.tsx # 통합 레이아웃 +├── V2Group.tsx # 통합 그룹 +├── V2Media.tsx # 통합 미디어 +├── V2Biz.tsx # 통합 비즈니스 +└── V2Hierarchy.tsx # 통합 계층 + +frontend/types/ +└── v2-components.ts # 타입 정의 + +db/migrations/ +└── v2_component_schema.sql # DB 스키마 (미실행) +``` + +--- + +## 사용 예시 + +### 기본 사용법 + +```tsx +import { + V2Input, + V2Select, + V2Date, + V2List, + V2ComponentRenderer +} from "@/components/v2"; + +// V2Input 사용 + + +// V2Select 사용 + + +// V2Date 사용 + + +// V2List 사용 + +``` + +### 동적 렌더링 + +```tsx +import { V2ComponentRenderer } from "@/components/v2"; + +// v2Type에 따라 자동으로 적절한 컴포넌트 렌더링 +; +``` + +--- + +## 주의사항 + +### 기존 컴포넌트와의 공존 + +1. **기존 컴포넌트는 그대로 유지**: 모든 레거시 컴포넌트는 정상 동작 +2. **신규 화면에서만 V2 컴포넌트 사용**: 기존 화면에 영향 없음 +3. **마이그레이션 없음**: 자동 마이그레이션 진행하지 않음 + +### 데이터베이스 마이그레이션 + +`db/migrations/v2_component_schema.sql` 파일은 아직 실행되지 않았습니다. +필요시 수동으로 실행해야 합니다: + +```bash +psql -h localhost -U postgres -d plm_db -f db/migrations/v2_component_schema.sql +``` + +--- + +## 다음 단계 (선택) + +1. **화면 관리 에디터 통합**: V2 컴포넌트를 화면 에디터의 컴포넌트 팔레트에 추가 +2. **기존 비즈니스 컴포넌트 연동**: V2Biz의 플레이스홀더를 실제 구현으로 교체 +3. **테스트 페이지 작성**: 모든 V2 컴포넌트 데모 페이지 +4. **문서화**: 각 컴포넌트별 상세 사용 가이드 + +--- + +## 관련 문서 + +- `PLAN_RENEWAL.md`: 리뉴얼 계획서 +- `docs/phase0-component-usage-analysis.md`: 컴포넌트 사용 현황 분석 +- `docs/phase0-migration-strategy.md`: 마이그레이션 전략 (참고용) + diff --git a/DOCKER.md b/docs/hyeonsu/DOCKER.md similarity index 100% rename from DOCKER.md rename to docs/hyeonsu/DOCKER.md diff --git a/docs/external-call-implementation-plan.md b/docs/hyeonsu/external-call-implementation-plan.md similarity index 100% rename from docs/external-call-implementation-plan.md rename to docs/hyeonsu/external-call-implementation-plan.md diff --git a/docs/external-connection-management-plan.md b/docs/hyeonsu/external-connection-management-plan.md similarity index 100% rename from docs/external-connection-management-plan.md rename to docs/hyeonsu/external-connection-management-plan.md diff --git a/docs/공통코드_관리_시스템_설계.md b/docs/hyeonsu/공통코드_관리_시스템_설계.md similarity index 100% rename from docs/공통코드_관리_시스템_설계.md rename to docs/hyeonsu/공통코드_관리_시스템_설계.md diff --git a/docs/조건부_연결_구현_계획.md b/docs/hyeonsu/조건부_연결_구현_계획.md similarity index 100% rename from docs/조건부_연결_구현_계획.md rename to docs/hyeonsu/조건부_연결_구현_계획.md diff --git a/docs/화면간_데이터_관계_설정_시스템_설계.md b/docs/hyeonsu/화면간_데이터_관계_설정_시스템_설계.md similarity index 100% rename from docs/화면간_데이터_관계_설정_시스템_설계.md rename to docs/hyeonsu/화면간_데이터_관계_설정_시스템_설계.md diff --git a/docs/ADMIN_STYLE_GUIDE_EXAMPLE.md b/docs/kjs/ADMIN_STYLE_GUIDE_EXAMPLE.md similarity index 100% rename from docs/ADMIN_STYLE_GUIDE_EXAMPLE.md rename to docs/kjs/ADMIN_STYLE_GUIDE_EXAMPLE.md diff --git a/docs/COLUMN_LABELS_MIGRATION_ANALYSIS.md b/docs/kjs/COLUMN_LABELS_MIGRATION_ANALYSIS.md similarity index 99% rename from docs/COLUMN_LABELS_MIGRATION_ANALYSIS.md rename to docs/kjs/COLUMN_LABELS_MIGRATION_ANALYSIS.md index 18436c90..803c2aa6 100644 --- a/docs/COLUMN_LABELS_MIGRATION_ANALYSIS.md +++ b/docs/kjs/COLUMN_LABELS_MIGRATION_ANALYSIS.md @@ -95,7 +95,7 @@ | 파일 | 참조 횟수 | 영향도 | 용도 | |------|----------|--------|------| -| `UnifiedRepeater.tsx` | 3회 | 🟢 낮음 | 타입 주석 | +| `V2Repeater.tsx` | 3회 | 🟢 낮음 | 타입 주석 | | `ScreenDesigner.tsx` | 2회 | 🟢 낮음 | 타입 주석 | | `ButtonConfigPanel.tsx` | 2회 | 🟢 낮음 | 타입 주석 | | `ScreenRelationFlow.tsx` | 2회 | 🟢 낮음 | 타입 주석 | diff --git a/DETAILED_FILE_MIGRATION_PLAN.md b/docs/kjs/DETAILED_FILE_MIGRATION_PLAN.md similarity index 100% rename from DETAILED_FILE_MIGRATION_PLAN.md rename to docs/kjs/DETAILED_FILE_MIGRATION_PLAN.md diff --git a/docs/Database_Schema_Collection.md b/docs/kjs/Database_Schema_Collection.md similarity index 100% rename from docs/Database_Schema_Collection.md rename to docs/kjs/Database_Schema_Collection.md diff --git a/Entity_조인_기능_개발계획서.md b/docs/kjs/Entity_조인_기능_개발계획서.md similarity index 100% rename from Entity_조인_기능_개발계획서.md rename to docs/kjs/Entity_조인_기능_개발계획서.md diff --git a/docs/FINAL_GRID_MIGRATION_ROADMAP.md b/docs/kjs/FINAL_GRID_MIGRATION_ROADMAP.md similarity index 100% rename from docs/FINAL_GRID_MIGRATION_ROADMAP.md rename to docs/kjs/FINAL_GRID_MIGRATION_ROADMAP.md diff --git a/docs/FLOW_DATA_STRUCTURE_GUIDE.md b/docs/kjs/FLOW_DATA_STRUCTURE_GUIDE.md similarity index 100% rename from docs/FLOW_DATA_STRUCTURE_GUIDE.md rename to docs/kjs/FLOW_DATA_STRUCTURE_GUIDE.md diff --git a/docs/FLOW_EXTERNAL_INTEGRATION_PLAN.md b/docs/kjs/FLOW_EXTERNAL_INTEGRATION_PLAN.md similarity index 100% rename from docs/FLOW_EXTERNAL_INTEGRATION_PLAN.md rename to docs/kjs/FLOW_EXTERNAL_INTEGRATION_PLAN.md diff --git a/docs/FLOW_HYBRID_MODE_USAGE_GUIDE.md b/docs/kjs/FLOW_HYBRID_MODE_USAGE_GUIDE.md similarity index 100% rename from docs/FLOW_HYBRID_MODE_USAGE_GUIDE.md rename to docs/kjs/FLOW_HYBRID_MODE_USAGE_GUIDE.md diff --git a/docs/FLOW_MANAGEMENT_UI_DESIGN.md b/docs/kjs/FLOW_MANAGEMENT_UI_DESIGN.md similarity index 100% rename from docs/FLOW_MANAGEMENT_UI_DESIGN.md rename to docs/kjs/FLOW_MANAGEMENT_UI_DESIGN.md diff --git a/docs/GRID_SYSTEM_REDESIGN_PLAN.md b/docs/kjs/GRID_SYSTEM_REDESIGN_PLAN.md similarity index 100% rename from docs/GRID_SYSTEM_REDESIGN_PLAN.md rename to docs/kjs/GRID_SYSTEM_REDESIGN_PLAN.md diff --git a/docs/NodeJS_Refactoring_Rules.md b/docs/kjs/NodeJS_Refactoring_Rules.md similarity index 100% rename from docs/NodeJS_Refactoring_Rules.md rename to docs/kjs/NodeJS_Refactoring_Rules.md diff --git a/docs/PHASE1_FLOW_IMPLEMENTATION_SUMMARY.md b/docs/kjs/PHASE1_FLOW_IMPLEMENTATION_SUMMARY.md similarity index 100% rename from docs/PHASE1_FLOW_IMPLEMENTATION_SUMMARY.md rename to docs/kjs/PHASE1_FLOW_IMPLEMENTATION_SUMMARY.md diff --git a/PLAN_RENEWAL.md b/docs/kjs/PLAN_RENEWAL.md similarity index 76% rename from PLAN_RENEWAL.md rename to docs/kjs/PLAN_RENEWAL.md index 7d5575a6..1cd03e4e 100644 --- a/PLAN_RENEWAL.md +++ b/docs/kjs/PLAN_RENEWAL.md @@ -2,7 +2,7 @@ ## 1. 개요 -현재 **68개 이상**으로 파편화된 화면 관리 컴포넌트들을 **9개의 핵심 통합 컴포넌트(Unified Components)**로 재편합니다. +현재 **68개 이상**으로 파편화된 화면 관리 컴포넌트들을 **9개의 핵심 통합 컴포넌트(V2 Components)**로 재편합니다. 각 컴포넌트는 **속성(Config)** 설정을 통해 다양한 형태(View Mode)와 기능(Behavior)을 수행하도록 설계되어, 유지보수성과 확장성을 극대화합니다. ### 현재 컴포넌트 현황 (AS-IS) @@ -24,11 +24,11 @@ | 통합 컴포넌트 (TO-BE) | 포함되는 기존 컴포넌트 (AS-IS) | 핵심 속성 (Configuration) | | :-------------------- | :------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------- | -| **1. Unified Select** | Select, Radio, Checkbox, Boolean, Code, Entity, Combobox, Toggle | **`mode`**: "dropdown" / "radio" / "check" / "tag"
**`source`**: "static" / "code" / "db" / "api"
**`dependency`**: { parentField: "..." } | -| **2. Unified Input** | Text, Number, Email, Tel, Password, Color, Search, Integer, Decimal | **`type`**: "text" / "number" / "password"
**`format`**: "email", "currency", "biz_no"
**`mask`**: "000-0000-0000" | -| **3. Unified Date** | Date, Time, DateTime, DateRange, Month, Year | **`type`**: "date" / "time" / "datetime"
**`range`**: true/false | -| **4. Unified Text** | Textarea, RichEditor, Markdown, HTML | **`mode`**: "simple" / "rich" / "code"
**`rows`**: number | -| **5. Unified Media** | File, Image, Video, Audio, Attachment | **`type`**: "file" / "image"
**`multiple`**: true/false
**`preview`**: true/false | +| **1. V2 Select** | Select, Radio, Checkbox, Boolean, Code, Entity, Combobox, Toggle | **`mode`**: "dropdown" / "radio" / "check" / "tag"
**`source`**: "static" / "code" / "db" / "api"
**`dependency`**: { parentField: "..." } | +| **2. V2 Input** | Text, Number, Email, Tel, Password, Color, Search, Integer, Decimal | **`type`**: "text" / "number" / "password"
**`format`**: "email", "currency", "biz_no"
**`mask`**: "000-0000-0000" | +| **3. V2 Date** | Date, Time, DateTime, DateRange, Month, Year | **`type`**: "date" / "time" / "datetime"
**`range`**: true/false | +| **4. V2 Text** | Textarea, RichEditor, Markdown, HTML | **`mode`**: "simple" / "rich" / "code"
**`rows`**: number | +| **5. V2 Media** | File, Image, Video, Audio, Attachment | **`type`**: "file" / "image"
**`multiple`**: true/false
**`preview`**: true/false | ### B. 구조/데이터 위젯 (Structure & Data Widgets) - 4종 @@ -36,10 +36,10 @@ | 통합 컴포넌트 (TO-BE) | 포함되는 기존 컴포넌트 (AS-IS) | 핵심 속성 (Configuration) | 활용 예시 | | :-------------------- | :-------------------------------------------------- | :------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------- | -| **6. Unified List** | **Table, Card List, Repeater, DataGrid, List View** | **`viewMode`**: "table" / "card" / "kanban"
**`editable`**: true/false | - `viewMode='table'`: 엑셀형 리스트
- `viewMode='card'`: **카드 디스플레이**
- `editable=true`: **반복 필드 그룹** | -| **7. Unified Layout** | **Row, Col, Split Panel, Grid, Spacer** | **`type`**: "grid" / "split" / "flex"
**`columns`**: number | - `type='split'`: **화면 분할 패널**
- `type='grid'`: 격자 레이아웃 | -| **8. Unified Group** | Tab, Accordion, FieldSet, Modal, Section | **`type`**: "tab" / "accordion" / "modal" | - 탭이나 아코디언으로 내용 그룹화 | -| **9. Unified Biz** | **Rack Structure**, Calendar, Gantt | **`type`**: "rack" / "calendar" / "gantt" | - `type='rack'`: **랙 구조 설정**
- 특수 비즈니스 로직 플러그인 탑재 | +| **6. V2 List** | **Table, Card List, Repeater, DataGrid, List View** | **`viewMode`**: "table" / "card" / "kanban"
**`editable`**: true/false | - `viewMode='table'`: 엑셀형 리스트
- `viewMode='card'`: **카드 디스플레이**
- `editable=true`: **반복 필드 그룹** | +| **7. V2 Layout** | **Row, Col, Split Panel, Grid, Spacer** | **`type`**: "grid" / "split" / "flex"
**`columns`**: number | - `type='split'`: **화면 분할 패널**
- `type='grid'`: 격자 레이아웃 | +| **8. V2 Group** | Tab, Accordion, FieldSet, Modal, Section | **`type`**: "tab" / "accordion" / "modal" | - 탭이나 아코디언으로 내용 그룹화 | +| **9. V2 Biz** | **Rack Structure**, Calendar, Gantt | **`type`**: "rack" / "calendar" / "gantt" | - `type='rack'`: **랙 구조 설정**
- 특수 비즈니스 로직 플러그인 탑재 | ### C. Config Panel 통합 전략 (핵심) @@ -60,16 +60,16 @@ ### Case 1: "테이블을 카드 리스트로 변경" - **AS-IS**: `DataTable` 컴포넌트를 삭제하고 `CardList` 컴포넌트를 새로 추가해야 함. -- **TO-BE**: `UnifiedList`의 속성창에서 **[View Mode]**를 `Table` → `Card`로 변경하면 즉시 반영. +- **TO-BE**: `V2List`의 속성창에서 **[View Mode]**를 `Table` → `Card`로 변경하면 즉시 반영. ### Case 2: "단일 선택을 라디오 버튼으로 변경" - **AS-IS**: `SelectWidget`을 삭제하고 `RadioWidget` 추가. -- **TO-BE**: `UnifiedSelect` 속성창에서 **[Display Mode]**를 `Dropdown` → `Radio`로 변경. +- **TO-BE**: `V2Select` 속성창에서 **[Display Mode]**를 `Dropdown` → `Radio`로 변경. ### Case 3: "입력 폼에 반복 필드(Repeater) 추가" -- **TO-BE**: `UnifiedList` 컴포넌트 배치 후 `editable: true`, `viewMode: "table"` 설정. +- **TO-BE**: `V2List` 컴포넌트 배치 후 `editable: true`, `viewMode: "table"` 설정. --- @@ -80,7 +80,7 @@ 통합 작업 전 필수 분석 및 설계를 진행합니다. - [ ] 기존 컴포넌트 사용 현황 분석 (화면별 위젯 사용 빈도 조사) -- [ ] 데이터 마이그레이션 전략 설계 (`widgetType` → `UnifiedWidget.type` 매핑 정의) +- [ ] 데이터 마이그레이션 전략 설계 (`widgetType` → `V2Widget.type` 매핑 정의) - [ ] `sys_input_type` 테이블 JSON Schema 설계 - [ ] DynamicConfigPanel 프로토타입 설계 @@ -88,9 +88,9 @@ 가장 중복이 많고 효과가 즉각적인 입력 필드부터 통합합니다. -- [ ] **UnifiedInput 구현**: Text, Number, Email, Tel, Password 통합 -- [ ] **UnifiedSelect 구현**: Select, Radio, Checkbox, Boolean 통합 -- [ ] **UnifiedDate 구현**: Date, DateTime, Time 통합 +- [ ] **V2Input 구현**: Text, Number, Email, Tel, Password 통합 +- [ ] **V2Select 구현**: Select, Radio, Checkbox, Boolean 통합 +- [ ] **V2Date 구현**: Date, DateTime, Time 통합 - [ ] 기존 위젯과 **병행 운영** (deprecated 마킹, 삭제하지 않음) ### Phase 2: Config Panel 통합 (2주) @@ -105,15 +105,15 @@ 프로젝트의 데이터를 보여주는 핵심 뷰를 통합합니다. -- [ ] **UnifiedList 구현**: Table, Card, Repeater 통합 렌더러 개발 -- [ ] **UnifiedLayout 구현**: Split Panel, Grid, Flex 통합 -- [ ] **UnifiedGroup 구현**: Tab, Accordion, Modal 통합 +- [ ] **V2List 구현**: Table, Card, Repeater 통합 렌더러 개발 +- [ ] **V2Layout 구현**: Split Panel, Grid, Flex 통합 +- [ ] **V2Group 구현**: Tab, Accordion, Modal 통합 ### Phase 4: 안정화 및 마이그레이션 (2주) 신규 컴포넌트 안정화 후 점진적 전환을 진행합니다. -- [ ] 신규 화면은 Unified 컴포넌트만 사용하도록 가이드 +- [ ] 신규 화면은 V2 컴포넌트만 사용하도록 가이드 - [ ] 기존 화면 데이터 마이그레이션 스크립트 개발 - [ ] 마이그레이션 테스트 (스테이징 환경) - [ ] 문서화 및 개발 가이드 작성 @@ -122,7 +122,7 @@ 충분한 안정화 기간 후 레거시 컴포넌트 정리를 검토합니다. -- [ ] 사용 현황 재분석 (Unified 전환율 확인) +- [ ] 사용 현황 재분석 (V2 전환율 확인) - [ ] 미전환 화면 목록 정리 - [ ] 레거시 컴포넌트 삭제 여부 결정 (별도 회의) @@ -132,27 +132,27 @@ ### 5.1 위젯 타입 매핑 테이블 -기존 `widgetType`을 신규 Unified 컴포넌트로 매핑합니다. +기존 `widgetType`을 신규 V2 컴포넌트로 매핑합니다. | 기존 widgetType | 신규 컴포넌트 | 속성 설정 | | :-------------- | :------------ | :------------------------------ | -| `text` | UnifiedInput | `type: "text"` | -| `number` | UnifiedInput | `type: "number"` | -| `email` | UnifiedInput | `type: "text", format: "email"` | -| `tel` | UnifiedInput | `type: "text", format: "tel"` | -| `select` | UnifiedSelect | `mode: "dropdown"` | -| `radio` | UnifiedSelect | `mode: "radio"` | -| `checkbox` | UnifiedSelect | `mode: "check"` | -| `date` | UnifiedDate | `type: "date"` | -| `datetime` | UnifiedDate | `type: "datetime"` | -| `textarea` | UnifiedText | `mode: "simple"` | -| `file` | UnifiedMedia | `type: "file"` | -| `image` | UnifiedMedia | `type: "image"` | +| `text` | V2Input | `type: "text"` | +| `number` | V2Input | `type: "number"` | +| `email` | V2Input | `type: "text", format: "email"` | +| `tel` | V2Input | `type: "text", format: "tel"` | +| `select` | V2Select | `mode: "dropdown"` | +| `radio` | V2Select | `mode: "radio"` | +| `checkbox` | V2Select | `mode: "check"` | +| `date` | V2Date | `type: "date"` | +| `datetime` | V2Date | `type: "datetime"` | +| `textarea` | V2Text | `mode: "simple"` | +| `file` | V2Media | `type: "file"` | +| `image` | V2Media | `type: "image"` | ### 5.2 마이그레이션 원칙 1. **비파괴적 전환**: 기존 데이터 구조 유지, 신규 필드 추가 방식 -2. **하위 호환성**: 기존 `widgetType` 필드는 유지, `unifiedType` 필드 추가 +2. **하위 호환성**: 기존 `widgetType` 필드는 유지, `v2Type` 필드 추가 3. **점진적 전환**: 화면 수정 시점에 자동 또는 수동 전환 --- @@ -183,7 +183,7 @@ 현재 `frontend/lib/registry/components/`에 등록된 모든 컴포넌트의 통합 가능 여부를 분석했습니다. -#### UnifiedInput으로 통합 (4개) +#### V2Input으로 통합 (4개) | 현재 컴포넌트 | 매핑 속성 | 비고 | | :------------- | :--------------- | :------------- | @@ -192,7 +192,7 @@ | slider-basic | `type: "slider"` | 속성 추가 필요 | | button-primary | `type: "button"` | 별도 검토 | -#### UnifiedSelect로 통합 (8개) +#### V2Select로 통합 (8개) | 현재 컴포넌트 | 매핑 속성 | 비고 | | :------------------------ | :----------------------------------- | :------------- | @@ -205,19 +205,19 @@ | mail-recipient-selector | `mode: "multi", type: "email"` | 복합 컴포넌트 | | location-swap-selector | `mode: "swap"` | 특수 UI | -#### UnifiedDate로 통합 (1개) +#### V2Date로 통합 (1개) | 현재 컴포넌트 | 매핑 속성 | 비고 | | :------------ | :------------- | :--- | | date-input | `type: "date"` | | -#### UnifiedText로 통합 (1개) +#### V2Text로 통합 (1개) | 현재 컴포넌트 | 매핑 속성 | 비고 | | :------------- | :--------------- | :--- | | textarea-basic | `mode: "simple"` | | -#### UnifiedMedia로 통합 (3개) +#### V2Media로 통합 (3개) | 현재 컴포넌트 | 매핑 속성 | 비고 | | :------------ | :------------------------------ | :--- | @@ -225,7 +225,7 @@ | image-widget | `type: "image"` | | | image-display | `type: "image", readonly: true` | | -#### UnifiedList로 통합 (8개) +#### V2List로 통합 (8개) | 현재 컴포넌트 | 매핑 속성 | 비고 | | :-------------------- | :------------------------------------ | :------------ | @@ -238,7 +238,7 @@ | table-search-widget | `viewMode: "table", searchable: true` | | | tax-invoice-list | `viewMode: "table", bizType: "tax"` | 특수 비즈니스 | -#### UnifiedLayout으로 통합 (4개) +#### V2Layout으로 통합 (4개) | 현재 컴포넌트 | 매핑 속성 | 비고 | | :------------------ | :-------------------------- | :------------- | @@ -247,7 +247,7 @@ | divider-line | `type: "divider"` | 속성 추가 필요 | | screen-split-panel | `type: "screen-embed"` | 화면 임베딩 | -#### UnifiedGroup으로 통합 (5개) +#### V2Group으로 통합 (5개) | 현재 컴포넌트 | 매핑 속성 | 비고 | | :------------------- | :--------------------- | :------------ | @@ -257,7 +257,7 @@ | section-card | `type: "card-section"` | | | universal-form-modal | `type: "form-modal"` | 복합 컴포넌트 | -#### UnifiedBiz로 통합 (7개) +#### V2Biz로 통합 (7개) | 현재 컴포넌트 | 매핑 속성 | 비고 | | :-------------------- | :------------------------ | :--------------- | @@ -274,8 +274,8 @@ | 현재 컴포넌트 | 문제점 | 제안 | | :-------------------------- | :------------------- | :------------------------------ | | conditional-container | 조건부 렌더링 로직 | 공통 속성으로 분리 | -| selected-items-detail-input | 복합 (선택+상세입력) | UnifiedList + UnifiedGroup 조합 | -| text-display | 읽기 전용 텍스트 | UnifiedInput (readonly: true) | +| selected-items-detail-input | 복합 (선택+상세입력) | V2List + V2Group 조합 | +| text-display | 읽기 전용 텍스트 | V2Input (readonly: true) | ### 8.2 매핑 분석 결과 @@ -291,7 +291,7 @@ ### 8.3 속성 확장 필요 사항 -#### UnifiedInput 속성 확장 +#### V2Input 속성 확장 ```typescript // 기존 @@ -301,7 +301,7 @@ type: "text" | "number" | "password"; type: "text" | "number" | "password" | "slider" | "color" | "button"; ``` -#### UnifiedSelect 속성 확장 +#### V2Select 속성 확장 ```typescript // 기존 @@ -311,7 +311,7 @@ mode: "dropdown" | "radio" | "check" | "tag"; mode: "dropdown" | "radio" | "check" | "tag" | "toggle" | "swap"; ``` -#### UnifiedLayout 속성 확장 +#### V2Layout 속성 확장 ```typescript // 기존 @@ -326,8 +326,8 @@ type: "grid" | "split" | "flex" | "divider" | "screen-embed"; `conditional-container`의 기능을 모든 컴포넌트에서 사용 가능한 공통 속성으로 분리합니다. ```typescript -// 모든 Unified 컴포넌트에 적용 가능한 공통 속성 -interface BaseUnifiedProps { +// 모든 V2 컴포넌트에 적용 가능한 공통 속성 +interface BaseV2Props { // ... 기존 속성 /** 조건부 렌더링 설정 */ @@ -356,12 +356,12 @@ DB 테이블 `cascading_hierarchy_group`에서 4가지 계층 타입을 지원 | **BOM** | 자재명세서 구조 | 부품 > 하위부품 | | **TREE** | 일반 트리 | 카테고리 | -### 9.2 통합 방안: UnifiedHierarchy 신설 (10번째 컴포넌트) +### 9.2 통합 방안: V2Hierarchy 신설 (10번째 컴포넌트) 계층 구조는 일반 입력/표시 위젯과 성격이 다르므로 **별도 컴포넌트로 분리**합니다. ```typescript -interface UnifiedHierarchyProps { +interface V2HierarchyProps { /** 계층 유형 */ type: "tree" | "org" | "bom" | "cascading"; @@ -400,16 +400,16 @@ interface UnifiedHierarchyProps { | # | 컴포넌트 | 역할 | 커버 범위 | | :-: | :------------------- | :------------- | :----------------------------------- | -| 1 | **UnifiedInput** | 단일 값 입력 | text, number, slider, button 등 | -| 2 | **UnifiedSelect** | 선택 입력 | dropdown, radio, checkbox, toggle 등 | -| 3 | **UnifiedDate** | 날짜/시간 입력 | date, datetime, time, range | -| 4 | **UnifiedText** | 다중 행 텍스트 | textarea, rich editor, markdown | -| 5 | **UnifiedMedia** | 파일/미디어 | file, image, video, audio | -| 6 | **UnifiedList** | 데이터 목록 | table, card, repeater, kanban | -| 7 | **UnifiedLayout** | 레이아웃 배치 | grid, split, flex, divider | -| 8 | **UnifiedGroup** | 콘텐츠 그룹화 | tabs, accordion, section, modal | -| 9 | **UnifiedBiz** | 비즈니스 특화 | flow, rack, map, numbering 등 | -| 10 | **UnifiedHierarchy** | 계층 구조 | tree, org, bom, cascading | +| 1 | **V2Input** | 단일 값 입력 | text, number, slider, button 등 | +| 2 | **V2Select** | 선택 입력 | dropdown, radio, checkbox, toggle 등 | +| 3 | **V2Date** | 날짜/시간 입력 | date, datetime, time, range | +| 4 | **V2Text** | 다중 행 텍스트 | textarea, rich editor, markdown | +| 5 | **V2Media** | 파일/미디어 | file, image, video, audio | +| 6 | **V2List** | 데이터 목록 | table, card, repeater, kanban | +| 7 | **V2Layout** | 레이아웃 배치 | grid, split, flex, divider | +| 8 | **V2Group** | 콘텐츠 그룹화 | tabs, accordion, section, modal | +| 9 | **V2Biz** | 비즈니스 특화 | flow, rack, map, numbering 등 | +| 10 | **V2Hierarchy** | 계층 구조 | tree, org, bom, cascading | --- @@ -443,14 +443,14 @@ interface UnifiedHierarchyProps { ### 11.3 속성 통합 설계 -#### 2단계 연쇄 → UnifiedSelect 속성 +#### 2단계 연쇄 → V2Select 속성 ```typescript // AS-IS: 별도 관리 메뉴에서 정의 후 참조 // TO-BE: 컴포넌트 속성에서 직접 정의 - 계층 구조 설정 | | - 조건부 필터 | → 공통 conditional 속성 | | - 자동 입력 | → 공통 autoFill 속성 | -| - 상호 배제 | → UnifiedSelect.mutualExclusion 속성 | +| - 상호 배제 | → V2Select.mutualExclusion 속성 | | - 카테고리 값 연쇄 | → 카테고리 관리와 통합 | --- diff --git a/README-WINDOWS.md b/docs/kjs/README-WINDOWS.md similarity index 100% rename from README-WINDOWS.md rename to docs/kjs/README-WINDOWS.md diff --git a/docs/TODO.md b/docs/kjs/TODO.md similarity index 100% rename from docs/TODO.md rename to docs/kjs/TODO.md diff --git a/docs/V2_COMPONENT_COUPLING_ANALYSIS.md b/docs/kjs/V2_COMPONENT_COUPLING_ANALYSIS.md similarity index 85% rename from docs/V2_COMPONENT_COUPLING_ANALYSIS.md rename to docs/kjs/V2_COMPONENT_COUPLING_ANALYSIS.md index 47ec2bec..054df1b5 100644 --- a/docs/V2_COMPONENT_COUPLING_ANALYSIS.md +++ b/docs/kjs/V2_COMPONENT_COUPLING_ANALYSIS.md @@ -1,4 +1,4 @@ -# V2 컴포넌트 및 Unified 폼 컴포넌트 결합도 분석 보고서 +# V2 컴포넌트 및 V2 폼 컴포넌트 결합도 분석 보고서 > 작성일: 2026-01-26 > 목적: 컴포넌트 간 결합도 분석 및 느슨한 결합 전환 가능성 평가 @@ -29,23 +29,23 @@ | 16 | v2-table-search-widget | `v2-table-search-widget/` | 테이블 검색 위젯 | | 17 | v2-tabs-widget | `v2-tabs-widget/` | 탭 위젯 | | 18 | v2-text-display | `v2-text-display/` | 텍스트 표시 | -| 19 | v2-unified-repeater | `v2-unified-repeater/` | 통합 리피터 | +| 19 | v2-repeater | `v2-repeater/` | 통합 리피터 | -### 1.2 Unified 폼 컴포넌트 (11개) +### 1.2 V2 폼 컴포넌트 (11개) | # | 컴포넌트 | 파일 | 주요 용도 | |---|---------|------|----------| -| 1 | UnifiedInput | `UnifiedInput.tsx` | 텍스트/숫자/이메일 등 입력 | -| 2 | UnifiedSelect | `UnifiedSelect.tsx` | 선택박스/라디오/체크박스 | -| 3 | UnifiedDate | `UnifiedDate.tsx` | 날짜/시간 입력 | -| 4 | UnifiedRepeater | `UnifiedRepeater.tsx` | 리피터 (테이블 형태) | -| 5 | UnifiedLayout | `UnifiedLayout.tsx` | 레이아웃 컨테이너 | -| 6 | UnifiedGroup | `UnifiedGroup.tsx` | 그룹 컨테이너 (카드/탭/접기) | -| 7 | UnifiedHierarchy | `UnifiedHierarchy.tsx` | 계층 구조 표시 | -| 8 | UnifiedList | `UnifiedList.tsx` | 리스트 표시 | -| 9 | UnifiedMedia | `UnifiedMedia.tsx` | 파일/이미지/비디오 업로드 | -| 10 | UnifiedBiz | `UnifiedBiz.tsx` | 비즈니스 컴포넌트 | -| 11 | UnifiedFormContext | `UnifiedFormContext.tsx` | 폼 상태 관리 컨텍스트 | +| 1 | V2Input | `V2Input.tsx` | 텍스트/숫자/이메일 등 입력 | +| 2 | V2Select | `V2Select.tsx` | 선택박스/라디오/체크박스 | +| 3 | V2Date | `V2Date.tsx` | 날짜/시간 입력 | +| 4 | V2Repeater | `V2Repeater.tsx` | 리피터 (테이블 형태) | +| 5 | V2Layout | `V2Layout.tsx` | 레이아웃 컨테이너 | +| 6 | V2Group | `V2Group.tsx` | 그룹 컨테이너 (카드/탭/접기) | +| 7 | V2Hierarchy | `V2Hierarchy.tsx` | 계층 구조 표시 | +| 8 | V2List | `V2List.tsx` | 리스트 표시 | +| 9 | V2Media | `V2Media.tsx` | 파일/이미지/비디오 업로드 | +| 10 | V2Biz | `V2Biz.tsx` | 비즈니스 컴포넌트 | +| 11 | V2FormContext | `V2FormContext.tsx` | 폼 상태 관리 컨텍스트 | --- @@ -140,29 +140,29 @@ window.addEventListener("checkboxSelectionChange", handleSelectionChange); | v2-table-search-widget | ❌ | 0개 | ❌ | 🟢 1/10 | | v2-text-display | ❌ | 0개 | ❌ | 🟢 1/10 | | v2-repeat-screen-modal | ❌ | 0개 | ❌ | 🟢 1/10 | -| v2-unified-repeater | ❌ | 0개 | ❌ | 🟢 1/10 | +| v2-repeater | ❌ | 0개 | ❌ | 🟢 1/10 | -### 2.3 Unified 폼 컴포넌트 결합도 상세 +### 2.3 V2 폼 컴포넌트 결합도 상세 | 컴포넌트 | buttonActions Import | CustomEvent 사용 | window.__ 사용 | 결합도 점수 | |---------|---------------------|------------------|----------------|------------| -| **UnifiedRepeater** | ❌ | 7개 수신/발생 | 2개 사용 | 🔴 8/10 | -| **UnifiedFormContext** | ❌ | 3개 발생 | ❌ | 🟠 4/10 | -| UnifiedInput | ❌ | 0개 | ❌ | 🟢 1/10 | -| UnifiedSelect | ❌ | 0개 | ❌ | 🟢 1/10 | -| UnifiedDate | ❌ | 0개 | ❌ | 🟢 1/10 | -| UnifiedLayout | ❌ | 0개 | ❌ | 🟢 1/10 | -| UnifiedGroup | ❌ | 0개 | ❌ | 🟢 1/10 | -| UnifiedHierarchy | ❌ | 0개 | ❌ | 🟢 1/10 | -| UnifiedList | ❌ | 0개 (TableList 래핑) | ❌ | 🟢 2/10 | -| UnifiedMedia | ❌ | 0개 | ❌ | 🟢 1/10 | -| UnifiedBiz | ❌ | 0개 | ❌ | 🟢 1/10 | +| **V2Repeater** | ❌ | 7개 수신/발생 | 2개 사용 | 🔴 8/10 | +| **V2FormContext** | ❌ | 3개 발생 | ❌ | 🟠 4/10 | +| V2Input | ❌ | 0개 | ❌ | 🟢 1/10 | +| V2Select | ❌ | 0개 | ❌ | 🟢 1/10 | +| V2Date | ❌ | 0개 | ❌ | 🟢 1/10 | +| V2Layout | ❌ | 0개 | ❌ | 🟢 1/10 | +| V2Group | ❌ | 0개 | ❌ | 🟢 1/10 | +| V2Hierarchy | ❌ | 0개 | ❌ | 🟢 1/10 | +| V2List | ❌ | 0개 (TableList 래핑) | ❌ | 🟢 2/10 | +| V2Media | ❌ | 0개 | ❌ | 🟢 1/10 | +| V2Biz | ❌ | 0개 | ❌ | 🟢 1/10 | -**UnifiedRepeater 상세:** +**V2Repeater 상세:** ```typescript // 전역 상태 사용 -window.__unifiedRepeaterInstances = new Set(); -window.__unifiedRepeaterInstances.add(targetTableName); +window.__v2RepeaterInstances = new Set(); +window.__v2RepeaterInstances.add(targetTableName); // CustomEvent 수신 window.addEventListener("repeaterSave", handleSaveEvent); @@ -171,7 +171,7 @@ window.addEventListener("componentDataTransfer", handleComponentDataTransfer); window.addEventListener("splitPanelDataTransfer", handleSplitPanelDataTransfer); ``` -**UnifiedFormContext 상세:** +**V2FormContext 상세:** ```typescript // CustomEvent 발생 (레거시 호환) window.dispatchEvent(new CustomEvent("beforeFormSave", { detail: eventDetail })); @@ -211,7 +211,7 @@ window.dispatchEvent(new CustomEvent("afterFormSave", { detail: { ... } })); │ │ ▼ ▼ ┌───────────┐ ┌───────────┐ - │Unified │ │Unified │ + │V2 │ │V2 │ │Repeater │ │FormContext│ └───────────┘ └───────────┘ ``` @@ -227,10 +227,10 @@ window.dispatchEvent(new CustomEvent("afterFormSave", { detail: { ... } })); | `refreshTable` | v2-button-primary, buttonActions | 테이블 데이터 새로고침 | | `closeEditModal` | v2-button-primary, buttonActions | 수정 모달 닫기 | | `saveSuccessInModal` | v2-button-primary, buttonActions | 저장 성공 알림 (연속 등록) | -| `beforeFormSave` | UnifiedFormContext, buttonActions | 저장 전 데이터 수집 | -| `afterFormSave` | UnifiedFormContext | 저장 완료 알림 | +| `beforeFormSave` | V2FormContext, buttonActions | 저장 전 데이터 수집 | +| `afterFormSave` | V2FormContext | 저장 완료 알림 | | `tableListDataChange` | v2-table-list | 테이블 데이터 변경 알림 | -| `repeaterDataChange` | UnifiedRepeater | 리피터 데이터 변경 알림 | +| `repeaterDataChange` | V2Repeater | 리피터 데이터 변경 알림 | | `repeaterSave` | buttonActions | 리피터 저장 요청 | | `openScreenModal` | v2-split-panel-layout | 화면 모달 열기 | | `refreshCardDisplay` | buttonActions | 카드 디스플레이 새로고침 | @@ -240,13 +240,13 @@ window.dispatchEvent(new CustomEvent("afterFormSave", { detail: { ... } })); | 이벤트명 | 수신 컴포넌트 | 처리 내용 | |---------|-------------|----------| | `refreshTable` | v2-table-list, v2-split-panel-layout | 데이터 재조회 | -| `beforeFormSave` | v2-repeat-container, UnifiedRepeater | formData에 섹션 데이터 추가 | +| `beforeFormSave` | v2-repeat-container, V2Repeater | formData에 섹션 데이터 추가 | | `tableListDataChange` | v2-aggregation-widget, v2-repeat-container | 집계 재계산, 데이터 동기화 | | `repeaterDataChange` | v2-aggregation-widget, v2-repeat-container | 집계 재계산, 데이터 동기화 | -| `repeaterSave` | UnifiedRepeater | 리피터 데이터 저장 실행 | +| `repeaterSave` | V2Repeater | 리피터 데이터 저장 실행 | | `selectionChange` | v2-aggregation-widget | 선택 기반 집계 | -| `componentDataTransfer` | UnifiedRepeater | 컴포넌트 간 데이터 전달 | -| `splitPanelDataTransfer` | UnifiedRepeater | 분할 패널 데이터 전달 | +| `componentDataTransfer` | V2Repeater | 컴포넌트 간 데이터 전달 | +| `splitPanelDataTransfer` | V2Repeater | 분할 패널 데이터 전달 | | `refreshCardDisplay` | v2-card-display | 카드 데이터 재조회 | --- @@ -255,7 +255,7 @@ window.dispatchEvent(new CustomEvent("afterFormSave", { detail: { ... } })); | 전역 변수 | 사용 컴포넌트 | 용도 | 위험도 | |----------|-------------|------|--------| -| `window.__unifiedRepeaterInstances` | UnifiedRepeater, buttonActions | 리피터 인스턴스 추적 | 🟠 중간 | +| `window.__v2RepeaterInstances` | V2Repeater, buttonActions | 리피터 인스턴스 추적 | 🟠 중간 | | `window.__relatedButtonsTargetTables` | v2-table-list | 관련 버튼 대상 테이블 | 🟠 중간 | | `window.__relatedButtonsSelectedData` | v2-table-list, buttonActions | 관련 버튼 선택 데이터 | 🟠 중간 | | `window.__dataRegistry` | v2-table-list (v1/v2) | 테이블 데이터 레지스트리 | 🟠 중간 | @@ -272,12 +272,12 @@ window.dispatchEvent(new CustomEvent("afterFormSave", { detail: { ... } })); | 🟠 중간 (4-6점) | 4개 | v2-repeat-container, v2-split-panel-layout, v2-aggregation-widget, v2-tabs-widget | | 🟢 낮음 (1-3점) | 12개 | 나머지 | -### 6.2 Unified 컴포넌트 (11개) +### 6.2 V2 컴포넌트 (11개) | 결합도 수준 | 개수 | 컴포넌트 | |------------|------|---------| -| 🔴 높음 (7-10점) | 1개 | UnifiedRepeater | -| 🟠 중간 (4-6점) | 1개 | UnifiedFormContext | +| 🔴 높음 (7-10점) | 1개 | V2Repeater | +| 🟠 중간 (4-6점) | 1개 | V2FormContext | | 🟢 낮음 (1-3점) | 9개 | 나머지 | ### 6.3 전체 결합도 분포 @@ -288,14 +288,14 @@ window.dispatchEvent(new CustomEvent("afterFormSave", { detail: { ... } })); 높은 결합도 (🔴): 3개 (10.3%) ├── v2-button-primary ├── v2-table-list -└── UnifiedRepeater +└── V2Repeater 중간 결합도 (🟠): 5개 (17.2%) ├── v2-repeat-container ├── v2-split-panel-layout ├── v2-aggregation-widget ├── v2-tabs-widget -└── UnifiedFormContext +└── V2FormContext 낮은 결합도 (🟢): 21개 (72.5%) └── 나머지 모든 컴포넌트 @@ -318,7 +318,7 @@ v2-table-list 오류 발생 시: ├── related-button 이벤트 미발생 → 관련 버튼 비활성화 └── 전역 상태 오염 가능성 -UnifiedRepeater 오류 발생 시: +V2Repeater 오류 발생 시: ├── beforeFormSave 처리 실패 → 리피터 데이터 저장 누락 ├── repeaterSave 수신 실패 → 저장 요청 무시 └── 전역 인스턴스 레지스트리 오류 @@ -330,7 +330,7 @@ UnifiedRepeater 오류 발생 시: |---------|-----------------|----------| | v2-button-primary | 저장/삭제 전체 | ❌ 격리 안됨 | | v2-table-list | 집계/관련버튼 | ❌ 격리 안됨 | -| UnifiedRepeater | 리피터 저장 | ❌ 격리 안됨 | +| V2Repeater | 리피터 저장 | ❌ 격리 안됨 | | v2-aggregation-widget | 자신만 | ✅ 부분 격리 | | v2-repeat-container | 자신만 | ✅ 부분 격리 | | 나머지 21개 | 자신만 | ✅ 완전 격리 | @@ -357,7 +357,7 @@ UnifiedRepeater 오류 발생 시: |---------|---------|----------| | 1 | v2-button-primary | buttonActions 의존성 제거, 독립 저장 서비스 | | 2 | v2-table-list | 전역 상태 제거, EventBus 전환 | -| 3 | UnifiedRepeater | 전역 상태 제거, EventBus 전환 | +| 3 | V2Repeater | 전역 상태 제거, EventBus 전환 | ### 8.3 3단계: 이벤트 통합 (2-3일) @@ -422,7 +422,7 @@ UnifiedRepeater 오류 발생 시: |---------|-----------------|-------------------|-------------|------| | **v2-button-primary** | ✅ | ✅ | ✅ | 완료 | | **v2-table-list** | ✅ | - | ✅ | 완료 | -| **UnifiedRepeater** | ✅ | - | ✅ | 완료 | +| **V2Repeater** | ✅ | - | ✅ | 완료 | ### 10.3 아키텍처 특징 @@ -469,7 +469,7 @@ useEffect(() => { ### 11.1 현재 상태 요약 - **전체 29개 컴포넌트 중 72.5%(21개)는 이미 낮은 결합도**를 가지고 있어 독립적으로 동작 -- **핵심 문제 컴포넌트 3개 (v2-button-primary, v2-table-list, UnifiedRepeater) 마이그레이션 완료** +- **핵심 문제 컴포넌트 3개 (v2-button-primary, v2-table-list, V2Repeater) 마이그레이션 완료** - **buttonActions.ts (7,145줄)**는 추후 분할 예정 (현재는 동작 유지) ### 11.2 달성 목표 @@ -514,22 +514,22 @@ frontend/ │ │ ├── v2-table-search-widget/ │ │ ├── v2-tabs-widget/ │ │ ├── v2-text-display/ -│ │ └── v2-unified-repeater/ +│ │ └── v2-repeater/ │ └── utils/ │ └── buttonActions.ts (7,145줄) └── components/ - └── unified/ - ├── UnifiedInput.tsx - ├── UnifiedSelect.tsx - ├── UnifiedDate.tsx - ├── UnifiedRepeater.tsx - ├── UnifiedLayout.tsx - ├── UnifiedGroup.tsx - ├── UnifiedHierarchy.tsx - ├── UnifiedList.tsx - ├── UnifiedMedia.tsx - ├── UnifiedBiz.tsx - └── UnifiedFormContext.tsx + └── v2/ + ├── V2Input.tsx + ├── V2Select.tsx + ├── V2Date.tsx + ├── V2Repeater.tsx + ├── V2Layout.tsx + ├── V2Group.tsx + ├── V2Hierarchy.tsx + ├── V2List.tsx + ├── V2Media.tsx + ├── V2Biz.tsx + └── V2FormContext.tsx ``` ## 부록 B: V2 Core 파일 구조 (구현됨) diff --git a/docs/V2_COMPONENT_GUIDE.md b/docs/kjs/V2_COMPONENT_GUIDE.md similarity index 92% rename from docs/V2_COMPONENT_GUIDE.md rename to docs/kjs/V2_COMPONENT_GUIDE.md index b3e0fbc8..37f2247a 100644 --- a/docs/V2_COMPONENT_GUIDE.md +++ b/docs/kjs/V2_COMPONENT_GUIDE.md @@ -67,7 +67,7 @@ V2(Version 2) 컴포넌트는 기존 레거시 컴포넌트의 문제점을 해 |------------|------|------| | `v2-rack-structure` | 렉 구조 | 창고 렉 구조 표시 | | `v2-repeat-screen-modal` | 반복 화면 모달 | 반복 가능한 화면 모달 | -| `v2-unified-repeater` | 통합 리피터 | 통합 리피터 테이블 | +| `v2-repeater` | 통합 리피터 | 통합 리피터 테이블 | --- @@ -172,10 +172,10 @@ import { V2ErrorBoundary } from "@/lib/v2-core"; |--------|------|--------|--------| | `v2:table:refresh` | 테이블 새로고침 | v2-button-primary | v2-table-list | | `v2:table:data:change` | 테이블 데이터 변경 | v2-table-list | v2-aggregation-widget | -| `v2:form:save:collect` | 폼 저장 전 데이터 수집 | buttonActions | v2-repeat-container, UnifiedRepeater | +| `v2:form:save:collect` | 폼 저장 전 데이터 수집 | buttonActions | v2-repeat-container, V2Repeater | | `v2:modal:close` | 모달 닫기 | v2-button-primary | EditModal | | `v2:modal:save:success` | 모달 저장 성공 | v2-button-primary | EditModal | -| `v2:repeater:save` | 리피터 저장 | buttonActions | UnifiedRepeater | +| `v2:repeater:save` | 리피터 저장 | buttonActions | V2Repeater | | `v2:component:error` | 컴포넌트 에러 | V2ErrorBoundary | 로깅/모니터링 | ### 4.2 이벤트 흐름 다이어그램 @@ -333,7 +333,7 @@ export const BadConfigPanel: React.FC = ({ config, onChange }) => { |---------|-------------|---------------|-------------| | v2-button-primary | ✅ | ✅ | ✅ | | v2-table-list | ✅ | ✅ | ✅ | -| UnifiedRepeater | ✅ | ✅ | ✅ | +| V2Repeater | ✅ | ✅ | ✅ | ### 7.3 장애 격리 검증 @@ -341,7 +341,7 @@ export const BadConfigPanel: React.FC = ({ config, onChange }) => { v2-button-primary 에러 발생 시: ├── V2ErrorBoundary 캐치 → 버튼만 에러 UI 표시 ├── v2-table-list: 정상 동작 ✅ -└── UnifiedRepeater: 정상 동작 ✅ +└── V2Repeater: 정상 동작 ✅ v2-table-list 에러 발생 시: ├── V2ErrorBoundary 캐치 → 테이블만 에러 UI 표시 @@ -351,30 +351,30 @@ v2-table-list 에러 발생 시: --- -## 8. Unified 폼 컴포넌트 +## 8. V2 폼 컴포넌트 ### 8.1 목록 (11개) | 컴포넌트 | 파일 | 용도 | |---------|------|------| -| UnifiedInput | UnifiedInput.tsx | 텍스트/숫자/이메일/채번 입력 | -| UnifiedSelect | UnifiedSelect.tsx | 선택박스/라디오/체크박스/카테고리 | -| UnifiedDate | UnifiedDate.tsx | 날짜/시간 입력 | -| UnifiedRepeater | UnifiedRepeater.tsx | 리피터 테이블 | -| UnifiedLayout | UnifiedLayout.tsx | 레이아웃 컨테이너 | -| UnifiedGroup | UnifiedGroup.tsx | 그룹 컨테이너 | -| UnifiedHierarchy | UnifiedHierarchy.tsx | 계층 구조 표시 | -| UnifiedList | UnifiedList.tsx | 리스트 표시 | -| UnifiedMedia | UnifiedMedia.tsx | 파일/이미지/비디오 | -| UnifiedBiz | UnifiedBiz.tsx | 비즈니스 컴포넌트 | -| UnifiedFormContext | UnifiedFormContext.tsx | 폼 상태 관리 | +| V2Input | V2Input.tsx | 텍스트/숫자/이메일/채번 입력 | +| V2Select | V2Select.tsx | 선택박스/라디오/체크박스/카테고리 | +| V2Date | V2Date.tsx | 날짜/시간 입력 | +| V2Repeater | V2Repeater.tsx | 리피터 테이블 | +| V2Layout | V2Layout.tsx | 레이아웃 컨테이너 | +| V2Group | V2Group.tsx | 그룹 컨테이너 | +| V2Hierarchy | V2Hierarchy.tsx | 계층 구조 표시 | +| V2List | V2List.tsx | 리스트 표시 | +| V2Media | V2Media.tsx | 파일/이미지/비디오 | +| V2Biz | V2Biz.tsx | 비즈니스 컴포넌트 | +| V2FormContext | V2FormContext.tsx | 폼 상태 관리 | ### 8.2 inputType 자동 처리 -Unified 컴포넌트는 `inputType`에 따라 자동으로 적절한 UI를 렌더링합니다: +V2 컴포넌트는 `inputType`에 따라 자동으로 적절한 UI를 렌더링합니다: ```typescript -// UnifiedInput.tsx +// V2Input.tsx switch (inputType) { case "numbering": // 채번 규칙 자동 조회 및 코드 생성 @@ -386,7 +386,7 @@ switch (inputType) { break; } -// UnifiedSelect.tsx +// V2Select.tsx switch (inputType) { case "category": // 카테고리 값 자동 조회 및 드롭다운 표시 @@ -468,8 +468,8 @@ V2 Core: V2 컴포넌트: - frontend/lib/registry/components/v2-*/ -Unified 폼 컴포넌트: -- frontend/components/unified/ +V2 폼 컴포넌트: +- frontend/components/v2/ 채번/카테고리 테스트 테이블: - db/migrations/040_create_numbering_rules_test.sql @@ -534,6 +534,6 @@ frontend/lib/registry/components/ ├── v2-table-search-widget/ ├── v2-tabs-widget/ ├── v2-text-display/ -└── v2-unified-repeater/ +└── v2-repeater/ ``` diff --git a/docs/WIDTH_REMOVAL_MIGRATION_PLAN.md b/docs/kjs/WIDTH_REMOVAL_MIGRATION_PLAN.md similarity index 100% rename from docs/WIDTH_REMOVAL_MIGRATION_PLAN.md rename to docs/kjs/WIDTH_REMOVAL_MIGRATION_PLAN.md diff --git a/docs/input-type-detail-type-system.md b/docs/kjs/input-type-detail-type-system.md similarity index 100% rename from docs/input-type-detail-type-system.md rename to docs/kjs/input-type-detail-type-system.md diff --git a/docs/node-action-target-selection-plan.md b/docs/kjs/node-action-target-selection-plan.md similarity index 100% rename from docs/node-action-target-selection-plan.md rename to docs/kjs/node-action-target-selection-plan.md diff --git a/docs/phase0-component-usage-analysis.md b/docs/kjs/phase0-component-usage-analysis.md similarity index 71% rename from docs/phase0-component-usage-analysis.md rename to docs/kjs/phase0-component-usage-analysis.md index 4c74ffd5..54c437eb 100644 --- a/docs/phase0-component-usage-analysis.md +++ b/docs/kjs/phase0-component-usage-analysis.md @@ -15,29 +15,29 @@ ### 상위 15개 컴포넌트 -| 순위 | 컴포넌트 | 사용 횟수 | 사용 화면 수 | Unified 매핑 | +| 순위 | 컴포넌트 | 사용 횟수 | 사용 화면 수 | V2 매핑 | | :--: | :-------------------------- | :-------: | :----------: | :------------------------------ | -| 1 | button-primary | 571 | 364 | UnifiedInput (type: button) | -| 2 | text-input | 805 | 166 | **UnifiedInput (type: text)** | -| 3 | table-list | 130 | 130 | UnifiedList (viewMode: table) | -| 4 | table-search-widget | 127 | 127 | UnifiedList (searchable: true) | -| 5 | select-basic | 121 | 76 | **UnifiedSelect** | -| 6 | number-input | 86 | 34 | **UnifiedInput (type: number)** | -| 7 | date-input | 83 | 51 | **UnifiedDate** | -| 8 | file-upload | 41 | 18 | UnifiedMedia (type: file) | -| 9 | tabs-widget | 39 | 39 | UnifiedGroup (type: tabs) | -| 10 | split-panel-layout | 39 | 39 | UnifiedLayout (type: split) | -| 11 | category-manager | 38 | 38 | UnifiedBiz (type: category) | -| 12 | numbering-rule | 31 | 31 | UnifiedBiz (type: numbering) | +| 1 | button-primary | 571 | 364 | V2Input (type: button) | +| 2 | text-input | 805 | 166 | **V2Input (type: text)** | +| 3 | table-list | 130 | 130 | V2List (viewMode: table) | +| 4 | table-search-widget | 127 | 127 | V2List (searchable: true) | +| 5 | select-basic | 121 | 76 | **V2Select** | +| 6 | number-input | 86 | 34 | **V2Input (type: number)** | +| 7 | date-input | 83 | 51 | **V2Date** | +| 8 | file-upload | 41 | 18 | V2Media (type: file) | +| 9 | tabs-widget | 39 | 39 | V2Group (type: tabs) | +| 10 | split-panel-layout | 39 | 39 | V2Layout (type: split) | +| 11 | category-manager | 38 | 38 | V2Biz (type: category) | +| 12 | numbering-rule | 31 | 31 | V2Biz (type: numbering) | | 13 | selected-items-detail-input | 29 | 29 | 복합 컴포넌트 | -| 14 | modal-repeater-table | 25 | 25 | UnifiedList (modal: true) | -| 15 | image-widget | 29 | 29 | UnifiedMedia (type: image) | +| 14 | modal-repeater-table | 25 | 25 | V2List (modal: true) | +| 15 | image-widget | 29 | 29 | V2Media (type: image) | --- -## 2. Unified 컴포넌트별 통합 대상 분석 +## 2. V2 컴포넌트별 통합 대상 분석 -### UnifiedInput (예상 통합 대상: 891개) +### V2Input (예상 통합 대상: 891개) | 기존 컴포넌트 | 사용 횟수 | 비율 | | :------------ | :-------: | :---: | @@ -46,7 +46,7 @@ **우선순위: 1위** - 가장 많이 사용되는 컴포넌트 -### UnifiedSelect (예상 통합 대상: 140개) +### V2Select (예상 통합 대상: 140개) | 기존 컴포넌트 | 사용 횟수 | widgetType | | :------------------------ | :-------: | :--------- | @@ -59,7 +59,7 @@ **우선순위: 2위** - 다양한 모드 지원 필요 -### UnifiedDate (예상 통합 대상: 83개) +### V2Date (예상 통합 대상: 83개) | 기존 컴포넌트 | 사용 횟수 | | :---------------- | :-------: | @@ -69,7 +69,7 @@ **우선순위: 3위** -### UnifiedList (예상 통합 대상: 283개) +### V2List (예상 통합 대상: 283개) | 기존 컴포넌트 | 사용 횟수 | 비고 | | :-------------------- | :-------: | :---------- | @@ -82,14 +82,14 @@ **우선순위: 4위** - 핵심 데이터 표시 컴포넌트 -### UnifiedMedia (예상 통합 대상: 70개) +### V2Media (예상 통합 대상: 70개) | 기존 컴포넌트 | 사용 횟수 | | :------------ | :-------: | | file-upload | 41 | | image-widget | 29 | -### UnifiedLayout (예상 통합 대상: 62개) +### V2Layout (예상 통합 대상: 62개) | 기존 컴포넌트 | 사용 횟수 | | :------------------ | :-------: | @@ -97,7 +97,7 @@ | screen-split-panel | 21 | | split-panel-layout2 | 2 | -### UnifiedGroup (예상 통합 대상: 99개) +### V2Group (예상 통합 대상: 99개) | 기존 컴포넌트 | 사용 횟수 | | :-------------------- | :-------: | @@ -109,7 +109,7 @@ | universal-form-modal | 7 | | repeat-screen-modal | 5 | -### UnifiedBiz (예상 통합 대상: 79개) +### V2Biz (예상 통합 대상: 79개) | 기존 컴포넌트 | 사용 횟수 | | :--------------------- | :-------: | @@ -127,27 +127,27 @@ ### Phase 1 우선순위 (즉시 효과가 큰 컴포넌트) -| 순위 | Unified 컴포넌트 | 통합 대상 수 | 영향 화면 수 | 이유 | +| 순위 | V2 컴포넌트 | 통합 대상 수 | 영향 화면 수 | 이유 | | :---: | :---------------- | :----------: | :----------: | :--------------- | -| **1** | **UnifiedInput** | 891개 | 200+ | 가장 많이 사용 | -| **2** | **UnifiedSelect** | 140개 | 100+ | 다양한 모드 필요 | -| **3** | **UnifiedDate** | 83개 | 51 | 비교적 단순 | +| **1** | **V2Input** | 891개 | 200+ | 가장 많이 사용 | +| **2** | **V2Select** | 140개 | 100+ | 다양한 모드 필요 | +| **3** | **V2Date** | 83개 | 51 | 비교적 단순 | ### Phase 2 우선순위 (데이터 표시 컴포넌트) -| 순위 | Unified 컴포넌트 | 통합 대상 수 | 이유 | +| 순위 | V2 컴포넌트 | 통합 대상 수 | 이유 | | :---: | :---------------- | :----------: | :--------------- | -| **4** | **UnifiedList** | 283개 | 핵심 데이터 표시 | -| **5** | **UnifiedLayout** | 62개 | 레이아웃 구조 | -| **6** | **UnifiedGroup** | 99개 | 콘텐츠 그룹화 | +| **4** | **V2List** | 283개 | 핵심 데이터 표시 | +| **5** | **V2Layout** | 62개 | 레이아웃 구조 | +| **6** | **V2Group** | 99개 | 콘텐츠 그룹화 | ### Phase 3 우선순위 (특수 컴포넌트) -| 순위 | Unified 컴포넌트 | 통합 대상 수 | 이유 | +| 순위 | V2 컴포넌트 | 통합 대상 수 | 이유 | | :---: | :------------------- | :----------: | :------------ | -| **7** | **UnifiedMedia** | 70개 | 파일/이미지 | -| **8** | **UnifiedBiz** | 79개 | 비즈니스 특화 | -| **9** | **UnifiedHierarchy** | 0개 | 신규 기능 | +| **7** | **V2Media** | 70개 | 파일/이미지 | +| **8** | **V2Biz** | 79개 | 비즈니스 특화 | +| **9** | **V2Hierarchy** | 0개 | 신규 기능 | --- @@ -156,8 +156,8 @@ ### 4.1 button-primary 분리 검토 - 사용량: 571개 (1위) -- 현재 계획: UnifiedInput에 포함 -- **제안**: 별도 `UnifiedButton` 컴포넌트로 분리 검토 +- 현재 계획: V2Input에 포함 +- **제안**: 별도 `V2Button` 컴포넌트로 분리 검토 - 버튼은 입력과 성격이 다름 - 액션 타입, 스타일, 권한 등 복잡한 설정 필요 @@ -181,5 +181,5 @@ 1. [ ] 데이터 마이그레이션 전략 설계 (Phase 0-2) 2. [ ] sys_input_type JSON Schema 설계 (Phase 0-3) 3. [ ] DynamicConfigPanel 프로토타입 (Phase 0-4) -4. [ ] UnifiedInput 구현 시작 (Phase 1-1) +4. [ ] V2Input 구현 시작 (Phase 1-1) diff --git a/docs/phase0-migration-strategy.md b/docs/kjs/phase0-migration-strategy.md similarity index 80% rename from docs/phase0-migration-strategy.md rename to docs/kjs/phase0-migration-strategy.md index 6ee91643..ef257060 100644 --- a/docs/phase0-migration-strategy.md +++ b/docs/kjs/phase0-migration-strategy.md @@ -67,8 +67,8 @@ "componentConfig": { ... }, // 신규 필드 추가 - "unifiedType": "UnifiedInput", // 새로운 통합 컴포넌트 타입 - "unifiedConfig": { // 새로운 설정 구조 + "v2Type": "V2Input", // 새로운 통합 컴포넌트 타입 + "v2Config": { // 새로운 설정 구조 "type": "text", "format": "none", "placeholder": "텍스트를 입력하세요" @@ -87,13 +87,13 @@ ### 2.2 렌더링 로직 수정 ```typescript -// 렌더러에서 unifiedType 우선 사용 +// 렌더러에서 v2Type 우선 사용 function renderComponent(props: ComponentProps) { - // 신규 타입이 있으면 Unified 컴포넌트 사용 - if (props.unifiedType) { - return ; } @@ -109,7 +109,7 @@ function renderComponent(props: ComponentProps) { ## 3. 컴포넌트별 매핑 규칙 -### 3.1 text-input → UnifiedInput +### 3.1 text-input → V2Input ```typescript // AS-IS @@ -126,8 +126,8 @@ function renderComponent(props: ComponentProps) { // TO-BE { - "unifiedType": "UnifiedInput", - "unifiedConfig": { + "v2Type": "V2Input", + "v2Config": { "type": "text", // componentConfig.webType 또는 "text" "format": "none", // componentConfig.format "placeholder": "..." // componentConfig.placeholder @@ -135,7 +135,7 @@ function renderComponent(props: ComponentProps) { } ``` -### 3.2 number-input → UnifiedInput +### 3.2 number-input → V2Input ```typescript // AS-IS @@ -152,8 +152,8 @@ function renderComponent(props: ComponentProps) { // TO-BE { - "unifiedType": "UnifiedInput", - "unifiedConfig": { + "v2Type": "V2Input", + "v2Config": { "type": "number", "min": 0, "max": 100, @@ -162,7 +162,7 @@ function renderComponent(props: ComponentProps) { } ``` -### 3.3 select-basic → UnifiedSelect +### 3.3 select-basic → V2Select ```typescript // AS-IS (code 타입) @@ -178,8 +178,8 @@ function renderComponent(props: ComponentProps) { // TO-BE { - "unifiedType": "UnifiedSelect", - "unifiedConfig": { + "v2Type": "V2Select", + "v2Config": { "mode": "dropdown", "source": "code", "codeGroup": "ORDER_STATUS" @@ -200,8 +200,8 @@ function renderComponent(props: ComponentProps) { // TO-BE { - "unifiedType": "UnifiedSelect", - "unifiedConfig": { + "v2Type": "V2Select", + "v2Config": { "mode": "dropdown", "source": "entity", "searchable": true, @@ -211,7 +211,7 @@ function renderComponent(props: ComponentProps) { } ``` -### 3.4 date-input → UnifiedDate +### 3.4 date-input → V2Date ```typescript // AS-IS @@ -226,8 +226,8 @@ function renderComponent(props: ComponentProps) { // TO-BE { - "unifiedType": "UnifiedDate", - "unifiedConfig": { + "v2Type": "V2Date", + "v2Config": { "type": "date", "format": "YYYY-MM-DD" } @@ -245,11 +245,11 @@ function renderComponent(props: ComponentProps) { interface MigrationResult { success: boolean; - unifiedType: string; - unifiedConfig: Record; + v2Type: string; + v2Config: Record; } -export function migrateToUnified( +export function migrateToV2( componentType: string, componentConfig: Record ): MigrationResult { @@ -258,8 +258,8 @@ export function migrateToUnified( case 'text-input': return { success: true, - unifiedType: 'UnifiedInput', - unifiedConfig: { + v2Type: 'V2Input', + v2Config: { type: componentConfig.webType || 'text', format: componentConfig.format || 'none', placeholder: componentConfig.placeholder @@ -269,8 +269,8 @@ export function migrateToUnified( case 'number-input': return { success: true, - unifiedType: 'UnifiedInput', - unifiedConfig: { + v2Type: 'V2Input', + v2Config: { type: 'number', min: componentConfig.min, max: componentConfig.max, @@ -281,8 +281,8 @@ export function migrateToUnified( case 'select-basic': return { success: true, - unifiedType: 'UnifiedSelect', - unifiedConfig: { + v2Type: 'V2Select', + v2Config: { mode: 'dropdown', source: componentConfig.webType || 'static', codeGroup: componentConfig.codeCategory, @@ -295,8 +295,8 @@ export function migrateToUnified( case 'date-input': return { success: true, - unifiedType: 'UnifiedDate', - unifiedConfig: { + v2Type: 'V2Date', + v2Config: { type: componentConfig.webType || 'date', format: componentConfig.format } @@ -305,8 +305,8 @@ export function migrateToUnified( default: return { success: false, - unifiedType: '', - unifiedConfig: {} + v2Type: '', + v2Config: {} }; } } @@ -322,8 +322,8 @@ SELECT * FROM screen_layouts; -- 마이그레이션 실행 (text-input 예시) UPDATE screen_layouts SET properties = properties || jsonb_build_object( - 'unifiedType', 'UnifiedInput', - 'unifiedConfig', jsonb_build_object( + 'v2Type', 'V2Input', + 'v2Config', jsonb_build_object( 'type', COALESCE(properties->'componentConfig'->>'webType', 'text'), 'format', COALESCE(properties->'componentConfig'->>'format', 'none'), 'placeholder', properties->'componentConfig'->>'placeholder' @@ -352,7 +352,7 @@ WHERE sl.layout_id = slb.layout_id; -- 또는 신규 필드만 제거 UPDATE screen_layouts -SET properties = properties - 'unifiedType' - 'unifiedConfig' - '_migration'; +SET properties = properties - 'v2Type' - 'v2Config' - '_migration'; ``` ### 5.2 단계적 롤백 @@ -362,7 +362,7 @@ SET properties = properties - 'unifiedType' - 'unifiedConfig' - '_migration'; async function rollbackScreen(screenId: number) { await db.query(` UPDATE screen_layouts sl - SET properties = properties - 'unifiedType' - 'unifiedConfig' - '_migration' + SET properties = properties - 'v2Type' - 'v2Config' - '_migration' WHERE screen_id = $1 `, [screenId]); } @@ -375,9 +375,9 @@ async function rollbackScreen(screenId: number) { | 단계 | 작업 | 대상 | 시점 | |:---:|:---|:---|:---| | 1 | 백업 테이블 생성 | 전체 | Phase 1 시작 전 | -| 2 | UnifiedInput 마이그레이션 | text-input, number-input | Phase 1 중 | -| 3 | UnifiedSelect 마이그레이션 | select-basic | Phase 1 중 | -| 4 | UnifiedDate 마이그레이션 | date-input | Phase 1 중 | +| 2 | V2Input 마이그레이션 | text-input, number-input | Phase 1 중 | +| 3 | V2Select 마이그레이션 | select-basic | Phase 1 중 | +| 4 | V2Date 마이그레이션 | date-input | Phase 1 중 | | 5 | 검증 및 테스트 | 전체 | Phase 1 완료 후 | | 6 | 레거시 필드 제거 | 전체 | Phase 5 (추후) | diff --git a/docs/screen-management-dynamic-system-plan.md b/docs/kjs/screen-management-dynamic-system-plan.md similarity index 100% rename from docs/screen-management-dynamic-system-plan.md rename to docs/kjs/screen-management-dynamic-system-plan.md diff --git a/docs/shadcn-ui-레이아웃-패턴-분석-보고서.md b/docs/kjs/shadcn-ui-레이아웃-패턴-분석-보고서.md similarity index 99% rename from docs/shadcn-ui-레이아웃-패턴-분석-보고서.md rename to docs/kjs/shadcn-ui-레이아웃-패턴-분석-보고서.md index 521a8840..caac803c 100644 --- a/docs/shadcn-ui-레이아웃-패턴-분석-보고서.md +++ b/docs/kjs/shadcn-ui-레이아웃-패턴-분석-보고서.md @@ -477,7 +477,7 @@ className={cn( - ✅ `FileComponentConfigPanel.tsx`: `text-gray-900` → `text-foreground`, `text-blue-*` → `text-primary` - ✅ `ButtonConfigPanel.tsx`: 모든 `text-gray-*`, `bg-gray-*`, `hover:bg-gray-*` 교체 -- ✅ `UnifiedPropertiesPanel.tsx`: 모든 `text-gray-*`, `border-gray-*` 교체 +- ✅ `V2PropertiesPanel.tsx`: 모든 `text-gray-*`, `border-gray-*` 교체 - ✅ `app/(main)/admin/page.tsx`: 전체 페이지 하드코딩 색상 교체 - ✅ `CardDisplayComponent.tsx`: 모든 `text-gray-*`, `bg-gray-*`, 인라인 색상 교체 - ✅ `getComponentConfigPanel.tsx`: 로딩 상태 하드코딩 색상 교체 diff --git a/docs/shadcn-ui-완전가이드.md b/docs/kjs/shadcn-ui-완전가이드.md similarity index 100% rename from docs/shadcn-ui-완전가이드.md rename to docs/kjs/shadcn-ui-완전가이드.md diff --git a/docs/shadcn-ui-적용-상태-분석-보고서.md b/docs/kjs/shadcn-ui-적용-상태-분석-보고서.md similarity index 100% rename from docs/shadcn-ui-적용-상태-분석-보고서.md rename to docs/kjs/shadcn-ui-적용-상태-분석-보고서.md diff --git a/docs/권한_그룹_관리_상세_가이드.md b/docs/kjs/권한_그룹_관리_상세_가이드.md similarity index 100% rename from docs/권한_그룹_관리_상세_가이드.md rename to docs/kjs/권한_그룹_관리_상세_가이드.md diff --git a/docs/권한_그룹_메뉴_필터링_가이드.md b/docs/kjs/권한_그룹_메뉴_필터링_가이드.md similarity index 100% rename from docs/권한_그룹_메뉴_필터링_가이드.md rename to docs/kjs/권한_그룹_메뉴_필터링_가이드.md diff --git a/docs/권한_그룹_시스템_설계.md b/docs/kjs/권한_그룹_시스템_설계.md similarity index 100% rename from docs/권한_그룹_시스템_설계.md rename to docs/kjs/권한_그룹_시스템_설계.md diff --git a/docs/권한_시스템_마이그레이션_완료.md b/docs/kjs/권한_시스템_마이그레이션_완료.md similarity index 100% rename from docs/권한_시스템_마이그레이션_완료.md rename to docs/kjs/권한_시스템_마이그레이션_완료.md diff --git a/docs/권한_체계_가이드.md b/docs/kjs/권한_체계_가이드.md similarity index 100% rename from docs/권한_체계_가이드.md rename to docs/kjs/권한_체계_가이드.md diff --git a/docs/그리드_컬럼수_옵션_통합.md b/docs/kjs/그리드_컬럼수_옵션_통합.md similarity index 100% rename from docs/그리드_컬럼수_옵션_통합.md rename to docs/kjs/그리드_컬럼수_옵션_통합.md diff --git a/docs/기간별_단가_설정_가이드.md b/docs/kjs/기간별_단가_설정_가이드.md similarity index 100% rename from docs/기간별_단가_설정_가이드.md rename to docs/kjs/기간별_단가_설정_가이드.md diff --git a/docs/노드_구조_개선안.md b/docs/kjs/노드_구조_개선안.md similarity index 100% rename from docs/노드_구조_개선안.md rename to docs/kjs/노드_구조_개선안.md diff --git a/docs/노드_기반_제어_시스템_개선_계획.md b/docs/kjs/노드_기반_제어_시스템_개선_계획.md similarity index 100% rename from docs/노드_기반_제어_시스템_개선_계획.md rename to docs/kjs/노드_기반_제어_시스템_개선_계획.md diff --git a/docs/노드_시스템_버튼_통합_분석.md b/docs/kjs/노드_시스템_버튼_통합_분석.md similarity index 100% rename from docs/노드_시스템_버튼_통합_분석.md rename to docs/kjs/노드_시스템_버튼_통합_분석.md diff --git a/docs/노드_실행_엔진_설계.md b/docs/kjs/노드_실행_엔진_설계.md similarity index 100% rename from docs/노드_실행_엔진_설계.md rename to docs/kjs/노드_실행_엔진_설계.md diff --git a/docs/노드_연결_규칙_설계.md b/docs/kjs/노드_연결_규칙_설계.md similarity index 100% rename from docs/노드_연결_규칙_설계.md rename to docs/kjs/노드_연결_규칙_설계.md diff --git a/노드_플로우_데이터소스_설정_가이드.md b/docs/kjs/노드_플로우_데이터소스_설정_가이드.md similarity index 100% rename from 노드_플로우_데이터소스_설정_가이드.md rename to docs/kjs/노드_플로우_데이터소스_설정_가이드.md diff --git a/docs/노드플로우_개선사항.md b/docs/kjs/노드플로우_개선사항.md similarity index 100% rename from docs/노드플로우_개선사항.md rename to docs/kjs/노드플로우_개선사항.md diff --git a/docs/다국어_관리_시스템_개선_계획서.md b/docs/kjs/다국어_관리_시스템_개선_계획서.md similarity index 100% rename from docs/다국어_관리_시스템_개선_계획서.md rename to docs/kjs/다국어_관리_시스템_개선_계획서.md diff --git a/docs/다국어_시스템_가이드.md b/docs/kjs/다국어_시스템_가이드.md similarity index 100% rename from docs/다국어_시스템_가이드.md rename to docs/kjs/다국어_시스템_가이드.md diff --git a/데이터소스_일관성_개선_완료.md b/docs/kjs/데이터소스_일관성_개선_완료.md similarity index 100% rename from 데이터소스_일관성_개선_완료.md rename to docs/kjs/데이터소스_일관성_개선_완료.md diff --git a/동적_테이블_접근_시스템_개선_완료.md b/docs/kjs/동적_테이블_접근_시스템_개선_완료.md similarity index 100% rename from 동적_테이블_접근_시스템_개선_완료.md rename to docs/kjs/동적_테이블_접근_시스템_개선_완료.md diff --git a/docs/레벨기반_연쇄드롭다운_설계.md b/docs/kjs/레벨기반_연쇄드롭다운_설계.md similarity index 100% rename from docs/레벨기반_연쇄드롭다운_설계.md rename to docs/kjs/레벨기반_연쇄드롭다운_설계.md diff --git a/docs/리소스_기반_권한_시스템_가이드.md b/docs/kjs/리소스_기반_권한_시스템_가이드.md similarity index 100% rename from docs/리소스_기반_권한_시스템_가이드.md rename to docs/kjs/리소스_기반_권한_시스템_가이드.md diff --git a/docs/멀티테넌시_구현_현황_분석_보고서.md b/docs/kjs/멀티테넌시_구현_현황_분석_보고서.md similarity index 100% rename from docs/멀티테넌시_구현_현황_분석_보고서.md rename to docs/kjs/멀티테넌시_구현_현황_분석_보고서.md diff --git a/docs/메뉴_기반_권한_시스템_가이드.md b/docs/kjs/메뉴_기반_권한_시스템_가이드.md similarity index 100% rename from docs/메뉴_기반_권한_시스템_가이드.md rename to docs/kjs/메뉴_기반_권한_시스템_가이드.md diff --git a/docs/메뉴_복사_기능_구현_계획서.md b/docs/kjs/메뉴_복사_기능_구현_계획서.md similarity index 100% rename from docs/메뉴_복사_기능_구현_계획서.md rename to docs/kjs/메뉴_복사_기능_구현_계획서.md diff --git a/docs/메뉴_회사별_필터링_개선_완료.md b/docs/kjs/메뉴_회사별_필터링_개선_완료.md similarity index 100% rename from docs/메뉴_회사별_필터링_개선_완료.md rename to docs/kjs/메뉴_회사별_필터링_개선_완료.md diff --git a/docs/메뉴_회사별_필터링_구현_완료.md b/docs/kjs/메뉴_회사별_필터링_구현_완료.md similarity index 100% rename from docs/메뉴_회사별_필터링_구현_완료.md rename to docs/kjs/메뉴_회사별_필터링_구현_완료.md diff --git a/docs/메일발송_기능_사용_가이드.md b/docs/kjs/메일발송_기능_사용_가이드.md similarity index 100% rename from docs/메일발송_기능_사용_가이드.md rename to docs/kjs/메일발송_기능_사용_가이드.md diff --git a/버튼_제어관리_기능_통합_계획서.md b/docs/kjs/버튼_제어관리_기능_통합_계획서.md similarity index 100% rename from 버튼_제어관리_기능_통합_계획서.md rename to docs/kjs/버튼_제어관리_기능_통합_계획서.md diff --git a/버튼_제어관리_기능_통합_잠재적_문제점_분석.md b/docs/kjs/버튼_제어관리_기능_통합_잠재적_문제점_분석.md similarity index 100% rename from 버튼_제어관리_기능_통합_잠재적_문제점_분석.md rename to docs/kjs/버튼_제어관리_기능_통합_잠재적_문제점_분석.md diff --git a/버튼_제어관리_성능_최적화_전략.md b/docs/kjs/버튼_제어관리_성능_최적화_전략.md similarity index 100% rename from 버튼_제어관리_성능_최적화_전략.md rename to docs/kjs/버튼_제어관리_성능_최적화_전략.md diff --git a/선택항목_상세입력_완전_자동화_가이드.md b/docs/kjs/선택항목_상세입력_완전_자동화_가이드.md similarity index 100% rename from 선택항목_상세입력_완전_자동화_가이드.md rename to docs/kjs/선택항목_상세입력_완전_자동화_가이드.md diff --git a/선택항목_상세입력_컴포넌트_완성_가이드.md b/docs/kjs/선택항목_상세입력_컴포넌트_완성_가이드.md similarity index 100% rename from 선택항목_상세입력_컴포넌트_완성_가이드.md rename to docs/kjs/선택항목_상세입력_컴포넌트_완성_가이드.md diff --git a/수주등록_화면_개발_계획서.md b/docs/kjs/수주등록_화면_개발_계획서.md similarity index 100% rename from 수주등록_화면_개발_계획서.md rename to docs/kjs/수주등록_화면_개발_계획서.md diff --git a/스크롤_문제_해결_가이드.md b/docs/kjs/스크롤_문제_해결_가이드.md similarity index 100% rename from 스크롤_문제_해결_가이드.md rename to docs/kjs/스크롤_문제_해결_가이드.md diff --git a/docs/시스템_강점_어필_문서.md b/docs/kjs/시스템_강점_어필_문서.md similarity index 100% rename from docs/시스템_강점_어필_문서.md rename to docs/kjs/시스템_강점_어필_문서.md diff --git a/시연_시나리오.md b/docs/kjs/시연_시나리오.md similarity index 100% rename from 시연_시나리오.md rename to docs/kjs/시연_시나리오.md diff --git a/docs/엑셀_다운로드_개선_계획.md b/docs/kjs/엑셀_다운로드_개선_계획.md similarity index 100% rename from docs/엑셀_다운로드_개선_계획.md rename to docs/kjs/엑셀_다운로드_개선_계획.md diff --git a/docs/엑셀_다운로드_개선_계획_v2.md b/docs/kjs/엑셀_다운로드_개선_계획_v2.md similarity index 100% rename from docs/엑셀_다운로드_개선_계획_v2.md rename to docs/kjs/엑셀_다운로드_개선_계획_v2.md diff --git a/docs/영업_계약_수정.md b/docs/kjs/영업_계약_수정.md similarity index 100% rename from docs/영업_계약_수정.md rename to docs/kjs/영업_계약_수정.md diff --git a/docs/외부_DB_연결_풀_가이드.md b/docs/kjs/외부_DB_연결_풀_가이드.md similarity index 100% rename from docs/외부_DB_연결_풀_가이드.md rename to docs/kjs/외부_DB_연결_풀_가이드.md diff --git a/외부호출_데이터_매핑_시스템_설계서.md b/docs/kjs/외부호출_데이터_매핑_시스템_설계서.md similarity index 100% rename from 외부호출_데이터_매핑_시스템_설계서.md rename to docs/kjs/외부호출_데이터_매핑_시스템_설계서.md diff --git a/제어관리_데이터소스_확장_가이드.md b/docs/kjs/제어관리_데이터소스_확장_가이드.md similarity index 100% rename from 제어관리_데이터소스_확장_가이드.md rename to docs/kjs/제어관리_데이터소스_확장_가이드.md diff --git a/제어관리_시스템_개선_계획서.md b/docs/kjs/제어관리_시스템_개선_계획서.md similarity index 100% rename from 제어관리_시스템_개선_계획서.md rename to docs/kjs/제어관리_시스템_개선_계획서.md diff --git a/제어관리_외부커넥션_통합_개선_계획서.md b/docs/kjs/제어관리_외부커넥션_통합_개선_계획서.md similarity index 100% rename from 제어관리_외부커넥션_통합_개선_계획서.md rename to docs/kjs/제어관리_외부커넥션_통합_개선_계획서.md diff --git a/제어관리_외부커넥션_통합_기능_가이드.md b/docs/kjs/제어관리_외부커넥션_통합_기능_가이드.md similarity index 100% rename from 제어관리_외부커넥션_통합_기능_가이드.md rename to docs/kjs/제어관리_외부커넥션_통합_기능_가이드.md diff --git a/제어관리_외부호출_REST_API_구현_계획서.md b/docs/kjs/제어관리_외부호출_REST_API_구현_계획서.md similarity index 100% rename from 제어관리_외부호출_REST_API_구현_계획서.md rename to docs/kjs/제어관리_외부호출_REST_API_구현_계획서.md diff --git a/제어관리_트랜잭션_및_조건부실행_개선방안.md b/docs/kjs/제어관리_트랜잭션_및_조건부실행_개선방안.md similarity index 100% rename from 제어관리_트랜잭션_및_조건부실행_개선방안.md rename to docs/kjs/제어관리_트랜잭션_및_조건부실행_개선방안.md diff --git a/docs/즉시저장_버튼_액션_구현_계획서.md b/docs/kjs/즉시저장_버튼_액션_구현_계획서.md similarity index 100% rename from docs/즉시저장_버튼_액션_구현_계획서.md rename to docs/kjs/즉시저장_버튼_액션_구현_계획서.md diff --git a/docs/집계위젯_개발진행상황.md b/docs/kjs/집계위젯_개발진행상황.md similarity index 96% rename from docs/집계위젯_개발진행상황.md rename to docs/kjs/집계위젯_개발진행상황.md index ae3eb693..2f54df97 100644 --- a/docs/집계위젯_개발진행상황.md +++ b/docs/kjs/집계위젯_개발진행상황.md @@ -65,9 +65,9 @@ - `allComponents` → `screenComponents` 변환이 `getComponentConfigPanel.tsx`에서 수행되지만, 실제 컴포넌트 목록이 비어있음 **해결 필요 사항**: -1. `UnifiedPropertiesPanel`에서 `allComponents`가 제대로 전달되는지 확인 +1. `V2PropertiesPanel`에서 `allComponents`가 제대로 전달되는지 확인 2. `getComponentConfigPanel.tsx`에서 `screenComponents` 변환 로직 디버깅 -3. 필터링 조건 확인 (table-list, v2-table-list, unified-repeater 등) +3. 필터링 조건 확인 (table-list, v2-table-list, v2-repeater 등) **관련 코드**: ```typescript @@ -205,7 +205,7 @@ console.log("[AggregationWidget] selectableComponents:", filtered); ## 관련 파일 - `frontend/lib/utils/getComponentConfigPanel.tsx` - `screenComponents` 변환 -- `frontend/components/screen/panels/UnifiedPropertiesPanel.tsx` - `allComponents` 전달 +- `frontend/components/screen/panels/V2PropertiesPanel.tsx` - `allComponents` 전달 - `frontend/components/screen/ScreenDesigner.tsx` - `layout.components` 전달 diff --git a/docs/채번규칙_멀티테넌시_버그_수정_완료.md b/docs/kjs/채번규칙_멀티테넌시_버그_수정_완료.md similarity index 100% rename from docs/채번규칙_멀티테넌시_버그_수정_완료.md rename to docs/kjs/채번규칙_멀티테넌시_버그_수정_완료.md diff --git a/docs/채번규칙_컴포넌트_구현_완료.md b/docs/kjs/채번규칙_컴포넌트_구현_완료.md similarity index 100% rename from docs/채번규칙_컴포넌트_구현_완료.md rename to docs/kjs/채번규칙_컴포넌트_구현_완료.md diff --git a/채번규칙_테이블기반_자동감지_구현_완료.md b/docs/kjs/채번규칙_테이블기반_자동감지_구현_완료.md similarity index 100% rename from 채번규칙_테이블기반_자동감지_구현_완료.md rename to docs/kjs/채번규칙_테이블기반_자동감지_구현_완료.md diff --git a/채번규칙_테이블기반_필터링_구현_계획서.md b/docs/kjs/채번규칙_테이블기반_필터링_구현_계획서.md similarity index 100% rename from 채번규칙_테이블기반_필터링_구현_계획서.md rename to docs/kjs/채번규칙_테이블기반_필터링_구현_계획서.md diff --git a/채번규칙_테이블기반_필터링_구현_완료_보고서.md b/docs/kjs/채번규칙_테이블기반_필터링_구현_완료_보고서.md similarity index 100% rename from 채번규칙_테이블기반_필터링_구현_완료_보고서.md rename to docs/kjs/채번규칙_테이블기반_필터링_구현_완료_보고서.md diff --git a/카테고리_관리_컴포넌트_구현_계획서.md b/docs/kjs/카테고리_관리_컴포넌트_구현_계획서.md similarity index 100% rename from 카테고리_관리_컴포넌트_구현_계획서.md rename to docs/kjs/카테고리_관리_컴포넌트_구현_계획서.md diff --git a/docs/카테고리_멀티테넌시_버그_분석.md b/docs/kjs/카테고리_멀티테넌시_버그_분석.md similarity index 100% rename from docs/카테고리_멀티테넌시_버그_분석.md rename to docs/kjs/카테고리_멀티테넌시_버그_분석.md diff --git a/docs/카테고리_멀티테넌시_버그_수정_완료.md b/docs/kjs/카테고리_멀티테넌시_버그_수정_완료.md similarity index 100% rename from docs/카테고리_멀티테넌시_버그_수정_완료.md rename to docs/kjs/카테고리_멀티테넌시_버그_수정_완료.md diff --git a/카테고리_메뉴기반_전환_계획서.md b/docs/kjs/카테고리_메뉴기반_전환_계획서.md similarity index 100% rename from 카테고리_메뉴기반_전환_계획서.md rename to docs/kjs/카테고리_메뉴기반_전환_계획서.md diff --git a/docs/카테고리_메뉴별_컬럼_분리_구현_완료_보고서.md b/docs/kjs/카테고리_메뉴별_컬럼_분리_구현_완료_보고서.md similarity index 100% rename from docs/카테고리_메뉴별_컬럼_분리_구현_완료_보고서.md rename to docs/kjs/카테고리_메뉴별_컬럼_분리_구현_완료_보고서.md diff --git a/docs/카테고리_메뉴별_컬럼_분리_전략.md b/docs/kjs/카테고리_메뉴별_컬럼_분리_전략.md similarity index 100% rename from docs/카테고리_메뉴별_컬럼_분리_전략.md rename to docs/kjs/카테고리_메뉴별_컬럼_분리_전략.md diff --git a/docs/카테고리_메뉴스코프_개선_계획서.md b/docs/kjs/카테고리_메뉴스코프_개선_계획서.md similarity index 100% rename from docs/카테고리_메뉴스코프_개선_계획서.md rename to docs/kjs/카테고리_메뉴스코프_개선_계획서.md diff --git a/카테고리_시스템_구현_계획서.md b/docs/kjs/카테고리_시스템_구현_계획서.md similarity index 100% rename from 카테고리_시스템_구현_계획서.md rename to docs/kjs/카테고리_시스템_구현_계획서.md diff --git a/카테고리_시스템_재구현_계획서.md b/docs/kjs/카테고리_시스템_재구현_계획서.md similarity index 100% rename from 카테고리_시스템_재구현_계획서.md rename to docs/kjs/카테고리_시스템_재구현_계획서.md diff --git a/카테고리_시스템_재구현_완료_보고서.md b/docs/kjs/카테고리_시스템_재구현_완료_보고서.md similarity index 100% rename from 카테고리_시스템_재구현_완료_보고서.md rename to docs/kjs/카테고리_시스템_재구현_완료_보고서.md diff --git a/카테고리_시스템_최종_완료_보고서.md b/docs/kjs/카테고리_시스템_최종_완료_보고서.md similarity index 99% rename from 카테고리_시스템_최종_완료_보고서.md rename to docs/kjs/카테고리_시스템_최종_완료_보고서.md index ff072d9d..2cb3a954 100644 --- a/카테고리_시스템_최종_완료_보고서.md +++ b/docs/kjs/카테고리_시스템_최종_완료_보고서.md @@ -57,7 +57,7 @@ ### Phase 3: 화면관리 시스템 통합 ✅ 10. **ComponentType 추가** - - `unified-core.ts`에 `"category-manager"` 추가 + - `v2-core.ts`에 `"category-manager"` 추가 11. **ComponentRegistry 등록** - `CategoryManagerRenderer.tsx` 생성 @@ -113,7 +113,7 @@ frontend/components/table-category/CategoryValueManager.tsx (수정) ``` frontend/lib/api/tableCategoryValue.ts (수정) frontend/types/tableCategoryValue.ts (수정) -frontend/types/unified-core.ts (수정) +frontend/types/v2-core.ts (수정) ``` ### 프론트엔드 - 화면관리 시스템 diff --git a/카테고리_채번_메뉴스코프_전환_통합_계획서.md b/docs/kjs/카테고리_채번_메뉴스코프_전환_통합_계획서.md similarity index 100% rename from 카테고리_채번_메뉴스코프_전환_통합_계획서.md rename to docs/kjs/카테고리_채번_메뉴스코프_전환_통합_계획서.md diff --git a/카테고리_컴포넌트_DB_호환성_분석.md b/docs/kjs/카테고리_컴포넌트_DB_호환성_분석.md similarity index 100% rename from 카테고리_컴포넌트_DB_호환성_분석.md rename to docs/kjs/카테고리_컴포넌트_DB_호환성_분석.md diff --git a/카테고리_컴포넌트_구현_완료.md b/docs/kjs/카테고리_컴포넌트_구현_완료.md similarity index 100% rename from 카테고리_컴포넌트_구현_완료.md rename to docs/kjs/카테고리_컴포넌트_구현_완료.md diff --git a/카테고리_타입_구현_완료.md b/docs/kjs/카테고리_타입_구현_완료.md similarity index 100% rename from 카테고리_타입_구현_완료.md rename to docs/kjs/카테고리_타입_구현_완료.md diff --git a/docs/컴포넌트_기본_너비_설정_가이드.md b/docs/kjs/컴포넌트_기본_너비_설정_가이드.md similarity index 100% rename from docs/컴포넌트_기본_너비_설정_가이드.md rename to docs/kjs/컴포넌트_기본_너비_설정_가이드.md diff --git a/docs/컴포넌트_분석_및_통합_계획.md b/docs/kjs/컴포넌트_분석_및_통합_계획.md similarity index 78% rename from docs/컴포넌트_분석_및_통합_계획.md rename to docs/kjs/컴포넌트_분석_및_통합_계획.md index 88be78c8..b421dcfb 100644 --- a/docs/컴포넌트_분석_및_통합_계획.md +++ b/docs/kjs/컴포넌트_분석_및_통합_계획.md @@ -7,16 +7,16 @@ | 번호 | 컴포넌트 ID | 한글명 | 패널 표시 | 통합 대상 | |------|-------------|--------|----------|----------| -| 1 | rack-structure | 렉 구조 설정 | 숨김 | UnifiedBiz (rack) | +| 1 | rack-structure | 렉 구조 설정 | 숨김 | V2Biz (rack) | | 2 | mail-recipient-selector | 메일 수신자 선택 | 숨김 | DataFlow 전용 | | 3 | repeater-field-group | 반복 필드 그룹 | 숨김 | 현재 사용 안함 | | 4 | universal-form-modal | 범용 폼 모달 | **유지** | 독립 유지 | | 5 | selected-items-detail-input | 선택 항목 상세입력 | **유지** | 독립 유지 | -| 6 | entity-search-input | 엔티티 검색 입력 | 숨김 | UnifiedSelect (entity 모드) | -| 7 | image-widget | 이미지 위젯 | 숨김 | UnifiedMedia (image) | -| 8 | autocomplete-search-input | 자동완성 검색 입력 | 숨김 | UnifiedSelect (autocomplete 모드) | +| 6 | entity-search-input | 엔티티 검색 입력 | 숨김 | V2Select (entity 모드) | +| 7 | image-widget | 이미지 위젯 | 숨김 | V2Media (image) | +| 8 | autocomplete-search-input | 자동완성 검색 입력 | 숨김 | V2Select (autocomplete 모드) | | 9 | location-swap-selector | 출발지/도착지 선택 | **유지** | 독립 유지 | -| 10 | file-upload | 파일 업로드 | 숨김 | UnifiedMedia (file) | +| 10 | file-upload | 파일 업로드 | 숨김 | V2Media (file) | --- @@ -38,10 +38,10 @@ - formData 컨텍스트 의존 (창고ID, 층, 구역 등) ### 통합 방안 -- **결정**: `UnifiedBiz` 컴포넌트의 `rack` 비즈니스 타입으로 통합 -- **이유**: 비즈니스 특화 컴포넌트이므로 UnifiedBiz가 적합 +- **결정**: `V2Biz` 컴포넌트의 `rack` 비즈니스 타입으로 통합 +- **이유**: 비즈니스 특화 컴포넌트이므로 V2Biz가 적합 - **작업**: - - UnifiedBiz에서 bizType="rack" 선택 시 RackStructureComponent 렌더링 + - V2Biz에서 bizType="rack" 선택 시 RackStructureComponent 렌더링 - 설정 패널 통합 --- @@ -117,7 +117,7 @@ - 복잡한 입력 시나리오 지원 ### 통합 방안 -- **결정**: `UnifiedGroup`의 `formModal` 타입으로 통합 검토 +- **결정**: `V2Group`의 `formModal` 타입으로 통합 검토 - **현실적 접근**: - 당장 통합보다는 ScreenModal 시스템과의 차별화 유지 - 향후 섹션 기반 레이아웃 기능을 ScreenModal에 반영 @@ -161,14 +161,14 @@ - **webType**: entity ### 분석 -- UnifiedSelect의 entity 모드와 기능 중복 +- V2Select의 entity 모드와 기능 중복 - 모달 검색 기능이 차별점 - EntityWidget과도 유사 ### 통합 방안 -- **결정**: `UnifiedSelect` entity 모드로 통합 +- **결정**: `V2Select` entity 모드로 통합 - **작업**: - - UnifiedSelect에 `searchMode: "modal" | "inline" | "autocomplete"` 옵션 추가 + - V2Select에 `searchMode: "modal" | "inline" | "autocomplete"` 옵션 추가 - 모달 검색 UI 통합 - 기존 entity-search-input은 deprecated 처리 @@ -187,15 +187,15 @@ - **webType**: image ### 분석 -- UnifiedMedia의 ImageUploader와 기능 동일 +- V2Media의 ImageUploader와 기능 동일 - 이미 ImageWidget 컴포넌트 재사용 중 ### 통합 방안 -- **결정**: `UnifiedMedia` image 타입으로 통합 완료 -- **상태**: 이미 UnifiedMedia.ImageUploader로 구현됨 +- **결정**: `V2Media` image 타입으로 통합 완료 +- **상태**: 이미 V2Media.ImageUploader로 구현됨 - **작업**: - 컴포넌트 패널에서 image-widget 제거 - - UnifiedMedia 사용 권장 + - V2Media 사용 권장 --- @@ -216,9 +216,9 @@ - Command/Popover 기반 자동완성 ### 통합 방안 -- **결정**: `UnifiedSelect` entity 모드의 autocomplete 옵션으로 통합 +- **결정**: `V2Select` entity 모드의 autocomplete 옵션으로 통합 - **작업**: - - UnifiedSelect에서 `searchMode: "autocomplete"` 옵션 추가 + - V2Select에서 `searchMode: "autocomplete"` 옵션 추가 - 자동완성 검색 로직 통합 --- @@ -263,15 +263,15 @@ - **webType**: file ### 분석 -- UnifiedMedia의 FileUploader와 기능 동일 +- V2Media의 FileUploader와 기능 동일 - attach_file_info 테이블 연동 ### 통합 방안 -- **결정**: `UnifiedMedia` file 타입으로 통합 -- **상태**: 이미 UnifiedMedia.FileUploader로 구현됨 +- **결정**: `V2Media` file 타입으로 통합 +- **상태**: 이미 V2Media.FileUploader로 구현됨 - **작업**: - 컴포넌트 패널에서 file-upload 제거 - - UnifiedMedia 사용 권장 + - V2Media 사용 권장 --- @@ -281,16 +281,16 @@ | 컴포넌트 | 통합 대상 | 예상 작업량 | 비고 | |----------|----------|------------|------| -| image-widget | UnifiedMedia (image) | 1일 | 이미 구현됨, 패널에서 숨기기만 | -| file-upload | UnifiedMedia (file) | 1일 | 이미 구현됨, 패널에서 숨기기만 | +| image-widget | V2Media (image) | 1일 | 이미 구현됨, 패널에서 숨기기만 | +| file-upload | V2Media (file) | 1일 | 이미 구현됨, 패널에서 숨기기만 | ### Phase 2: 기능 통합 필요 (중간 작업) | 컴포넌트 | 통합 대상 | 예상 작업량 | 비고 | |----------|----------|------------|------| -| entity-search-input | UnifiedSelect (entity) | 3일 | 모달 검색 모드 추가 | -| autocomplete-search-input | UnifiedSelect (entity) | 2일 | autocomplete 모드 추가 | -| rack-structure | UnifiedBiz (rack) | 2일 | 비즈니스 타입 연결 | +| entity-search-input | V2Select (entity) | 3일 | 모달 검색 모드 추가 | +| autocomplete-search-input | V2Select (entity) | 2일 | autocomplete 모드 추가 | +| rack-structure | V2Biz (rack) | 2일 | 비즈니스 타입 연결 | ### Phase 3: 독립 유지 (작업 없음) @@ -308,29 +308,29 @@ ### 즉시 실행 가능한 작업 1. **ComponentsPanel 정리**: - - `image-widget`, `file-upload` 숨김 처리 (UnifiedMedia 사용) + - `image-widget`, `file-upload` 숨김 처리 (V2Media 사용) - 중복 컴포넌트 정리 -2. **UnifiedBiz 연결**: +2. **V2Biz 연결**: - `bizType: "rack"` 선택 시 `RackStructureComponent` 렌더링 연결 ### 향후 계획 -1. UnifiedSelect에 entity 검색 모드 통합 -2. UnifiedMedia 설정 패널 강화 +1. V2Select에 entity 검색 모드 통합 +2. V2Media 설정 패널 강화 3. 독립 유지 컴포넌트들의 문서화 --- ## 컴포넌트 패널 정리 제안 -### 숨길 컴포넌트 (Unified로 대체됨) -- `image-widget` → UnifiedMedia 사용 -- `file-upload` → UnifiedMedia 사용 -- `entity-search-input` → UnifiedSelect (entity 모드) 사용 예정 -- `autocomplete-search-input` → UnifiedSelect (autocomplete 모드) 사용 예정 +### 숨길 컴포넌트 (V2로 대체됨) +- `image-widget` → V2Media 사용 +- `file-upload` → V2Media 사용 +- `entity-search-input` → V2Select (entity 모드) 사용 예정 +- `autocomplete-search-input` → V2Select (autocomplete 모드) 사용 예정 ### 유지할 컴포넌트 (독립 기능) -- `rack-structure` - WMS 전용 (UnifiedBiz 연결 예정) +- `rack-structure` - WMS 전용 (V2Biz 연결 예정) - `mail-recipient-selector` - 메일 시스템 전용 - `repeater-field-group` - 반복 입력 전용 - `universal-form-modal` - 복잡한 폼 전용 diff --git a/코드_채번_규칙_컴포넌트_구현_계획서.md b/docs/kjs/코드_채번_규칙_컴포넌트_구현_계획서.md similarity index 99% rename from 코드_채번_규칙_컴포넌트_구현_계획서.md rename to docs/kjs/코드_채번_규칙_컴포넌트_구현_계획서.md index 69af1e04..84ca34e3 100644 --- a/코드_채번_규칙_컴포넌트_구현_계획서.md +++ b/docs/kjs/코드_채번_규칙_컴포넌트_구현_계획서.md @@ -371,7 +371,7 @@ ON CONFLICT (rule_id, part_order, company_code) DO NOTHING; - ✅ 실시간 미리보기 업데이트 ### Phase 5: 화면관리 시스템 통합 ✅ -- ✅ `unified-core.ts`에 "numbering-rule" ComponentType 추가 +- ✅ `v2-core.ts`에 "numbering-rule" ComponentType 추가 - ✅ `screen-management.ts`에 ComponentData 유니온 타입 추가 - ✅ `RealtimePreview.tsx`에 렌더링 로직 추가 - ✅ `TemplatesPanel.tsx`에 "관리자" 카테고리 및 템플릿 추가 diff --git a/docs/테이블_검색필터_컴포넌트_분리_계획서.md b/docs/kjs/테이블_검색필터_컴포넌트_분리_계획서.md similarity index 100% rename from docs/테이블_검색필터_컴포넌트_분리_계획서.md rename to docs/kjs/테이블_검색필터_컴포넌트_분리_계획서.md diff --git a/테이블_그룹핑_기능_구현_계획서.md b/docs/kjs/테이블_그룹핑_기능_구현_계획서.md similarity index 100% rename from 테이블_그룹핑_기능_구현_계획서.md rename to docs/kjs/테이블_그룹핑_기능_구현_계획서.md diff --git a/테이블_동적_생성_기능_개발_계획서.md b/docs/kjs/테이블_동적_생성_기능_개발_계획서.md similarity index 100% rename from 테이블_동적_생성_기능_개발_계획서.md rename to docs/kjs/테이블_동적_생성_기능_개발_계획서.md diff --git a/테이블_복제_기능_구현_계획서.md b/docs/kjs/테이블_복제_기능_구현_계획서.md similarity index 100% rename from 테이블_복제_기능_구현_계획서.md rename to docs/kjs/테이블_복제_기능_구현_계획서.md diff --git a/docs/테이블_컬럼_타입_멀티테넌시_구조적_문제_분석.md b/docs/kjs/테이블_컬럼_타입_멀티테넌시_구조적_문제_분석.md similarity index 100% rename from docs/테이블_컬럼_타입_멀티테넌시_구조적_문제_분석.md rename to docs/kjs/테이블_컬럼_타입_멀티테넌시_구조적_문제_분석.md diff --git a/docs/테이블_컬럼_타입_멀티테넌시_수정_완료.md b/docs/kjs/테이블_컬럼_타입_멀티테넌시_수정_완료.md similarity index 100% rename from docs/테이블_컬럼_타입_멀티테넌시_수정_완료.md rename to docs/kjs/테이블_컬럼_타입_멀티테넌시_수정_완료.md diff --git a/테이블_타입_관리_개선_계획서.md b/docs/kjs/테이블_타입_관리_개선_계획서.md similarity index 99% rename from 테이블_타입_관리_개선_계획서.md rename to docs/kjs/테이블_타입_관리_개선_계획서.md index fd8ab7f1..e97fb452 100644 --- a/테이블_타입_관리_개선_계획서.md +++ b/docs/kjs/테이블_타입_관리_개선_계획서.md @@ -131,7 +131,7 @@ export class InputTypeProcessor { #### 1.1 입력 타입 enum 업데이트 ```typescript -// frontend/types/unified-web-types.ts +// frontend/types/v2-web-types.ts export type InputType = | "text" // 텍스트 | "number" // 숫자 diff --git a/테이블_타입_관리_개선_사용_가이드.md b/docs/kjs/테이블_타입_관리_개선_사용_가이드.md similarity index 100% rename from 테이블_타입_관리_개선_사용_가이드.md rename to docs/kjs/테이블_타입_관리_개선_사용_가이드.md diff --git a/docs/테이블_타입관리_성능최적화_결과.md b/docs/kjs/테이블_타입관리_성능최적화_결과.md similarity index 100% rename from docs/테이블_타입관리_성능최적화_결과.md rename to docs/kjs/테이블_타입관리_성능최적화_결과.md diff --git a/docs/테이블_패널_컴포넌트_기본_너비_설정.md b/docs/kjs/테이블_패널_컴포넌트_기본_너비_설정.md similarity index 100% rename from docs/테이블_패널_컴포넌트_기본_너비_설정.md rename to docs/kjs/테이블_패널_컴포넌트_기본_너비_설정.md diff --git a/플로우_위젯_컬럼_표시_설정_구현_완료.md b/docs/kjs/플로우_위젯_컬럼_표시_설정_구현_완료.md similarity index 100% rename from 플로우_위젯_컬럼_표시_설정_구현_완료.md rename to docs/kjs/플로우_위젯_컬럼_표시_설정_구현_완료.md diff --git a/화면_임베딩_및_데이터_전달_시스템_구현_계획서.md b/docs/kjs/화면_임베딩_및_데이터_전달_시스템_구현_계획서.md similarity index 100% rename from 화면_임베딩_및_데이터_전달_시스템_구현_계획서.md rename to docs/kjs/화면_임베딩_및_데이터_전달_시스템_구현_계획서.md diff --git a/화면_임베딩_시스템_Phase1-4_구현_완료.md b/docs/kjs/화면_임베딩_시스템_Phase1-4_구현_완료.md similarity index 100% rename from 화면_임베딩_시스템_Phase1-4_구현_완료.md rename to docs/kjs/화면_임베딩_시스템_Phase1-4_구현_완료.md diff --git a/화면_임베딩_시스템_충돌_분석_보고서.md b/docs/kjs/화면_임베딩_시스템_충돌_분석_보고서.md similarity index 100% rename from 화면_임베딩_시스템_충돌_분석_보고서.md rename to docs/kjs/화면_임베딩_시스템_충돌_분석_보고서.md diff --git a/화면관리_검증_시스템_사용_가이드.md b/docs/kjs/화면관리_검증_시스템_사용_가이드.md similarity index 97% rename from 화면관리_검증_시스템_사용_가이드.md rename to docs/kjs/화면관리_검증_시스템_사용_가이드.md index 6eddd98c..aec2752c 100644 --- a/화면관리_검증_시스템_사용_가이드.md +++ b/docs/kjs/화면관리_검증_시스템_사용_가이드.md @@ -24,7 +24,7 @@ ### 프론트엔드 -- **TypeScript**: 통합 타입 정의 (`unified-web-types.ts`) +- **TypeScript**: 통합 타입 정의 (`v2-web-types.ts`) - **React Hooks**: 실시간 검증 (`useFormValidation`) - **Validation Utils**: 클라이언트 검증 로직 (`formValidation.ts`) - **Enhanced Service**: 통합 폼 서비스 (`enhancedFormService.ts`) @@ -33,7 +33,7 @@ - **Enhanced Service**: 개선된 동적 폼 서비스 (`enhancedDynamicFormService.ts`) - **Table Management API**: 테이블 관리 API (`tableManagementController.ts`) -- **Type Safety**: 통합 웹타입 정의 (`unified-web-types.ts`) +- **Type Safety**: 통합 웹타입 정의 (`v2-web-types.ts`) ## 🎮 사용 방법 @@ -126,7 +126,7 @@ interface DisplayOptions { 새로운 웹타입을 추가하려면 양쪽 모두 업데이트해야 합니다: -**프론트엔드** (`frontend/types/unified-web-types.ts`): +**프론트엔드** (`frontend/types/v2-web-types.ts`): ```typescript export type BaseWebType = @@ -136,7 +136,7 @@ export type BaseWebType = | "new-type"; // 새 타입 추가 ``` -**백엔드** (`backend-node/src/types/unified-web-types.ts`): +**백엔드** (`backend-node/src/types/v2-web-types.ts`): ```typescript export type BaseWebType = diff --git a/화면관리_및_테이블관리_개선사항_목록.md b/docs/kjs/화면관리_및_테이블관리_개선사항_목록.md similarity index 100% rename from 화면관리_및_테이블관리_개선사항_목록.md rename to docs/kjs/화면관리_및_테이블관리_개선사항_목록.md diff --git a/docs/화면관리_시스템_설계.md b/docs/kjs/화면관리_시스템_설계.md similarity index 100% rename from docs/화면관리_시스템_설계.md rename to docs/kjs/화면관리_시스템_설계.md diff --git a/화면관리_타입_문제_분석_및_해결방안.md b/docs/kjs/화면관리_타입_문제_분석_및_해결방안.md similarity index 98% rename from 화면관리_타입_문제_분석_및_해결방안.md rename to docs/kjs/화면관리_타입_문제_분석_및_해결방안.md index e299872c..d6ca93d9 100644 --- a/화면관리_타입_문제_분석_및_해결방안.md +++ b/docs/kjs/화면관리_타입_문제_분석_및_해결방안.md @@ -251,7 +251,7 @@ export interface ButtonDataflowConfig { ``` frontend/types/ -├── unified-core.ts # 핵심 공통 타입들 +├── v2-core.ts # 핵심 공통 타입들 ├── screen-management.ts # 화면관리 전용 타입 ├── control-management.ts # 제어관리 전용 타입 ├── table-management.ts # 테이블관리 전용 타입 @@ -261,7 +261,7 @@ frontend/types/ ### 1.2 WebType 통합 정의 ```typescript -// frontend/types/unified-core.ts +// frontend/types/v2-core.ts export type WebType = | "text" | "number" @@ -289,7 +289,7 @@ export type DynamicWebType = WebType | string; ### 1.3 ButtonActionType 통합 정의 ```typescript -// frontend/types/unified-core.ts +// frontend/types/v2-core.ts export type ButtonActionType = | "save" | "cancel" @@ -351,7 +351,7 @@ export const mapWebTypeStandardToDefinition = ( ```typescript // frontend/types/table-management.ts -export interface UnifiedColumnInfo { +export interface V2ColumnInfo { // 공통 필드 tableName: string; columnName: string; diff --git a/docs/AI_비용_및_하드웨어_요구사항_분석.md b/docs/leeheejin/AI_비용_및_하드웨어_요구사항_분석.md similarity index 100% rename from docs/AI_비용_및_하드웨어_요구사항_분석.md rename to docs/leeheejin/AI_비용_및_하드웨어_요구사항_분석.md diff --git a/docs/AI_어시스턴트_사용가이드.md b/docs/leeheejin/AI_어시스턴트_사용가이드.md similarity index 100% rename from docs/AI_어시스턴트_사용가이드.md rename to docs/leeheejin/AI_어시스턴트_사용가이드.md diff --git a/docs/GroupBy_컴포넌트_적용완료.md b/docs/leeheejin/GroupBy_컴포넌트_적용완료.md similarity index 100% rename from docs/GroupBy_컴포넌트_적용완료.md rename to docs/leeheejin/GroupBy_컴포넌트_적용완료.md diff --git a/docs/GroupBy_컴포넌트화_완료.md b/docs/leeheejin/GroupBy_컴포넌트화_완료.md similarity index 100% rename from docs/GroupBy_컴포넌트화_완료.md rename to docs/leeheejin/GroupBy_컴포넌트화_완료.md diff --git a/docs/OCR_문자인식_통합완료.md b/docs/leeheejin/OCR_문자인식_통합완료.md similarity index 100% rename from docs/OCR_문자인식_통합완료.md rename to docs/leeheejin/OCR_문자인식_통합완료.md diff --git a/docs/PanelResize_컴포넌트_적용완료.md b/docs/leeheejin/PanelResize_컴포넌트_적용완료.md similarity index 100% rename from docs/PanelResize_컴포넌트_적용완료.md rename to docs/leeheejin/PanelResize_컴포넌트_적용완료.md diff --git a/docs/TableActionBar_컴포넌트_완성.md b/docs/leeheejin/TableActionBar_컴포넌트_완성.md similarity index 100% rename from docs/TableActionBar_컴포넌트_완성.md rename to docs/leeheejin/TableActionBar_컴포넌트_완성.md diff --git a/UI_개선사항_문서.md b/docs/leeheejin/UI_개선사항_문서.md similarity index 100% rename from UI_개선사항_문서.md rename to docs/leeheejin/UI_개선사항_문서.md diff --git a/docs/shadcn-ui_디자인_시스템_가이드.md b/docs/leeheejin/shadcn-ui_디자인_시스템_가이드.md similarity index 100% rename from docs/shadcn-ui_디자인_시스템_가이드.md rename to docs/leeheejin/shadcn-ui_디자인_시스템_가이드.md diff --git a/docs/shadcn-ui_디자인_시스템_적용_완료_보고서.md b/docs/leeheejin/shadcn-ui_디자인_시스템_적용_완료_보고서.md similarity index 100% rename from docs/shadcn-ui_디자인_시스템_적용_완료_보고서.md rename to docs/leeheejin/shadcn-ui_디자인_시스템_적용_완료_보고서.md diff --git a/docs/공정관리_방법론.md b/docs/leeheejin/공정관리_방법론.md similarity index 100% rename from docs/공정관리_방법론.md rename to docs/leeheejin/공정관리_방법론.md diff --git a/docs/그룹화_옵션_저장_가이드.md b/docs/leeheejin/그룹화_옵션_저장_가이드.md similarity index 100% rename from docs/그룹화_옵션_저장_가이드.md rename to docs/leeheejin/그룹화_옵션_저장_가이드.md diff --git a/docs/기상청_API키_발급가이드.md b/docs/leeheejin/기상청_API키_발급가이드.md similarity index 100% rename from docs/기상청_API키_발급가이드.md rename to docs/leeheejin/기상청_API키_발급가이드.md diff --git a/docs/날씨위젯_API키_설정가이드.md b/docs/leeheejin/날씨위젯_API키_설정가이드.md similarity index 100% rename from docs/날씨위젯_API키_설정가이드.md rename to docs/leeheejin/날씨위젯_API키_설정가이드.md diff --git a/docs/리스크알림_API키_발급가이드.md b/docs/leeheejin/리스크알림_API키_발급가이드.md similarity index 100% rename from docs/리스크알림_API키_발급가이드.md rename to docs/leeheejin/리스크알림_API키_발급가이드.md diff --git a/메일관리_기능_리스트.md b/docs/leeheejin/메일관리_기능_리스트.md similarity index 100% rename from 메일관리_기능_리스트.md rename to docs/leeheejin/메일관리_기능_리스트.md diff --git a/메일관리_시스템_구현_계획서.md b/docs/leeheejin/메일관리_시스템_구현_계획서.md similarity index 100% rename from 메일관리_시스템_구현_계획서.md rename to docs/leeheejin/메일관리_시스템_구현_계획서.md diff --git a/메일시스템_검증_보고서.md b/docs/leeheejin/메일시스템_검증_보고서.md similarity index 100% rename from 메일시스템_검증_보고서.md rename to docs/leeheejin/메일시스템_검증_보고서.md diff --git a/docs/생산계획_수량조정_분할_기능_안내.md b/docs/leeheejin/생산계획_수량조정_분할_기능_안내.md similarity index 100% rename from docs/생산계획_수량조정_분할_기능_안내.md rename to docs/leeheejin/생산계획_수량조정_분할_기능_안내.md diff --git a/docs/외부_DB_연결_관리_기능_가이드.md b/docs/leeheejin/외부_DB_연결_관리_기능_가이드.md similarity index 100% rename from docs/외부_DB_연결_관리_기능_가이드.md rename to docs/leeheejin/외부_DB_연결_관리_기능_가이드.md diff --git a/docs/외부_DB_연결_관리_기능_개선_계획.md b/docs/leeheejin/외부_DB_연결_관리_기능_개선_계획.md similarity index 100% rename from docs/외부_DB_연결_관리_기능_개선_계획.md rename to docs/leeheejin/외부_DB_연결_관리_기능_개선_계획.md diff --git a/외부_데이터베이스_제어관리_시스템_계획서.md b/docs/leeheejin/외부_데이터베이스_제어관리_시스템_계획서.md similarity index 100% rename from 외부_데이터베이스_제어관리_시스템_계획서.md rename to docs/leeheejin/외부_데이터베이스_제어관리_시스템_계획서.md diff --git a/docs/위젯_승격_완료_보고서.md b/docs/leeheejin/위젯_승격_완료_보고서.md similarity index 100% rename from docs/위젯_승격_완료_보고서.md rename to docs/leeheejin/위젯_승격_완료_보고서.md diff --git a/docs/창고관리_개발자_가이드.md b/docs/leeheejin/창고관리_개발자_가이드.md similarity index 100% rename from docs/창고관리_개발자_가이드.md rename to docs/leeheejin/창고관리_개발자_가이드.md diff --git a/docs/창고관리_모바일_사용가이드.md b/docs/leeheejin/창고관리_모바일_사용가이드.md similarity index 100% rename from docs/창고관리_모바일_사용가이드.md rename to docs/leeheejin/창고관리_모바일_사용가이드.md diff --git a/docs/창고관리_시스템_완성_보고서.md b/docs/leeheejin/창고관리_시스템_완성_보고서.md similarity index 100% rename from docs/창고관리_시스템_완성_보고서.md rename to docs/leeheejin/창고관리_시스템_완성_보고서.md diff --git a/docs/컬럼_매핑_사용_가이드.md b/docs/leeheejin/컬럼_매핑_사용_가이드.md similarity index 100% rename from docs/컬럼_매핑_사용_가이드.md rename to docs/leeheejin/컬럼_매핑_사용_가이드.md diff --git a/docs/컴포넌트화_최종_완료_보고서.md b/docs/leeheejin/컴포넌트화_최종_완료_보고서.md similarity index 100% rename from docs/컴포넌트화_최종_완료_보고서.md rename to docs/leeheejin/컴포넌트화_최종_완료_보고서.md diff --git a/docs/테스트_위젯_누락_기능_분석_보고서.md b/docs/leeheejin/테스트_위젯_누락_기능_분석_보고서.md similarity index 100% rename from docs/테스트_위젯_누락_기능_분석_보고서.md rename to docs/leeheejin/테스트_위젯_누락_기능_분석_보고서.md diff --git a/docs/unified-components-implementation.md b/docs/unified-components-implementation.md deleted file mode 100644 index 663e344e..00000000 --- a/docs/unified-components-implementation.md +++ /dev/null @@ -1,192 +0,0 @@ -# Unified Components 구현 완료 보고서 - -## 구현 일시 - -2024-12-19 - -## 구현된 컴포넌트 목록 (10개) - -### Phase 1: 핵심 입력 컴포넌트 - -| 컴포넌트 | 파일 | 모드/타입 | 설명 | -| :---------------- | :------------------ | :-------------------------------------------- | :---------------------- | -| **UnifiedInput** | `UnifiedInput.tsx` | text, number, password, slider, color, button | 통합 입력 컴포넌트 | -| **UnifiedSelect** | `UnifiedSelect.tsx` | dropdown, radio, check, tag, toggle, swap | 통합 선택 컴포넌트 | -| **UnifiedDate** | `UnifiedDate.tsx` | date, time, datetime + range | 통합 날짜/시간 컴포넌트 | - -### Phase 2: 레이아웃 및 그룹 컴포넌트 - -| 컴포넌트 | 파일 | 모드/타입 | 설명 | -| :---------------- | :------------------ | :-------------------------------------------------------- | :--------------------- | -| **UnifiedList** | `UnifiedList.tsx` | table, card, kanban, list | 통합 리스트 컴포넌트 | -| **UnifiedLayout** | `UnifiedLayout.tsx` | grid, split, flex, divider, screen-embed | 통합 레이아웃 컴포넌트 | -| **UnifiedGroup** | `UnifiedGroup.tsx` | tabs, accordion, section, card-section, modal, form-modal | 통합 그룹 컴포넌트 | - -### Phase 3: 미디어 및 비즈니스 컴포넌트 - -| 컴포넌트 | 파일 | 모드/타입 | 설명 | -| :------------------- | :--------------------- | :------------------------------------------------------------- | :---------------------- | -| **UnifiedMedia** | `UnifiedMedia.tsx` | file, image, video, audio | 통합 미디어 컴포넌트 | -| **UnifiedBiz** | `UnifiedBiz.tsx` | flow, rack, map, numbering, category, mapping, related-buttons | 통합 비즈니스 컴포넌트 | -| **UnifiedHierarchy** | `UnifiedHierarchy.tsx` | tree, org, bom, cascading | 통합 계층 구조 컴포넌트 | - ---- - -## 공통 인프라 - -### 설정 패널 - -- **DynamicConfigPanel**: JSON Schema 기반 동적 설정 UI 생성 - -### 렌더러 - -- **UnifiedComponentRenderer**: unifiedType에 따른 동적 컴포넌트 렌더링 - ---- - -## 파일 구조 - -``` -frontend/components/unified/ -├── index.ts # 모듈 인덱스 -├── UnifiedComponentRenderer.tsx # 동적 렌더러 -├── DynamicConfigPanel.tsx # JSON Schema 설정 패널 -├── UnifiedInput.tsx # 통합 입력 -├── UnifiedSelect.tsx # 통합 선택 -├── UnifiedDate.tsx # 통합 날짜 -├── UnifiedList.tsx # 통합 리스트 -├── UnifiedLayout.tsx # 통합 레이아웃 -├── UnifiedGroup.tsx # 통합 그룹 -├── UnifiedMedia.tsx # 통합 미디어 -├── UnifiedBiz.tsx # 통합 비즈니스 -└── UnifiedHierarchy.tsx # 통합 계층 - -frontend/types/ -└── unified-components.ts # 타입 정의 - -db/migrations/ -└── unified_component_schema.sql # DB 스키마 (미실행) -``` - ---- - -## 사용 예시 - -### 기본 사용법 - -```tsx -import { - UnifiedInput, - UnifiedSelect, - UnifiedDate, - UnifiedList, - UnifiedComponentRenderer -} from "@/components/unified"; - -// UnifiedInput 사용 - - -// UnifiedSelect 사용 - - -// UnifiedDate 사용 - - -// UnifiedList 사용 - -``` - -### 동적 렌더링 - -```tsx -import { UnifiedComponentRenderer } from "@/components/unified"; - -// unifiedType에 따라 자동으로 적절한 컴포넌트 렌더링 -; -``` - ---- - -## 주의사항 - -### 기존 컴포넌트와의 공존 - -1. **기존 컴포넌트는 그대로 유지**: 모든 레거시 컴포넌트는 정상 동작 -2. **신규 화면에서만 Unified 컴포넌트 사용**: 기존 화면에 영향 없음 -3. **마이그레이션 없음**: 자동 마이그레이션 진행하지 않음 - -### 데이터베이스 마이그레이션 - -`db/migrations/unified_component_schema.sql` 파일은 아직 실행되지 않았습니다. -필요시 수동으로 실행해야 합니다: - -```bash -psql -h localhost -U postgres -d plm_db -f db/migrations/unified_component_schema.sql -``` - ---- - -## 다음 단계 (선택) - -1. **화면 관리 에디터 통합**: Unified 컴포넌트를 화면 에디터의 컴포넌트 팔레트에 추가 -2. **기존 비즈니스 컴포넌트 연동**: UnifiedBiz의 플레이스홀더를 실제 구현으로 교체 -3. **테스트 페이지 작성**: 모든 Unified 컴포넌트 데모 페이지 -4. **문서화**: 각 컴포넌트별 상세 사용 가이드 - ---- - -## 관련 문서 - -- `PLAN_RENEWAL.md`: 리뉴얼 계획서 -- `docs/phase0-component-usage-analysis.md`: 컴포넌트 사용 현황 분석 -- `docs/phase0-migration-strategy.md`: 마이그레이션 전략 (참고용) - diff --git a/frontend/app/(main)/admin/screenMng/screenMngList/page.tsx b/frontend/app/(main)/admin/screenMng/screenMngList/page.tsx index 32ebf967..2104c711 100644 --- a/frontend/app/(main)/admin/screenMng/screenMngList/page.tsx +++ b/frontend/app/(main)/admin/screenMng/screenMngList/page.tsx @@ -10,7 +10,7 @@ import ScreenDesigner from "@/components/screen/ScreenDesigner"; import TemplateManager from "@/components/screen/TemplateManager"; import { ScreenGroupTreeView } from "@/components/screen/ScreenGroupTreeView"; import { ScreenRelationFlow } from "@/components/screen/ScreenRelationFlow"; -import { UnifiedComponentsDemo } from "@/components/unified"; +import { V2ComponentsDemo } from "@/components/v2"; import { ScrollToTop } from "@/components/common/ScrollToTop"; import { ScreenDefinition } from "@/types/screen"; import { screenApi } from "@/lib/api/screen"; @@ -18,7 +18,7 @@ import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import CreateScreenModal from "@/components/screen/CreateScreenModal"; // 단계별 진행을 위한 타입 정의 -type Step = "list" | "design" | "template" | "unified-test"; +type Step = "list" | "design" | "template" | "v2-test"; type ViewMode = "tree" | "table"; export default function ScreenManagementPage() { @@ -143,11 +143,11 @@ export default function ScreenManagementPage() { ); } - // Unified 컴포넌트 테스트 모드 - if (currentStep === "unified-test") { + // V2 컴포넌트 테스트 모드 + if (currentStep === "v2-test") { return (
- goToStep("list")} /> + goToStep("list")} />
); } @@ -162,14 +162,14 @@ export default function ScreenManagementPage() {

화면을 그룹별로 관리하고 데이터 관계를 확인합니다

- {/* Unified 컴포넌트 테스트 버튼 */} + {/* V2 컴포넌트 테스트 버튼 */} {/* 뷰 모드 전환 */} setViewMode(v as ViewMode)}> diff --git a/frontend/app/(main)/admin/validation-demo/page.tsx b/frontend/app/(main)/admin/validation-demo/page.tsx index 2372c4ea..a4607ec0 100644 --- a/frontend/app/(main)/admin/validation-demo/page.tsx +++ b/frontend/app/(main)/admin/validation-demo/page.tsx @@ -16,7 +16,7 @@ import { useFormValidation } from "@/hooks/useFormValidation"; import { enhancedFormService } from "@/lib/services/enhancedFormService"; import { tableManagementApi } from "@/lib/api/tableManagement"; import { ComponentData, WidgetComponent, ColumnInfo, ScreenDefinition } from "@/types/screen"; -import { normalizeWebType } from "@/types/unified-web-types"; +import { normalizeWebType } from "@/types/v2-web-types"; // 테스트용 화면 정의 const TEST_SCREEN_DEFINITION: ScreenDefinition = { diff --git a/frontend/components/admin/UserToolbar.tsx b/frontend/components/admin/UserToolbar.tsx index 9b27fa91..94a81c83 100644 --- a/frontend/components/admin/UserToolbar.tsx +++ b/frontend/components/admin/UserToolbar.tsx @@ -26,7 +26,7 @@ export function UserToolbar({ const [showAdvancedSearch, setShowAdvancedSearch] = useState(false); // 통합 검색어 변경 - const handleUnifiedSearchChange = (value: string) => { + const handleV2SearchChange = (value: string) => { onSearchChange({ searchValue: value, // 통합 검색 시 고급 검색 필드들 클리어 @@ -79,7 +79,7 @@ export function UserToolbar({ handleUnifiedSearchChange(e.target.value)} + onChange={(e) => handleV2SearchChange(e.target.value)} disabled={isAdvancedSearchMode} className={`h-10 pl-10 text-sm ${ isSearching ? "border-primary ring-2 ring-primary/20" : "" diff --git a/frontend/components/admin/dashboard/widget-sections/ListWidgetSection.tsx b/frontend/components/admin/dashboard/widget-sections/ListWidgetSection.tsx index a7186d50..2565e44b 100644 --- a/frontend/components/admin/dashboard/widget-sections/ListWidgetSection.tsx +++ b/frontend/components/admin/dashboard/widget-sections/ListWidgetSection.tsx @@ -7,7 +7,7 @@ import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Switch } from "@/components/ui/switch"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { UnifiedColumnEditor } from "../widgets/list-widget/UnifiedColumnEditor"; +import { V2ColumnEditor } from "../widgets/list-widget/V2ColumnEditor"; import { ListTableOptions } from "../widgets/list-widget/ListTableOptions"; import { Plus, Trash2, ChevronDown, ChevronUp, X, Check } from "lucide-react"; import { Checkbox } from "@/components/ui/checkbox"; @@ -114,7 +114,7 @@ export function ListWidgetSection({ queryResult, config, onConfigChange }: ListW {queryResult && queryResult.columns.length > 0 && (
- +
)} diff --git a/frontend/components/admin/dashboard/widgets/list-widget/UnifiedColumnEditor.tsx b/frontend/components/admin/dashboard/widgets/list-widget/V2ColumnEditor.tsx similarity index 98% rename from frontend/components/admin/dashboard/widgets/list-widget/UnifiedColumnEditor.tsx rename to frontend/components/admin/dashboard/widgets/list-widget/V2ColumnEditor.tsx index 5f27012f..dcb56faf 100644 --- a/frontend/components/admin/dashboard/widgets/list-widget/UnifiedColumnEditor.tsx +++ b/frontend/components/admin/dashboard/widgets/list-widget/V2ColumnEditor.tsx @@ -7,7 +7,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { Plus, Trash2, GripVertical } from "lucide-react"; import { Checkbox } from "@/components/ui/checkbox"; -interface UnifiedColumnEditorProps { +interface V2ColumnEditorProps { queryResult: QueryResult | null; config: ListWidgetConfig; onConfigChange: (updates: Partial) => void; @@ -19,7 +19,7 @@ interface UnifiedColumnEditorProps { * - 모든 필드 편집 가능 (필드명, 표시 이름, 정렬) * - 수동으로 컬럼 추가 가능 */ -export function UnifiedColumnEditor({ queryResult, config, onConfigChange }: UnifiedColumnEditorProps) { +export function V2ColumnEditor({ queryResult, config, onConfigChange }: V2ColumnEditorProps) { const [draggedIndex, setDraggedIndex] = useState(null); const columns = config.columns || []; diff --git a/frontend/components/dataflow/node-editor/FlowEditor.tsx b/frontend/components/dataflow/node-editor/FlowEditor.tsx index 9c4ad7e8..07531249 100644 --- a/frontend/components/dataflow/node-editor/FlowEditor.tsx +++ b/frontend/components/dataflow/node-editor/FlowEditor.tsx @@ -11,7 +11,7 @@ import "reactflow/dist/style.css"; import { useFlowEditorStore } from "@/lib/stores/flowEditorStore"; import { apiClient } from "@/lib/api/client"; import { NodePalette } from "./sidebar/NodePalette"; -import { LeftUnifiedToolbar, ToolbarButton } from "@/components/screen/toolbar/LeftUnifiedToolbar"; +import { LeftV2Toolbar, ToolbarButton } from "@/components/screen/toolbar/LeftV2Toolbar"; import { Boxes, Settings } from "lucide-react"; import { PropertiesPanel } from "./panels/PropertiesPanel"; import { ValidationNotification } from "./ValidationNotification"; @@ -332,7 +332,7 @@ function FlowEditorInner({ initialFlowId, onSaveComplete, embedded = false }: Fl return (
{/* 좌측 통합 툴바 */} - = ({ className }) => { } } - // 🆕 리피터 데이터(배열)를 마스터 저장에서 제외 (UnifiedRepeater가 별도로 저장) + // 🆕 리피터 데이터(배열)를 마스터 저장에서 제외 (V2Repeater가 별도로 저장) const masterDataToSave: Record = {}; Object.entries(dataToSave).forEach(([key, value]) => { if (!Array.isArray(value)) { @@ -832,7 +832,7 @@ export const EditModal: React.FC = ({ className }) => { if (response.success) { const masterRecordId = response.data?.id || formData.id; - // 🆕 리피터 데이터 저장 이벤트 발생 (UnifiedRepeater 컴포넌트가 리스닝) + // 🆕 리피터 데이터 저장 이벤트 발생 (V2Repeater 컴포넌트가 리스닝) window.dispatchEvent( new CustomEvent("repeaterSave", { detail: { diff --git a/frontend/components/screen/InteractiveScreenViewer.tsx b/frontend/components/screen/InteractiveScreenViewer.tsx index 837ad250..fbcf8243 100644 --- a/frontend/components/screen/InteractiveScreenViewer.tsx +++ b/frontend/components/screen/InteractiveScreenViewer.tsx @@ -43,7 +43,7 @@ import { DynamicWebTypeRenderer } from "@/lib/registry/DynamicWebTypeRenderer"; import { enhancedFormService } from "@/lib/services/enhancedFormService"; import { FormValidationIndicator } from "@/components/common/FormValidationIndicator"; import { useFormValidation } from "@/hooks/useFormValidation"; -import { UnifiedColumnInfo as ColumnInfo } from "@/types"; +import { V2ColumnInfo as ColumnInfo } from "@/types"; import { isFileComponent } from "@/lib/utils/componentTypeUtils"; import { buildGridClasses } from "@/lib/constants/columnSpans"; import { cn } from "@/lib/utils"; @@ -1641,7 +1641,7 @@ export const InteractiveScreenViewer: React.FC = ( company_code: mappedData.company_code || companyCodeValue, // ✅ 입력값 우선, 없으면 user.companyCode }; - // 🆕 리피터 데이터(배열)를 마스터 저장에서 제외 (UnifiedRepeater가 별도로 저장) + // 🆕 리피터 데이터(배열)를 마스터 저장에서 제외 (V2Repeater가 별도로 저장) const masterDataWithUserInfo: Record = {}; Object.entries(dataWithUserInfo).forEach(([key, value]) => { if (!Array.isArray(value)) { @@ -1664,7 +1664,7 @@ export const InteractiveScreenViewer: React.FC = ( if (result.success) { const masterRecordId = result.data?.id || formData.id; - // 🆕 리피터 데이터 저장 이벤트 발생 (UnifiedRepeater 컴포넌트가 리스닝) + // 🆕 리피터 데이터 저장 이벤트 발생 (V2Repeater 컴포넌트가 리스닝) window.dispatchEvent( new CustomEvent("repeaterSave", { detail: { diff --git a/frontend/components/screen/InteractiveScreenViewerDynamic.tsx b/frontend/components/screen/InteractiveScreenViewerDynamic.tsx index 5c80c81e..735fb53c 100644 --- a/frontend/components/screen/InteractiveScreenViewerDynamic.tsx +++ b/frontend/components/screen/InteractiveScreenViewerDynamic.tsx @@ -532,7 +532,7 @@ export const InteractiveScreenViewerDynamic: React.FC = {}; Object.entries(formData).forEach(([key, value]) => { // 배열 데이터는 리피터 데이터이므로 제외 @@ -554,7 +554,7 @@ export const InteractiveScreenViewerDynamic: React.FC { + const v2TableRelations = useMemo(() => { // DB 관계 const dbRelations = tableRelations.map(item => ({ ...item, @@ -839,14 +839,14 @@ function TableRelationTab({ - ) : unifiedTableRelations.length === 0 ? ( + ) : v2TableRelations.length === 0 ? ( 등록된 테이블 연결이 없습니다. ) : ( - unifiedTableRelations.map((item) => ( + v2TableRelations.map((item) => ( (null); - const [editingDesignerItem, setEditingDesignerItem] = useState(null); + const [editingDesignerItem, setEditingDesignerItem] = useState(null); const [formData, setFormData] = useState({ field_name: "", save_table: tableName || "", @@ -1011,7 +1011,7 @@ function JoinSettingTab({ // 화면 디자이너 조인 설정을 통합 형식으로 변환 // 1. 현재 테이블의 조인 설정 - const directJoins: UnifiedJoinItem[] = (existingConfig?.joinColumnRefs || []).map((ref, idx) => ({ + const directJoins: V2JoinItem[] = (existingConfig?.joinColumnRefs || []).map((ref, idx) => ({ id: `designer-direct-${idx}`, source: "designer" as const, save_table: tableName || "", @@ -1025,7 +1025,7 @@ function JoinSettingTab({ })); // 2. 필터 테이블들의 조인 설정 (화면 노드에서 열었을 때) - const filterTableJoins: UnifiedJoinItem[] = (existingConfig?.filterTables || []).flatMap((ft, ftIdx) => + const filterTableJoins: V2JoinItem[] = (existingConfig?.filterTables || []).flatMap((ft, ftIdx) => (ft.joinColumnRefs || []).map((ref, refIdx) => ({ id: `designer-filter-${ftIdx}-${refIdx}`, source: "designer" as const, @@ -1041,10 +1041,10 @@ function JoinSettingTab({ ); // 모든 디자이너 조인 설정 통합 - const designerJoins: UnifiedJoinItem[] = [...directJoins, ...filterTableJoins]; + const designerJoins: V2JoinItem[] = [...directJoins, ...filterTableJoins]; // DB 조인 설정을 통합 형식으로 변환 - const dbJoins: UnifiedJoinItem[] = fieldJoins.map((item) => ({ + const dbJoins: V2JoinItem[] = fieldJoins.map((item) => ({ id: item.id, source: "db" as const, save_table: item.save_table, @@ -1058,7 +1058,7 @@ function JoinSettingTab({ })); // 통합된 조인 목록 (화면 디자이너 + DB) - const unifiedJoins = [...designerJoins, ...dbJoins]; + const v2Joins = [...designerJoins, ...dbJoins]; // 저장 테이블 변경 시 컬럼 로드 useEffect(() => { @@ -1114,7 +1114,7 @@ function JoinSettingTab({ }; // 통합 목록에서 수정 버튼 클릭 - const handleEditUnified = (item: UnifiedJoinItem) => { + const handleEditV2 = (item: V2JoinItem) => { if (item.source === "db") { // DB 설정은 기존 로직 사용 const originalItem = fieldJoins.find(j => j.id === item.id); @@ -1142,7 +1142,7 @@ function JoinSettingTab({ }; // 통합 목록에서 삭제 버튼 클릭 - const handleDeleteUnified = async (item: UnifiedJoinItem) => { + const handleDeleteV2 = async (item: V2JoinItem) => { if (item.source === "db") { // DB 설정만 삭제 가능 await handleDelete(item.id as number); @@ -1403,7 +1403,7 @@ function JoinSettingTab({ 조인 설정 목록 - 총 {unifiedJoins.length}개 + 총 {v2Joins.length}개
@@ -1425,14 +1425,14 @@ function JoinSettingTab({ - ) : unifiedJoins.length === 0 ? ( + ) : v2Joins.length === 0 ? ( 등록된 조인 설정이 없습니다. ) : ( - unifiedJoins.map((item) => ( + v2Joins.map((item) => ( {item.source === "designer" ? ( @@ -1460,7 +1460,7 @@ function JoinSettingTab({ variant="ghost" size="icon" className="h-7 w-7" - onClick={() => handleEditUnified(item)} + onClick={() => handleEditV2(item)} title={item.source === "designer" ? "DB 설정으로 저장" : "수정"} > @@ -1470,7 +1470,7 @@ function JoinSettingTab({ variant="ghost" size="icon" className="h-7 w-7 text-destructive" - onClick={() => handleDeleteUnified(item)} + onClick={() => handleDeleteV2(item)} title="삭제" > diff --git a/frontend/components/screen/SaveModal.tsx b/frontend/components/screen/SaveModal.tsx index f2f82292..1c848d6a 100644 --- a/frontend/components/screen/SaveModal.tsx +++ b/frontend/components/screen/SaveModal.tsx @@ -206,7 +206,7 @@ export const SaveModal: React.FC = ({ company_code: dataToSave.company_code || companyCodeValue, // ✅ 입력값 우선, 없으면 user.companyCode }; - // 🆕 리피터 데이터(배열)를 마스터 저장에서 제외 (UnifiedRepeater가 별도로 저장) + // 🆕 리피터 데이터(배열)를 마스터 저장에서 제외 (V2Repeater가 별도로 저장) const masterDataWithUserInfo: Record = {}; Object.entries(dataWithUserInfo).forEach(([key, value]) => { if (!Array.isArray(value)) { @@ -233,7 +233,7 @@ export const SaveModal: React.FC = ({ if (result.success) { const masterRecordId = result.data?.id || dataToSave.id; - // 🆕 리피터 데이터 저장 이벤트 발생 (UnifiedRepeater 컴포넌트가 리스닝) + // 🆕 리피터 데이터 저장 이벤트 발생 (V2Repeater 컴포넌트가 리스닝) window.dispatchEvent( new CustomEvent("repeaterSave", { detail: { diff --git a/frontend/components/screen/ScreenDesigner.tsx b/frontend/components/screen/ScreenDesigner.tsx index c9279b1d..cd5db47f 100644 --- a/frontend/components/screen/ScreenDesigner.tsx +++ b/frontend/components/screen/ScreenDesigner.tsx @@ -17,7 +17,7 @@ import { SCREEN_RESOLUTIONS, } from "@/types/screen"; import { generateComponentId } from "@/lib/utils/generateId"; -import { getComponentIdFromWebType, createUnifiedConfigFromColumn, getUnifiedConfigFromWebType } from "@/lib/utils/webTypeMapping"; +import { getComponentIdFromWebType, createV2ConfigFromColumn, getV2ConfigFromWebType } from "@/lib/utils/webTypeMapping"; import { createGroupComponent, calculateBoundingBox, @@ -106,7 +106,7 @@ import { TableOptionsProvider } from "@/contexts/TableOptionsContext"; // 새로운 통합 UI 컴포넌트 import { SlimToolbar } from "./toolbar/SlimToolbar"; -import { UnifiedPropertiesPanel } from "./panels/UnifiedPropertiesPanel"; +import { V2PropertiesPanel } from "./panels/V2PropertiesPanel"; // 컴포넌트 초기화 (새 시스템) import "@/lib/registry/components"; @@ -123,7 +123,7 @@ interface ScreenDesignerProps { const panelConfigs: PanelConfig[] = [ // 통합 패널 (컴포넌트 + 편집 탭) { - id: "unified", + id: "v2", title: "패널", defaultPosition: "left", defaultWidth: 240, @@ -188,7 +188,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU // 컴포넌트가 선택되면 통합 패널 자동 열기 if (component) { - openPanel("unified"); + openPanel("v2"); } }, [openPanel], @@ -211,7 +211,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU }); // 탭 내부 컴포넌트 선택 시 일반 컴포넌트 선택 해제 setSelectedComponent(null); - openPanel("unified"); + openPanel("v2"); }, [openPanel], ); @@ -1119,7 +1119,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU referenceTable: detailSettings?.referenceTable || col.referenceTable || col.reference_table, referenceColumn: detailSettings?.referenceColumn || col.referenceColumn || col.reference_column, displayColumn: detailSettings?.displayColumn || col.displayColumn || col.display_column, - // detailSettings 전체 보존 (Unified 컴포넌트용) + // detailSettings 전체 보존 (V2 컴포넌트용) detailSettings, }; }); @@ -2904,8 +2904,8 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU const dropX = (e.clientX - tabContentRect.left) / zoomLevel; const dropY = (e.clientY - tabContentRect.top) / zoomLevel; - // 🆕 Unified 컴포넌트 매핑 사용 (일반 캔버스와 동일) - const unifiedMapping = createUnifiedConfigFromColumn({ + // 🆕 V2 컴포넌트 매핑 사용 (일반 캔버스와 동일) + const v2Mapping = createV2ConfigFromColumn({ widgetType: column.widgetType, columnName: column.columnName, columnLabel: column.columnLabel, @@ -2942,14 +2942,14 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU const newTabComponent = { id: `tab_comp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, - componentType: unifiedMapping.componentType, // unified-input, unified-select 등 + componentType: v2Mapping.componentType, // v2-input, v2-select 등 label: column.columnLabel || column.columnName, position: { x: Math.max(0, dropX), y: Math.max(0, dropY) }, size: componentSize, inputType: column.inputType || column.widgetType, // 🆕 inputType 저장 (설정 패널용) widgetType: column.widgetType, // 🆕 widgetType 저장 componentConfig: { - ...unifiedMapping.componentConfig, // Unified 컴포넌트 기본 설정 + ...v2Mapping.componentConfig, // V2 컴포넌트 기본 설정 columnName: column.columnName, tableName: column.tableName, inputType: column.inputType || column.widgetType, // 🆕 componentConfig에도 저장 @@ -3218,8 +3218,8 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU const relativeX = e.clientX - containerRect.left; const relativeY = e.clientY - containerRect.top; - // 🆕 Unified 컴포넌트 매핑 사용 - const unifiedMapping = createUnifiedConfigFromColumn({ + // 🆕 V2 컴포넌트 매핑 사용 + const v2Mapping = createV2ConfigFromColumn({ widgetType: column.widgetType, columnName: column.columnName, columnLabel: column.columnLabel, @@ -3236,9 +3236,9 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU // 웹타입별 기본 너비 계산 (10px 단위 고정) const componentWidth = getDefaultWidth(column.widgetType); - console.log("🎯 폼 컨테이너 Unified 컴포넌트 생성:", { + console.log("🎯 폼 컨테이너 V2 컴포넌트 생성:", { widgetType: column.widgetType, - unifiedType: unifiedMapping.componentType, + v2Type: v2Mapping.componentType, componentWidth, }); @@ -3247,14 +3247,14 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU newComponent = { id: generateComponentId(), - type: "component", // ✅ Unified 컴포넌트 시스템 사용 + type: "component", // ✅ V2 컴포넌트 시스템 사용 label: column.columnLabel || column.columnName, tableName: table.tableName, columnName: column.columnName, required: isEntityJoinColumn ? false : column.required, // 조인 컬럼은 필수 아님 readonly: isEntityJoinColumn, // 🆕 엔티티 조인 컬럼은 읽기 전용 parentId: formContainerId, // 폼 컨테이너의 자식으로 설정 - componentType: unifiedMapping.componentType, // unified-input, unified-select 등 + componentType: v2Mapping.componentType, // v2-input, v2-select 등 position: { x: relativeX, y: relativeY, z: 1 } as Position, size: { width: componentWidth, height: getDefaultHeight(column.widgetType) }, // 코드 타입인 경우 코드 카테고리 정보 추가 @@ -3276,8 +3276,8 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU labelMarginBottom: "6px", }, componentConfig: { - type: unifiedMapping.componentType, // unified-input, unified-select 등 - ...unifiedMapping.componentConfig, // Unified 컴포넌트 기본 설정 + type: v2Mapping.componentType, // v2-input, v2-select 등 + ...v2Mapping.componentConfig, // V2 컴포넌트 기본 설정 ...(isEntityJoinColumn && { disabled: true }), // 🆕 조인 컬럼은 비활성화 }, }; @@ -3285,8 +3285,8 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU return; // 폼 컨테이너를 찾을 수 없으면 드롭 취소 } } else { - // 일반 캔버스에 드롭한 경우 - 🆕 Unified 컴포넌트 시스템 사용 - const unifiedMapping = createUnifiedConfigFromColumn({ + // 일반 캔버스에 드롭한 경우 - 🆕 V2 컴포넌트 시스템 사용 + const v2Mapping = createV2ConfigFromColumn({ widgetType: column.widgetType, columnName: column.columnName, columnLabel: column.columnLabel, @@ -3303,9 +3303,9 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU // 웹타입별 기본 너비 계산 (10px 단위 고정) const componentWidth = getDefaultWidth(column.widgetType); - console.log("🎯 캔버스 Unified 컴포넌트 생성:", { + console.log("🎯 캔버스 V2 컴포넌트 생성:", { widgetType: column.widgetType, - unifiedType: unifiedMapping.componentType, + v2Type: v2Mapping.componentType, componentWidth, }); @@ -3314,13 +3314,13 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU newComponent = { id: generateComponentId(), - type: "component", // ✅ Unified 컴포넌트 시스템 사용 + type: "component", // ✅ V2 컴포넌트 시스템 사용 label: column.columnLabel || column.columnName, // 컬럼 라벨 우선, 없으면 컬럼명 tableName: table.tableName, columnName: column.columnName, required: isEntityJoinColumn ? false : column.required, // 조인 컬럼은 필수 아님 readonly: isEntityJoinColumn, // 🆕 엔티티 조인 컬럼은 읽기 전용 - componentType: unifiedMapping.componentType, // unified-input, unified-select 등 + componentType: v2Mapping.componentType, // v2-input, v2-select 등 position: { x, y, z: 1 } as Position, size: { width: componentWidth, height: getDefaultHeight(column.widgetType) }, // 코드 타입인 경우 코드 카테고리 정보 추가 @@ -3342,8 +3342,8 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU labelMarginBottom: "8px", }, componentConfig: { - type: unifiedMapping.componentType, // unified-input, unified-select 등 - ...unifiedMapping.componentConfig, // Unified 컴포넌트 기본 설정 + type: v2Mapping.componentType, // v2-input, v2-select 등 + ...v2Mapping.componentConfig, // V2 컴포넌트 기본 설정 ...(isEntityJoinColumn && { disabled: true }), // 🆕 조인 컬럼은 비활성화 }, }; @@ -4919,18 +4919,18 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU onGenerateMultilang={handleGenerateMultilang} isGeneratingMultilang={isGeneratingMultilang} onOpenMultilangSettings={() => setShowMultilangSettingsModal(true)} - isPanelOpen={panelStates.unified?.isOpen || false} - onTogglePanel={() => togglePanel("unified")} + isPanelOpen={panelStates.v2?.isOpen || false} + onTogglePanel={() => togglePanel("v2")} /> {/* 메인 컨테이너 (패널들 + 캔버스) */}
{/* 통합 패널 - 좌측 사이드바 제거 후 너비 300px로 확장 */} - {panelStates.unified?.isOpen && ( + {panelStates.v2?.isOpen && (

패널

- = ({ sourceTableName = compConfig.tableName || compConfig.selectedTable || null; if (sourceTableName) break; } - if (compType === "unified-list") { + if (compType === "v2-list") { sourceTableName = compConfig.dataSource?.table || compConfig.tableName || null; if (sourceTableName) break; } diff --git a/frontend/components/screen/config-panels/DataFilterConfigPanel.tsx b/frontend/components/screen/config-panels/DataFilterConfigPanel.tsx index f3e65199..fe089ac5 100644 --- a/frontend/components/screen/config-panels/DataFilterConfigPanel.tsx +++ b/frontend/components/screen/config-panels/DataFilterConfigPanel.tsx @@ -9,12 +9,12 @@ import { Switch } from "@/components/ui/switch"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { Trash2, Plus, ChevronDown, ChevronRight } from "lucide-react"; import { ColumnFilter, DataFilterConfig } from "@/types/screen-management"; -import { UnifiedColumnInfo } from "@/types/table-management"; +import { V2ColumnInfo } from "@/types/table-management"; import { getCategoryValues } from "@/lib/api/tableCategoryValue"; interface DataFilterConfigPanelProps { tableName?: string; - columns?: UnifiedColumnInfo[]; + columns?: V2ColumnInfo[]; config?: DataFilterConfig; onConfigChange: (config: DataFilterConfig) => void; menuObjid?: number; // 🆕 메뉴 OBJID (카테고리 값 조회 시 필요) diff --git a/frontend/components/screen/panels/ComponentsPanel.tsx b/frontend/components/screen/panels/ComponentsPanel.tsx index 5b74519c..8f055bc3 100644 --- a/frontend/components/screen/panels/ComponentsPanel.tsx +++ b/frontend/components/screen/panels/ComponentsPanel.tsx @@ -43,26 +43,26 @@ export function ComponentsPanel({ return components; }, []); - // Unified 컴포넌트 정의 (새로운 통합 컴포넌트 시스템) - // 입력 컴포넌트(unified-input, unified-select, unified-date)는 테이블 컬럼 드래그 시 자동 생성되므로 숨김 - const unifiedComponents = useMemo( + // V2 컴포넌트 정의 (새로운 통합 컴포넌트 시스템) + // 입력 컴포넌트(v2-input, v2-select, v2-date)는 테이블 컬럼 드래그 시 자동 생성되므로 숨김 + const v2Components = useMemo( () => [ - // unified-input: 테이블 컬럼 드래그 시 자동 생성되므로 숨김 처리 - // unified-select: 테이블 컬럼 드래그 시 자동 생성되므로 숨김 처리 - // unified-date: 테이블 컬럼 드래그 시 자동 생성되므로 숨김 처리 - // unified-layout: 중첩 드래그앤드롭 기능 미구현으로 숨김 처리 - // unified-group: 중첩 드래그앤드롭 기능 미구현으로 숨김 처리 - // unified-list: table-list, card-display로 분리하여 숨김 처리 - // unified-media 제거 - 테이블 컬럼의 image/file 입력 타입으로 사용 - // unified-biz 제거 - 개별 컴포넌트(flow-widget, rack-structure, numbering-rule)로 직접 표시 - // unified-hierarchy 제거 - 현재 미사용 + // v2-input: 테이블 컬럼 드래그 시 자동 생성되므로 숨김 처리 + // v2-select: 테이블 컬럼 드래그 시 자동 생성되므로 숨김 처리 + // v2-date: 테이블 컬럼 드래그 시 자동 생성되므로 숨김 처리 + // v2-layout: 중첩 드래그앤드롭 기능 미구현으로 숨김 처리 + // v2-group: 중첩 드래그앤드롭 기능 미구현으로 숨김 처리 + // v2-list: table-list, card-display로 분리하여 숨김 처리 + // v2-media 제거 - 테이블 컬럼의 image/file 입력 타입으로 사용 + // v2-biz 제거 - 개별 컴포넌트(flow-widget, rack-structure, numbering-rule)로 직접 표시 + // v2-hierarchy 제거 - 현재 미사용 { - id: "v2-unified-repeater", + id: "v2-repeater", name: "리피터 그리드", description: "행 단위로 데이터를 추가/수정/삭제", category: "data" as ComponentCategory, - tags: ["repeater", "table", "modal", "button", "unified", "v2"], + tags: ["repeater", "table", "modal", "button", "v2", "v2"], defaultSize: { width: 600, height: 300 }, }, ] as ComponentDefinition[], @@ -78,24 +78,24 @@ export function ComponentsPanel({ "number-input", "date-input", "textarea-basic", - // Unified 컴포넌트로 대체됨 - "image-widget", // → UnifiedMedia (image) - "file-upload", // → UnifiedMedia (file) - "entity-search-input", // → UnifiedSelect (entity 모드) - "autocomplete-search-input", // → UnifiedSelect (autocomplete 모드) + // V2 컴포넌트로 대체됨 + "image-widget", // → V2Media (image) + "file-upload", // → V2Media (file) + "entity-search-input", // → V2Select (entity 모드) + "autocomplete-search-input", // → V2Select (autocomplete 모드) // DataFlow 전용 (일반 화면에서 불필요) "mail-recipient-selector", // 현재 사용 안함 "repeater-field-group", - // unified-repeater로 통합됨 - "simple-repeater-table", // → unified-repeater (inline 모드) - "modal-repeater-table", // → unified-repeater (modal 모드) + // v2-repeater로 통합됨 + "simple-repeater-table", // → v2-repeater (inline 모드) + "modal-repeater-table", // → v2-repeater (modal 모드) // 특수 업무용 컴포넌트 (일반 화면에서 불필요) "tax-invoice-list", // 세금계산서 전용 "customer-item-mapping", // 고객-품목 매핑 전용 // card-display는 별도 컴포넌트로 유지 - // unified-media로 통합됨 - "image-display", // → unified-media (image) + // v2-media로 통합됨 + "image-display", // → v2-media (image) // 공통코드관리로 통합 예정 "category-manager", // → 공통코드관리 기능으로 통합 예정 // 분할 패널 정리 (split-panel-layout v1 유지) @@ -106,12 +106,12 @@ export function ComponentsPanel({ "conditional-container", // 조건부 컨테이너 "universal-form-modal", // 범용 폼 모달 // 통합 미디어 (테이블 컬럼 입력 타입으로 사용) - "unified-media", // → 테이블 컬럼의 image/file 입력 타입으로 사용 + "v2-media", // → 테이블 컬럼의 image/file 입력 타입으로 사용 // 플로우 위젯 숨김 처리 "flow-widget", // 선택 항목 상세입력 - 기존 컴포넌트 조합으로 대체 가능 "selected-items-detail-input", - // 연관 데이터 버튼 - unified-repeater로 대체 가능 + // 연관 데이터 버튼 - v2-repeater로 대체 가능 "related-data-buttons", // ===== V2로 대체된 기존 컴포넌트 (v2 버전만 사용) ===== "button-primary", // → v2-button-primary @@ -126,7 +126,7 @@ export function ComponentsPanel({ "section-card", // → v2-section-card "location-swap-selector", // → v2-location-swap-selector "rack-structure", // → v2-rack-structure - "unified-repeater", // → v2-unified-repeater (아래 unifiedComponents에서 별도 처리) + "v2-repeater", // → v2-repeater (아래 v2Components에서 별도 처리) "repeat-container", // → v2-repeat-container "repeat-screen-modal", // → v2-repeat-screen-modal "pivot-grid", // → v2-pivot-grid @@ -146,9 +146,9 @@ export function ComponentsPanel({ utility: allComponents.filter( (c) => c.category === ComponentCategory.UTILITY && !hiddenComponents.includes(c.id), ), - unified: unifiedComponents, + v2: v2Components, }; - }, [allComponents, unifiedComponents]); + }, [allComponents, v2Components]); // 카테고리별 검색 필터링 const getFilteredComponents = (category: keyof typeof componentsByCategory) => { @@ -322,7 +322,7 @@ export function ComponentsPanel({ {(() => { const allFilteredComponents = [ - ...getFilteredComponents("unified"), + ...getFilteredComponents("v2"), ...getFilteredComponents("action"), ...getFilteredComponents("display"), ...getFilteredComponents("data"), diff --git a/frontend/components/screen/panels/DetailSettingsPanel.tsx b/frontend/components/screen/panels/DetailSettingsPanel.tsx index a7fcc5e1..7ec1c557 100644 --- a/frontend/components/screen/panels/DetailSettingsPanel.tsx +++ b/frontend/components/screen/panels/DetailSettingsPanel.tsx @@ -22,8 +22,8 @@ import { FileComponentConfigPanel } from "./FileComponentConfigPanel"; import { WebTypeConfigPanel } from "./WebTypeConfigPanel"; import { isFileComponent } from "@/lib/utils/componentTypeUtils"; import { BaseInputType, getBaseInputType, getDetailTypes, DetailTypeOption } from "@/types/input-type-mapping"; -import { ConditionalConfigPanel } from "@/components/unified/ConditionalConfigPanel"; -import { ConditionalConfig } from "@/types/unified-components"; +import { ConditionalConfigPanel } from "@/components/v2/ConditionalConfigPanel"; +import { ConditionalConfig } from "@/types/v2-components"; // 새로운 컴포넌트 설정 패널들 import import { ButtonConfigPanel as NewButtonConfigPanel } from "../config-panels/ButtonConfigPanel"; diff --git a/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx b/frontend/components/screen/panels/V2PropertiesPanel.tsx similarity index 95% rename from frontend/components/screen/panels/UnifiedPropertiesPanel.tsx rename to frontend/components/screen/panels/V2PropertiesPanel.tsx index 8177bf7e..be0d265b 100644 --- a/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx +++ b/frontend/components/screen/panels/V2PropertiesPanel.tsx @@ -61,10 +61,10 @@ import { DynamicComponentConfigPanel } from "@/lib/utils/getComponentConfigPanel import StyleEditor from "../StyleEditor"; import { Slider } from "@/components/ui/slider"; import { Zap } from "lucide-react"; -import { ConditionalConfigPanel } from "@/components/unified/ConditionalConfigPanel"; -import { ConditionalConfig } from "@/types/unified-components"; +import { ConditionalConfigPanel } from "@/components/v2/ConditionalConfigPanel"; +import { ConditionalConfig } from "@/types/v2-components"; -interface UnifiedPropertiesPanelProps { +interface V2PropertiesPanelProps { selectedComponent?: ComponentData; tables: TableInfo[]; onUpdateProperty: (componentId: string, path: string, value: any) => void; @@ -83,7 +83,7 @@ interface UnifiedPropertiesPanelProps { currentScreenCompanyCode?: string; } -export const UnifiedPropertiesPanel: React.FC = ({ +export const V2PropertiesPanel: React.FC = ({ selectedComponent, tables, onUpdateProperty, @@ -204,27 +204,27 @@ export const UnifiedPropertiesPanel: React.FC = ({ selectedComponent.componentConfig?.id || (selectedComponent.type === "component" ? selectedComponent.id : null); // 🆕 독립 컴포넌트 (table-search-widget 등) - // 🆕 Unified 컴포넌트 직접 감지 및 설정 패널 렌더링 - if (componentId?.startsWith("unified-")) { - const unifiedConfigPanels: Record void }>> = { - "unified-input": require("@/components/unified/config-panels/UnifiedInputConfigPanel").UnifiedInputConfigPanel, - "unified-select": require("@/components/unified/config-panels/UnifiedSelectConfigPanel") - .UnifiedSelectConfigPanel, - "unified-date": require("@/components/unified/config-panels/UnifiedDateConfigPanel").UnifiedDateConfigPanel, - "unified-list": require("@/components/unified/config-panels/UnifiedListConfigPanel").UnifiedListConfigPanel, - "unified-layout": require("@/components/unified/config-panels/UnifiedLayoutConfigPanel") - .UnifiedLayoutConfigPanel, - "unified-group": require("@/components/unified/config-panels/UnifiedGroupConfigPanel").UnifiedGroupConfigPanel, - "unified-media": require("@/components/unified/config-panels/UnifiedMediaConfigPanel").UnifiedMediaConfigPanel, - "unified-biz": require("@/components/unified/config-panels/UnifiedBizConfigPanel").UnifiedBizConfigPanel, - "unified-hierarchy": require("@/components/unified/config-panels/UnifiedHierarchyConfigPanel") - .UnifiedHierarchyConfigPanel, + // 🆕 V2 컴포넌트 직접 감지 및 설정 패널 렌더링 + if (componentId?.startsWith("v2-")) { + const v2ConfigPanels: Record void }>> = { + "v2-input": require("@/components/v2/config-panels/V2InputConfigPanel").V2InputConfigPanel, + "v2-select": require("@/components/v2/config-panels/V2SelectConfigPanel") + .V2SelectConfigPanel, + "v2-date": require("@/components/v2/config-panels/V2DateConfigPanel").V2DateConfigPanel, + "v2-list": require("@/components/v2/config-panels/V2ListConfigPanel").V2ListConfigPanel, + "v2-layout": require("@/components/v2/config-panels/V2LayoutConfigPanel") + .V2LayoutConfigPanel, + "v2-group": require("@/components/v2/config-panels/V2GroupConfigPanel").V2GroupConfigPanel, + "v2-media": require("@/components/v2/config-panels/V2MediaConfigPanel").V2MediaConfigPanel, + "v2-biz": require("@/components/v2/config-panels/V2BizConfigPanel").V2BizConfigPanel, + "v2-hierarchy": require("@/components/v2/config-panels/V2HierarchyConfigPanel") + .V2HierarchyConfigPanel, }; - const UnifiedConfigPanel = unifiedConfigPanels[componentId]; - if (UnifiedConfigPanel) { + const V2ConfigPanel = v2ConfigPanels[componentId]; + if (V2ConfigPanel) { const currentConfig = selectedComponent.componentConfig || {}; - const handleUnifiedConfigChange = (newConfig: any) => { + const handleV2ConfigChange = (newConfig: any) => { onUpdateProperty(selectedComponent.id, "componentConfig", { ...currentConfig, ...newConfig }); }; @@ -236,16 +236,16 @@ export const UnifiedPropertiesPanel: React.FC = ({ // 컴포넌트별 추가 props const extraProps: Record = {}; - if (componentId === "unified-select") { + if (componentId === "v2-select") { extraProps.inputType = inputType; } - if (componentId === "unified-list") { + if (componentId === "v2-list") { extraProps.currentTableName = currentTableName; } return (
- +
); } @@ -685,16 +685,16 @@ export const UnifiedPropertiesPanel: React.FC = ({ "entity-search-input", "autocomplete-search-input", // 새로운 통합 입력 컴포넌트 - "unified-input", - "unified-select", - "unified-entity-select", - "unified-checkbox", - "unified-radio", - "unified-textarea", - "unified-date", - "unified-datetime", - "unified-time", - "unified-file", + "v2-input", + "v2-select", + "v2-entity-select", + "v2-checkbox", + "v2-radio", + "v2-textarea", + "v2-date", + "v2-datetime", + "v2-time", + "v2-file", ]; // 현재 컴포넌트가 입력 필드인지 확인 @@ -942,10 +942,10 @@ export const UnifiedPropertiesPanel: React.FC = ({ ); } - // 🆕 3.5. Unified 컴포넌트 - 반드시 다른 체크보다 먼저 처리 - const unifiedComponentType = + // 🆕 3.5. V2 컴포넌트 - 반드시 다른 체크보다 먼저 처리 + const v2ComponentType = (selectedComponent as any).componentType || selectedComponent.componentConfig?.type || ""; - if (unifiedComponentType.startsWith("unified-")) { + if (v2ComponentType.startsWith("v2-")) { const configPanel = renderComponentConfigPanel(); if (configPanel) { return
{configPanel}
; @@ -1232,7 +1232,7 @@ export const UnifiedPropertiesPanel: React.FC = ({ console.log("✅ [renderDetailTab] 일반 위젯 렌더링 시작"); return (
- {console.log("🔍 [UnifiedPropertiesPanel] widget.webType:", widget.webType, "widget:", widget)} + {console.log("🔍 [V2PropertiesPanel] widget.webType:", widget.webType, "widget:", widget)} {/* WebType 선택 (있는 경우만) */} {widget.webType && (
@@ -1442,7 +1442,7 @@ export const UnifiedPropertiesPanel: React.FC = ({ ?.filter((c) => { // 자기 자신 제외 if (c.id === selectedComponent.id) return false; - // widget 타입 또는 component 타입 (Unified 컴포넌트 포함) + // widget 타입 또는 component 타입 (V2 컴포넌트 포함) return c.type === "widget" || c.type === "component"; }) .map((c) => { @@ -1453,7 +1453,7 @@ export const UnifiedPropertiesPanel: React.FC = ({ // 정적 옵션 추출 (select, dropdown, radio, entity 등) let options: Array<{ value: string; label: string }> | undefined; - // Unified 컴포넌트의 경우 + // V2 컴포넌트의 경우 if (config.options && Array.isArray(config.options)) { options = config.options; } @@ -1529,4 +1529,4 @@ export const UnifiedPropertiesPanel: React.FC = ({ ); }; -export default UnifiedPropertiesPanel; +export default V2PropertiesPanel; diff --git a/frontend/components/screen/toolbar/LeftUnifiedToolbar.tsx b/frontend/components/screen/toolbar/LeftV2Toolbar.tsx similarity index 93% rename from frontend/components/screen/toolbar/LeftUnifiedToolbar.tsx rename to frontend/components/screen/toolbar/LeftV2Toolbar.tsx index 3d767ff1..efa0640f 100644 --- a/frontend/components/screen/toolbar/LeftUnifiedToolbar.tsx +++ b/frontend/components/screen/toolbar/LeftV2Toolbar.tsx @@ -15,13 +15,13 @@ export interface ToolbarButton { panelWidth: number; } -interface LeftUnifiedToolbarProps { +interface LeftV2ToolbarProps { buttons: ToolbarButton[]; panelStates: Record; onTogglePanel: (panelId: string) => void; } -export const LeftUnifiedToolbar: React.FC = ({ buttons, panelStates, onTogglePanel }) => { +export const LeftV2Toolbar: React.FC = ({ buttons, panelStates, onTogglePanel }) => { // 그룹별로 버튼 분류 const sourceButtons = buttons.filter((btn) => btn.group === "source"); const editorButtons = buttons.filter((btn) => btn.group === "editor"); @@ -75,7 +75,7 @@ export const LeftUnifiedToolbar: React.FC = ({ buttons, export const defaultToolbarButtons: ToolbarButton[] = [ // 통합 패널 (컴포넌트 + 편집 탭) { - id: "unified", + id: "v2", label: "패널", icon: , shortcut: "P", @@ -84,4 +84,4 @@ export const defaultToolbarButtons: ToolbarButton[] = [ }, ]; -export default LeftUnifiedToolbar; +export default LeftV2Toolbar; diff --git a/frontend/components/unified/UnifiedComponentRenderer.tsx b/frontend/components/unified/UnifiedComponentRenderer.tsx deleted file mode 100644 index 6b486b0a..00000000 --- a/frontend/components/unified/UnifiedComponentRenderer.tsx +++ /dev/null @@ -1,111 +0,0 @@ -"use client"; - -/** - * UnifiedComponentRenderer - * - * Unified 컴포넌트를 동적으로 렌더링하는 컴포넌트 - * props.unifiedType에 따라 적절한 컴포넌트를 렌더링 - */ - -import React, { forwardRef, useMemo } from "react"; -import { - UnifiedComponentProps, - isUnifiedInput, - isUnifiedSelect, - isUnifiedDate, - isUnifiedText, - isUnifiedMedia, - isUnifiedList, - isUnifiedLayout, - isUnifiedGroup, - isUnifiedBiz, - isUnifiedHierarchy, -} from "@/types/unified-components"; -import { UnifiedInput } from "./UnifiedInput"; -import { UnifiedSelect } from "./UnifiedSelect"; -import { UnifiedDate } from "./UnifiedDate"; -import { UnifiedList } from "./UnifiedList"; -import { UnifiedLayout } from "./UnifiedLayout"; -import { UnifiedGroup } from "./UnifiedGroup"; -import { UnifiedMedia } from "./UnifiedMedia"; -import { UnifiedBiz } from "./UnifiedBiz"; -import { UnifiedHierarchy } from "./UnifiedHierarchy"; - -interface UnifiedComponentRendererProps { - props: UnifiedComponentProps; - className?: string; -} - -/** - * Unified 컴포넌트 렌더러 - */ -export const UnifiedComponentRenderer = forwardRef( - ({ props, className }, ref) => { - const component = useMemo(() => { - // 타입 가드를 사용하여 적절한 컴포넌트 렌더링 - if (isUnifiedInput(props)) { - return ; - } - - if (isUnifiedSelect(props)) { - return ; - } - - if (isUnifiedDate(props)) { - return ; - } - - if (isUnifiedText(props)) { - // UnifiedText는 UnifiedInput의 textarea 모드로 대체 - // 필요시 별도 구현 - return ( -
- UnifiedText (UnifiedInput textarea 모드 사용 권장) -
- ); - } - - if (isUnifiedMedia(props)) { - return ; - } - - if (isUnifiedList(props)) { - return ; - } - - if (isUnifiedLayout(props)) { - return ; - } - - if (isUnifiedGroup(props)) { - return ; - } - - if (isUnifiedBiz(props)) { - return ; - } - - if (isUnifiedHierarchy(props)) { - return ; - } - - // 알 수 없는 타입 - return ( -
- 알 수 없는 컴포넌트 타입: {(props as { unifiedType?: string }).unifiedType} -
- ); - }, [props]); - - return ( -
- {component} -
- ); - } -); - -UnifiedComponentRenderer.displayName = "UnifiedComponentRenderer"; - -export default UnifiedComponentRenderer; - diff --git a/frontend/components/unified/config-panels/index.ts b/frontend/components/unified/config-panels/index.ts deleted file mode 100644 index 4a582264..00000000 --- a/frontend/components/unified/config-panels/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Unified 컴포넌트 설정 패널 인덱스 - */ - -export { UnifiedInputConfigPanel } from "./UnifiedInputConfigPanel"; -export { UnifiedSelectConfigPanel } from "./UnifiedSelectConfigPanel"; -export { UnifiedDateConfigPanel } from "./UnifiedDateConfigPanel"; -export { UnifiedListConfigPanel } from "./UnifiedListConfigPanel"; -export { UnifiedLayoutConfigPanel } from "./UnifiedLayoutConfigPanel"; -export { UnifiedGroupConfigPanel } from "./UnifiedGroupConfigPanel"; -export { UnifiedMediaConfigPanel } from "./UnifiedMediaConfigPanel"; -export { UnifiedBizConfigPanel } from "./UnifiedBizConfigPanel"; -export { UnifiedHierarchyConfigPanel } from "./UnifiedHierarchyConfigPanel"; - - diff --git a/frontend/components/unified/index.ts b/frontend/components/unified/index.ts deleted file mode 100644 index cf21866e..00000000 --- a/frontend/components/unified/index.ts +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Unified Components 모듈 인덱스 - * - * 10개의 통합 컴포넌트 시스템 - */ - -// Phase 1 컴포넌트 -export { UnifiedInput } from "./UnifiedInput"; -export { UnifiedSelect } from "./UnifiedSelect"; -export { UnifiedDate } from "./UnifiedDate"; - -// Phase 2 컴포넌트 -export { UnifiedList } from "./UnifiedList"; -export { UnifiedLayout } from "./UnifiedLayout"; -export { UnifiedGroup } from "./UnifiedGroup"; - -// Phase 3 컴포넌트 -export { UnifiedMedia } from "./UnifiedMedia"; -export { UnifiedBiz } from "./UnifiedBiz"; -export { UnifiedHierarchy } from "./UnifiedHierarchy"; - -// UnifiedText는 UnifiedInput의 textarea 모드로 대체 가능 - -// 렌더러 -export { UnifiedComponentRenderer } from "./UnifiedComponentRenderer"; - -// 설정 패널 -export { DynamicConfigPanel, COMMON_SCHEMAS } from "./DynamicConfigPanel"; - -// 데모 컴포넌트 -export { UnifiedComponentsDemo } from "./UnifiedComponentsDemo"; - -// 폼 컨텍스트 및 액션 -export { - UnifiedFormProvider, - useUnifiedForm, - useUnifiedFormOptional, - useUnifiedField, - useCascadingOptions, - useFormActions, - useRepeaterField, -} from "./UnifiedFormContext"; - -// 설정 UI 패널 -export { ConditionalConfigPanel } from "./ConditionalConfigPanel"; - -// 폼 관련 타입 re-export -export type { - FormStatus, - FieldError, - FieldState, - SubmitConfig, - SubmitResult, - ValidationResult, - FieldMapping, - ScreenDataTransferConfig, - FormCompatibilityBridge, -} from "@/types/unified-form"; - -// 타입 re-export -export type { - // 공통 타입 - UnifiedComponentType, - UnifiedBaseProps, - ConditionalConfig, - AutoFillConfig, - CascadingConfig, - MutualExclusionConfig, - - // UnifiedInput 타입 - UnifiedInputType, - UnifiedInputFormat, - UnifiedInputConfig, - UnifiedInputProps, - - // UnifiedSelect 타입 - UnifiedSelectMode, - UnifiedSelectSource, - SelectOption, - UnifiedSelectConfig, - UnifiedSelectProps, - - // UnifiedDate 타입 - UnifiedDateType, - UnifiedDateConfig, - UnifiedDateProps, - - // UnifiedList 타입 - UnifiedListViewMode, - ListColumn, - UnifiedListConfig, - UnifiedListProps, - - // UnifiedLayout 타입 - UnifiedLayoutType, - UnifiedLayoutConfig, - UnifiedLayoutProps, - - // UnifiedGroup 타입 - UnifiedGroupType, - TabItem, - UnifiedGroupConfig, - UnifiedGroupProps, - - // UnifiedMedia 타입 - UnifiedMediaType, - UnifiedMediaConfig, - UnifiedMediaProps, - - // UnifiedBiz 타입 - UnifiedBizType, - UnifiedBizConfig, - UnifiedBizProps, - - // UnifiedHierarchy 타입 - UnifiedHierarchyType, - UnifiedHierarchyViewMode, - HierarchyNode, - UnifiedHierarchyConfig, - UnifiedHierarchyProps, - - // 통합 Props - UnifiedComponentProps, -} from "@/types/unified-components"; - diff --git a/frontend/components/unified/ConditionalConfigPanel.tsx b/frontend/components/v2/ConditionalConfigPanel.tsx similarity index 99% rename from frontend/components/unified/ConditionalConfigPanel.tsx rename to frontend/components/v2/ConditionalConfigPanel.tsx index ca60dd7f..99d7f813 100644 --- a/frontend/components/unified/ConditionalConfigPanel.tsx +++ b/frontend/components/v2/ConditionalConfigPanel.tsx @@ -20,7 +20,7 @@ import { Separator } from "@/components/ui/separator"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; import { Zap, Plus, Trash2, HelpCircle, Check, ChevronsUpDown } from "lucide-react"; -import { ConditionalConfig } from "@/types/unified-components"; +import { ConditionalConfig } from "@/types/v2-components"; import { cn } from "@/lib/utils"; // ===== 타입 정의 ===== diff --git a/frontend/components/unified/DynamicConfigPanel.tsx b/frontend/components/v2/DynamicConfigPanel.tsx similarity index 96% rename from frontend/components/unified/DynamicConfigPanel.tsx rename to frontend/components/v2/DynamicConfigPanel.tsx index 514bda89..3ecf6855 100644 --- a/frontend/components/unified/DynamicConfigPanel.tsx +++ b/frontend/components/v2/DynamicConfigPanel.tsx @@ -4,7 +4,7 @@ * DynamicConfigPanel * * JSON Schema 기반으로 동적으로 설정 UI를 생성하는 패널 - * 모든 Unified 컴포넌트의 설정을 단일 컴포넌트로 처리 + * 모든 V2 컴포넌트의 설정을 단일 컴포넌트로 처리 */ import React, { useCallback, useMemo } from "react"; @@ -17,11 +17,11 @@ import { Textarea } from "@/components/ui/textarea"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { ChevronDown } from "lucide-react"; -import { JSONSchemaProperty, UnifiedConfigSchema } from "@/types/unified-components"; +import { JSONSchemaProperty, V2ConfigSchema } from "@/types/v2-components"; import { cn } from "@/lib/utils"; interface DynamicConfigPanelProps { - schema: UnifiedConfigSchema; + schema: V2ConfigSchema; config: Record; onChange: (key: string, value: unknown) => void; className?: string; @@ -254,8 +254,8 @@ export function DynamicConfigPanel({ * 기본 스키마들 (자주 사용되는 설정) */ export const COMMON_SCHEMAS = { - // UnifiedInput 기본 스키마 - UnifiedInput: { + // V2Input 기본 스키마 + V2Input: { type: "object" as const, properties: { type: { @@ -291,8 +291,8 @@ export const COMMON_SCHEMAS = { }, }, - // UnifiedSelect 기본 스키마 - UnifiedSelect: { + // V2Select 기본 스키마 + V2Select: { type: "object" as const, properties: { mode: { @@ -338,8 +338,8 @@ export const COMMON_SCHEMAS = { }, }, - // UnifiedDate 기본 스키마 - UnifiedDate: { + // V2Date 기본 스키마 + V2Date: { type: "object" as const, properties: { type: { @@ -365,7 +365,7 @@ export const COMMON_SCHEMAS = { }, }, }, -} satisfies Record; +} satisfies Record; export default DynamicConfigPanel; diff --git a/frontend/components/unified/UnifiedBiz.tsx b/frontend/components/v2/V2Biz.tsx similarity index 97% rename from frontend/components/unified/UnifiedBiz.tsx rename to frontend/components/v2/V2Biz.tsx index 7b08590d..2ce97050 100644 --- a/frontend/components/unified/UnifiedBiz.tsx +++ b/frontend/components/v2/V2Biz.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedBiz + * V2Biz * * 통합 비즈니스 컴포넌트 * - flow: 플로우/워크플로우 @@ -18,7 +18,7 @@ import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { cn } from "@/lib/utils"; -import { UnifiedBizProps } from "@/types/unified-components"; +import { V2BizProps } from "@/types/v2-components"; import { GitBranch, LayoutGrid, @@ -256,9 +256,9 @@ const RelatedButtonsBiz = forwardRef( +export const V2Biz = forwardRef( (props, ref) => { const { id, @@ -343,7 +343,7 @@ export const UnifiedBiz = forwardRef( } ); -UnifiedBiz.displayName = "UnifiedBiz"; +V2Biz.displayName = "V2Biz"; -export default UnifiedBiz; +export default V2Biz; diff --git a/frontend/components/v2/V2ComponentRenderer.tsx b/frontend/components/v2/V2ComponentRenderer.tsx new file mode 100644 index 00000000..61eb6213 --- /dev/null +++ b/frontend/components/v2/V2ComponentRenderer.tsx @@ -0,0 +1,111 @@ +"use client"; + +/** + * V2ComponentRenderer + * + * V2 컴포넌트를 동적으로 렌더링하는 컴포넌트 + * props.v2Type에 따라 적절한 컴포넌트를 렌더링 + */ + +import React, { forwardRef, useMemo } from "react"; +import { + V2ComponentProps, + isV2Input, + isV2Select, + isV2Date, + isV2Text, + isV2Media, + isV2List, + isV2Layout, + isV2Group, + isV2Biz, + isV2Hierarchy, +} from "@/types/v2-components"; +import { V2Input } from "./V2Input"; +import { V2Select } from "./V2Select"; +import { V2Date } from "./V2Date"; +import { V2List } from "./V2List"; +import { V2Layout } from "./V2Layout"; +import { V2Group } from "./V2Group"; +import { V2Media } from "./V2Media"; +import { V2Biz } from "./V2Biz"; +import { V2Hierarchy } from "./V2Hierarchy"; + +interface V2ComponentRendererProps { + props: V2ComponentProps; + className?: string; +} + +/** + * V2 컴포넌트 렌더러 + */ +export const V2ComponentRenderer = forwardRef( + ({ props, className }, ref) => { + const component = useMemo(() => { + // 타입 가드를 사용하여 적절한 컴포넌트 렌더링 + if (isV2Input(props)) { + return ; + } + + if (isV2Select(props)) { + return ; + } + + if (isV2Date(props)) { + return ; + } + + if (isV2Text(props)) { + // V2Text는 V2Input의 textarea 모드로 대체 + // 필요시 별도 구현 + return ( +
+ V2Text (V2Input textarea 모드 사용 권장) +
+ ); + } + + if (isV2Media(props)) { + return ; + } + + if (isV2List(props)) { + return ; + } + + if (isV2Layout(props)) { + return ; + } + + if (isV2Group(props)) { + return ; + } + + if (isV2Biz(props)) { + return ; + } + + if (isV2Hierarchy(props)) { + return ; + } + + // 알 수 없는 타입 + return ( +
+ 알 수 없는 컴포넌트 타입: {(props as { v2Type?: string }).v2Type} +
+ ); + }, [props]); + + return ( +
+ {component} +
+ ); + } +); + +V2ComponentRenderer.displayName = "V2ComponentRenderer"; + +export default V2ComponentRenderer; + diff --git a/frontend/components/unified/UnifiedComponentsDemo.tsx b/frontend/components/v2/V2ComponentsDemo.tsx similarity index 87% rename from frontend/components/unified/UnifiedComponentsDemo.tsx rename to frontend/components/v2/V2ComponentsDemo.tsx index 1913404f..477dcd7c 100644 --- a/frontend/components/unified/UnifiedComponentsDemo.tsx +++ b/frontend/components/v2/V2ComponentsDemo.tsx @@ -1,9 +1,9 @@ "use client"; /** - * UnifiedComponentsDemo + * V2ComponentsDemo * - * Unified 컴포넌트들을 테스트하고 미리볼 수 있는 데모 페이지 + * V2 컴포넌트들을 테스트하고 미리볼 수 있는 데모 페이지 */ import React, { useState } from "react"; @@ -14,29 +14,29 @@ import { Button } from "@/components/ui/button"; import { Separator } from "@/components/ui/separator"; import { ArrowLeft, Code, Eye, Zap } from "lucide-react"; -// Unified 컴포넌트들 -import { UnifiedInput } from "./UnifiedInput"; -import { UnifiedSelect } from "./UnifiedSelect"; -import { UnifiedDate } from "./UnifiedDate"; -import { UnifiedList } from "./UnifiedList"; -import { UnifiedLayout } from "./UnifiedLayout"; -import { UnifiedGroup } from "./UnifiedGroup"; -import { UnifiedMedia } from "./UnifiedMedia"; -import { UnifiedBiz } from "./UnifiedBiz"; -import { UnifiedHierarchy } from "./UnifiedHierarchy"; +// V2 컴포넌트들 +import { V2Input } from "./V2Input"; +import { V2Select } from "./V2Select"; +import { V2Date } from "./V2Date"; +import { V2List } from "./V2List"; +import { V2Layout } from "./V2Layout"; +import { V2Group } from "./V2Group"; +import { V2Media } from "./V2Media"; +import { V2Biz } from "./V2Biz"; +import { V2Hierarchy } from "./V2Hierarchy"; // 조건부 로직 -import { UnifiedFormProvider, useUnifiedForm } from "./UnifiedFormContext"; +import { V2FormProvider, useV2Form } from "./V2FormContext"; import { ConditionalConfigPanel } from "./ConditionalConfigPanel"; // 타입 -import { HierarchyNode, ConditionalConfig } from "@/types/unified-components"; +import { HierarchyNode, ConditionalConfig } from "@/types/v2-components"; -interface UnifiedComponentsDemoProps { +interface V2ComponentsDemoProps { onBack?: () => void; } -export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) { +export function V2ComponentsDemo({ onBack }: V2ComponentsDemoProps) { const [activeTab, setActiveTab] = useState("conditional"); // 데모용 상태 @@ -114,7 +114,7 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) { )}
-

Unified 컴포넌트 테스트

+

V2 컴포넌트 테스트

10개의 통합 컴포넌트를 테스트합니다

@@ -146,11 +146,11 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) { - {/* UnifiedInput 탭 */} + {/* V2Input 탭 */} - UnifiedInput + V2Input 통합 입력 컴포넌트 - text, number, password, slider, color, button @@ -158,61 +158,61 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) {
{/* Text Input */} - setInputValues({ ...inputValues, text: String(v) })} /> {/* Number Input */} - setInputValues({ ...inputValues, number: Number(v) })} /> {/* Password Input */} - setInputValues({ ...inputValues, password: String(v) })} /> {/* Slider Input */} - setInputValues({ ...inputValues, slider: Number(v) })} /> {/* Color Input */} - setInputValues({ ...inputValues, color: String(v) })} /> {/* Button */} - - {/* UnifiedSelect 탭 */} + {/* V2Select 탭 */} - UnifiedSelect + V2Select 통합 선택 컴포넌트 - dropdown, radio, check, tag, toggle, swap @@ -245,10 +245,10 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) {
{/* Dropdown */} - {/* Radio */} - {/* Checkbox */} - {/* Tag */} - {/* Toggle */} - - {/* UnifiedDate 탭 */} + {/* V2Date 탭 */} - UnifiedDate + V2Date 통합 날짜/시간 컴포넌트 - date, time, datetime, range @@ -344,40 +344,40 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) {
{/* Date */} - setDateValues({ ...dateValues, date: String(v) })} /> {/* Time */} - setDateValues({ ...dateValues, time: String(v) })} /> {/* DateTime */} - setDateValues({ ...dateValues, datetime: String(v) })} /> {/* Range */} - setDateValues({ ...dateValues, range: v as [string, string] })} @@ -396,20 +396,20 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) { - {/* UnifiedList 탭 */} + {/* V2List 탭 */} - UnifiedList + V2List 통합 리스트 컴포넌트 - table, card, list - - {/* UnifiedLayout 탭 */} + {/* V2Layout 탭 */} - UnifiedLayout + V2Layout 통합 레이아웃 컴포넌트 - 12컬럼 그리드 시스템, split, flex @@ -448,9 +448,9 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) {

{/* 12컬럼 전체 보기 */} - {Array.from({ length: 12 }).map((_, i) => ( @@ -458,7 +458,7 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) { {i + 1}
))} - +
@@ -466,9 +466,9 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) { {/* col-span 예시 */}

col-span 활용 예시

-
@@ -507,7 +507,7 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) {
col-span-4 (1/3)
- +
@@ -515,9 +515,9 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) { {/* Split Layout */}

Split Layout (리사이즈 가능)

- @@ -527,26 +527,26 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) {
오른쪽 패널
-
+
- {/* UnifiedGroup 탭 */} + {/* V2Group 탭 */} - UnifiedGroup + V2Group 통합 그룹 컴포넌트 - tabs, accordion, section, card-section {/* Tabs */} - {/* Accordion */} -

이 내용은 접었다 펼 수 있습니다.

-
+ {/* Card Section */} -

카드 스타일 섹션 내용입니다.

-
+
- {/* UnifiedMedia 탭 */} + {/* V2Media 탭 */} - UnifiedMedia + V2Media 통합 미디어 컴포넌트 - file, image, video, audio @@ -602,18 +602,18 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) {
{/* File Upload */} - {/* Image Upload */} -
@@ -621,30 +621,30 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) {
- {/* UnifiedBiz 탭 */} + {/* V2Biz 탭 */} - UnifiedBiz + V2Biz 통합 비즈니스 컴포넌트 - numbering, category, flow 등 (플레이스홀더)
- - -
@@ -652,11 +652,11 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) {
- {/* UnifiedHierarchy 탭 */} + {/* V2Hierarchy 탭 */} - UnifiedHierarchy + V2Hierarchy 통합 계층 구조 컴포넌트 - tree, org, bom, cascading @@ -666,9 +666,9 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) { {/* Tree View */}

트리 뷰

- @@ -677,9 +677,9 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) { {/* Cascading Dropdowns */}

연쇄 드롭다운

- @@ -694,7 +694,7 @@ export function UnifiedComponentsDemo({ onBack }: UnifiedComponentsDemoProps) { ); } -export default UnifiedComponentsDemo; +export default V2ComponentsDemo; // ===== 조건부 동작 데모 컴포넌트 ===== @@ -708,7 +708,7 @@ export default UnifiedComponentsDemo; */ function ConditionalDemo() { return ( - - + ); } function ConditionalDemoContent() { - const { formData, setValue, evaluateCondition } = useUnifiedForm(); + const { formData, setValue, evaluateCondition } = useV2Form(); // 국가별 도시 데이터 const cityOptions: Record> = { @@ -794,11 +794,11 @@ function ConditionalDemoContent() { {/* 계약 유형 선택 */} -

B2B 전용 입력 필드

- setValue("companyName", v)} /> - setValue("employeeCount", v)} @@ -842,19 +842,19 @@ function ConditionalDemoContent() {

B2C 전용 입력 필드

- setValue("customerName", v)} /> - setValue("phone", v)} @@ -875,10 +875,10 @@ function ConditionalDemoContent() {
-

VIP 전용 혜택 설정

-
{/* 국가 선택 */} - {/* 도시 선택 (국가에 따라 옵션 변경) */} - }) { const [demoConfig, setDemoConfig] = useState(undefined); - const { evaluateCondition } = useUnifiedForm(); + const { evaluateCondition } = useV2Form(); // 데모용 필드 목록 (현재 폼의 필드들) const availableFields = [ @@ -1076,11 +1076,11 @@ function ConditionalConfigUIDemo({ formData }: { formData: Record

대상 필드:

{conditionResult.visible ? ( - ) : ( diff --git a/frontend/components/unified/UnifiedDate.tsx b/frontend/components/v2/V2Date.tsx similarity index 98% rename from frontend/components/unified/UnifiedDate.tsx rename to frontend/components/v2/V2Date.tsx index 5f63d701..c825b1bf 100644 --- a/frontend/components/unified/UnifiedDate.tsx +++ b/frontend/components/v2/V2Date.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedDate + * V2Date * * 통합 날짜/시간 컴포넌트 * - date: 날짜 선택 @@ -20,7 +20,7 @@ import { Button } from "@/components/ui/button"; import { Calendar } from "@/components/ui/calendar"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { cn } from "@/lib/utils"; -import { UnifiedDateProps, UnifiedDateType } from "@/types/unified-components"; +import { V2DateProps, V2DateType } from "@/types/v2-components"; // 날짜 형식 매핑 const DATE_FORMATS: Record = { @@ -367,9 +367,9 @@ const DateTimePicker = forwardRef< DateTimePicker.displayName = "DateTimePicker"; /** - * 메인 UnifiedDate 컴포넌트 + * 메인 V2Date 컴포넌트 */ -export const UnifiedDate = forwardRef((props, ref) => { +export const V2Date = forwardRef((props, ref) => { const { id, label, required, readonly, disabled, style, size, config: configProp, value, onChange } = props; // config가 없으면 기본값 사용 @@ -483,6 +483,6 @@ export const UnifiedDate = forwardRef((props, ); }); -UnifiedDate.displayName = "UnifiedDate"; +V2Date.displayName = "V2Date"; -export default UnifiedDate; +export default V2Date; diff --git a/frontend/components/unified/UnifiedFormContext.tsx b/frontend/components/v2/V2FormContext.tsx similarity index 93% rename from frontend/components/unified/UnifiedFormContext.tsx rename to frontend/components/v2/V2FormContext.tsx index f24e947e..4f2553ac 100644 --- a/frontend/components/unified/UnifiedFormContext.tsx +++ b/frontend/components/v2/V2FormContext.tsx @@ -1,17 +1,17 @@ "use client"; /** - * UnifiedFormContext + * V2FormContext * - * Unified 컴포넌트들이 폼 상태를 공유하고 + * V2 컴포넌트들이 폼 상태를 공유하고 * 조건부 로직, 저장/검증/초기화 등의 폼 액션을 처리할 수 있도록 하는 Context * * 레거시 컴포넌트와의 호환성을 유지하면서 새로운 기능을 제공합니다. */ import React, { createContext, useContext, useState, useCallback, useMemo, useRef } from "react"; -import { ConditionalConfig, CascadingConfig } from "@/types/unified-components"; -import { ValidationRule } from "@/types/unified-core"; +import { ConditionalConfig, CascadingConfig } from "@/types/v2-components"; +import { ValidationRule } from "@/types/v2-core"; import type { FormStatus, FieldError, @@ -20,7 +20,7 @@ import type { SubmitResult, ValidationResult, FormEventDetail, -} from "@/types/unified-form"; +} from "@/types/v2-form"; // ===== 레거시 타입 호환 (기존 코드와 호환) ===== @@ -37,7 +37,7 @@ export interface FormState { // ===== 확장된 Context 타입 ===== -export interface UnifiedFormContextValue { +export interface V2FormContextValue { // === 기존 기능 (하위 호환) === formData: Record; fieldStates: FormState; @@ -93,7 +93,7 @@ export interface UnifiedFormContextValue { // ===== Context 생성 ===== -const UnifiedFormContext = createContext(null); +const V2FormContext = createContext(null); // ===== 조건 평가 함수 ===== @@ -145,7 +145,7 @@ const initialFormStatus: FormStatus = { // ===== Provider Props ===== -interface UnifiedFormProviderProps { +interface V2FormProviderProps { children: React.ReactNode; initialValues?: Record; onChange?: (formData: Record) => void; @@ -162,7 +162,7 @@ interface UnifiedFormProviderProps { // ===== Provider 컴포넌트 ===== -export function UnifiedFormProvider({ +export function V2FormProvider({ children, initialValues = {}, onChange, @@ -171,7 +171,7 @@ export function UnifiedFormProvider({ onError, onReset, emitLegacyEvents = true, -}: UnifiedFormProviderProps) { +}: V2FormProviderProps) { // 기존 상태 const [formData, setFormData] = useState>(initialValues); const [fieldStates, setFieldStates] = useState({}); @@ -503,7 +503,7 @@ export function UnifiedFormProvider({ // ===== Context 값 ===== - const contextValue = useMemo(() => ({ + const contextValue = useMemo(() => ({ // 기존 기능 formData, fieldStates, @@ -565,37 +565,37 @@ export function UnifiedFormProvider({ ]); return ( - + {children} - + ); } // ===== 커스텀 훅 ===== /** - * UnifiedForm 컨텍스트 사용 (Context가 없으면 에러) + * V2Form 컨텍스트 사용 (Context가 없으면 에러) */ -export function useUnifiedForm(): UnifiedFormContextValue { - const context = useContext(UnifiedFormContext); +export function useV2Form(): V2FormContextValue { + const context = useContext(V2FormContext); if (!context) { - throw new Error("useUnifiedForm must be used within UnifiedFormProvider"); + throw new Error("useV2Form must be used within V2FormProvider"); } return context; } /** - * UnifiedForm 컨텍스트 사용 (Context가 없어도 에러 안 남, null 반환) + * V2Form 컨텍스트 사용 (Context가 없어도 에러 안 남, null 반환) * 레거시 호환성을 위해 사용 */ -export function useUnifiedFormOptional(): UnifiedFormContextValue | null { - return useContext(UnifiedFormContext); +export function useV2FormOptional(): V2FormContextValue | null { + return useContext(V2FormContext); } /** * 개별 필드 훅 - 조건부 상태와 값을 한 번에 관리 */ -export function useUnifiedField( +export function useV2Field( fieldId: string, conditional?: ConditionalConfig ): { @@ -605,7 +605,7 @@ export function useUnifiedField( disabled: boolean; error?: FieldError; } { - const { getValue, setValue, evaluateCondition, errors } = useUnifiedForm(); + const { getValue, setValue, evaluateCondition, errors } = useV2Form(); const value = getValue(fieldId); const { visible, disabled } = evaluateCondition(fieldId, conditional); @@ -631,7 +631,7 @@ export function useCascadingOptions( options: T[], cascading?: CascadingConfig ): T[] { - const { getCascadingFilter } = useUnifiedForm(); + const { getCascadingFilter } = useV2Form(); if (!cascading) return options; @@ -648,7 +648,7 @@ export function useCascadingOptions( * 폼 액션 훅 - 저장/검증/초기화 등 액션에만 접근 */ export function useFormActions() { - const { submit, reset, validate, clear, hasChanges, status, errors } = useUnifiedForm(); + const { submit, reset, validate, clear, hasChanges, status, errors } = useV2Form(); return { submit, @@ -676,7 +676,7 @@ export function useRepeaterField = Record = Record( +export const V2Group = forwardRef( (props, ref) => { const { id, @@ -450,7 +450,7 @@ export const UnifiedGroup = forwardRef( } ); -UnifiedGroup.displayName = "UnifiedGroup"; +V2Group.displayName = "V2Group"; -export default UnifiedGroup; +export default V2Group; diff --git a/frontend/components/unified/UnifiedHierarchy.tsx b/frontend/components/v2/V2Hierarchy.tsx similarity index 97% rename from frontend/components/unified/UnifiedHierarchy.tsx rename to frontend/components/v2/V2Hierarchy.tsx index 24b70b1f..17a3e141 100644 --- a/frontend/components/unified/UnifiedHierarchy.tsx +++ b/frontend/components/v2/V2Hierarchy.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedHierarchy + * V2Hierarchy * * 통합 계층 구조 컴포넌트 * - tree: 트리 뷰 @@ -17,7 +17,7 @@ import { Input } from "@/components/ui/input"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { cn } from "@/lib/utils"; -import { UnifiedHierarchyProps, HierarchyNode } from "@/types/unified-components"; +import { V2HierarchyProps, HierarchyNode } from "@/types/v2-components"; import { ChevronRight, ChevronDown, @@ -382,9 +382,9 @@ const CascadingView = forwardRef( +export const V2Hierarchy = forwardRef( (props, ref) => { const { id, @@ -495,7 +495,7 @@ export const UnifiedHierarchy = forwardRef = { +const FORMAT_PATTERNS: Record = { none: { pattern: /.*/, placeholder: "" }, email: { pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, placeholder: "example@email.com" }, tel: { pattern: /^\d{2,3}-\d{3,4}-\d{4}$/, placeholder: "010-1234-5678" }, @@ -64,7 +64,7 @@ const TextInput = forwardRef< { value?: string | number; onChange?: (value: string) => void; - format?: UnifiedInputFormat; + format?: V2InputFormat; mask?: string; placeholder?: string; readonly?: boolean; @@ -326,9 +326,9 @@ const TextareaInput = forwardRef< TextareaInput.displayName = "TextareaInput"; /** - * 메인 UnifiedInput 컴포넌트 + * 메인 V2Input 컴포넌트 */ -export const UnifiedInput = forwardRef((props, ref) => { +export const V2Input = forwardRef((props, ref) => { const { id, label, required, readonly, disabled, style, size, config: configProp, value, onChange } = props; // formData 추출 (채번규칙 날짜 컬럼 기준 생성 시 사용) @@ -336,7 +336,7 @@ export const UnifiedInput = forwardRef((props const columnName = (props as any).columnName; // config가 없으면 기본값 사용 - const config = (configProp || { type: "text" }) as UnifiedInputConfig & { + const config = (configProp || { type: "text" }) as V2InputConfig & { inputType?: string; rows?: number; autoGeneration?: AutoGenerationConfig; @@ -811,6 +811,6 @@ export const UnifiedInput = forwardRef((props ); }); -UnifiedInput.displayName = "UnifiedInput"; +V2Input.displayName = "V2Input"; -export default UnifiedInput; +export default V2Input; diff --git a/frontend/components/unified/UnifiedLayout.tsx b/frontend/components/v2/V2Layout.tsx similarity index 97% rename from frontend/components/unified/UnifiedLayout.tsx rename to frontend/components/v2/V2Layout.tsx index f0e2f5f6..f58b9e93 100644 --- a/frontend/components/unified/UnifiedLayout.tsx +++ b/frontend/components/v2/V2Layout.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedLayout + * V2Layout * * 통합 레이아웃 컴포넌트 * - grid: 그리드 레이아웃 @@ -13,7 +13,7 @@ import React, { forwardRef, useCallback, useRef, useState } from "react"; import { cn } from "@/lib/utils"; -import { UnifiedLayoutProps } from "@/types/unified-components"; +import { V2LayoutProps } from "@/types/v2-components"; import { GripVertical, GripHorizontal } from "lucide-react"; /** @@ -302,9 +302,9 @@ const ScreenEmbedLayout = forwardRef( +export const V2Layout = forwardRef( (props, ref) => { const { id, @@ -393,7 +393,7 @@ export const UnifiedLayout = forwardRef( } ); -UnifiedLayout.displayName = "UnifiedLayout"; +V2Layout.displayName = "V2Layout"; -export default UnifiedLayout; +export default V2Layout; diff --git a/frontend/components/unified/UnifiedList.tsx b/frontend/components/v2/V2List.tsx similarity index 93% rename from frontend/components/unified/UnifiedList.tsx rename to frontend/components/v2/V2List.tsx index 59178276..cd615239 100644 --- a/frontend/components/unified/UnifiedList.tsx +++ b/frontend/components/v2/V2List.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedList + * V2List * * 통합 리스트 컴포넌트 * 기존 TableListComponent를 래핑하여 동일한 기능 제공 @@ -9,13 +9,13 @@ import React, { forwardRef, useMemo } from "react"; import { TableListComponent } from "@/lib/registry/components/table-list/TableListComponent"; -import { UnifiedListProps } from "@/types/unified-components"; +import { V2ListProps } from "@/types/v2-components"; /** - * 메인 UnifiedList 컴포넌트 + * 메인 V2List 컴포넌트 * 기존 TableListComponent의 모든 기능을 그대로 사용 */ -export const UnifiedList = forwardRef((props, ref) => { +export const V2List = forwardRef((props, ref) => { const { id, style, size, config: configProp, onRowSelect } = props; // config가 없으면 기본값 사용 @@ -28,7 +28,7 @@ export const UnifiedList = forwardRef((props, // 테이블명 추출 (여러 가능한 경로에서 시도) const tableName = config.dataSource?.table || (config as any).tableName || (props as any).tableName; - // columns 형식 변환 (UnifiedListConfigPanel 형식 -> TableListComponent 형식) + // columns 형식 변환 (V2ListConfigPanel 형식 -> TableListComponent 형식) const tableColumns = useMemo( () => (config.columns || []).map((col: any, index: number) => ({ @@ -49,7 +49,7 @@ export const UnifiedList = forwardRef((props, // TableListComponent에 전달할 component 객체 생성 const componentObj = useMemo( () => ({ - id: id || "unified-list", + id: id || "v2-list", type: "table-list", config: { selectedTable: tableName, @@ -173,4 +173,4 @@ export const UnifiedList = forwardRef((props, ); }); -UnifiedList.displayName = "UnifiedList"; +V2List.displayName = "V2List"; diff --git a/frontend/components/unified/UnifiedMedia.tsx b/frontend/components/v2/V2Media.tsx similarity index 98% rename from frontend/components/unified/UnifiedMedia.tsx rename to frontend/components/v2/V2Media.tsx index e6e77018..71553651 100644 --- a/frontend/components/unified/UnifiedMedia.tsx +++ b/frontend/components/v2/V2Media.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedMedia + * V2Media * * 통합 미디어 컴포넌트 * - file: 파일 업로드 @@ -14,7 +14,7 @@ import React, { forwardRef, useCallback, useRef, useState } from "react"; import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; -import { UnifiedMediaProps } from "@/types/unified-components"; +import { V2MediaProps } from "@/types/v2-components"; import { Upload, X, File, Image as ImageIcon, Video, Music, Eye, Download, Trash2 } from "lucide-react"; /** @@ -454,9 +454,9 @@ const AudioPlayer = forwardRef( +export const V2Media = forwardRef( (props, ref) => { const { id, @@ -569,7 +569,7 @@ export const UnifiedMedia = forwardRef( } ); -UnifiedMedia.displayName = "UnifiedMedia"; +V2Media.displayName = "V2Media"; -export default UnifiedMedia; +export default V2Media; diff --git a/frontend/components/unified/UnifiedRepeater.tsx b/frontend/components/v2/V2Repeater.tsx similarity index 95% rename from frontend/components/unified/UnifiedRepeater.tsx rename to frontend/components/v2/V2Repeater.tsx index 187f6b05..ee80d0d7 100644 --- a/frontend/components/unified/UnifiedRepeater.tsx +++ b/frontend/components/v2/V2Repeater.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedRepeater 컴포넌트 + * V2Repeater 컴포넌트 * * 렌더링 모드: * - inline: 현재 테이블 컬럼 직접 입력 @@ -15,11 +15,11 @@ import { Button } from "@/components/ui/button"; import { Plus } from "lucide-react"; import { cn } from "@/lib/utils"; import { - UnifiedRepeaterConfig, - UnifiedRepeaterProps, - RepeaterColumnConfig as UnifiedColumnConfig, + V2RepeaterConfig, + V2RepeaterProps, + RepeaterColumnConfig as V2ColumnConfig, DEFAULT_REPEATER_CONFIG, -} from "@/types/unified-repeater"; +} from "@/types/v2-repeater"; import { apiClient } from "@/lib/api/client"; import { allocateNumberingCode } from "@/lib/api/numberingRule"; import { v2EventBus, V2_EVENTS, V2ErrorBoundary } from "@/lib/v2-core"; @@ -29,14 +29,14 @@ import { RepeaterTable } from "@/lib/registry/components/modal-repeater-table/Re import { ItemSelectionModal } from "@/lib/registry/components/modal-repeater-table/ItemSelectionModal"; import { RepeaterColumnConfig } from "@/lib/registry/components/modal-repeater-table/types"; -// 전역 UnifiedRepeater 등록 (buttonActions에서 사용) +// 전역 V2Repeater 등록 (buttonActions에서 사용) declare global { interface Window { - __unifiedRepeaterInstances?: Set; + __v2RepeaterInstances?: Set; } } -export const UnifiedRepeater: React.FC = ({ +export const V2Repeater: React.FC = ({ config: propConfig, parentId, data: initialData, @@ -45,7 +45,7 @@ export const UnifiedRepeater: React.FC = ({ className, }) => { // 설정 병합 - const config: UnifiedRepeaterConfig = useMemo( + const config: V2RepeaterConfig = useMemo( () => ({ ...DEFAULT_REPEATER_CONFIG, ...propConfig, @@ -93,15 +93,15 @@ export const UnifiedRepeater: React.FC = ({ : config.dataSource?.tableName; if (targetTableName) { - if (!window.__unifiedRepeaterInstances) { - window.__unifiedRepeaterInstances = new Set(); + if (!window.__v2RepeaterInstances) { + window.__v2RepeaterInstances = new Set(); } - window.__unifiedRepeaterInstances.add(targetTableName); + window.__v2RepeaterInstances.add(targetTableName); } return () => { - if (targetTableName && window.__unifiedRepeaterInstances) { - window.__unifiedRepeaterInstances.delete(targetTableName); + if (targetTableName && window.__v2RepeaterInstances) { + window.__v2RepeaterInstances.delete(targetTableName); } }; }, [config.useCustomTable, config.mainTableName, config.dataSource?.tableName]); @@ -123,7 +123,7 @@ export const UnifiedRepeater: React.FC = ({ return; } - // UnifiedRepeater 저장 시작 + // V2Repeater 저장 시작 const saveInfo = { tableName, useCustomTable: config.useCustomTable, @@ -132,7 +132,7 @@ export const UnifiedRepeater: React.FC = ({ masterRecordId, dataLength: data.length, }; - console.log("UnifiedRepeater 저장 시작", saveInfo); + console.log("V2Repeater 저장 시작", saveInfo); try { // 테이블 유효 컬럼 조회 @@ -198,7 +198,7 @@ export const UnifiedRepeater: React.FC = ({ } } catch (error) { - console.error("❌ UnifiedRepeater 저장 실패:", error); + console.error("❌ V2Repeater 저장 실패:", error); throw error; } }; @@ -214,7 +214,7 @@ export const UnifiedRepeater: React.FC = ({ await handleSaveEvent({ detail: payload } as CustomEvent); } }, - { componentId: `unified-repeater-${config.dataSource?.tableName}` } + { componentId: `v2-repeater-${config.dataSource?.tableName}` } ); // 레거시 이벤트도 계속 지원 (점진적 마이그레이션) @@ -344,12 +344,12 @@ export const UnifiedRepeater: React.FC = ({ loadSourceColumnLabels(); }, [resolvedSourceTable, isModalMode]); - // UnifiedColumnConfig → RepeaterColumnConfig 변환 + // V2ColumnConfig → RepeaterColumnConfig 변환 // 🆕 모든 컬럼을 columns 배열의 순서대로 처리 (isSourceDisplay 플래그로 구분) const repeaterColumns: RepeaterColumnConfig[] = useMemo(() => { return config.columns - .filter((col: UnifiedColumnConfig) => col.visible !== false) - .map((col: UnifiedColumnConfig): RepeaterColumnConfig => { + .filter((col: V2ColumnConfig) => col.visible !== false) + .map((col: V2ColumnConfig): RepeaterColumnConfig => { const colInfo = currentTableColumnInfo[col.key]; const inputType = col.inputType || colInfo?.inputType || "text"; @@ -724,7 +724,7 @@ export const UnifiedRepeater: React.FC = ({ } as CustomEvent; await handleBeforeFormSave(fakeEvent); }, - { componentId: `unified-repeater-${config.dataSource?.tableName}` } + { componentId: `v2-repeater-${config.dataSource?.tableName}` } ); // 레거시 이벤트도 계속 지원 (점진적 마이그레이션) @@ -829,7 +829,7 @@ export const UnifiedRepeater: React.FC = ({ } as CustomEvent; handleComponentDataTransfer(fakeEvent); }, - { componentId: `unified-repeater-${config.dataSource?.tableName}` } + { componentId: `v2-repeater-${config.dataSource?.tableName}` } ); const unsubscribeSplitPanel = v2EventBus.subscribe( @@ -844,7 +844,7 @@ export const UnifiedRepeater: React.FC = ({ } as CustomEvent; handleSplitPanelDataTransfer(fakeEvent); }, - { componentId: `unified-repeater-${config.dataSource?.tableName}` } + { componentId: `v2-repeater-${config.dataSource?.tableName}` } ); // 레거시 이벤트도 계속 지원 (점진적 마이그레이션) @@ -921,19 +921,19 @@ export const UnifiedRepeater: React.FC = ({ ); }; -UnifiedRepeater.displayName = "UnifiedRepeater"; +V2Repeater.displayName = "V2Repeater"; // V2ErrorBoundary로 래핑된 안전한 버전 export -export const SafeUnifiedRepeater: React.FC = (props) => { +export const SafeV2Repeater: React.FC = (props) => { return ( - + ); }; -export default UnifiedRepeater; +export default V2Repeater; diff --git a/frontend/components/unified/UnifiedSelect.tsx b/frontend/components/v2/V2Select.tsx similarity index 97% rename from frontend/components/unified/UnifiedSelect.tsx rename to frontend/components/v2/V2Select.tsx index a5c604be..31c5ba2a 100644 --- a/frontend/components/unified/UnifiedSelect.tsx +++ b/frontend/components/v2/V2Select.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedSelect + * V2Select * * 통합 선택 컴포넌트 * - dropdown: 드롭다운 선택 @@ -23,10 +23,10 @@ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, Command import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { cn } from "@/lib/utils"; -import { UnifiedSelectProps, SelectOption } from "@/types/unified-components"; +import { V2SelectProps, SelectOption } from "@/types/v2-components"; import { Check, ChevronsUpDown, X, ArrowLeftRight } from "lucide-react"; import { apiClient } from "@/lib/api/client"; -import UnifiedFormContext from "./UnifiedFormContext"; +import V2FormContext from "./V2FormContext"; /** * 드롭다운 선택 컴포넌트 @@ -439,9 +439,9 @@ const SwapSelect = forwardRef( +export const V2Select = forwardRef( (props, ref) => { const { id, @@ -488,7 +488,7 @@ export const UnifiedSelect = forwardRef( const parentField = config.parentField; // FormContext에서 부모 필드 값 가져오기 (Context가 없으면 null) - const formContext = useContext(UnifiedFormContext); + const formContext = useContext(V2FormContext); // 부모 필드의 값 계산 const parentValue = useMemo(() => { @@ -634,7 +634,7 @@ export const UnifiedSelect = forwardRef( } } else if (!isValidColumnName) { // columnName이 없거나 유효하지 않으면 빈 옵션 - console.warn("UnifiedSelect: 유효한 columnName이 없어 옵션을 로드하지 않습니다.", { tableName, columnName }); + console.warn("V2Select: 유효한 columnName이 없어 옵션을 로드하지 않습니다.", { tableName, columnName }); } } @@ -777,7 +777,7 @@ export const UnifiedSelect = forwardRef( } ); -UnifiedSelect.displayName = "UnifiedSelect"; +V2Select.displayName = "V2Select"; -export default UnifiedSelect; +export default V2Select; diff --git a/frontend/components/unified/config-panels/UnifiedBizConfigPanel.tsx b/frontend/components/v2/config-panels/V2BizConfigPanel.tsx similarity index 98% rename from frontend/components/unified/config-panels/UnifiedBizConfigPanel.tsx rename to frontend/components/v2/config-panels/V2BizConfigPanel.tsx index 20cdab2c..0a6c133b 100644 --- a/frontend/components/unified/config-panels/UnifiedBizConfigPanel.tsx +++ b/frontend/components/v2/config-panels/V2BizConfigPanel.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedBiz 설정 패널 + * V2Biz 설정 패널 * 통합 비즈니스 컴포넌트의 세부 설정을 관리합니다. */ @@ -13,7 +13,7 @@ import { Separator } from "@/components/ui/separator"; import { Checkbox } from "@/components/ui/checkbox"; import { tableTypeApi } from "@/lib/api/screen"; -interface UnifiedBizConfigPanelProps { +interface V2BizConfigPanelProps { config: Record; onChange: (config: Record) => void; } @@ -28,7 +28,7 @@ interface ColumnOption { displayName: string; } -export const UnifiedBizConfigPanel: React.FC = ({ +export const V2BizConfigPanel: React.FC = ({ config, onChange, }) => { @@ -453,6 +453,6 @@ export const UnifiedBizConfigPanel: React.FC = ({ ); }; -UnifiedBizConfigPanel.displayName = "UnifiedBizConfigPanel"; +V2BizConfigPanel.displayName = "V2BizConfigPanel"; -export default UnifiedBizConfigPanel; +export default V2BizConfigPanel; diff --git a/frontend/components/unified/config-panels/UnifiedDateConfigPanel.tsx b/frontend/components/v2/config-panels/V2DateConfigPanel.tsx similarity index 95% rename from frontend/components/unified/config-panels/UnifiedDateConfigPanel.tsx rename to frontend/components/v2/config-panels/V2DateConfigPanel.tsx index 1cf49360..1308a700 100644 --- a/frontend/components/unified/config-panels/UnifiedDateConfigPanel.tsx +++ b/frontend/components/v2/config-panels/V2DateConfigPanel.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedDate 설정 패널 + * V2Date 설정 패널 * 통합 날짜 컴포넌트의 세부 설정을 관리합니다. */ @@ -12,12 +12,12 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { Separator } from "@/components/ui/separator"; import { Checkbox } from "@/components/ui/checkbox"; -interface UnifiedDateConfigPanelProps { +interface V2DateConfigPanelProps { config: Record; onChange: (config: Record) => void; } -export const UnifiedDateConfigPanel: React.FC = ({ +export const V2DateConfigPanel: React.FC = ({ config, onChange, }) => { @@ -142,8 +142,8 @@ export const UnifiedDateConfigPanel: React.FC = ({ ); }; -UnifiedDateConfigPanel.displayName = "UnifiedDateConfigPanel"; +V2DateConfigPanel.displayName = "V2DateConfigPanel"; -export default UnifiedDateConfigPanel; +export default V2DateConfigPanel; diff --git a/frontend/components/unified/config-panels/UnifiedGroupConfigPanel.tsx b/frontend/components/v2/config-panels/V2GroupConfigPanel.tsx similarity index 96% rename from frontend/components/unified/config-panels/UnifiedGroupConfigPanel.tsx rename to frontend/components/v2/config-panels/V2GroupConfigPanel.tsx index 36178821..6c3750bf 100644 --- a/frontend/components/unified/config-panels/UnifiedGroupConfigPanel.tsx +++ b/frontend/components/v2/config-panels/V2GroupConfigPanel.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedGroup 설정 패널 + * V2Group 설정 패널 * 통합 그룹 컴포넌트의 세부 설정을 관리합니다. */ @@ -14,12 +14,12 @@ import { Checkbox } from "@/components/ui/checkbox"; import { Button } from "@/components/ui/button"; import { Plus, Trash2 } from "lucide-react"; -interface UnifiedGroupConfigPanelProps { +interface V2GroupConfigPanelProps { config: Record; onChange: (config: Record) => void; } -export const UnifiedGroupConfigPanel: React.FC = ({ +export const V2GroupConfigPanel: React.FC = ({ config, onChange, }) => { @@ -215,8 +215,8 @@ export const UnifiedGroupConfigPanel: React.FC = ( ); }; -UnifiedGroupConfigPanel.displayName = "UnifiedGroupConfigPanel"; +V2GroupConfigPanel.displayName = "V2GroupConfigPanel"; -export default UnifiedGroupConfigPanel; +export default V2GroupConfigPanel; diff --git a/frontend/components/unified/config-panels/UnifiedHierarchyConfigPanel.tsx b/frontend/components/v2/config-panels/V2HierarchyConfigPanel.tsx similarity index 98% rename from frontend/components/unified/config-panels/UnifiedHierarchyConfigPanel.tsx rename to frontend/components/v2/config-panels/V2HierarchyConfigPanel.tsx index 1f170879..db234c91 100644 --- a/frontend/components/unified/config-panels/UnifiedHierarchyConfigPanel.tsx +++ b/frontend/components/v2/config-panels/V2HierarchyConfigPanel.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedHierarchy 설정 패널 + * V2Hierarchy 설정 패널 * 통합 계층 컴포넌트의 세부 설정을 관리합니다. */ @@ -13,7 +13,7 @@ import { Separator } from "@/components/ui/separator"; import { Checkbox } from "@/components/ui/checkbox"; import { tableTypeApi } from "@/lib/api/screen"; -interface UnifiedHierarchyConfigPanelProps { +interface V2HierarchyConfigPanelProps { config: Record; onChange: (config: Record) => void; } @@ -28,7 +28,7 @@ interface ColumnOption { displayName: string; } -export const UnifiedHierarchyConfigPanel: React.FC = ({ +export const V2HierarchyConfigPanel: React.FC = ({ config, onChange, }) => { @@ -405,6 +405,6 @@ export const UnifiedHierarchyConfigPanel: React.FC; onChange: (config: Record) => void; menuObjid?: number; // 메뉴 OBJID (채번 규칙 필터링용) } -export const UnifiedInputConfigPanel: React.FC = ({ config, onChange, menuObjid }) => { +export const V2InputConfigPanel: React.FC = ({ config, onChange, menuObjid }) => { // 채번 규칙 목록 상태 const [numberingRules, setNumberingRules] = useState([]); const [loadingRules, setLoadingRules] = useState(false); @@ -484,6 +484,6 @@ export const UnifiedInputConfigPanel: React.FC = ( ); }; -UnifiedInputConfigPanel.displayName = "UnifiedInputConfigPanel"; +V2InputConfigPanel.displayName = "V2InputConfigPanel"; -export default UnifiedInputConfigPanel; +export default V2InputConfigPanel; diff --git a/frontend/components/unified/config-panels/UnifiedLayoutConfigPanel.tsx b/frontend/components/v2/config-panels/V2LayoutConfigPanel.tsx similarity index 97% rename from frontend/components/unified/config-panels/UnifiedLayoutConfigPanel.tsx rename to frontend/components/v2/config-panels/V2LayoutConfigPanel.tsx index 527881a7..d160e3fb 100644 --- a/frontend/components/unified/config-panels/UnifiedLayoutConfigPanel.tsx +++ b/frontend/components/v2/config-panels/V2LayoutConfigPanel.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedLayout 설정 패널 + * V2Layout 설정 패널 * 통합 레이아웃 컴포넌트의 세부 설정을 관리합니다. */ @@ -12,12 +12,12 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { Separator } from "@/components/ui/separator"; import { Checkbox } from "@/components/ui/checkbox"; -interface UnifiedLayoutConfigPanelProps { +interface V2LayoutConfigPanelProps { config: Record; onChange: (config: Record) => void; } -export const UnifiedLayoutConfigPanel: React.FC = ({ +export const V2LayoutConfigPanel: React.FC = ({ config, onChange, }) => { @@ -249,8 +249,8 @@ export const UnifiedLayoutConfigPanel: React.FC = ); }; -UnifiedLayoutConfigPanel.displayName = "UnifiedLayoutConfigPanel"; +V2LayoutConfigPanel.displayName = "V2LayoutConfigPanel"; -export default UnifiedLayoutConfigPanel; +export default V2LayoutConfigPanel; diff --git a/frontend/components/unified/config-panels/UnifiedListConfigPanel.tsx b/frontend/components/v2/config-panels/V2ListConfigPanel.tsx similarity index 86% rename from frontend/components/unified/config-panels/UnifiedListConfigPanel.tsx rename to frontend/components/v2/config-panels/V2ListConfigPanel.tsx index 00f75579..e60d0387 100644 --- a/frontend/components/unified/config-panels/UnifiedListConfigPanel.tsx +++ b/frontend/components/v2/config-panels/V2ListConfigPanel.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedList 설정 패널 + * V2List 설정 패널 * TableListConfigPanel을 래핑하여 동일한 설정 기능을 제공합니다. * 카드 표시는 별도의 card-display 컴포넌트를 사용합니다. */ @@ -10,7 +10,7 @@ import React, { useMemo } from "react"; import { TableListConfigPanel } from "@/lib/registry/components/table-list/TableListConfigPanel"; import { TableListConfig } from "@/lib/registry/components/table-list/types"; -interface UnifiedListConfigPanelProps { +interface V2ListConfigPanelProps { config: Record; onChange: (config: Record) => void; /** 현재 화면의 테이블명 */ @@ -18,17 +18,17 @@ interface UnifiedListConfigPanelProps { } /** - * UnifiedList 설정 패널 + * V2List 설정 패널 * TableListConfigPanel과 동일한 기능을 제공 */ -export const UnifiedListConfigPanel: React.FC = ({ +export const V2ListConfigPanel: React.FC = ({ config, onChange, currentTableName, }) => { - // UnifiedList config를 TableListConfig 형식으로 변환 + // V2List config를 TableListConfig 형식으로 변환 const tableListConfig: TableListConfig = useMemo(() => { - // 컬럼 형식 변환: UnifiedList columns -> TableList columns + // 컬럼 형식 변환: V2List columns -> TableList columns const columns = (config.columns || []).map((col: any, index: number) => ({ columnName: col.key || col.columnName || col.field || "", displayName: col.title || col.header || col.displayName || col.key || col.columnName || col.field || "", @@ -50,7 +50,7 @@ export const UnifiedListConfigPanel: React.FC = ({ columns, useCustomTable: config.useCustomTable, customTableName: config.customTableName, - isReadOnly: config.isReadOnly !== false, // UnifiedList는 기본적으로 읽기 전용 + isReadOnly: config.isReadOnly !== false, // V2List는 기본적으로 읽기 전용 displayMode: "table", // 테이블 모드 고정 (카드는 card-display 컴포넌트 사용) pagination: config.pagination !== false ? { enabled: true, @@ -84,7 +84,7 @@ export const UnifiedListConfigPanel: React.FC = ({ }; }, [config, currentTableName]); - // TableListConfig 변경을 UnifiedList config 형식으로 변환 + // TableListConfig 변경을 V2List config 형식으로 변환 const handleConfigChange = (partialConfig: Partial) => { const newConfig: Record = { ...config }; @@ -113,7 +113,7 @@ export const UnifiedListConfigPanel: React.FC = ({ newConfig.isReadOnly = partialConfig.isReadOnly; } - // 컬럼 형식 변환: TableList columns -> UnifiedList columns + // 컬럼 형식 변환: TableList columns -> V2List columns if (partialConfig.columns !== undefined) { newConfig.columns = partialConfig.columns.map((col: any) => ({ key: col.columnName, @@ -149,7 +149,7 @@ export const UnifiedListConfigPanel: React.FC = ({ newConfig.dataFilter = partialConfig.dataFilter; } - console.log("⚙️ UnifiedListConfigPanel handleConfigChange:", { partialConfig, newConfig }); + console.log("⚙️ V2ListConfigPanel handleConfigChange:", { partialConfig, newConfig }); onChange(newConfig); }; @@ -162,6 +162,6 @@ export const UnifiedListConfigPanel: React.FC = ({ ); }; -UnifiedListConfigPanel.displayName = "UnifiedListConfigPanel"; +V2ListConfigPanel.displayName = "V2ListConfigPanel"; -export default UnifiedListConfigPanel; +export default V2ListConfigPanel; diff --git a/frontend/components/unified/config-panels/UnifiedMediaConfigPanel.tsx b/frontend/components/v2/config-panels/V2MediaConfigPanel.tsx similarity index 96% rename from frontend/components/unified/config-panels/UnifiedMediaConfigPanel.tsx rename to frontend/components/v2/config-panels/V2MediaConfigPanel.tsx index 7279fec5..ee63803c 100644 --- a/frontend/components/unified/config-panels/UnifiedMediaConfigPanel.tsx +++ b/frontend/components/v2/config-panels/V2MediaConfigPanel.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedMedia 설정 패널 + * V2Media 설정 패널 * 통합 미디어 컴포넌트의 세부 설정을 관리합니다. */ @@ -12,12 +12,12 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { Separator } from "@/components/ui/separator"; import { Checkbox } from "@/components/ui/checkbox"; -interface UnifiedMediaConfigPanelProps { +interface V2MediaConfigPanelProps { config: Record; onChange: (config: Record) => void; } -export const UnifiedMediaConfigPanel: React.FC = ({ +export const V2MediaConfigPanel: React.FC = ({ config, onChange, }) => { @@ -205,8 +205,8 @@ export const UnifiedMediaConfigPanel: React.FC = ( ); }; -UnifiedMediaConfigPanel.displayName = "UnifiedMediaConfigPanel"; +V2MediaConfigPanel.displayName = "V2MediaConfigPanel"; -export default UnifiedMediaConfigPanel; +export default V2MediaConfigPanel; diff --git a/frontend/components/unified/config-panels/UnifiedRepeaterConfigPanel.tsx b/frontend/components/v2/config-panels/V2RepeaterConfigPanel.tsx similarity index 99% rename from frontend/components/unified/config-panels/UnifiedRepeaterConfigPanel.tsx rename to frontend/components/v2/config-panels/V2RepeaterConfigPanel.tsx index eca39bd3..459c9e79 100644 --- a/frontend/components/unified/config-panels/UnifiedRepeaterConfigPanel.tsx +++ b/frontend/components/v2/config-panels/V2RepeaterConfigPanel.tsx @@ -1,7 +1,7 @@ "use client"; /** - * UnifiedRepeater 설정 패널 + * V2Repeater 설정 패널 * * 렌더링 모드별 설정: * - inline: 현재 화면 테이블 컬럼 직접 입력 @@ -51,12 +51,12 @@ import { getAvailableNumberingRules, getAvailableNumberingRulesForScreen } from import { NumberingRuleConfig } from "@/types/numbering-rule"; import { cn } from "@/lib/utils"; import { - UnifiedRepeaterConfig, + V2RepeaterConfig, RepeaterColumnConfig, DEFAULT_REPEATER_CONFIG, RENDER_MODE_OPTIONS, MODAL_SIZE_OPTIONS, -} from "@/types/unified-repeater"; +} from "@/types/v2-repeater"; // 테이블 엔티티 관계 정보 interface TableRelation { @@ -66,9 +66,9 @@ interface TableRelation { referenceColumn: string; // 마스터 테이블의 PK 컬럼 } -interface UnifiedRepeaterConfigPanelProps { - config: UnifiedRepeaterConfig; - onChange: (config: UnifiedRepeaterConfig) => void; +interface V2RepeaterConfigPanelProps { + config: V2RepeaterConfig; + onChange: (config: V2RepeaterConfig) => void; currentTableName?: string; screenTableName?: string; tableColumns?: any[]; @@ -103,7 +103,7 @@ interface CalculationRule { label?: string; } -export const UnifiedRepeaterConfigPanel: React.FC = ({ +export const V2RepeaterConfigPanel: React.FC = ({ config: propConfig, onChange, currentTableName: propCurrentTableName, @@ -113,7 +113,7 @@ export const UnifiedRepeaterConfigPanel: React.FC ({ + const config: V2RepeaterConfig = useMemo(() => ({ ...DEFAULT_REPEATER_CONFIG, ...propConfig, renderMode: propConfig?.renderMode || DEFAULT_REPEATER_CONFIG.renderMode, @@ -314,7 +314,7 @@ export const UnifiedRepeaterConfigPanel: React.FC) => { + (updates: Partial) => { onChange({ ...config, ...updates }); }, [config, onChange], @@ -1442,6 +1442,6 @@ export const UnifiedRepeaterConfigPanel: React.FC; onChange: (config: Record) => void; /** 컬럼의 inputType (entity 타입인 경우에만 엔티티 소스 표시) */ inputType?: string; } -export const UnifiedSelectConfigPanel: React.FC = ({ +export const V2SelectConfigPanel: React.FC = ({ config, onChange, inputType, @@ -358,6 +358,6 @@ export const UnifiedSelectConfigPanel: React.FC = ); }; -UnifiedSelectConfigPanel.displayName = "UnifiedSelectConfigPanel"; +V2SelectConfigPanel.displayName = "V2SelectConfigPanel"; -export default UnifiedSelectConfigPanel; +export default V2SelectConfigPanel; diff --git a/frontend/components/v2/config-panels/index.ts b/frontend/components/v2/config-panels/index.ts new file mode 100644 index 00000000..76c2090b --- /dev/null +++ b/frontend/components/v2/config-panels/index.ts @@ -0,0 +1,15 @@ +/** + * V2 컴포넌트 설정 패널 인덱스 + */ + +export { V2InputConfigPanel } from "./V2InputConfigPanel"; +export { V2SelectConfigPanel } from "./V2SelectConfigPanel"; +export { V2DateConfigPanel } from "./V2DateConfigPanel"; +export { V2ListConfigPanel } from "./V2ListConfigPanel"; +export { V2LayoutConfigPanel } from "./V2LayoutConfigPanel"; +export { V2GroupConfigPanel } from "./V2GroupConfigPanel"; +export { V2MediaConfigPanel } from "./V2MediaConfigPanel"; +export { V2BizConfigPanel } from "./V2BizConfigPanel"; +export { V2HierarchyConfigPanel } from "./V2HierarchyConfigPanel"; + + diff --git a/frontend/components/v2/index.ts b/frontend/components/v2/index.ts new file mode 100644 index 00000000..563089e9 --- /dev/null +++ b/frontend/components/v2/index.ts @@ -0,0 +1,125 @@ +/** + * V2 Components 모듈 인덱스 + * + * V2 통합 컴포넌트 시스템 + */ + +// Phase 1 컴포넌트 +export { V2Input } from "./V2Input"; +export { V2Select } from "./V2Select"; +export { V2Date } from "./V2Date"; + +// Phase 2 컴포넌트 +export { V2List } from "./V2List"; +export { V2Layout } from "./V2Layout"; +export { V2Group } from "./V2Group"; + +// Phase 3 컴포넌트 +export { V2Media } from "./V2Media"; +export { V2Biz } from "./V2Biz"; +export { V2Hierarchy } from "./V2Hierarchy"; + +// V2Text는 V2Input의 textarea 모드로 대체 가능 + +// 렌더러 +export { V2ComponentRenderer } from "./V2ComponentRenderer"; + +// 설정 패널 +export { DynamicConfigPanel, COMMON_SCHEMAS } from "./DynamicConfigPanel"; + +// 데모 컴포넌트 +export { V2ComponentsDemo } from "./V2ComponentsDemo"; + +// 폼 컨텍스트 및 액션 +export { + V2FormProvider, + useV2Form, + useV2FormOptional, + useV2Field, + useCascadingOptions, + useFormActions, + useRepeaterField, +} from "./V2FormContext"; + +// 설정 UI 패널 +export { ConditionalConfigPanel } from "./ConditionalConfigPanel"; + +// 폼 관련 타입 re-export +export type { + FormStatus, + FieldError, + FieldState, + SubmitConfig, + SubmitResult, + ValidationResult, + FieldMapping, + ScreenDataTransferConfig, + FormCompatibilityBridge, +} from "@/types/v2-form"; + +// 타입 re-export +export type { + // 공통 타입 + V2ComponentType, + V2BaseProps, + ConditionalConfig, + AutoFillConfig, + CascadingConfig, + MutualExclusionConfig, + + // V2Input 타입 + V2InputType, + V2InputFormat, + V2InputConfig, + V2InputProps, + + // V2Select 타입 + V2SelectMode, + V2SelectSource, + SelectOption, + V2SelectConfig, + V2SelectProps, + + // V2Date 타입 + V2DateType, + V2DateConfig, + V2DateProps, + + // V2List 타입 + V2ListViewMode, + ListColumn, + V2ListConfig, + V2ListProps, + + // V2Layout 타입 + V2LayoutType, + V2LayoutConfig, + V2LayoutProps, + + // V2Group 타입 + V2GroupType, + TabItem, + V2GroupConfig, + V2GroupProps, + + // V2Media 타입 + V2MediaType, + V2MediaConfig, + V2MediaProps, + + // V2Biz 타입 + V2BizType, + V2BizConfig, + V2BizProps, + + // V2Hierarchy 타입 + V2HierarchyType, + V2HierarchyViewMode, + HierarchyNode, + V2HierarchyConfig, + V2HierarchyProps, + + // 통합 Props + V2ComponentProps, +} from "@/types/v2-components"; + diff --git a/frontend/components/unified/registerUnifiedComponents.ts b/frontend/components/v2/registerV2Components.ts similarity index 53% rename from frontend/components/unified/registerUnifiedComponents.ts rename to frontend/components/v2/registerV2Components.ts index 5675ccba..ffe8cc17 100644 --- a/frontend/components/unified/registerUnifiedComponents.ts +++ b/frontend/components/v2/registerV2Components.ts @@ -1,9 +1,9 @@ "use client"; /** - * Unified 컴포넌트 레지스트리 등록 + * V2 컴포넌트 레지스트리 등록 * - * 9개의 Unified 컴포넌트를 ComponentRegistry에 등록합니다. + * 9개의 V2 컴포넌트를 ComponentRegistry에 등록합니다. */ import { ComponentRegistry } from "@/lib/registry/ComponentRegistry"; @@ -11,39 +11,39 @@ import { ComponentDefinition, ComponentCategory } from "@/types/component"; import { WebType } from "@/types/screen"; // 실제 컴포넌트 import -import { UnifiedInput } from "./UnifiedInput"; -import { UnifiedSelect } from "./UnifiedSelect"; -import { UnifiedDate } from "./UnifiedDate"; -import { UnifiedList } from "./UnifiedList"; -import { UnifiedLayout } from "./UnifiedLayout"; -import { UnifiedGroup } from "./UnifiedGroup"; -import { UnifiedMedia } from "./UnifiedMedia"; -import { UnifiedBiz } from "./UnifiedBiz"; -import { UnifiedHierarchy } from "./UnifiedHierarchy"; +import { V2Input } from "./V2Input"; +import { V2Select } from "./V2Select"; +import { V2Date } from "./V2Date"; +import { V2List } from "./V2List"; +import { V2Layout } from "./V2Layout"; +import { V2Group } from "./V2Group"; +import { V2Media } from "./V2Media"; +import { V2Biz } from "./V2Biz"; +import { V2Hierarchy } from "./V2Hierarchy"; // 설정 패널 import -import { UnifiedInputConfigPanel } from "./config-panels/UnifiedInputConfigPanel"; -import { UnifiedSelectConfigPanel } from "./config-panels/UnifiedSelectConfigPanel"; -import { UnifiedDateConfigPanel } from "./config-panels/UnifiedDateConfigPanel"; -import { UnifiedListConfigPanel } from "./config-panels/UnifiedListConfigPanel"; -import { UnifiedLayoutConfigPanel } from "./config-panels/UnifiedLayoutConfigPanel"; -import { UnifiedGroupConfigPanel } from "./config-panels/UnifiedGroupConfigPanel"; -import { UnifiedMediaConfigPanel } from "./config-panels/UnifiedMediaConfigPanel"; -import { UnifiedBizConfigPanel } from "./config-panels/UnifiedBizConfigPanel"; -import { UnifiedHierarchyConfigPanel } from "./config-panels/UnifiedHierarchyConfigPanel"; +import { V2InputConfigPanel } from "./config-panels/V2InputConfigPanel"; +import { V2SelectConfigPanel } from "./config-panels/V2SelectConfigPanel"; +import { V2DateConfigPanel } from "./config-panels/V2DateConfigPanel"; +import { V2ListConfigPanel } from "./config-panels/V2ListConfigPanel"; +import { V2LayoutConfigPanel } from "./config-panels/V2LayoutConfigPanel"; +import { V2GroupConfigPanel } from "./config-panels/V2GroupConfigPanel"; +import { V2MediaConfigPanel } from "./config-panels/V2MediaConfigPanel"; +import { V2BizConfigPanel } from "./config-panels/V2BizConfigPanel"; +import { V2HierarchyConfigPanel } from "./config-panels/V2HierarchyConfigPanel"; -// Unified 컴포넌트 정의 -const unifiedComponentDefinitions: ComponentDefinition[] = [ +// V2 컴포넌트 정의 +const v2ComponentDefinitions: ComponentDefinition[] = [ { - id: "unified-input", + id: "v2-input", name: "통합 입력", description: "텍스트, 숫자, 비밀번호, 슬라이더, 컬러 등 다양한 입력 타입을 지원하는 통합 컴포넌트", - category: ComponentCategory.UNIFIED, + category: ComponentCategory.V2, webType: "text" as WebType, - component: UnifiedInput as any, - tags: ["input", "text", "number", "password", "slider", "color", "unified"], + component: V2Input as any, + tags: ["input", "text", "number", "password", "slider", "color", "v2"], defaultSize: { width: 200, height: 40 }, - configPanel: UnifiedInputConfigPanel as any, + configPanel: V2InputConfigPanel as any, defaultConfig: { inputType: "text", format: "none", @@ -51,15 +51,15 @@ const unifiedComponentDefinitions: ComponentDefinition[] = [ }, }, { - id: "unified-select", + id: "v2-select", name: "통합 선택", description: "드롭다운, 라디오, 체크박스, 태그, 토글 등 다양한 선택 방식을 지원하는 통합 컴포넌트", - category: ComponentCategory.UNIFIED, + category: ComponentCategory.V2, webType: "select" as WebType, - component: UnifiedSelect as any, - tags: ["select", "dropdown", "radio", "checkbox", "toggle", "unified"], + component: V2Select as any, + tags: ["select", "dropdown", "radio", "checkbox", "toggle", "v2"], defaultSize: { width: 200, height: 40 }, - configPanel: UnifiedSelectConfigPanel as any, + configPanel: V2SelectConfigPanel as any, defaultConfig: { mode: "dropdown", source: "static", @@ -67,30 +67,30 @@ const unifiedComponentDefinitions: ComponentDefinition[] = [ }, }, { - id: "unified-date", + id: "v2-date", name: "통합 날짜", description: "날짜, 시간, 날짜시간, 날짜 범위 등을 지원하는 통합 컴포넌트", - category: ComponentCategory.UNIFIED, + category: ComponentCategory.V2, webType: "date" as WebType, - component: UnifiedDate as any, - tags: ["date", "time", "datetime", "datepicker", "unified"], + component: V2Date as any, + tags: ["date", "time", "datetime", "datepicker", "v2"], defaultSize: { width: 200, height: 40 }, - configPanel: UnifiedDateConfigPanel as any, + configPanel: V2DateConfigPanel as any, defaultConfig: { dateType: "date", format: "YYYY-MM-DD", }, }, { - id: "unified-list", + id: "v2-list", name: "통합 목록", description: "테이블, 카드, 칸반, 리스트 등 다양한 데이터 표시 방식을 지원하는 통합 컴포넌트", - category: ComponentCategory.UNIFIED, + category: ComponentCategory.V2, webType: "list" as WebType, - component: UnifiedList as any, - tags: ["list", "table", "card", "kanban", "data", "unified"], + component: V2List as any, + tags: ["list", "table", "card", "kanban", "data", "v2"], defaultSize: { width: 600, height: 400 }, - configPanel: UnifiedListConfigPanel as any, + configPanel: V2ListConfigPanel as any, defaultConfig: { viewMode: "table", source: "static", @@ -100,15 +100,15 @@ const unifiedComponentDefinitions: ComponentDefinition[] = [ }, }, { - id: "unified-layout", + id: "v2-layout", name: "통합 레이아웃", description: "그리드, 분할 패널, 플렉스 등 다양한 레이아웃 구조를 지원하는 통합 컴포넌트", - category: ComponentCategory.UNIFIED, + category: ComponentCategory.V2, webType: "container" as WebType, - component: UnifiedLayout as any, - tags: ["layout", "grid", "split", "flex", "container", "unified"], + component: V2Layout as any, + tags: ["layout", "grid", "split", "flex", "container", "v2"], defaultSize: { width: 400, height: 300 }, - configPanel: UnifiedLayoutConfigPanel as any, + configPanel: V2LayoutConfigPanel as any, defaultConfig: { layoutType: "grid", columns: 2, @@ -117,15 +117,15 @@ const unifiedComponentDefinitions: ComponentDefinition[] = [ }, }, { - id: "unified-group", + id: "v2-group", name: "통합 그룹", description: "탭, 아코디언, 섹션, 모달 등 그룹 요소를 지원하는 통합 컴포넌트", - category: ComponentCategory.UNIFIED, + category: ComponentCategory.V2, webType: "group" as WebType, - component: UnifiedGroup as any, - tags: ["group", "tabs", "accordion", "section", "modal", "unified"], + component: V2Group as any, + tags: ["group", "tabs", "accordion", "section", "modal", "v2"], defaultSize: { width: 400, height: 300 }, - configPanel: UnifiedGroupConfigPanel as any, + configPanel: V2GroupConfigPanel as any, defaultConfig: { groupType: "section", title: "", @@ -134,15 +134,15 @@ const unifiedComponentDefinitions: ComponentDefinition[] = [ }, }, { - id: "unified-media", + id: "v2-media", name: "통합 미디어", description: "이미지, 비디오, 오디오, 파일 업로드 등을 지원하는 통합 컴포넌트", - category: ComponentCategory.UNIFIED, + category: ComponentCategory.V2, webType: "file" as WebType, - component: UnifiedMedia as any, - tags: ["media", "image", "video", "audio", "file", "upload", "unified"], + component: V2Media as any, + tags: ["media", "image", "video", "audio", "file", "upload", "v2"], defaultSize: { width: 300, height: 200 }, - configPanel: UnifiedMediaConfigPanel as any, + configPanel: V2MediaConfigPanel as any, defaultConfig: { mediaType: "image", multiple: false, @@ -150,29 +150,29 @@ const unifiedComponentDefinitions: ComponentDefinition[] = [ }, }, { - id: "unified-biz", + id: "v2-biz", name: "통합 비즈니스", description: "플로우, 랙, 채번규칙 등 비즈니스 기능을 지원하는 통합 컴포넌트", - category: ComponentCategory.UNIFIED, + category: ComponentCategory.V2, webType: "custom" as WebType, - component: UnifiedBiz as any, - tags: ["business", "flow", "rack", "numbering", "category", "unified"], + component: V2Biz as any, + tags: ["business", "flow", "rack", "numbering", "category", "v2"], defaultSize: { width: 500, height: 400 }, - configPanel: UnifiedBizConfigPanel as any, + configPanel: V2BizConfigPanel as any, defaultConfig: { bizType: "flow", }, }, { - id: "unified-hierarchy", + id: "v2-hierarchy", name: "통합 계층", description: "트리, 조직도, BOM, 연쇄 선택박스 등 계층 구조를 지원하는 통합 컴포넌트", - category: ComponentCategory.UNIFIED, + category: ComponentCategory.V2, webType: "tree" as WebType, - component: UnifiedHierarchy as any, - tags: ["hierarchy", "tree", "org-chart", "bom", "cascading", "unified"], + component: V2Hierarchy as any, + tags: ["hierarchy", "tree", "org-chart", "bom", "cascading", "v2"], defaultSize: { width: 400, height: 400 }, - configPanel: UnifiedHierarchyConfigPanel as any, + configPanel: V2HierarchyConfigPanel as any, defaultConfig: { hierarchyType: "tree", viewMode: "tree", @@ -182,10 +182,10 @@ const unifiedComponentDefinitions: ComponentDefinition[] = [ ]; /** - * Unified 컴포넌트들을 ComponentRegistry에 등록 + * V2 컴포넌트들을 ComponentRegistry에 등록 */ -export function registerUnifiedComponents(): void { - for (const definition of unifiedComponentDefinitions) { +export function registerV2Components(): void { + for (const definition of v2ComponentDefinitions) { try { // 이미 등록되어 있으면 스킵 if (ComponentRegistry.getComponent(definition.id)) { @@ -193,9 +193,9 @@ export function registerUnifiedComponents(): void { } ComponentRegistry.registerComponent(definition); } catch (error) { - console.error(`❌ Unified 컴포넌트 등록 실패: ${definition.id}`, error); + console.error(`❌ V2 컴포넌트 등록 실패: ${definition.id}`, error); } } } -export default registerUnifiedComponents; +export default registerV2Components; diff --git a/frontend/hooks/useFormCompatibility.ts b/frontend/hooks/useFormCompatibility.ts index a6bc1476..5b69e46e 100644 --- a/frontend/hooks/useFormCompatibility.ts +++ b/frontend/hooks/useFormCompatibility.ts @@ -3,16 +3,16 @@ /** * 폼 호환성 브릿지 훅 * - * 레거시 컴포넌트와 새로운 Unified 폼 시스템 간의 호환성을 제공합니다. + * 레거시 컴포넌트와 새로운 V2 폼 시스템 간의 호환성을 제공합니다. * * 사용 시나리오: - * 1. UnifiedFormProvider 내부에서 사용 → Unified 시스템 사용 - * 2. UnifiedFormProvider 없이 사용 → 레거시 방식 사용 + * 1. V2FormProvider 내부에서 사용 → V2 시스템 사용 + * 2. V2FormProvider 없이 사용 → 레거시 방식 사용 * 3. 두 시스템이 공존할 때 → 양쪽에 모두 전파 */ import { useCallback, useContext, useMemo } from "react"; -import UnifiedFormContext, { useUnifiedFormOptional } from "@/components/unified/UnifiedFormContext"; +import V2FormContext, { useV2FormOptional } from "@/components/v2/V2FormContext"; import { useScreenContext } from "@/contexts/ScreenContext"; import type { FormCompatibilityBridge, @@ -22,7 +22,7 @@ import type { ValidationResult, FieldError, FormEventDetail, -} from "@/types/unified-form"; +} from "@/types/v2-form"; /** * 폼 호환성 브릿지 훅 @@ -45,16 +45,16 @@ import type { export function useFormCompatibility(options: FormCompatibilityOptions = {}): FormCompatibilityBridge { const { legacyOnFormDataChange, screenContext: externalScreenContext, emitLegacyEvents = true } = options; - // Unified 시스템 (있으면 사용) - const unifiedContext = useUnifiedFormOptional(); + // V2 시스템 (있으면 사용) + const v2Context = useV2FormOptional(); // ScreenContext (레거시 시스템) const internalScreenContext = useScreenContext(); const screenContext = externalScreenContext || internalScreenContext; // 모드 판별 - const isUnifiedMode = !!unifiedContext; - const isLegacyMode = !unifiedContext; + const isV2Mode = !!v2Context; + const isLegacyMode = !v2Context; // ===== 값 관리 ===== @@ -63,9 +63,9 @@ export function useFormCompatibility(options: FormCompatibilityOptions = {}): Fo */ const getValue = useCallback( (field: string): unknown => { - // Unified 시스템 우선 - if (unifiedContext) { - return unifiedContext.getValue(field); + // V2 시스템 우선 + if (v2Context) { + return v2Context.getValue(field); } // ScreenContext 폴백 @@ -75,7 +75,7 @@ export function useFormCompatibility(options: FormCompatibilityOptions = {}): Fo return undefined; }, - [unifiedContext, screenContext?.formData], + [v2Context, screenContext?.formData], ); /** @@ -83,9 +83,9 @@ export function useFormCompatibility(options: FormCompatibilityOptions = {}): Fo */ const setValue = useCallback( (field: string, value: unknown) => { - // 1. Unified 시스템 - if (unifiedContext) { - unifiedContext.setValue(field, value); + // 1. V2 시스템 + if (v2Context) { + v2Context.setValue(field, value); } // 2. ScreenContext (레거시) @@ -98,20 +98,20 @@ export function useFormCompatibility(options: FormCompatibilityOptions = {}): Fo legacyOnFormDataChange(field, value); } }, - [unifiedContext, screenContext, legacyOnFormDataChange], + [v2Context, screenContext, legacyOnFormDataChange], ); // ===== 폼 데이터 ===== const formData = useMemo(() => { - if (unifiedContext) { - return unifiedContext.formData as Record; + if (v2Context) { + return v2Context.formData as Record; } if (screenContext?.formData) { return screenContext.formData as Record; } return {}; - }, [unifiedContext, screenContext?.formData]); + }, [v2Context, screenContext?.formData]); // ===== 폼 액션 ===== @@ -120,9 +120,9 @@ export function useFormCompatibility(options: FormCompatibilityOptions = {}): Fo */ const submit = useCallback( async (config?: Partial): Promise => { - // Unified 시스템이 있으면 그쪽 사용 (레거시 이벤트도 내부적으로 발생시킴) - if (unifiedContext) { - return unifiedContext.submit(config); + // V2 시스템이 있으면 그쪽 사용 (레거시 이벤트도 내부적으로 발생시킴) + if (v2Context) { + return v2Context.submit(config); } // 레거시 모드: beforeFormSave 이벤트 발생 @@ -140,36 +140,36 @@ export function useFormCompatibility(options: FormCompatibilityOptions = {}): Fo return { success: true, data: formData }; }, - [unifiedContext, formData, emitLegacyEvents], + [v2Context, formData, emitLegacyEvents], ); /** * 초기화 */ const reset = useCallback(() => { - if (unifiedContext) { - unifiedContext.reset(); + if (v2Context) { + v2Context.reset(); } // 레거시 모드에서는 특별한 처리 없음 (외부에서 처리) - }, [unifiedContext]); + }, [v2Context]); /** * 검증 */ const validate = useCallback(async (): Promise => { - if (unifiedContext) { - return unifiedContext.validate(); + if (v2Context) { + return v2Context.validate(); } // 레거시 모드에서는 항상 valid return { valid: true, errors: [] }; - }, [unifiedContext]); + }, [v2Context]); // ===== 상태 ===== - const isSubmitting = unifiedContext?.status.isSubmitting ?? false; - const isDirty = unifiedContext?.status.isDirty ?? false; - const errors = unifiedContext?.errors ?? []; + const isSubmitting = v2Context?.status.isSubmitting ?? false; + const isDirty = v2Context?.status.isDirty ?? false; + const errors = v2Context?.errors ?? []; return { // 값 관리 @@ -188,7 +188,7 @@ export function useFormCompatibility(options: FormCompatibilityOptions = {}): Fo errors, // 모드 - isUnifiedMode, + isV2Mode, isLegacyMode, }; } @@ -212,25 +212,25 @@ function useScreenContext() { * DynamicComponentRenderer에서 사용하는 통합 onChange 핸들러 생성 * * @example - * const handleChange = createUnifiedChangeHandler({ + * const handleChange = createV2ChangeHandler({ * fieldName: "customer_name", - * unifiedContext, + * v2Context, * screenContext, * legacyOnFormDataChange: props.onFormDataChange, * }); */ -export function createUnifiedChangeHandler(options: { +export function createV2ChangeHandler(options: { fieldName: string; - unifiedContext?: ReturnType; + v2Context?: ReturnType; screenContext?: { updateFormData?: (field: string, value: unknown) => void }; legacyOnFormDataChange?: (field: string, value: unknown) => void; }): (value: unknown) => void { - const { fieldName, unifiedContext, screenContext, legacyOnFormDataChange } = options; + const { fieldName, v2Context, screenContext, legacyOnFormDataChange } = options; return (value: unknown) => { - // 1. Unified 시스템 - if (unifiedContext) { - unifiedContext.setValue(fieldName, value); + // 1. V2 시스템 + if (v2Context) { + v2Context.setValue(fieldName, value); } // 2. ScreenContext diff --git a/frontend/hooks/useScreenDataTransfer.ts b/frontend/hooks/useScreenDataTransfer.ts index 29b4f4f5..73332aa0 100644 --- a/frontend/hooks/useScreenDataTransfer.ts +++ b/frontend/hooks/useScreenDataTransfer.ts @@ -16,7 +16,7 @@ import type { ScreenDataTransferConfig, FieldMapping, DataTransferTrigger, -} from "@/types/unified-form"; +} from "@/types/v2-form"; // ===== 이벤트 이름 상수 ===== export const SCREEN_DATA_TRANSFER_EVENT = "screenDataTransfer"; diff --git a/frontend/lib/api/user.ts b/frontend/lib/api/user.ts index 6a829042..81365b74 100644 --- a/frontend/lib/api/user.ts +++ b/frontend/lib/api/user.ts @@ -10,7 +10,7 @@ interface ApiResponse { message?: string; errorCode?: string; total?: number; - searchType?: "unified" | "single" | "advanced" | "none"; // 검색 타입 정보 + searchType?: "v2" | "single" | "advanced" | "none"; // 검색 타입 정보 pagination?: { page: number; limit: number; diff --git a/frontend/lib/registry/DynamicComponentRenderer.tsx b/frontend/lib/registry/DynamicComponentRenderer.tsx index 8ef6a943..35e129bb 100644 --- a/frontend/lib/registry/DynamicComponentRenderer.tsx +++ b/frontend/lib/registry/DynamicComponentRenderer.tsx @@ -6,22 +6,22 @@ import { DynamicLayoutRenderer } from "./DynamicLayoutRenderer"; import { ComponentRegistry } from "./ComponentRegistry"; import { filterDOMProps } from "@/lib/utils/domPropsFilter"; -// Unified 컴포넌트 import +// V2 컴포넌트 import import { - UnifiedInput, - UnifiedSelect, - UnifiedDate, - UnifiedList, - UnifiedLayout, - UnifiedGroup, - UnifiedMedia, - UnifiedBiz, - UnifiedHierarchy, -} from "@/components/unified"; -import { UnifiedRepeater } from "@/components/unified/UnifiedRepeater"; + V2Input, + V2Select, + V2Date, + V2List, + V2Layout, + V2Group, + V2Media, + V2Biz, + V2Hierarchy, +} from "@/components/v2"; +import { V2Repeater } from "@/components/v2/V2Repeater"; // 통합 폼 시스템 import -import { useUnifiedFormOptional } from "@/components/unified/UnifiedFormContext"; +import { useV2FormOptional } from "@/components/v2/V2FormContext"; // 컴포넌트 렌더러 인터페이스 export interface ComponentRenderer { @@ -192,8 +192,8 @@ export const DynamicComponentRenderer: React.FC = // 🆕 레거시 타입을 v2 컴포넌트로 매핑 (v2 컴포넌트가 없으면 원본 유지) const mapToV2ComponentType = (type: string | undefined): string | undefined => { if (!type) return type; - // 이미 v2- 또는 unified- 접두사가 있으면 그대로 반환 - if (type.startsWith("v2-") || type.startsWith("unified-")) return type; + // 이미 v2- 또는 v2- 접두사가 있으면 그대로 반환 + if (type.startsWith("v2-") || type.startsWith("v2-")) return type; // 레거시 타입을 v2로 매핑 시도 const v2Type = `v2-${type}`; // v2 버전이 등록되어 있는지 확인 @@ -208,26 +208,26 @@ export const DynamicComponentRenderer: React.FC = // 컴포넌트 타입 변환 완료 - // 🆕 Unified 폼 시스템 연동 (최상위에서 한 번만 호출) + // 🆕 V2 폼 시스템 연동 (최상위에서 한 번만 호출) // eslint-disable-next-line react-hooks/rules-of-hooks - const unifiedFormContextForUnified = useUnifiedFormOptional(); + const v2FormContextLocal = useV2FormOptional(); - // 🆕 Unified 컴포넌트 처리 - if (componentType?.startsWith("unified-")) { - const unifiedType = componentType as string; + // 🆕 V2 컴포넌트 처리 + if (componentType?.startsWith("v2-")) { + const v2Type = componentType as string; const config = (component as any).componentConfig || {}; const fieldName = (component as any).columnName || component.id; - // Unified 시스템이 있으면 거기서 값 가져오기, 없으면 props.formData 사용 - const currentValue = unifiedFormContextForUnified - ? unifiedFormContextForUnified.getValue(fieldName) + // V2 시스템이 있으면 거기서 값 가져오기, 없으면 props.formData 사용 + const currentValue = v2FormContextLocal + ? v2FormContextLocal.getValue(fieldName) : props.formData?.[fieldName]; // 🆕 통합 onChange 핸들러 - 양쪽 시스템에 전파 const handleChange = (value: any) => { - // 1. Unified 시스템에 전파 - if (unifiedFormContextForUnified) { - unifiedFormContextForUnified.setValue(fieldName, value); + // 1. V2 시스템에 전파 + if (v2FormContextLocal) { + v2FormContextLocal.setValue(fieldName, value); } // 2. 레거시 콜백도 호출 (호환성) if (props.onFormDataChange) { @@ -252,15 +252,16 @@ export const DynamicComponentRenderer: React.FC = position: component.position, }; - switch (unifiedType) { - case "unified-input": + switch (v2Type) { + // V2 입력 컴포넌트 + case "v2-input": return ( - = buttonText: config.buttonText, buttonVariant: config.buttonVariant, autoGeneration: config.autoGeneration, - tableName: (component as any).tableName || props.tableName, // 🆕 채번용 테이블명 + tableName: (component as any).tableName || props.tableName, }} autoGeneration={config.autoGeneration} formData={props.formData} @@ -278,12 +279,12 @@ export const DynamicComponentRenderer: React.FC = /> ); - case "unified-select": - // 🆕 unified-select는 항상 테이블 컬럼에서 distinct 값을 자동 로드 - // 정적 옵션(static)은 사용하지 않음 + // V2 선택 컴포넌트 + case "v2-select": + // v2-select는 항상 테이블 컬럼에서 distinct 값을 자동 로드 return ( - = /> ); - case "unified-date": + // V2 날짜 컴포넌트 + case "v2-date": return ( - = /> ); - case "unified-list": + case "v2-list": // 데이터 소스: config.data > props.tableDisplayData > [] const listData = config.data?.length > 0 ? config.data : props.tableDisplayData || []; return ( - = /> ); - case "unified-layout": + case "v2-layout": return ( - = }} > {children} - + ); - case "unified-group": + case "v2-group": return ( - = title={config.title} > {children} - + ); - case "unified-media": + case "v2-media": return ( - = /> ); - case "unified-biz": + case "v2-biz": return ( - = /> ); - case "unified-hierarchy": + case "v2-hierarchy": return ( - = /> ); - case "unified-repeater": + case "v2-repeater": // 🆕 저장 설정 추출 (useCustomTable, mainTableName, foreignKeyColumn) const repeaterTargetTable = config.useCustomTable && config.mainTableName ? config.mainTableName : config.dataSource?.tableName; return ( - = return (
-
Unified 컴포넌트
-
알 수 없는 타입: {unifiedType}
+
V2 컴포넌트
+
알 수 없는 타입: {v2Type}
); @@ -653,12 +655,12 @@ export const DynamicComponentRenderer: React.FC = currentValue = formData?.[fieldName] || ""; } - // 🆕 Unified 폼 시스템 연동 (Context가 있으면 사용, 없으면 null) + // 🆕 V2 폼 시스템 연동 (Context가 있으면 사용, 없으면 null) // eslint-disable-next-line react-hooks/rules-of-hooks - const unifiedFormContext = useUnifiedFormOptional(); + const v2FormContext = useV2FormOptional(); // onChange 핸들러 - 컴포넌트 타입에 따라 다르게 처리 - // 🆕 Unified 시스템과 레거시 시스템 모두에 전파 + // 🆕 V2 시스템과 레거시 시스템 모두에 전파 const handleChange = (value: any) => { // autocomplete-search-input, entity-search-input은 자체적으로 onFormDataChange를 호출하므로 중복 저장 방지 if (componentType === "autocomplete-search-input" || componentType === "entity-search-input") { @@ -672,9 +674,9 @@ export const DynamicComponentRenderer: React.FC = actualValue = value.target.value; } - // 1. Unified 폼 시스템에 전파 (있으면) - if (unifiedFormContext) { - unifiedFormContext.setValue(fieldName, actualValue); + // 1. V2 폼 시스템에 전파 (있으면) + if (v2FormContext) { + v2FormContext.setValue(fieldName, actualValue); } // 2. 레거시 onFormDataChange 콜백도 호출 (호환성 유지) diff --git a/frontend/lib/registry/components/index.ts b/frontend/lib/registry/components/index.ts index c56461ef..3afee0f4 100644 --- a/frontend/lib/registry/components/index.ts +++ b/frontend/lib/registry/components/index.ts @@ -80,7 +80,7 @@ import "./section-card/SectionCardRenderer"; import "./tabs/tabs-component"; import "./location-swap-selector/LocationSwapSelectorRenderer"; import "./rack-structure/RackStructureRenderer"; -import "./unified-repeater/UnifiedRepeaterRenderer"; +import "./v2-repeater/V2RepeaterRenderer"; import "./pivot-grid/PivotGridRenderer"; import "./aggregation-widget/AggregationWidgetRenderer"; import "./repeat-container/RepeatContainerRenderer"; @@ -88,7 +88,7 @@ import "./repeat-container/RepeatContainerRenderer"; // ============================================================ // V2 컴포넌트들 (화면관리 전용 - 충돌 방지용 별도 버전) // ============================================================ -import "./v2-unified-repeater/UnifiedRepeaterRenderer"; +import "./v2-repeater/V2RepeaterRenderer"; import "./v2-button-primary/ButtonPrimaryRenderer"; import "./v2-split-panel-layout/SplitPanelLayoutRenderer"; import "./v2-aggregation-widget/AggregationWidgetRenderer"; diff --git a/frontend/lib/registry/components/repeat-container/types.ts b/frontend/lib/registry/components/repeat-container/types.ts index 961c11e7..6ab7e701 100644 --- a/frontend/lib/registry/components/repeat-container/types.ts +++ b/frontend/lib/registry/components/repeat-container/types.ts @@ -3,7 +3,7 @@ import { ComponentConfig } from "@/types/component"; /** * 리피터 컨테이너 데이터 소스 타입 */ -export type DataSourceType = "table-list" | "unified-repeater" | "externalData" | "manual"; +export type DataSourceType = "table-list" | "v2-repeater" | "externalData" | "manual"; /** * 리피터 컨테이너 레이아웃 타입 diff --git a/frontend/lib/registry/components/unified-repeater/index.ts b/frontend/lib/registry/components/unified-repeater/index.ts deleted file mode 100644 index b6a57fb8..00000000 --- a/frontend/lib/registry/components/unified-repeater/index.ts +++ /dev/null @@ -1,101 +0,0 @@ -/** - * UnifiedRepeater 컴포넌트 정의 - * - * 반복 데이터 관리를 위한 통합 컴포넌트 - * 기존 simple-repeater-table, modal-repeater-table, repeat-screen-modal, related-data-buttons 통합 - */ - -import { ComponentCategory } from "@/types/component"; -import { createComponentDefinition } from "../../utils/createComponentDefinition"; -import { UnifiedRepeaterConfigPanel } from "@/components/unified/config-panels/UnifiedRepeaterConfigPanel"; -import { UnifiedRepeater } from "@/components/unified/UnifiedRepeater"; - -export const UnifiedRepeaterDefinition = createComponentDefinition({ - id: "unified-repeater", - name: "통합 반복 데이터", - description: "반복 데이터 관리 (인라인/모달/버튼 모드)", - category: ComponentCategory.UNIFIED, - webType: "entity", // 반복 데이터는 엔티티 참조 타입 - version: "1.0.0", - component: UnifiedRepeater, // React 컴포넌트 (필수) - - // 기본 속성 - defaultProps: { - config: { - renderMode: "inline", - dataSource: { - tableName: "", - foreignKey: "", - referenceKey: "", - }, - columns: [], - modal: { - size: "md", - }, - button: { - sourceType: "manual", - manualButtons: [], - layout: "horizontal", - style: "outline", - }, - features: { - showAddButton: true, - showDeleteButton: true, - inlineEdit: false, - dragSort: false, - showRowNumber: false, - selectable: false, - multiSelect: false, - }, - }, - }, - - // 설정 스키마 - configSchema: { - renderMode: { - type: "select", - label: "렌더링 모드", - options: [ - { value: "inline", label: "인라인 (테이블)" }, - { value: "modal", label: "모달" }, - { value: "button", label: "버튼" }, - { value: "mixed", label: "혼합 (테이블 + 버튼)" }, - ], - }, - "dataSource.tableName": { - type: "tableSelect", - label: "데이터 테이블", - description: "반복 데이터가 저장된 테이블", - }, - "dataSource.foreignKey": { - type: "columnSelect", - label: "연결 키 (FK)", - description: "부모 레코드를 참조하는 컬럼", - dependsOn: "dataSource.tableName", - }, - "dataSource.referenceKey": { - type: "columnSelect", - label: "상위 키", - description: "현재 화면 테이블의 PK 컬럼", - useCurrentTable: true, - }, - }, - - // 이벤트 - events: ["onDataChange", "onRowClick", "onButtonClick"], - - // 아이콘 - icon: "Repeat", - - // 태그 - tags: ["data", "repeater", "table", "modal", "button", "unified"], - - // 설정 패널 - configPanel: UnifiedRepeaterConfigPanel, - - // v2-unified-repeater 사용으로 패널에서 숨김 - hidden: true, -}); - -export default UnifiedRepeaterDefinition; - diff --git a/frontend/lib/registry/components/v2-aggregation-widget/AggregationWidgetConfigPanel.tsx b/frontend/lib/registry/components/v2-aggregation-widget/AggregationWidgetConfigPanel.tsx index fb19312c..bd29c7fc 100644 --- a/frontend/lib/registry/components/v2-aggregation-widget/AggregationWidgetConfigPanel.tsx +++ b/frontend/lib/registry/components/v2-aggregation-widget/AggregationWidgetConfigPanel.tsx @@ -150,8 +150,8 @@ export function AggregationWidgetConfigPanel({ const filtered = screenComponents.filter(comp => comp.componentType === "table-list" || comp.componentType === "v2-table-list" || - comp.componentType === "unified-repeater" || - comp.componentType === "v2-unified-repeater" || + comp.componentType === "v2-repeater" || + comp.componentType === "v2-repeater" || comp.componentType === "repeat-container" || comp.componentType === "v2-repeat-container" ); @@ -414,9 +414,9 @@ export function AggregationWidgetConfigPanel({ // 연결 가능한 컴포넌트 (리피터, 테이블리스트) const linkableComponents = screenComponents.filter( - (c) => c.componentType === "v2-unified-repeater" || + (c) => c.componentType === "v2-repeater" || c.componentType === "v2-table-list" || - c.componentType === "unified-repeater" || + c.componentType === "v2-repeater" || c.componentType === "table-list" ); @@ -445,11 +445,11 @@ export function AggregationWidgetConfigPanel({ type.includes("radio") || type.includes("textarea") || type.includes("number") || - // unified-input, unified-select, unified-date 등 (unified-repeater 등은 제외) - type === "unified-input" || - type === "unified-select" || - type === "unified-date" || - type === "unified-hierarchy" + // v2-input, v2-select, v2-date 등 (v2-repeater 등은 제외) + type === "v2-input" || + type === "v2-select" || + type === "v2-date" || + type === "v2-hierarchy" ); // columnName이 있으면 입력 필드로 간주 (드래그로 배치된 필드) diff --git a/frontend/lib/registry/components/v2-date/index.ts b/frontend/lib/registry/components/v2-date/index.ts new file mode 100644 index 00000000..43e5bf07 --- /dev/null +++ b/frontend/lib/registry/components/v2-date/index.ts @@ -0,0 +1,77 @@ +/** + * V2Date 컴포넌트 정의 + * + * 날짜, 시간, 날짜시간, 날짜범위 등 다양한 날짜/시간 입력을 지원하는 통합 컴포넌트 + */ + +import { ComponentCategory } from "@/types/component"; +import { createComponentDefinition } from "../../utils/createComponentDefinition"; +import { V2DateConfigPanel } from "@/components/v2/config-panels/V2DateConfigPanel"; +import { V2Date } from "@/components/v2/V2Date"; + +export const V2DateDefinition = createComponentDefinition({ + id: "v2-date", + name: "V2 날짜", + description: "날짜, 시간, 날짜시간, 날짜범위 등 다양한 날짜/시간 입력 지원", + category: ComponentCategory.INPUT, + webType: "date", + version: "2.0.0", + component: V2Date, + + // 기본 속성 + defaultProps: { + config: { + dateType: "date", + format: "YYYY-MM-DD", + placeholder: "날짜 선택", + required: false, + readonly: false, + disabled: false, + showTime: false, + use24Hours: true, + }, + }, + + // 설정 스키마 + configSchema: { + dateType: { + type: "select", + label: "날짜 타입", + options: [ + { value: "date", label: "날짜" }, + { value: "datetime", label: "날짜시간" }, + { value: "time", label: "시간" }, + { value: "daterange", label: "날짜 범위" }, + { value: "month", label: "월" }, + { value: "year", label: "연도" }, + ], + }, + format: { + type: "text", + label: "표시 형식", + placeholder: "YYYY-MM-DD", + }, + required: { + type: "boolean", + label: "필수 입력", + }, + showTime: { + type: "boolean", + label: "시간 표시", + }, + }, + + // 이벤트 + events: ["onChange", "onClear"], + + // 아이콘 + icon: "Calendar", + + // 태그 + tags: ["date", "time", "datetime", "calendar", "v2"], + + // 설정 패널 + configPanel: V2DateConfigPanel, +}); + +export default V2DateDefinition; diff --git a/frontend/lib/registry/components/v2-input/index.ts b/frontend/lib/registry/components/v2-input/index.ts new file mode 100644 index 00000000..c6650717 --- /dev/null +++ b/frontend/lib/registry/components/v2-input/index.ts @@ -0,0 +1,78 @@ +/** + * V2Input 컴포넌트 정의 + * + * 텍스트, 숫자, 비밀번호 등 다양한 입력 타입을 지원하는 통합 입력 컴포넌트 + */ + +import { ComponentCategory } from "@/types/component"; +import { createComponentDefinition } from "../../utils/createComponentDefinition"; +import { V2InputConfigPanel } from "@/components/v2/config-panels/V2InputConfigPanel"; +import { V2Input } from "@/components/v2/V2Input"; + +export const V2InputDefinition = createComponentDefinition({ + id: "v2-input", + name: "V2 입력", + description: "텍스트, 숫자, 비밀번호 등 다양한 입력 타입 지원", + category: ComponentCategory.INPUT, + webType: "text", + version: "2.0.0", + component: V2Input, + + // 기본 속성 + defaultProps: { + config: { + inputType: "text", + placeholder: "", + required: false, + readonly: false, + disabled: false, + maxLength: undefined, + minLength: undefined, + pattern: undefined, + showCounter: false, + }, + }, + + // 설정 스키마 + configSchema: { + inputType: { + type: "select", + label: "입력 타입", + options: [ + { value: "text", label: "텍스트" }, + { value: "number", label: "숫자" }, + { value: "password", label: "비밀번호" }, + { value: "email", label: "이메일" }, + { value: "tel", label: "전화번호" }, + { value: "url", label: "URL" }, + { value: "textarea", label: "여러 줄 텍스트" }, + ], + }, + placeholder: { + type: "text", + label: "플레이스홀더", + }, + required: { + type: "boolean", + label: "필수 입력", + }, + readonly: { + type: "boolean", + label: "읽기 전용", + }, + }, + + // 이벤트 + events: ["onChange", "onBlur", "onFocus"], + + // 아이콘 + icon: "TextCursorInput", + + // 태그 + tags: ["input", "text", "number", "v2"], + + // 설정 패널 + configPanel: V2InputConfigPanel, +}); + +export default V2InputDefinition; diff --git a/frontend/lib/registry/components/v2-repeat-container/types.ts b/frontend/lib/registry/components/v2-repeat-container/types.ts index 961c11e7..6ab7e701 100644 --- a/frontend/lib/registry/components/v2-repeat-container/types.ts +++ b/frontend/lib/registry/components/v2-repeat-container/types.ts @@ -3,7 +3,7 @@ import { ComponentConfig } from "@/types/component"; /** * 리피터 컨테이너 데이터 소스 타입 */ -export type DataSourceType = "table-list" | "unified-repeater" | "externalData" | "manual"; +export type DataSourceType = "table-list" | "v2-repeater" | "externalData" | "manual"; /** * 리피터 컨테이너 레이아웃 타입 diff --git a/frontend/lib/registry/components/unified-repeater/UnifiedRepeaterRenderer.tsx b/frontend/lib/registry/components/v2-repeater/V2RepeaterRenderer.tsx similarity index 78% rename from frontend/lib/registry/components/unified-repeater/UnifiedRepeaterRenderer.tsx rename to frontend/lib/registry/components/v2-repeater/V2RepeaterRenderer.tsx index bf1aec8e..908bc4f1 100644 --- a/frontend/lib/registry/components/unified-repeater/UnifiedRepeaterRenderer.tsx +++ b/frontend/lib/registry/components/v2-repeater/V2RepeaterRenderer.tsx @@ -1,17 +1,17 @@ "use client"; /** - * UnifiedRepeater 렌더러 + * V2Repeater 렌더러 * 컴포넌트 레지스트리에 등록하기 위한 래퍼 */ import React from "react"; import { ComponentRegistry } from "../../ComponentRegistry"; -import { UnifiedRepeater } from "@/components/unified/UnifiedRepeater"; -import { UnifiedRepeaterDefinition } from "./index"; -import { UnifiedRepeaterConfig, DEFAULT_REPEATER_CONFIG } from "@/types/unified-repeater"; +import { V2Repeater } from "@/components/v2/V2Repeater"; +import { V2RepeaterDefinition } from "./index"; +import { V2RepeaterConfig, DEFAULT_REPEATER_CONFIG } from "@/types/v2-repeater"; -interface UnifiedRepeaterRendererProps { +interface V2RepeaterRendererProps { component: any; data?: any; mode?: "view" | "edit"; @@ -22,7 +22,7 @@ interface UnifiedRepeaterRendererProps { parentId?: string | number; } -const UnifiedRepeaterRenderer: React.FC = ({ +const V2RepeaterRenderer: React.FC = ({ component, data, mode, @@ -32,12 +32,12 @@ const UnifiedRepeaterRenderer: React.FC = ({ onButtonClick, parentId, }) => { - // component.componentConfig 또는 component.config에서 UnifiedRepeaterConfig 추출 - const config: UnifiedRepeaterConfig = React.useMemo(() => { + // component.componentConfig 또는 component.config에서 V2RepeaterConfig 추출 + const config: V2RepeaterConfig = React.useMemo(() => { // 🆕 componentConfig 우선 (DB에서 properties.componentConfig로 저장됨) const componentConfig = component?.componentConfig || component?.config || component?.props?.config || {}; - console.log("📋 UnifiedRepeaterRenderer config 추출:", { + console.log("📋 V2RepeaterRenderer config 추출:", { hasComponentConfig: !!component?.componentConfig, hasConfig: !!component?.config, useCustomTable: componentConfig.useCustomTable, @@ -93,7 +93,7 @@ const UnifiedRepeaterRenderer: React.FC = ({ } return ( - = ({ // 컴포넌트 레지스트리에 등록 ComponentRegistry.registerComponent({ - ...UnifiedRepeaterDefinition, - render: (props: any) => , + ...V2RepeaterDefinition, + render: (props: any) => , }); -export default UnifiedRepeaterRenderer; +export default V2RepeaterRenderer; diff --git a/frontend/lib/registry/components/v2-unified-repeater/index.ts b/frontend/lib/registry/components/v2-repeater/index.ts similarity index 79% rename from frontend/lib/registry/components/v2-unified-repeater/index.ts rename to frontend/lib/registry/components/v2-repeater/index.ts index c7256f06..90760f49 100644 --- a/frontend/lib/registry/components/v2-unified-repeater/index.ts +++ b/frontend/lib/registry/components/v2-repeater/index.ts @@ -1,5 +1,5 @@ /** - * UnifiedRepeater 컴포넌트 정의 + * V2Repeater 컴포넌트 정의 * * 반복 데이터 관리를 위한 통합 컴포넌트 * 기존 simple-repeater-table, modal-repeater-table, repeat-screen-modal, related-data-buttons 통합 @@ -7,17 +7,17 @@ import { ComponentCategory } from "@/types/component"; import { createComponentDefinition } from "../../utils/createComponentDefinition"; -import { UnifiedRepeaterConfigPanel } from "@/components/unified/config-panels/UnifiedRepeaterConfigPanel"; -import { UnifiedRepeater } from "@/components/unified/UnifiedRepeater"; +import { V2RepeaterConfigPanel } from "@/components/v2/config-panels/V2RepeaterConfigPanel"; +import { V2Repeater } from "@/components/v2/V2Repeater"; -export const V2UnifiedRepeaterDefinition = createComponentDefinition({ - id: "v2-unified-repeater", +export const V2RepeaterDefinition = createComponentDefinition({ + id: "v2-repeater", name: "통합 반복 데이터", description: "반복 데이터 관리 (인라인/모달/버튼 모드)", - category: ComponentCategory.UNIFIED, + category: ComponentCategory.V2, webType: "entity", // 반복 데이터는 엔티티 참조 타입 version: "1.0.0", - component: UnifiedRepeater, // React 컴포넌트 (필수) + component: V2Repeater, // React 컴포넌트 (필수) // 기본 속성 defaultProps: { @@ -88,11 +88,14 @@ export const V2UnifiedRepeaterDefinition = createComponentDefinition({ icon: "Repeat", // 태그 - tags: ["data", "repeater", "table", "modal", "button", "unified"], + tags: ["data", "repeater", "table", "modal", "button", "v2"], // 설정 패널 - configPanel: UnifiedRepeaterConfigPanel, + configPanel: V2RepeaterConfigPanel, + + // v2-repeater 사용으로 패널에서 숨김 + hidden: true, }); -export default V2UnifiedRepeaterDefinition; +export default V2RepeaterDefinition; diff --git a/frontend/lib/registry/components/v2-select/index.ts b/frontend/lib/registry/components/v2-select/index.ts new file mode 100644 index 00000000..3f73b2ac --- /dev/null +++ b/frontend/lib/registry/components/v2-select/index.ts @@ -0,0 +1,84 @@ +/** + * V2Select 컴포넌트 정의 + * + * 드롭다운, 콤보박스, 라디오, 체크박스 등 다양한 선택 모드를 지원하는 통합 선택 컴포넌트 + */ + +import { ComponentCategory } from "@/types/component"; +import { createComponentDefinition } from "../../utils/createComponentDefinition"; +import { V2SelectConfigPanel } from "@/components/v2/config-panels/V2SelectConfigPanel"; +import { V2Select } from "@/components/v2/V2Select"; + +export const V2SelectDefinition = createComponentDefinition({ + id: "v2-select", + name: "V2 선택", + description: "드롭다운, 콤보박스, 라디오, 체크박스 등 다양한 선택 모드 지원", + category: ComponentCategory.INPUT, + webType: "select", + version: "2.0.0", + component: V2Select, + + // 기본 속성 + defaultProps: { + config: { + mode: "dropdown", + source: "distinct", // 기본: 테이블 컬럼에서 distinct 값 자동 로드 + multiple: false, + searchable: true, + placeholder: "선택하세요", + required: false, + readonly: false, + disabled: false, + }, + }, + + // 설정 스키마 + configSchema: { + mode: { + type: "select", + label: "선택 모드", + options: [ + { value: "dropdown", label: "드롭다운" }, + { value: "combobox", label: "콤보박스 (검색)" }, + { value: "radio", label: "라디오 버튼" }, + { value: "checkbox", label: "체크박스" }, + ], + }, + source: { + type: "select", + label: "데이터 소스", + options: [ + { value: "distinct", label: "테이블 컬럼 (자동)" }, + { value: "static", label: "정적 옵션" }, + { value: "code", label: "공통코드" }, + { value: "entity", label: "엔티티 참조" }, + ], + }, + multiple: { + type: "boolean", + label: "다중 선택", + }, + searchable: { + type: "boolean", + label: "검색 가능", + }, + required: { + type: "boolean", + label: "필수 선택", + }, + }, + + // 이벤트 + events: ["onChange", "onSearch", "onClear"], + + // 아이콘 + icon: "ChevronDown", + + // 태그 + tags: ["select", "dropdown", "combobox", "v2"], + + // 설정 패널 + configPanel: V2SelectConfigPanel, +}); + +export default V2SelectDefinition; diff --git a/frontend/lib/registry/components/v2-unified-repeater/UnifiedRepeaterRenderer.tsx b/frontend/lib/registry/components/v2-unified-repeater/UnifiedRepeaterRenderer.tsx deleted file mode 100644 index 480046d2..00000000 --- a/frontend/lib/registry/components/v2-unified-repeater/UnifiedRepeaterRenderer.tsx +++ /dev/null @@ -1,115 +0,0 @@ -"use client"; - -/** - * UnifiedRepeater 렌더러 - * 컴포넌트 레지스트리에 등록하기 위한 래퍼 - */ - -import React from "react"; -import { ComponentRegistry } from "../../ComponentRegistry"; -import { UnifiedRepeater } from "@/components/unified/UnifiedRepeater"; -import { V2UnifiedRepeaterDefinition } from "./index"; -import { UnifiedRepeaterConfig, DEFAULT_REPEATER_CONFIG } from "@/types/unified-repeater"; - -interface UnifiedRepeaterRendererProps { - component: any; - data?: any; - mode?: "view" | "edit"; - isPreview?: boolean; - onDataChange?: (data: any[]) => void; - onRowClick?: (row: any) => void; - onButtonClick?: (action: string, row?: any, buttonConfig?: any) => void; - parentId?: string | number; -} - -const UnifiedRepeaterRenderer: React.FC = ({ - component, - data, - mode, - isPreview, - onDataChange, - onRowClick, - onButtonClick, - parentId, -}) => { - // component.componentConfig 또는 component.config에서 UnifiedRepeaterConfig 추출 - const config: UnifiedRepeaterConfig = React.useMemo(() => { - // 🆕 componentConfig 우선 (DB에서 properties.componentConfig로 저장됨) - const componentConfig = component?.componentConfig || component?.config || component?.props?.config || {}; - - console.log("📋 V2UnifiedRepeaterRenderer config 추출:", { - hasComponentConfig: !!component?.componentConfig, - hasConfig: !!component?.config, - useCustomTable: componentConfig.useCustomTable, - mainTableName: componentConfig.mainTableName, - foreignKeyColumn: componentConfig.foreignKeyColumn, - }); - - return { - ...DEFAULT_REPEATER_CONFIG, - ...componentConfig, - dataSource: { - ...DEFAULT_REPEATER_CONFIG.dataSource, - ...componentConfig.dataSource, - }, - columns: componentConfig.columns || [], - features: { - ...DEFAULT_REPEATER_CONFIG.features, - ...componentConfig.features, - }, - modal: { - ...DEFAULT_REPEATER_CONFIG.modal, - ...componentConfig.modal, - }, - button: { - ...DEFAULT_REPEATER_CONFIG.button, - ...componentConfig.button, - }, - }; - }, [component]); - - // parentId 결정: props에서 전달받거나 data에서 추출 - const resolvedParentId = React.useMemo(() => { - if (parentId) return parentId; - if (data && config.dataSource?.referenceKey) { - return data[config.dataSource.referenceKey]; - } - return undefined; - }, [parentId, data, config.dataSource?.referenceKey]); - - // 미리보기 모드에서는 샘플 데이터 표시 - if (isPreview) { - return ( -
-
- 통합 반복 데이터 -
- - 모드: {config.renderMode} | 테이블: {config.dataSource?.tableName || "미설정"} - -
-
- ); - } - - return ( - - ); -}; - -// 컴포넌트 레지스트리에 등록 -ComponentRegistry.registerComponent({ - ...V2UnifiedRepeaterDefinition, - render: (props: any) => , -}); - -export default UnifiedRepeaterRenderer; - diff --git a/frontend/lib/registry/init.ts b/frontend/lib/registry/init.ts index 6f36fbb7..f74ebcd1 100644 --- a/frontend/lib/registry/init.ts +++ b/frontend/lib/registry/init.ts @@ -394,12 +394,12 @@ export function initializeWebTypeRegistry() { export function initializeRegistries() { initializeWebTypeRegistry(); - // Unified 컴포넌트 등록 + // V2 컴포넌트 등록 try { - const { registerUnifiedComponents } = require("@/components/unified/registerUnifiedComponents"); - registerUnifiedComponents(); + const { registerV2Components } = require("@/components/v2/registerV2Components"); + registerV2Components(); } catch (error) { - console.warn("⚠️ Unified 컴포넌트 등록 실패:", error); + console.warn("⚠️ V2 컴포넌트 등록 실패:", error); } // 필요한 경우 버튼 액션 레지스트리도 여기서 초기화 diff --git a/frontend/lib/schemas/componentConfig.ts b/frontend/lib/schemas/componentConfig.ts index f45fc49b..eca319fd 100644 --- a/frontend/lib/schemas/componentConfig.ts +++ b/frontend/lib/schemas/componentConfig.ts @@ -1,7 +1,7 @@ /** - * V2/Unified 컴포넌트 설정 스키마 및 병합 유틸리티 + * V2/V2 컴포넌트 설정 스키마 및 병합 유틸리티 * - * V2 컴포넌트와 Unified 컴포넌트의 overrides 스키마 및 기본값을 관리 + * V2 컴포넌트와 V2 컴포넌트의 overrides 스키마 및 기본값을 관리 * - 저장: component_url + overrides (차이값만) * - 로드: 코드 기본값 + overrides 병합 (Zod) */ @@ -421,8 +421,8 @@ const v2TabsWidgetOverridesSchema = z }) .passthrough(); -// v2-unified-repeater -const v2UnifiedRepeaterOverridesSchema = z +// v2-repeater +const v2V2RepeaterOverridesSchema = z .object({ renderMode: z.enum(["inline", "modal", "button", "mixed"]).default("inline"), dataSource: z @@ -465,11 +465,11 @@ const v2UnifiedRepeaterOverridesSchema = z .passthrough(); // ============================================ -// Unified 컴포넌트 overrides 스키마 정의 +// V2 컴포넌트 overrides 스키마 정의 // ============================================ -// unified-input -const unifiedInputOverridesSchema = z +// v2-input +const v2InputOverridesSchema = z .object({ inputType: z.string().default("text"), format: z.string().default("none"), @@ -477,8 +477,8 @@ const unifiedInputOverridesSchema = z }) .passthrough(); -// unified-select -const unifiedSelectOverridesSchema = z +// v2-select +const v2SelectOverridesSchema = z .object({ mode: z.string().default("dropdown"), source: z.string().default("static"), @@ -486,16 +486,16 @@ const unifiedSelectOverridesSchema = z }) .passthrough(); -// unified-date -const unifiedDateOverridesSchema = z +// v2-date +const v2DateOverridesSchema = z .object({ dateType: z.string().default("date"), format: z.string().default("YYYY-MM-DD"), }) .passthrough(); -// unified-list -const unifiedListOverridesSchema = z +// v2-list +const v2ListOverridesSchema = z .object({ viewMode: z.string().default("table"), source: z.string().default("static"), @@ -505,8 +505,8 @@ const unifiedListOverridesSchema = z }) .passthrough(); -// unified-layout -const unifiedLayoutOverridesSchema = z +// v2-layout +const v2LayoutOverridesSchema = z .object({ layoutType: z.string().default("grid"), columns: z.number().default(2), @@ -515,8 +515,8 @@ const unifiedLayoutOverridesSchema = z }) .passthrough(); -// unified-group -const unifiedGroupOverridesSchema = z +// v2-group +const v2GroupOverridesSchema = z .object({ groupType: z.string().default("section"), title: z.string().default(""), @@ -525,8 +525,8 @@ const unifiedGroupOverridesSchema = z }) .passthrough(); -// unified-media -const unifiedMediaOverridesSchema = z +// v2-media +const v2MediaOverridesSchema = z .object({ mediaType: z.string().default("image"), multiple: z.boolean().default(false), @@ -534,15 +534,15 @@ const unifiedMediaOverridesSchema = z }) .passthrough(); -// unified-biz -const unifiedBizOverridesSchema = z +// v2-biz +const v2BizOverridesSchema = z .object({ bizType: z.string().default("flow"), }) .passthrough(); -// unified-hierarchy -const unifiedHierarchyOverridesSchema = z +// v2-hierarchy +const v2HierarchyOverridesSchema = z .object({ hierarchyType: z.string().default("tree"), viewMode: z.string().default("tree"), @@ -550,8 +550,8 @@ const unifiedHierarchyOverridesSchema = z }) .passthrough(); -// unified-repeater -const unifiedRepeaterOverridesSchema = z +// v2-repeater +const v2RepeaterOverridesSchema = z .object({ renderMode: z.enum(["inline", "modal"]).default("inline"), mainTableName: z.string().optional(), @@ -631,19 +631,19 @@ const componentOverridesSchemaRegistry: Record> = { allowCloseable: false, persistSelection: false, }, - "v2-unified-repeater": { + "v2-repeater": { renderMode: "inline", dataSource: { tableName: "", foreignKey: "", referenceKey: "" }, columns: [], @@ -795,54 +795,54 @@ const componentDefaultsRegistry: Record> = { }, }, - // Unified 컴포넌트 - "unified-input": { + // V2 컴포넌트 + "v2-input": { inputType: "text", format: "none", placeholder: "", }, - "unified-select": { + "v2-select": { mode: "dropdown", source: "static", options: [], }, - "unified-date": { + "v2-date": { dateType: "date", format: "YYYY-MM-DD", }, - "unified-list": { + "v2-list": { viewMode: "table", source: "static", columns: [], pagination: true, sortable: true, }, - "unified-layout": { + "v2-layout": { layoutType: "grid", columns: 2, gap: "16", use12Column: true, }, - "unified-group": { + "v2-group": { groupType: "section", title: "", collapsible: false, defaultOpen: true, }, - "unified-media": { + "v2-media": { mediaType: "image", multiple: false, preview: true, }, - "unified-biz": { + "v2-biz": { bizType: "flow", }, - "unified-hierarchy": { + "v2-hierarchy": { hierarchyType: "tree", viewMode: "tree", dataSource: "static", }, - "unified-repeater": { + "v2-repeater": { renderMode: "inline", useCustomTable: false, dataSource: {}, diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index bc0ea0fe..07ce0691 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -1090,7 +1090,7 @@ export class ButtonActionExecutor { } // 🆕 배열 데이터(리피터 데이터) 제거 - 마스터 테이블에는 배열 데이터를 저장하지 않음 - // 리피터 데이터는 별도의 RepeaterFieldGroup/UnifiedRepeater 저장 로직에서 처리됨 + // 리피터 데이터는 별도의 RepeaterFieldGroup/V2Repeater 저장 로직에서 처리됨 for (const key of Object.keys(dataWithUserInfo)) { if (Array.isArray(dataWithUserInfo[key])) { delete dataWithUserInfo[key]; @@ -1164,11 +1164,11 @@ export class ButtonActionExecutor { // _targetTable이 없거나, _repeatScreenModal_ 키면 스킵 (다른 로직에서 처리) if (!repeaterTargetTable || fieldKey.startsWith("_repeatScreenModal_")) continue; - // 🆕 UnifiedRepeater가 등록된 테이블이면 RepeaterFieldGroup 저장 스킵 - // UnifiedRepeater가 repeaterSave 이벤트로 저장 처리함 + // 🆕 V2Repeater가 등록된 테이블이면 RepeaterFieldGroup 저장 스킵 + // V2Repeater가 repeaterSave 이벤트로 저장 처리함 // @ts-ignore - window에 동적 속성 사용 - const registeredUnifiedRepeaterTables = Array.from(window.__unifiedRepeaterInstances || []); - if (registeredUnifiedRepeaterTables.includes(repeaterTargetTable)) { + const registeredV2RepeaterTables = Array.from(window.__v2RepeaterInstances || []); + if (registeredV2RepeaterTables.includes(repeaterTargetTable)) { continue; } @@ -1344,20 +1344,20 @@ export class ButtonActionExecutor { } } - // 🆕 UnifiedRepeater 전역 등록 확인 + // 🆕 V2Repeater 전역 등록 확인 // @ts-ignore - window에 동적 속성 사용 - const unifiedRepeaterTables = Array.from(window.__unifiedRepeaterInstances || []); + const v2RepeaterTables = Array.from(window.__v2RepeaterInstances || []); // 메인 저장 건너뛰기 조건: // 1. RepeatScreenModal 또는 RepeaterFieldGroup에서 같은 테이블 처리 - // 2. UnifiedRepeater가 같은 테이블에 존재 (리피터 데이터에 메인 폼 데이터 병합되어 저장됨) + // 2. V2Repeater가 같은 테이블에 존재 (리피터 데이터에 메인 폼 데이터 병합되어 저장됨) const shouldSkipMainSave = repeatScreenModalTables.includes(tableName) || repeaterFieldGroupTables.includes(tableName) || - unifiedRepeaterTables.includes(tableName); + v2RepeaterTables.includes(tableName); if (shouldSkipMainSave) { - saveResult = { success: true, message: "RepeaterFieldGroup/RepeatScreenModal/UnifiedRepeater에서 처리" }; + saveResult = { success: true, message: "RepeaterFieldGroup/RepeatScreenModal/V2Repeater에서 처리" }; } else { saveResult = await DynamicFormApi.saveFormData({ screenId, @@ -1630,7 +1630,7 @@ export class ButtonActionExecutor { window.dispatchEvent(new CustomEvent("closeEditModal")); // EditModal 닫기 window.dispatchEvent(new CustomEvent("saveSuccessInModal")); // ScreenModal 연속 등록 모드 처리 - // UnifiedRepeater 저장 이벤트 발생 (메인 폼 데이터 + 리피터 데이터 병합 저장) + // V2Repeater 저장 이벤트 발생 (메인 폼 데이터 + 리피터 데이터 병합 저장) // 🔧 formData를 리피터에 전달하여 각 행에 병합 저장 const savedId = saveResult?.data?.id || saveResult?.data?.data?.id || formData.id || context.formData?.id; @@ -2852,7 +2852,7 @@ export class ButtonActionExecutor { if (autoDetectDataSource) { dataSourceId = config.dataSourceId; - // TableList, UnifiedList 또는 SplitPanelLayout에서 자동 감지 + // TableList, V2List 또는 SplitPanelLayout에서 자동 감지 if (!dataSourceId && context.allComponents) { // 1. table-list 컴포넌트 찾기 const tableListComponent = context.allComponents.find( @@ -2862,19 +2862,19 @@ export class ButtonActionExecutor { if (tableListComponent) { dataSourceId = tableListComponent.componentConfig.tableName; } else { - // 2. unified-list 컴포넌트 찾기 - const unifiedListComponent = context.allComponents.find( + // 2. v2-list 컴포넌트 찾기 + const v2ListComponent = context.allComponents.find( (comp: any) => - comp.componentType === "unified-list" && + comp.componentType === "v2-list" && (comp.componentConfig?.dataSource?.table || comp.componentConfig?.tableName), ); - if (unifiedListComponent) { + if (v2ListComponent) { dataSourceId = - unifiedListComponent.componentConfig.dataSource?.table || - unifiedListComponent.componentConfig.tableName; - console.log("✨ UnifiedList 자동 감지:", { - componentId: unifiedListComponent.id, + v2ListComponent.componentConfig.dataSource?.table || + v2ListComponent.componentConfig.tableName; + console.log("✨ V2List 자동 감지:", { + componentId: v2ListComponent.id, tableName: dataSourceId, }); } else { diff --git a/frontend/lib/utils/dbTypeMapping.ts b/frontend/lib/utils/dbTypeMapping.ts index a34cb847..f7cbc4c6 100644 --- a/frontend/lib/utils/dbTypeMapping.ts +++ b/frontend/lib/utils/dbTypeMapping.ts @@ -1,7 +1,7 @@ /** * DB 타입 → 웹 타입 자동 매핑 유틸리티 * - * 백엔드의 unified-web-types.ts와 동기화된 매핑 로직을 제공합니다. + * 백엔드의 v2-web-types.ts와 동기화된 매핑 로직을 제공합니다. * 테이블 정보를 불러올 때 DB 타입을 적절한 웹 타입으로 자동 변환합니다. */ @@ -9,7 +9,7 @@ import { WebType } from "@/types"; /** * PostgreSQL DB 타입 → 웹 타입 매핑 테이블 - * (백엔드 unified-web-types.ts와 동기화됨) + * (백엔드 v2-web-types.ts와 동기화됨) */ export const DB_TYPE_TO_WEB_TYPE: Record = { // 텍스트 타입 diff --git a/frontend/lib/utils/formValidation.ts b/frontend/lib/utils/formValidation.ts index 876a5d5f..c521455c 100644 --- a/frontend/lib/utils/formValidation.ts +++ b/frontend/lib/utils/formValidation.ts @@ -3,7 +3,7 @@ * 클라이언트 측에서 사전 검증을 수행하여 사용자 경험 향상 */ -import { WebType, DynamicWebType, isValidWebType, normalizeWebType } from "@/types/unified-web-types"; +import { WebType, DynamicWebType, isValidWebType, normalizeWebType } from "@/types/v2-web-types"; import { ColumnInfo, ComponentData, WidgetComponent } from "@/types/screen"; // 검증 결과 타입 diff --git a/frontend/lib/utils/getComponentConfigPanel.tsx b/frontend/lib/utils/getComponentConfigPanel.tsx index 7e3f0107..6de75bc1 100644 --- a/frontend/lib/utils/getComponentConfigPanel.tsx +++ b/frontend/lib/utils/getComponentConfigPanel.tsx @@ -7,17 +7,17 @@ import React from "react"; // 컴포넌트별 ConfigPanel 동적 import 맵 // 모든 ConfigPanel이 있는 컴포넌트를 여기에 등록해야 슬롯/중첩 컴포넌트에서 전용 설정 패널이 표시됨 const CONFIG_PANEL_MAP: Record Promise> = { - // ========== Unified 컴포넌트 ========== - "unified-input": () => import("@/components/unified/config-panels/UnifiedInputConfigPanel"), - "unified-select": () => import("@/components/unified/config-panels/UnifiedSelectConfigPanel"), - "unified-date": () => import("@/components/unified/config-panels/UnifiedDateConfigPanel"), - "unified-list": () => import("@/components/unified/config-panels/UnifiedListConfigPanel"), - "unified-media": () => import("@/components/unified/config-panels/UnifiedMediaConfigPanel"), - "unified-biz": () => import("@/components/unified/config-panels/UnifiedBizConfigPanel"), - "unified-group": () => import("@/components/unified/config-panels/UnifiedGroupConfigPanel"), - "unified-hierarchy": () => import("@/components/unified/config-panels/UnifiedHierarchyConfigPanel"), - "unified-layout": () => import("@/components/unified/config-panels/UnifiedLayoutConfigPanel"), - "unified-repeater": () => import("@/components/unified/config-panels/UnifiedRepeaterConfigPanel"), + // ========== V2 컴포넌트 ========== + "v2-input": () => import("@/components/v2/config-panels/V2InputConfigPanel"), + "v2-select": () => import("@/components/v2/config-panels/V2SelectConfigPanel"), + "v2-date": () => import("@/components/v2/config-panels/V2DateConfigPanel"), + "v2-list": () => import("@/components/v2/config-panels/V2ListConfigPanel"), + "v2-media": () => import("@/components/v2/config-panels/V2MediaConfigPanel"), + "v2-biz": () => import("@/components/v2/config-panels/V2BizConfigPanel"), + "v2-group": () => import("@/components/v2/config-panels/V2GroupConfigPanel"), + "v2-hierarchy": () => import("@/components/v2/config-panels/V2HierarchyConfigPanel"), + "v2-layout": () => import("@/components/v2/config-panels/V2LayoutConfigPanel"), + "v2-repeater": () => import("@/components/v2/config-panels/V2RepeaterConfigPanel"), // ========== 기본 입력 컴포넌트 ========== "text-input": () => import("@/lib/registry/components/text-input/TextInputConfigPanel"), @@ -71,8 +71,8 @@ const CONFIG_PANEL_MAP: Record Promise> = { "repeat-container": () => import("@/lib/registry/components/repeat-container/RepeatContainerConfigPanel"), "v2-repeat-container": () => import("@/lib/registry/components/v2-repeat-container/RepeatContainerConfigPanel"), "repeater-field-group": () => import("@/components/webtypes/config/RepeaterConfigPanel"), - "unified-repeater": () => import("@/components/unified/config-panels/UnifiedRepeaterConfigPanel"), - "v2-unified-repeater": () => import("@/components/unified/config-panels/UnifiedRepeaterConfigPanel"), + "v2-repeater": () => import("@/components/v2/config-panels/V2RepeaterConfigPanel"), + "v2-repeater": () => import("@/components/v2/config-panels/V2RepeaterConfigPanel"), "simple-repeater-table": () => import("@/lib/registry/components/simple-repeater-table/SimpleRepeaterTableConfigPanel"), "modal-repeater-table": () => import("@/lib/registry/components/modal-repeater-table/ModalRepeaterTableConfigPanel"), "repeat-screen-modal": () => import("@/lib/registry/components/repeat-screen-modal/RepeatScreenModalConfigPanel"), @@ -149,16 +149,16 @@ export async function getComponentConfigPanel(componentId: string): Promise = return ; } - // 🆕 Unified 컴포넌트들은 전용 props 사용 - if (componentId.startsWith("unified-")) { + // 🆕 V2 컴포넌트들은 전용 props 사용 + if (componentId.startsWith("v2-")) { return ( ; // 컴포넌트별 기본 설정 } /** - * 웹타입 → Unified 컴포넌트 매핑 테이블 + * 웹타입 → V2 컴포넌트 매핑 테이블 */ -export const WEB_TYPE_UNIFIED_MAPPING: Record = { - // 텍스트 입력 계열 → UnifiedInput +export const WEB_TYPE_V2_MAPPING: Record = { + // 텍스트 입력 계열 → V2Input text: { - componentType: "unified-input", + componentType: "v2-input", config: { inputType: "text", format: "none" }, }, email: { - componentType: "unified-input", + componentType: "v2-input", config: { inputType: "text", format: "email" }, }, password: { - componentType: "unified-input", + componentType: "v2-input", config: { inputType: "password" }, }, tel: { - componentType: "unified-input", + componentType: "v2-input", config: { inputType: "text", format: "tel" }, }, url: { - componentType: "unified-input", + componentType: "v2-input", config: { inputType: "text", format: "url" }, }, textarea: { - componentType: "unified-input", + componentType: "v2-input", config: { inputType: "textarea", rows: 3 }, }, - // 숫자 입력 → UnifiedInput + // 숫자 입력 → V2Input number: { - componentType: "unified-input", + componentType: "v2-input", config: { inputType: "number" }, }, decimal: { - componentType: "unified-input", + componentType: "v2-input", config: { inputType: "number", step: 0.01 }, }, - // 날짜/시간 → UnifiedDate + // 날짜/시간 → V2Date date: { - componentType: "unified-date", + componentType: "v2-date", config: { type: "date", format: "YYYY-MM-DD" }, }, datetime: { - componentType: "unified-date", + componentType: "v2-date", config: { type: "datetime", format: "YYYY-MM-DD HH:mm" }, }, time: { - componentType: "unified-date", + componentType: "v2-date", config: { type: "time", format: "HH:mm" }, }, - // 선택 입력 → UnifiedSelect + // 선택 입력 → V2Select select: { - componentType: "unified-select", + componentType: "v2-select", config: { mode: "dropdown", source: "static", options: [] }, }, dropdown: { - componentType: "unified-select", + componentType: "v2-select", config: { mode: "dropdown", source: "static", options: [] }, }, radio: { - componentType: "unified-select", + componentType: "v2-select", config: { mode: "radio", source: "static", options: [] }, }, checkbox: { - componentType: "unified-select", + componentType: "v2-select", config: { mode: "checkbox", source: "static", options: [] }, }, boolean: { - componentType: "unified-select", + componentType: "v2-select", config: { mode: "toggle", source: "static" }, }, - // 코드/참조 → UnifiedSelect (소스: code) + // 코드/참조 → V2Select (소스: code) code: { - componentType: "unified-select", + componentType: "v2-select", config: { mode: "dropdown", source: "code", codeGroup: "" }, }, - // 엔티티/참조 테이블 → UnifiedSelect (소스: entity) + // 엔티티/참조 테이블 → V2Select (소스: entity) entity: { - componentType: "unified-select", + componentType: "v2-select", config: { mode: "dropdown", source: "entity", searchable: true }, }, - // 카테고리 → UnifiedSelect (소스: category) + // 카테고리 → V2Select (소스: category) category: { - componentType: "unified-select", + componentType: "v2-select", config: { mode: "dropdown", source: "category" }, }, - // 파일/이미지 → UnifiedMedia + // 파일/이미지 → V2Media file: { - componentType: "unified-media", + componentType: "v2-media", config: { type: "file", multiple: false }, }, image: { - componentType: "unified-media", + componentType: "v2-media", config: { type: "image", showPreview: true }, }, img: { - componentType: "unified-media", + componentType: "v2-media", config: { type: "image", showPreview: true }, }, - // 버튼은 Unified 컴포넌트에서 제외 (기존 버튼 시스템 사용) + // 버튼은 V2 컴포넌트에서 제외 (기존 버튼 시스템 사용) button: { componentType: "button-primary", // 레거시 유지 config: {}, }, - // 라벨/텍스트 표시 → UnifiedInput (readonly) + // 라벨/텍스트 표시 → V2Input (readonly) label: { - componentType: "unified-input", + componentType: "v2-input", config: { inputType: "text", readonly: true }, }, }; @@ -138,55 +138,55 @@ export const WEB_TYPE_UNIFIED_MAPPING: Record = * 레거시 매핑 테이블 (하위 호환성) */ export const WEB_TYPE_COMPONENT_MAPPING: Record = { - text: "unified-input", - email: "unified-input", - password: "unified-input", - tel: "unified-input", - url: "unified-input", - number: "unified-input", - decimal: "unified-input", - textarea: "unified-input", - date: "unified-date", - datetime: "unified-date", - time: "unified-date", - select: "unified-select", - dropdown: "unified-select", - checkbox: "unified-select", - radio: "unified-select", - boolean: "unified-select", - code: "unified-select", - entity: "unified-select", - category: "unified-select", - file: "unified-media", - image: "unified-media", - img: "unified-media", + text: "v2-input", + email: "v2-input", + password: "v2-input", + tel: "v2-input", + url: "v2-input", + number: "v2-input", + decimal: "v2-input", + textarea: "v2-input", + date: "v2-date", + datetime: "v2-date", + time: "v2-date", + select: "v2-select", + dropdown: "v2-select", + checkbox: "v2-select", + radio: "v2-select", + boolean: "v2-select", + code: "v2-select", + entity: "v2-select", + category: "v2-select", + file: "v2-media", + image: "v2-media", + img: "v2-media", button: "button-primary", - label: "unified-input", + label: "v2-input", }; /** - * 웹타입을 Unified 컴포넌트 ID로 변환 + * 웹타입을 V2 컴포넌트 ID로 변환 */ export function getComponentIdFromWebType(webType: string): string { - const mapping = WEB_TYPE_UNIFIED_MAPPING[webType]; + const mapping = WEB_TYPE_V2_MAPPING[webType]; if (!mapping) { - console.warn(`웹타입 "${webType}"에 대한 Unified 매핑을 찾을 수 없습니다. 기본값 'unified-input' 사용`); - return "unified-input"; + console.warn(`웹타입 "${webType}"에 대한 V2 매핑을 찾을 수 없습니다. 기본값 'v2-input' 사용`); + return "v2-input"; } - console.log(`웹타입 "${webType}" → Unified 컴포넌트 "${mapping.componentType}" 매핑`); + console.log(`웹타입 "${webType}" → V2 컴포넌트 "${mapping.componentType}" 매핑`); return mapping.componentType; } /** - * 웹타입에 대한 Unified 컴포넌트 기본 설정 가져오기 + * 웹타입에 대한 V2 컴포넌트 기본 설정 가져오기 */ -export function getUnifiedConfigFromWebType(webType: string): Record { - const mapping = WEB_TYPE_UNIFIED_MAPPING[webType]; +export function getV2ConfigFromWebType(webType: string): Record { + const mapping = WEB_TYPE_V2_MAPPING[webType]; if (!mapping) { - console.warn(`웹타입 "${webType}"에 대한 Unified 설정을 찾을 수 없습니다. 기본 설정 사용`); + console.warn(`웹타입 "${webType}"에 대한 V2 설정을 찾을 수 없습니다. 기본 설정 사용`); return { inputType: "text" }; } @@ -194,14 +194,14 @@ export function getUnifiedConfigFromWebType(webType: string): Record } { - const mapping = getUnifiedMappingFromWebType(column.widgetType); + const mapping = getV2MappingFromWebType(column.widgetType); // detailSettings 파싱 (문자열이면 JSON 파싱) let parsedDetailSettings: Record = {}; @@ -331,21 +331,21 @@ export function getWebTypeFromComponentId(componentId: string): string { * 지원되는 모든 웹타입 목록 조회 */ export function getSupportedWebTypes(): string[] { - return Object.keys(WEB_TYPE_UNIFIED_MAPPING); + return Object.keys(WEB_TYPE_V2_MAPPING); } /** - * 지원되는 모든 Unified 컴포넌트 ID 목록 조회 + * 지원되는 모든 V2 컴포넌트 ID 목록 조회 */ export function getSupportedComponentIds(): string[] { - return [...new Set(Object.values(WEB_TYPE_UNIFIED_MAPPING).map((m) => m.componentType))]; + return [...new Set(Object.values(WEB_TYPE_V2_MAPPING).map((m) => m.componentType))]; } /** * 매핑 정보 조회 */ export function getWebTypeMappings(): WebTypeMapping[] { - return Object.entries(WEB_TYPE_UNIFIED_MAPPING).map(([webType, mapping]) => ({ + return Object.entries(WEB_TYPE_V2_MAPPING).map(([webType, mapping]) => ({ webType, componentId: mapping.componentType, description: `${webType} → ${mapping.componentType}`, diff --git a/frontend/test-scenarios/api-integration-tests.ts b/frontend/test-scenarios/api-integration-tests.ts index 7491e65f..403f2484 100644 --- a/frontend/test-scenarios/api-integration-tests.ts +++ b/frontend/test-scenarios/api-integration-tests.ts @@ -11,7 +11,7 @@ import { ScreenDefinition, LayoutData, TableInfo, - UnifiedColumnInfo, + V2ColumnInfo, ColumnTypeInfo, ButtonActionType, WebType, @@ -90,7 +90,7 @@ export class APIIntegrationTestSuite { const columns = response.data as ColumnTypeInfo[]; // 백엔드 타입을 프론트엔드 통합 타입으로 변환 테스트 - const unifiedColumns: UnifiedColumnInfo[] = columns.map((col) => ({ + const v2Columns: V2ColumnInfo[] = columns.map((col) => ({ columnName: col.columnName, displayName: col.displayName, dataType: col.dataType, @@ -107,26 +107,26 @@ export class APIIntegrationTestSuite { })); // 변환 검증 - unifiedColumns.forEach((unifiedCol, index) => { + v2Columns.forEach((v2Col, index) => { const originalCol = columns[index]; // WebType 변환 검증 - console.assert(isWebType(unifiedCol.webType), `컬럼 ${unifiedCol.columnName}: WebType 변환 실패`); + console.assert(isWebType(v2Col.webType), `컬럼 ${v2Col.columnName}: WebType 변환 실패`); // Y/N → boolean 변환 검증 console.assert( - typeof unifiedCol.isNullable === "boolean", - `컬럼 ${unifiedCol.columnName}: isNullable boolean 변환 실패`, + typeof v2Col.isNullable === "boolean", + `컬럼 ${v2Col.columnName}: isNullable boolean 변환 실패`, ); // JSON 파싱 검증 console.assert( - typeof unifiedCol.detailSettings === "object", - `컬럼 ${unifiedCol.columnName}: detailSettings 객체 변환 실패`, + typeof v2Col.detailSettings === "object", + `컬럼 ${v2Col.columnName}: detailSettings 객체 변환 실패`, ); }); - console.log(`✅ 컬럼 타입 API: ${unifiedColumns.length}개 컬럼 변환 완료`); + console.log(`✅ 컬럼 타입 API: ${v2Columns.length}개 컬럼 변환 완료`); return true; } } catch (error) { diff --git a/frontend/test-scenarios/type-safety-tests.ts b/frontend/test-scenarios/type-safety-tests.ts index 76c96a87..d8ccf389 100644 --- a/frontend/test-scenarios/type-safety-tests.ts +++ b/frontend/test-scenarios/type-safety-tests.ts @@ -27,7 +27,7 @@ import { booleanToYN, // 테이블 관련 - UnifiedColumnInfo, + V2ColumnInfo, ColumnTypeInfo, // 제어 관련 @@ -339,8 +339,8 @@ export class TypeSafetyTestSuite { maxLength: 100, }; - // 프론트엔드 통합 컬럼 정보로 변환 (UnifiedColumnInfo) - const unifiedColumnInfo: UnifiedColumnInfo = { + // 프론트엔드 통합 컬럼 정보로 변환 (V2ColumnInfo) + const v2ColumnInfo: V2ColumnInfo = { columnName: backendColumnInfo.columnName, displayName: backendColumnInfo.displayName, dataType: backendColumnInfo.dataType, @@ -357,12 +357,12 @@ export class TypeSafetyTestSuite { }; // 검증 - console.assert(isWebType(unifiedColumnInfo.webType), "WebType 변환 실패"); - console.assert(typeof unifiedColumnInfo.isNullable === "boolean", "isNullable 타입 변환 실패"); - console.assert(typeof unifiedColumnInfo.detailSettings === "object", "detailSettings JSON 파싱 실패"); + console.assert(isWebType(v2ColumnInfo.webType), "WebType 변환 실패"); + console.assert(typeof v2ColumnInfo.isNullable === "boolean", "isNullable 타입 변환 실패"); + console.assert(typeof v2ColumnInfo.detailSettings === "object", "detailSettings JSON 파싱 실패"); console.log("✅ 테이블 컬럼 타입 호환성 테스트 통과"); - console.log("변환된 컬럼 정보:", unifiedColumnInfo); + console.log("변환된 컬럼 정보:", v2ColumnInfo); } /** diff --git a/frontend/types/component.ts b/frontend/types/component.ts index 7408921b..306066c3 100644 --- a/frontend/types/component.ts +++ b/frontend/types/component.ts @@ -27,7 +27,7 @@ export enum ComponentCategory { SYSTEM = "system", // 시스템 컴포넌트 (에러 바운더리 등) ADMIN = "admin", // 관리자 전용 컴포넌트 CUSTOM = "custom", // 커스텀 컴포넌트 - UNIFIED = "unified", // 통합 컴포넌트 (새로운 Unified 컴포넌트 시스템) + V2 = "v2", // 통합 컴포넌트 (새로운 V2 컴포넌트 시스템) } /** @@ -369,7 +369,7 @@ export const COMPONENT_CATEGORIES_INFO = { description: "사용자 정의 컴포넌트", color: "#059669", }, - [ComponentCategory.UNIFIED]: { + [ComponentCategory.V2]: { name: "통합", description: "새로운 통합 컴포넌트 시스템", color: "#2563eb", diff --git a/frontend/types/control-management.ts b/frontend/types/control-management.ts index fc77f179..a1cc8130 100644 --- a/frontend/types/control-management.ts +++ b/frontend/types/control-management.ts @@ -11,7 +11,7 @@ import { ActiveStatus, TimestampFields, BaseApiResponse, -} from "./unified-core"; +} from "./v2-core"; // ===== 버튼 제어 관련 ===== diff --git a/frontend/types/index.ts b/frontend/types/index.ts index f296221c..39af48e3 100644 --- a/frontend/types/index.ts +++ b/frontend/types/index.ts @@ -6,7 +6,7 @@ */ // ===== 핵심 공통 타입들 ===== -export * from "./unified-core"; +export * from "./v2-core"; // ===== 시스템별 전용 타입들 ===== export * from "./screen-management"; @@ -15,7 +15,7 @@ export * from "./table-management"; // ===== 기존 호환성을 위한 re-export ===== -// unified-core에서 제공하는 주요 타입들을 직접 export +// v2-core에서 제공하는 주요 타입들을 직접 export export type { // 핵심 타입들 WebType, @@ -41,7 +41,7 @@ export type { // 이벤트 타입들 WebTypeEvent, ComponentEvent, -} from "./unified-core"; +} from "./v2-core"; // screen-management에서 제공하는 주요 타입들 export type { @@ -124,7 +124,7 @@ export type { export type { // 테이블 정보 TableInfo, - UnifiedColumnInfo, + V2ColumnInfo, ColumnTypeInfo, ColumnSettings, @@ -159,8 +159,8 @@ export type { // ===== 타입 가드 함수들 통합 export ===== -// unified-core 타입 가드들 -export { isWebType, isButtonActionType, isComponentType, ynToBoolean, booleanToYN } from "./unified-core"; +// v2-core 타입 가드들 +export { isWebType, isButtonActionType, isComponentType, ynToBoolean, booleanToYN } from "./v2-core"; // screen-management 타입 가드들 export { @@ -195,8 +195,8 @@ export { isRequiredColumn, isSystemColumn, mapWebTypeStandardToDefinition, - mapColumnTypeInfoToUnified, - mapUnifiedToColumnTypeInfo, + mapColumnTypeInfoToV2, + mapV2ToColumnTypeInfo, } from "./table-management"; // ===== 상수들 통합 export ===== @@ -207,12 +207,12 @@ export { WEB_TYPE_OPTIONS } from "./table-management"; // ===== 타입 별칭 (기존 호환성) ===== /** - * @deprecated screen.ts에서 이전하세요. unified-core.ts의 WebType을 사용하세요. + * @deprecated screen.ts에서 이전하세요. v2-core.ts의 WebType을 사용하세요. */ export type LegacyWebType = WebType; /** - * @deprecated screen.ts에서 이전하세요. unified-core.ts의 ButtonActionType을 사용하세요. + * @deprecated screen.ts에서 이전하세요. v2-core.ts의 ButtonActionType을 사용하세요. */ export type LegacyButtonActionType = ButtonActionType; diff --git a/frontend/types/input-type-mapping.ts b/frontend/types/input-type-mapping.ts index 4bddfe5f..fb2fd5d9 100644 --- a/frontend/types/input-type-mapping.ts +++ b/frontend/types/input-type-mapping.ts @@ -5,7 +5,7 @@ * 화면 관리에서 선택 가능한 세부 타입들을 정의합니다. */ -import { WebType } from "./unified-core"; +import { WebType } from "./v2-core"; /** * 핵심 입력 타입 diff --git a/frontend/types/screen-management.ts b/frontend/types/screen-management.ts index aefc48c4..67e8a934 100644 --- a/frontend/types/screen-management.ts +++ b/frontend/types/screen-management.ts @@ -16,7 +16,7 @@ import { CompanyCode, ActiveStatus, isWebType, -} from "./unified-core"; +} from "./v2-core"; import { ColumnSpanPreset } from "@/lib/constants/columnSpans"; import { ResponsiveComponentConfig } from "./responsive"; diff --git a/frontend/types/screen.ts b/frontend/types/screen.ts index 20347005..7888069b 100644 --- a/frontend/types/screen.ts +++ b/frontend/types/screen.ts @@ -5,7 +5,7 @@ * 새로운 코드에서는 다음을 사용해주세요: * - import { ... } from "@/types" (통합 타입 시스템) * - import { ... } from "@/types/screen-management" (화면관리 전용) - * - import { ... } from "@/types/unified-core" (핵심 공통 타입) + * - import { ... } from "@/types/v2-core" (핵심 공통 타입) */ // 🎯 새로운 통합 타입 시스템에서 re-export @@ -13,7 +13,7 @@ export * from "./index"; // 🔄 기존 호환성을 위한 타입 별칭들 export type { - // 핵심 타입들 (unified-core에서) + // 핵심 타입들 (v2-core에서) WebType, ButtonActionType, ComponentType, @@ -63,7 +63,7 @@ export type { // 테이블 정보 (table-management에서) TableInfo, - UnifiedColumnInfo as ColumnInfo, + V2ColumnInfo as ColumnInfo, // API 응답들 PaginatedResponse, diff --git a/frontend/types/table-management.ts b/frontend/types/table-management.ts index bd2cac09..665fa5c9 100644 --- a/frontend/types/table-management.ts +++ b/frontend/types/table-management.ts @@ -12,7 +12,7 @@ import { BaseApiResponse, PaginatedResponse, ConditionOperator, -} from "./unified-core"; +} from "./v2-core"; // ===== 기본 테이블 정보 ===== @@ -33,7 +33,7 @@ export interface TableInfo { /** * 통합된 컬럼 정보 (프론트엔드/백엔드 호환) */ -export interface UnifiedColumnInfo { +export interface V2ColumnInfo { // 기본 정보 tableName: string; columnName: string; @@ -317,7 +317,7 @@ export interface TableListResponse extends BaseApiResponse {} /** * 컬럼 목록 응답 */ -export interface ColumnListResponse extends BaseApiResponse {} +export interface ColumnListResponse extends BaseApiResponse {} /** * 컬럼 타입 정보 응답 (백엔드 호환) @@ -396,9 +396,9 @@ export const mapWebTypeStandardToDefinition = (standard: WebTypeStandard): WebTy }); /** - * ColumnTypeInfo를 UnifiedColumnInfo로 변환 + * ColumnTypeInfo를 V2ColumnInfo로 변환 */ -export const mapColumnTypeInfoToUnified = (columnInfo: ColumnTypeInfo): UnifiedColumnInfo => ({ +export const mapColumnTypeInfoToV2 = (columnInfo: ColumnTypeInfo): V2ColumnInfo => ({ tableName: columnInfo.tableName || "", columnName: columnInfo.columnName, displayName: columnInfo.displayName, @@ -424,31 +424,31 @@ export const mapColumnTypeInfoToUnified = (columnInfo: ColumnTypeInfo): UnifiedC }); /** - * UnifiedColumnInfo를 ColumnTypeInfo로 변환 + * V2ColumnInfo를 ColumnTypeInfo로 변환 */ -export const mapUnifiedToColumnTypeInfo = (unified: UnifiedColumnInfo): ColumnTypeInfo => ({ - tableName: unified.tableName, - columnName: unified.columnName, - displayName: unified.displayName, - dataType: unified.dataType, - dbType: unified.dbType, - webType: unified.webType, - inputType: unified.inputType, - detailSettings: unified.detailSettings ? JSON.stringify(unified.detailSettings) : "{}", - description: unified.description || "", - isNullable: unified.isNullable ? "Y" : "N", - isPrimaryKey: unified.isPrimaryKey, - defaultValue: unified.defaultValue, - maxLength: unified.maxLength, - numericPrecision: unified.numericPrecision, - numericScale: unified.numericScale, - isVisible: unified.isVisible, - displayOrder: unified.displayOrder, - codeCategory: unified.codeCategory, - codeValue: unified.codeValue, - referenceTable: unified.referenceTable, - referenceColumn: unified.referenceColumn, - displayColumn: unified.displayColumn, +export const mapV2ToColumnTypeInfo = (v2: V2ColumnInfo): ColumnTypeInfo => ({ + tableName: v2.tableName, + columnName: v2.columnName, + displayName: v2.displayName, + dataType: v2.dataType, + dbType: v2.dbType, + webType: v2.webType, + inputType: v2.inputType, + detailSettings: v2.detailSettings ? JSON.stringify(v2.detailSettings) : "{}", + description: v2.description || "", + isNullable: v2.isNullable ? "Y" : "N", + isPrimaryKey: v2.isPrimaryKey, + defaultValue: v2.defaultValue, + maxLength: v2.maxLength, + numericPrecision: v2.numericPrecision, + numericScale: v2.numericScale, + isVisible: v2.isVisible, + displayOrder: v2.displayOrder, + codeCategory: v2.codeCategory, + codeValue: v2.codeValue, + referenceTable: v2.referenceTable, + referenceColumn: v2.referenceColumn, + displayColumn: v2.displayColumn, }); // ===== 타입 가드 함수들 ===== @@ -484,7 +484,7 @@ export const isSelectWebType = (webType: string): boolean => { /** * 컬럼이 필수 필드인지 확인 */ -export const isRequiredColumn = (column: UnifiedColumnInfo): boolean => { +export const isRequiredColumn = (column: V2ColumnInfo): boolean => { return !column.isNullable || column.isPrimaryKey; }; diff --git a/frontend/types/unified-components.ts b/frontend/types/unified-components.ts deleted file mode 100644 index 19b0ff03..00000000 --- a/frontend/types/unified-components.ts +++ /dev/null @@ -1,512 +0,0 @@ -/** - * Unified 컴포넌트 타입 정의 - * - * 10개의 통합 컴포넌트 시스템을 위한 타입 정의 - * - UnifiedInput - * - UnifiedSelect - * - UnifiedDate - * - UnifiedText - * - UnifiedMedia - * - UnifiedList - * - UnifiedLayout - * - UnifiedGroup - * - UnifiedBiz - * - UnifiedHierarchy - */ - -import { Position, Size, CommonStyle, ValidationRule } from "./unified-core"; - -// ===== 공통 타입 ===== - -/** - * Unified 컴포넌트 타입 - */ -export type UnifiedComponentType = - | "UnifiedInput" - | "UnifiedSelect" - | "UnifiedDate" - | "UnifiedText" - | "UnifiedMedia" - | "UnifiedList" - | "UnifiedLayout" - | "UnifiedGroup" - | "UnifiedBiz" - | "UnifiedHierarchy"; - -/** - * 조건부 렌더링 설정 - */ -export interface ConditionalConfig { - enabled: boolean; - field: string; // 참조 필드 - operator: "=" | "!=" | ">" | "<" | "in" | "notIn" | "isEmpty" | "isNotEmpty"; - value: unknown; - action: "show" | "hide" | "disable" | "enable"; -} - -/** - * 자동 입력 설정 - */ -export interface AutoFillConfig { - enabled: boolean; - sourceTable: string; - filterColumn: string; - userField: "companyCode" | "userId" | "deptCode"; - displayColumn: string; -} - -/** - * 연쇄 관계 설정 - */ -export interface CascadingConfig { - parentField: string; - filterColumn: string; - clearOnChange?: boolean; -} - -/** - * 상호 배제 설정 - */ -export interface MutualExclusionConfig { - enabled: boolean; - targetField: string; - type: "exclusive" | "inclusive"; -} - -/** - * 공통 Unified 컴포넌트 속성 - */ -export interface UnifiedBaseProps { - id: string; - label?: string; - required?: boolean; - readonly?: boolean; - disabled?: boolean; - // 데이터 바인딩 - tableName?: string; - columnName?: string; - // 위치 및 크기 - position?: Position; - size?: Size; - // 스타일 - style?: CommonStyle; - // 조건부 및 자동화 - conditional?: ConditionalConfig; - autoFill?: AutoFillConfig; - // 유효성 검사 - validation?: ValidationRule[]; -} - -// ===== UnifiedInput ===== - -export type UnifiedInputType = "text" | "number" | "password" | "slider" | "color" | "button"; -export type UnifiedInputFormat = "none" | "email" | "tel" | "url" | "currency" | "biz_no"; - -export interface UnifiedInputConfig { - type: UnifiedInputType; - format?: UnifiedInputFormat; - mask?: string; - placeholder?: string; - // 숫자 전용 - min?: number; - max?: number; - step?: number; - // 버튼 전용 - buttonText?: string; - buttonVariant?: "default" | "destructive" | "outline" | "secondary" | "ghost"; - onClick?: () => void; -} - -export interface UnifiedInputProps extends UnifiedBaseProps { - unifiedType: "UnifiedInput"; - config: UnifiedInputConfig; - value?: string | number; - onChange?: (value: string | number) => void; -} - -// ===== UnifiedSelect ===== - -export type UnifiedSelectMode = "dropdown" | "radio" | "check" | "tag" | "toggle" | "swap"; -export type UnifiedSelectSource = "static" | "code" | "db" | "api" | "entity" | "category"; - -export interface SelectOption { - value: string; - label: string; -} - -export interface UnifiedSelectConfig { - mode: UnifiedSelectMode; - source: UnifiedSelectSource; - // 정적 옵션 (source: static) - options?: SelectOption[]; - // 코드 그룹 (source: code) - codeGroup?: string; - // DB 연결 (source: db) - table?: string; - valueColumn?: string; - labelColumn?: string; - filters?: Array<{ column: string; operator: string; value: unknown }>; - // 엔티티 연결 (source: entity) - entityTable?: string; - entityValueField?: string; - entityLabelField?: string; - entityValueColumn?: string; // alias for entityValueField - entityLabelColumn?: string; // alias for entityLabelField - // API 연결 (source: api) - apiEndpoint?: string; - // 카테고리 연결 (source: category) - 레거시, code로 자동 변환됨 - categoryTable?: string; - categoryColumn?: string; - // 공통 옵션 - searchable?: boolean; - multiple?: boolean; - maxSelect?: number; - allowClear?: boolean; - // 연쇄 관계 - cascading?: CascadingConfig; - // 상호 배제 - mutualExclusion?: MutualExclusionConfig; - // 계층 코드 연쇄 선택 (source: code일 때 계층 구조 사용) - hierarchical?: boolean; // 계층 구조 사용 여부 - parentField?: string; // 부모 값을 참조할 필드 (다른 컴포넌트의 columnName) -} - -export interface UnifiedSelectProps extends UnifiedBaseProps { - unifiedType: "UnifiedSelect"; - config: UnifiedSelectConfig; - value?: string | string[]; - onChange?: (value: string | string[]) => void; -} - -// ===== UnifiedDate ===== - -export type UnifiedDateType = "date" | "time" | "datetime"; - -export interface UnifiedDateConfig { - type: UnifiedDateType; - format?: string; - range?: boolean; - minDate?: string; - maxDate?: string; - showToday?: boolean; -} - -export interface UnifiedDateProps extends UnifiedBaseProps { - unifiedType: "UnifiedDate"; - config: UnifiedDateConfig; - value?: string | [string, string]; // 범위 선택 시 튜플 - onChange?: (value: string | [string, string]) => void; -} - -// ===== UnifiedText ===== - -export type UnifiedTextMode = "simple" | "rich" | "code" | "markdown"; - -export interface UnifiedTextConfig { - mode: UnifiedTextMode; - rows?: number; - maxLength?: number; - placeholder?: string; - resize?: "none" | "vertical" | "horizontal" | "both"; -} - -export interface UnifiedTextProps extends UnifiedBaseProps { - unifiedType: "UnifiedText"; - config: UnifiedTextConfig; - value?: string; - onChange?: (value: string) => void; -} - -// ===== UnifiedMedia ===== - -export type UnifiedMediaType = "file" | "image" | "video" | "audio"; - -export interface UnifiedMediaConfig { - type: UnifiedMediaType; - multiple?: boolean; - accept?: string; - maxSize?: number; - preview?: boolean; - uploadEndpoint?: string; -} - -export interface UnifiedMediaProps extends UnifiedBaseProps { - unifiedType: "UnifiedMedia"; - config: UnifiedMediaConfig; - value?: string | string[]; // 파일 URL 또는 배열 - onChange?: (value: string | string[]) => void; -} - -// ===== UnifiedList ===== - -export type UnifiedListViewMode = "table" | "card" | "kanban" | "list"; - -export interface ListColumn { - field: string; - header: string; - width?: number; - sortable?: boolean; - filterable?: boolean; - editable?: boolean; - format?: string; -} - -export interface UnifiedListCardConfig { - titleColumn?: string; - subtitleColumn?: string; - descriptionColumn?: string; - imageColumn?: string; - cardsPerRow?: number; - cardSpacing?: number; - showActions?: boolean; -} - -export interface UnifiedListConfig { - viewMode: UnifiedListViewMode; - editable?: boolean; - searchable?: boolean; - pageable?: boolean; - pageSize?: number; - columns?: ListColumn[]; - modal?: boolean; - cardConfig?: UnifiedListCardConfig; - // 데이터 소스 - dataSource?: { - table?: string; - api?: string; - filters?: Array<{ column: string; operator: string; value: unknown }>; - }; -} - -export interface UnifiedListProps extends UnifiedBaseProps { - unifiedType: "UnifiedList"; - config: UnifiedListConfig; - data?: Record[]; - selectedRows?: Record[]; - onRowSelect?: (rows: Record[]) => void; - onRowClick?: (row: Record) => void; -} - -// ===== UnifiedLayout ===== - -export type UnifiedLayoutType = "grid" | "split" | "flex" | "divider" | "screen-embed"; - -export interface UnifiedLayoutConfig { - type: UnifiedLayoutType; - columns?: number; // 12컬럼 시스템에서 실제 표시할 컬럼 수 (1-12) - gap?: string; - splitRatio?: number[]; - direction?: "horizontal" | "vertical"; - use12Column?: boolean; // 12컬럼 시스템 사용 여부 (기본 true) - // screen-embed 전용 - screenId?: number; -} - -export interface UnifiedLayoutProps extends UnifiedBaseProps { - unifiedType: "UnifiedLayout"; - config: UnifiedLayoutConfig; - children?: React.ReactNode; -} - -// ===== UnifiedGroup ===== - -export type UnifiedGroupType = "tabs" | "accordion" | "section" | "card-section" | "modal" | "form-modal"; - -export interface TabItem { - id: string; - title: string; - content?: React.ReactNode; -} - -export interface UnifiedGroupConfig { - type: UnifiedGroupType; - title?: string; - collapsible?: boolean; - defaultExpanded?: boolean; - // 탭 전용 - tabs?: TabItem[]; - activeTab?: string; - // 모달 전용 - modalSize?: "sm" | "md" | "lg" | "xl"; -} - -export interface UnifiedGroupProps extends UnifiedBaseProps { - unifiedType: "UnifiedGroup"; - config: UnifiedGroupConfig; - children?: React.ReactNode; - open?: boolean; - onOpenChange?: (open: boolean) => void; -} - -// ===== UnifiedBiz ===== - -export type UnifiedBizType = "flow" | "rack" | "map" | "numbering" | "category" | "mapping" | "related-buttons"; - -export interface UnifiedBizConfig { - type: UnifiedBizType; - // 각 타입별 설정은 제네릭하게 처리 - config?: Record; -} - -export interface UnifiedBizProps extends UnifiedBaseProps { - unifiedType: "UnifiedBiz"; - config: UnifiedBizConfig; -} - -// ===== UnifiedHierarchy ===== - -export type UnifiedHierarchyType = "tree" | "org" | "bom" | "cascading"; -export type UnifiedHierarchyViewMode = "tree" | "table" | "indent" | "dropdown"; - -export interface HierarchyNode { - id: string; - parentId?: string; - label: string; - children?: HierarchyNode[]; - data?: Record; -} - -export interface UnifiedHierarchyConfig { - type: UnifiedHierarchyType; - viewMode: UnifiedHierarchyViewMode; - source?: string; // 계층 그룹 코드 - editable?: boolean; - draggable?: boolean; - showQty?: boolean; // BOM 전용 - maxLevel?: number; -} - -export interface UnifiedHierarchyProps extends UnifiedBaseProps { - unifiedType: "UnifiedHierarchy"; - config: UnifiedHierarchyConfig; - data?: HierarchyNode[]; - selectedNode?: HierarchyNode; - onNodeSelect?: (node: HierarchyNode) => void; - onNodeMove?: (nodeId: string, newParentId: string) => void; -} - -// ===== 통합 Props 유니온 타입 ===== - -export type UnifiedComponentProps = - | UnifiedInputProps - | UnifiedSelectProps - | UnifiedDateProps - | UnifiedTextProps - | UnifiedMediaProps - | UnifiedListProps - | UnifiedLayoutProps - | UnifiedGroupProps - | UnifiedBizProps - | UnifiedHierarchyProps; - -// ===== 타입 가드 ===== - -export function isUnifiedInput(props: UnifiedComponentProps): props is UnifiedInputProps { - return props.unifiedType === "UnifiedInput"; -} - -export function isUnifiedSelect(props: UnifiedComponentProps): props is UnifiedSelectProps { - return props.unifiedType === "UnifiedSelect"; -} - -export function isUnifiedDate(props: UnifiedComponentProps): props is UnifiedDateProps { - return props.unifiedType === "UnifiedDate"; -} - -export function isUnifiedText(props: UnifiedComponentProps): props is UnifiedTextProps { - return props.unifiedType === "UnifiedText"; -} - -export function isUnifiedMedia(props: UnifiedComponentProps): props is UnifiedMediaProps { - return props.unifiedType === "UnifiedMedia"; -} - -export function isUnifiedList(props: UnifiedComponentProps): props is UnifiedListProps { - return props.unifiedType === "UnifiedList"; -} - -export function isUnifiedLayout(props: UnifiedComponentProps): props is UnifiedLayoutProps { - return props.unifiedType === "UnifiedLayout"; -} - -export function isUnifiedGroup(props: UnifiedComponentProps): props is UnifiedGroupProps { - return props.unifiedType === "UnifiedGroup"; -} - -export function isUnifiedBiz(props: UnifiedComponentProps): props is UnifiedBizProps { - return props.unifiedType === "UnifiedBiz"; -} - -export function isUnifiedHierarchy(props: UnifiedComponentProps): props is UnifiedHierarchyProps { - return props.unifiedType === "UnifiedHierarchy"; -} - -// ===== JSON Schema 타입 ===== - -export interface JSONSchemaProperty { - type: "string" | "number" | "boolean" | "array" | "object"; - title?: string; - description?: string; - enum?: string[]; - default?: unknown; - items?: JSONSchemaProperty; - properties?: Record; - required?: string[]; -} - -export interface UnifiedConfigSchema { - type: "object"; - properties: Record; - required?: string[]; -} - -// ===== 레거시 컴포넌트 → Unified 컴포넌트 매핑 ===== - -export const LEGACY_TO_UNIFIED_MAP: Record = { - // Input 계열 - "text-input": "UnifiedInput", - "number-input": "UnifiedInput", - "password-input": "UnifiedInput", - - // Select 계열 - "select-basic": "UnifiedSelect", - "radio-basic": "UnifiedSelect", - "checkbox-basic": "UnifiedSelect", - "entity-search-input": "UnifiedSelect", - "autocomplete-search-input": "UnifiedSelect", - - // Date 계열 - "date-input": "UnifiedDate", - - // Text 계열 - "textarea-basic": "UnifiedText", - - // Media 계열 - "file-upload": "UnifiedMedia", - "image-widget": "UnifiedMedia", - - // List 계열 - "table-list": "UnifiedList", - "table-search-widget": "UnifiedList", - "modal-repeater-table": "UnifiedList", - "repeater-field-group": "UnifiedList", - "card-display": "UnifiedList", - - // Layout 계열 - "split-panel-layout": "UnifiedLayout", - "screen-split-panel": "UnifiedLayout", - - // Group 계열 - "tabs-widget": "UnifiedGroup", - "section-paper": "UnifiedGroup", - "section-card": "UnifiedGroup", - "universal-form-modal": "UnifiedGroup", - - // Biz 계열 - "category-manager": "UnifiedBiz", - "numbering-rule": "UnifiedBiz", - "flow-widget": "UnifiedBiz", - - // Button (Input의 버튼 모드) - "button-primary": "UnifiedInput", -}; diff --git a/frontend/types/v2-components.ts b/frontend/types/v2-components.ts new file mode 100644 index 00000000..d985699d --- /dev/null +++ b/frontend/types/v2-components.ts @@ -0,0 +1,532 @@ +/** + * V2 컴포넌트 타입 정의 + * + * 10개의 통합 컴포넌트 시스템을 위한 타입 정의 + * - V2Input + * - V2Select + * - V2Date + * - V2Text + * - V2Media + * - V2List + * - V2Layout + * - V2Group + * - V2Biz + * - V2Hierarchy + */ + +import { Position, Size, CommonStyle, ValidationRule } from "./v2-core"; + +// ===== 공통 타입 ===== + +/** + * V2 컴포넌트 타입 + */ +export type V2ComponentType = + | "V2Input" + | "V2Select" + | "V2Date" + | "V2Text" + | "V2Media" + | "V2List" + | "V2Layout" + | "V2Group" + | "V2Biz" + | "V2Hierarchy"; + +/** + * 조건부 렌더링 설정 + */ +export interface ConditionalConfig { + enabled: boolean; + field: string; // 참조 필드 + operator: "=" | "!=" | ">" | "<" | "in" | "notIn" | "isEmpty" | "isNotEmpty"; + value: unknown; + action: "show" | "hide" | "disable" | "enable"; +} + +/** + * 자동 입력 설정 + */ +export interface AutoFillConfig { + enabled: boolean; + sourceTable: string; + filterColumn: string; + userField: "companyCode" | "userId" | "deptCode"; + displayColumn: string; +} + +/** + * 연쇄 관계 설정 + */ +export interface CascadingConfig { + parentField: string; + filterColumn: string; + clearOnChange?: boolean; +} + +/** + * 상호 배제 설정 + */ +export interface MutualExclusionConfig { + enabled: boolean; + targetField: string; + type: "exclusive" | "inclusive"; +} + +/** + * 공통 V2 컴포넌트 속성 + */ +export interface V2BaseProps { + id: string; + label?: string; + required?: boolean; + readonly?: boolean; + disabled?: boolean; + // 데이터 바인딩 + tableName?: string; + columnName?: string; + // 위치 및 크기 + position?: Position; + size?: Size; + // 스타일 + style?: CommonStyle; + // 조건부 및 자동화 + conditional?: ConditionalConfig; + autoFill?: AutoFillConfig; + // 유효성 검사 + validation?: ValidationRule[]; +} + +// ===== V2Input ===== + +export type V2InputType = "text" | "number" | "password" | "slider" | "color" | "button"; +export type V2InputFormat = "none" | "email" | "tel" | "url" | "currency" | "biz_no"; + +export interface V2InputConfig { + type: V2InputType; + inputType?: V2InputType; // type 별칭 + format?: V2InputFormat; + mask?: string; + placeholder?: string; + // 숫자 전용 + min?: number; + max?: number; + step?: number; + // 버튼 전용 + buttonText?: string; + buttonVariant?: "default" | "destructive" | "outline" | "secondary" | "ghost"; + onClick?: () => void; + // 테이블명 (채번용) + tableName?: string; +} + +export interface V2InputProps extends V2BaseProps { + v2Type: "V2Input"; + config: V2InputConfig; + value?: string | number; + onChange?: (value: string | number) => void; +} + +// ===== V2Select ===== + +export type V2SelectMode = "dropdown" | "radio" | "check" | "tag" | "toggle" | "swap"; +export type V2SelectSource = "static" | "code" | "db" | "api" | "entity" | "category"; + +export interface SelectOption { + value: string; + label: string; +} + +export interface V2SelectConfig { + mode: V2SelectMode; + source: V2SelectSource | "distinct" | "select"; // distinct/select 추가 (테이블 컬럼에서 자동 로드) + // 정적 옵션 (source: static) + options?: SelectOption[]; + // 코드 그룹 (source: code) + codeGroup?: string; + codeCategory?: string; // codeGroup 별칭 + // DB 연결 (source: db) + table?: string; + valueColumn?: string; + labelColumn?: string; + filters?: Array<{ column: string; operator: string; value: unknown }>; + // 엔티티 연결 (source: entity) + entityTable?: string; + entityValueField?: string; + entityLabelField?: string; + entityValueColumn?: string; // alias for entityValueField + entityLabelColumn?: string; // alias for entityLabelField + // API 연결 (source: api) + apiEndpoint?: string; + // 카테고리 연결 (source: category) - 레거시, code로 자동 변환됨 + categoryTable?: string; + categoryColumn?: string; + // 공통 옵션 + searchable?: boolean; + multiple?: boolean; + maxSelect?: number; + allowClear?: boolean; + // 연쇄 관계 + cascading?: CascadingConfig; + // 상호 배제 + mutualExclusion?: MutualExclusionConfig; + // 계층 코드 연쇄 선택 (source: code일 때 계층 구조 사용) + hierarchical?: boolean; // 계층 구조 사용 여부 + parentField?: string; // 부모 값을 참조할 필드 (다른 컴포넌트의 columnName) +} + +export interface V2SelectProps extends V2BaseProps { + v2Type: "V2Select"; + config: V2SelectConfig; + value?: string | string[]; + onChange?: (value: string | string[]) => void; +} + +// ===== V2Date ===== + +export type V2DateType = "date" | "time" | "datetime"; + +export interface V2DateConfig { + type: V2DateType; + format?: string; + range?: boolean; + minDate?: string; + maxDate?: string; + showToday?: boolean; +} + +export interface V2DateProps extends V2BaseProps { + v2Type: "V2Date"; + config: V2DateConfig; + value?: string | [string, string]; // 범위 선택 시 튜플 + onChange?: (value: string | [string, string]) => void; +} + +// ===== V2Text ===== + +export type V2TextMode = "simple" | "rich" | "code" | "markdown"; + +export interface V2TextConfig { + mode: V2TextMode; + rows?: number; + maxLength?: number; + placeholder?: string; + resize?: "none" | "vertical" | "horizontal" | "both"; +} + +export interface V2TextProps extends V2BaseProps { + v2Type: "V2Text"; + config: V2TextConfig; + value?: string; + onChange?: (value: string) => void; +} + +// ===== V2Media ===== + +export type V2MediaType = "file" | "image" | "video" | "audio"; + +export interface V2MediaConfig { + type: V2MediaType; + multiple?: boolean; + accept?: string; + maxSize?: number; + preview?: boolean; + uploadEndpoint?: string; +} + +export interface V2MediaProps extends V2BaseProps { + v2Type: "V2Media"; + config: V2MediaConfig; + value?: string | string[]; // 파일 URL 또는 배열 + onChange?: (value: string | string[]) => void; +} + +// ===== V2List ===== + +export type V2ListViewMode = "table" | "card" | "kanban" | "list"; + +export interface ListColumn { + field: string; + header: string; + width?: number; + sortable?: boolean; + filterable?: boolean; + editable?: boolean; + format?: string; +} + +export interface V2ListCardConfig { + titleColumn?: string; + subtitleColumn?: string; + descriptionColumn?: string; + imageColumn?: string; + cardsPerRow?: number; + cardSpacing?: number; + showActions?: boolean; +} + +export interface V2ListConfig { + viewMode: V2ListViewMode; + editable?: boolean; + searchable?: boolean; + pageable?: boolean; + pageSize?: number; + sortable?: boolean; + pagination?: boolean; + source?: "static" | "db" | "api"; // 데이터 소스 타입 + columns?: ListColumn[]; + modal?: boolean; + cardConfig?: V2ListCardConfig; + // 데이터 소스 + dataSource?: { + table?: string; + api?: string; + filters?: Array<{ column: string; operator: string; value: unknown }>; + }; +} + +export interface V2ListProps extends V2BaseProps { + v2Type: "V2List"; + config: V2ListConfig; + data?: Record[]; + selectedRows?: Record[]; + onRowSelect?: (rows: Record[]) => void; + onRowClick?: (row: Record) => void; +} + +// ===== V2Layout ===== + +export type V2LayoutType = "grid" | "split" | "flex" | "divider" | "screen-embed"; + +export interface V2LayoutConfig { + type: V2LayoutType; + columns?: number; // 12컬럼 시스템에서 실제 표시할 컬럼 수 (1-12) + gap?: string; + splitRatio?: number[]; + direction?: "horizontal" | "vertical"; + use12Column?: boolean; // 12컬럼 시스템 사용 여부 (기본 true) + // screen-embed 전용 + screenId?: number; +} + +export interface V2LayoutProps extends V2BaseProps { + v2Type: "V2Layout"; + config: V2LayoutConfig; + children?: React.ReactNode; +} + +// ===== V2Group ===== + +export type V2GroupType = "tabs" | "accordion" | "section" | "card-section" | "modal" | "form-modal"; + +export interface TabItem { + id: string; + title: string; + content?: React.ReactNode; +} + +export interface V2GroupConfig { + type: V2GroupType; + title?: string; + collapsible?: boolean; + defaultExpanded?: boolean; + defaultOpen?: boolean; // defaultExpanded 별칭 + // 탭 전용 + tabs?: TabItem[]; + activeTab?: string; + // 모달 전용 + modalSize?: "sm" | "md" | "lg" | "xl"; +} + +export interface V2GroupProps extends V2BaseProps { + v2Type: "V2Group"; + config: V2GroupConfig; + children?: React.ReactNode; + open?: boolean; + onOpenChange?: (open: boolean) => void; +} + +// ===== V2Biz ===== + +export type V2BizType = "flow" | "rack" | "map" | "numbering" | "category" | "mapping" | "related-buttons"; + +export interface V2BizConfig { + type: V2BizType; + // 각 타입별 설정은 제네릭하게 처리 + config?: Record; + // 플로우 전용 + flowConfig?: { + flowId?: number; + showProgress?: boolean; + }; +} + +export interface V2BizProps extends V2BaseProps { + v2Type: "V2Biz"; + config: V2BizConfig; +} + +// ===== V2Hierarchy ===== + +export type V2HierarchyType = "tree" | "org" | "bom" | "cascading"; +export type V2HierarchyViewMode = "tree" | "table" | "indent" | "dropdown"; + +export interface HierarchyNode { + id: string; + parentId?: string; + label: string; + children?: HierarchyNode[]; + data?: Record; +} + +export interface V2HierarchyConfig { + type: V2HierarchyType; + viewMode: V2HierarchyViewMode; + source?: string; // 계층 그룹 코드 + editable?: boolean; + draggable?: boolean; + showQty?: boolean; // BOM 전용 + maxLevel?: number; + // 데이터 소스 + dataSource?: { + table?: string; + idColumn?: string; + parentColumn?: string; + labelColumn?: string; + }; +} + +export interface V2HierarchyProps extends V2BaseProps { + v2Type: "V2Hierarchy"; + config: V2HierarchyConfig; + data?: HierarchyNode[]; + selectedNode?: HierarchyNode; + onNodeSelect?: (node: HierarchyNode) => void; + onNodeMove?: (nodeId: string, newParentId: string) => void; +} + +// ===== 통합 Props 유니온 타입 ===== + +export type V2ComponentProps = + | V2InputProps + | V2SelectProps + | V2DateProps + | V2TextProps + | V2MediaProps + | V2ListProps + | V2LayoutProps + | V2GroupProps + | V2BizProps + | V2HierarchyProps; + +// ===== 타입 가드 ===== + +export function isV2Input(props: V2ComponentProps): props is V2InputProps { + return props.v2Type === "V2Input"; +} + +export function isV2Select(props: V2ComponentProps): props is V2SelectProps { + return props.v2Type === "V2Select"; +} + +export function isV2Date(props: V2ComponentProps): props is V2DateProps { + return props.v2Type === "V2Date"; +} + +export function isV2Text(props: V2ComponentProps): props is V2TextProps { + return props.v2Type === "V2Text"; +} + +export function isV2Media(props: V2ComponentProps): props is V2MediaProps { + return props.v2Type === "V2Media"; +} + +export function isV2List(props: V2ComponentProps): props is V2ListProps { + return props.v2Type === "V2List"; +} + +export function isV2Layout(props: V2ComponentProps): props is V2LayoutProps { + return props.v2Type === "V2Layout"; +} + +export function isV2Group(props: V2ComponentProps): props is V2GroupProps { + return props.v2Type === "V2Group"; +} + +export function isV2Biz(props: V2ComponentProps): props is V2BizProps { + return props.v2Type === "V2Biz"; +} + +export function isV2Hierarchy(props: V2ComponentProps): props is V2HierarchyProps { + return props.v2Type === "V2Hierarchy"; +} + +// ===== JSON Schema 타입 ===== + +export interface JSONSchemaProperty { + type: "string" | "number" | "boolean" | "array" | "object"; + title?: string; + description?: string; + enum?: string[]; + default?: unknown; + items?: JSONSchemaProperty; + properties?: Record; + required?: string[]; +} + +export interface V2ConfigSchema { + type: "object"; + properties: Record; + required?: string[]; +} + +// ===== 레거시 컴포넌트 → V2 컴포넌트 매핑 ===== + +export const LEGACY_TO_V2_MAP: Record = { + // Input 계열 + "text-input": "V2Input", + "number-input": "V2Input", + "password-input": "V2Input", + + // Select 계열 + "select-basic": "V2Select", + "radio-basic": "V2Select", + "checkbox-basic": "V2Select", + "entity-search-input": "V2Select", + "autocomplete-search-input": "V2Select", + + // Date 계열 + "date-input": "V2Date", + + // Text 계열 + "textarea-basic": "V2Text", + + // Media 계열 + "file-upload": "V2Media", + "image-widget": "V2Media", + + // List 계열 + "table-list": "V2List", + "table-search-widget": "V2List", + "modal-repeater-table": "V2List", + "repeater-field-group": "V2List", + "card-display": "V2List", + + // Layout 계열 + "split-panel-layout": "V2Layout", + "screen-split-panel": "V2Layout", + + // Group 계열 + "tabs-widget": "V2Group", + "section-paper": "V2Group", + "section-card": "V2Group", + "universal-form-modal": "V2Group", + + // Biz 계열 + "category-manager": "V2Biz", + "numbering-rule": "V2Biz", + "flow-widget": "V2Biz", + + // Button (Input의 버튼 모드) + "button-primary": "V2Input", +}; diff --git a/frontend/types/unified-core.ts b/frontend/types/v2-core.ts similarity index 100% rename from frontend/types/unified-core.ts rename to frontend/types/v2-core.ts diff --git a/frontend/types/unified-form.ts b/frontend/types/v2-form.ts similarity index 96% rename from frontend/types/unified-form.ts rename to frontend/types/v2-form.ts index e3ecd13a..0b1d074d 100644 --- a/frontend/types/unified-form.ts +++ b/frontend/types/v2-form.ts @@ -1,10 +1,10 @@ /** * 통합 폼 시스템 타입 정의 * - * Unified 컴포넌트들과 레거시 컴포넌트들이 공유하는 폼 관련 타입 + * V2 컴포넌트들과 레거시 컴포넌트들이 공유하는 폼 관련 타입 */ -import { ValidationRule } from "./unified-core"; +import { ValidationRule } from "./v2-core"; // ===== 폼 상태 타입 ===== @@ -201,10 +201,10 @@ export interface ScreenDataTransferConfig { // ===== Context 타입 ===== /** - * 확장된 UnifiedFormContext 값 + * 확장된 V2FormContext 값 */ export interface ExtendedFormContextValue { - // === 기존 UnifiedFormContext 기능 === + // === 기존 V2FormContext 기능 === formData: Record; fieldStates: Record; @@ -301,7 +301,7 @@ export interface FormCompatibilityBridge { errors: FieldError[]; // 모드 - isUnifiedMode: boolean; // UnifiedFormContext 사용 여부 + isV2Mode: boolean; // V2FormContext 사용 여부 isLegacyMode: boolean; // 레거시 모드 여부 } diff --git a/frontend/types/unified-repeater.ts b/frontend/types/v2-repeater.ts similarity index 96% rename from frontend/types/unified-repeater.ts rename to frontend/types/v2-repeater.ts index 84385985..febacfb4 100644 --- a/frontend/types/unified-repeater.ts +++ b/frontend/types/v2-repeater.ts @@ -1,5 +1,5 @@ /** - * UnifiedRepeater 컴포넌트 타입 정의 + * V2Repeater 컴포넌트 타입 정의 * * 렌더링 모드: * - inline: 현재 테이블 컬럼 직접 입력 (simple-repeater-table) @@ -105,6 +105,7 @@ export interface RepeaterFeatureOptions { // 데이터 소스 설정 export interface RepeaterDataSource { // inline 모드: 현재 테이블 설정은 필요 없음 (컬럼만 선택) + tableName?: string; // 데이터 테이블명 (레거시 호환) // modal 모드: 소스 테이블 설정 sourceTable?: string; // 검색할 테이블 (엔티티 참조 테이블) @@ -135,7 +136,7 @@ export interface CalculationRule { } // 메인 설정 타입 -export interface UnifiedRepeaterConfig { +export interface V2RepeaterConfig { // 렌더링 모드 renderMode: RepeaterRenderMode; @@ -173,8 +174,8 @@ export interface UnifiedRepeaterConfig { } // 컴포넌트 Props -export interface UnifiedRepeaterProps { - config: UnifiedRepeaterConfig; +export interface V2RepeaterProps { + config: V2RepeaterConfig; parentId?: string | number; // 부모 레코드 ID data?: any[]; // 초기 데이터 (없으면 API로 로드) onDataChange?: (data: any[]) => void; @@ -183,7 +184,7 @@ export interface UnifiedRepeaterProps { } // 기본 설정값 -export const DEFAULT_REPEATER_CONFIG: UnifiedRepeaterConfig = { +export const DEFAULT_REPEATER_CONFIG: V2RepeaterConfig = { renderMode: "inline", dataSource: {}, columns: [], diff --git a/frontend/types/unified-web-types.ts b/frontend/types/v2-web-types.ts similarity index 100% rename from frontend/types/unified-web-types.ts rename to frontend/types/v2-web-types.ts