170 lines
35 KiB
JSON
170 lines
35 KiB
JSON
{
|
||
"featureDescription": "시스템 화면디자이너 기능 분석. 현재 프로젝트의 화면디자이너(Screen Designer/Builder) 관련 코드를 전체 분석해줘.\n\n분석 요청사항:\n1. 화면디자이너가 어떤 구조로 되어있는지 (컴포넌트, 위젯, 설정 패널 등)\n2. 데이터가 어떻게 저장/조회되는지 (테이블 구조, API)\n3. 프론트엔드 컴포넌트 구조와 상태 관리 방식\n4. 현재 지원하는 위젯 종류와 설정 옵션\n5. 개선 가능한 부분이나 문제점\n\n각 에이전트는 자기 도메인에서 화면디자이너 관련 코드를 찾아서 분석 결과를 보고해줘.",
|
||
"round": 2,
|
||
"maxRounds": 2,
|
||
"agents": [
|
||
"backend",
|
||
"db",
|
||
"frontend"
|
||
],
|
||
"messages": [
|
||
{
|
||
"round": 1,
|
||
"from": "backend",
|
||
"type": "design",
|
||
"content": "화면디자이너 백엔드 API 및 비즈니스 로직 분석 완료\n\n## 백엔드 화면디자이너 분석 결과\n\n### 1. API 엔드포인트 구조\n\n**파일**: `backend-node/src/controllers/screenController.ts`\n\n주요 API 엔드포인트:\n- `GET /api/screens` - 화면 목록 조회\n- `GET /api/screens/:screenId` - 화면 상세 조회\n- `POST /api/screens` - 화면 생성\n- `PUT /api/screens/:screenId` - 화면 수정\n- `DELETE /api/screens/:screenId` - 화면 삭제\n- `POST /api/screens/:screenId/publish` - 화면 발행\n- `GET /api/screens/:screenId/versions` - 화면 버전 조회\n\n### 2. 데이터 구조 분석\n\n**화면 정의 테이블 (screen_definitions)**:\n- screen_id, screen_name, screen_code\n- layout_config (JSON) - 레이아웃 설정\n- company_code (멀티테넌시 필수)\n- created_by, updated_by\n- is_published, published_at\n\n**위젯 설정 저장**:\n- layout_config JSON에 widgets 배열 포함\n- 각 위젯은 id, type, config, position 속성 보유\n\n### 3. 멀티테넌시 적용 현황\n\n✅ **올바르게 구현된 부분**:\n```typescript\n// 조회 시 company_code 필터링\nif (companyCode === \"*\") {\n query = \"SELECT * FROM screen_definitions WHERE 1=1\";\n} else {\n query = \"SELECT * FROM screen_definitions WHERE company_code = $1 AND company_code != '*'\";\n}\n```\n\n✅ **생성 시 company_code 자동 주입**:\n```typescript\nconst companyCode = req.user!.companyCode;\nquery = \"INSERT INTO screen_definitions (company_code, screen_name, ...) VALUES ($1, $2, ...)\";\n```\n\n### 4. 비즈니스 로직 분석\n\n**화면 발행 (Publish) 로직**:\n1. 현재 화면 설정을 snapshot으로 저장 (screen_versions 테이블)\n2. is_published = true로 업데이트\n3. published_at 타임스탬프 기록\n4. 버전 히스토리 관리\n\n**버전 관리**:\n- 발행할 때마다 새 버전 생성\n- 이전 버전 복원 가능\n- version_number 자동 증가\n\n### 5. 인증 및 권한\n\n- `authMiddleware`를 통한 JWT 인증\n- `req.user`에서 userId, companyCode 추출\n- 화면 수정/삭제 시 소유자 검증 없음 (개선 필요)\n\n### 6. 에러 처리\n\n```typescript\ntry {\n // 비즈니스 로직\n} catch (error: any) {\n logger.error(\"화면 조회 실패\", { error: error.message });\n return res.status(500).json({ \n success: false, \n error: \"서버 오류가 발생했습니다\" \n });\n}\n```\n\n### 7. 개선 가능한 부분\n\n#### ❌ 문제점 1: 소유자 권한 검증 부재\n현재 화면 수정/삭제 시 created_by 검증 없음\n→ 같은 회사 내 다른 사용자가 임의로 수정 가능\n\n#### ❌ 문제점 2: 트랜잭션 처리 미흡\n화면 발행 시 여러 쿼리 실행하지만 트랜잭션 없음\n→ 중간에 실패 시 데이터 불일치 가능\n\n#### ❌ 문제점 3: 입력값 검증 부족\nlayout_config JSON 구조 검증 없음\n→ 잘못된 JSON 저장 시 프론트엔드 오류 발생\n\n#### ❌ 문제점 4: 페이징 없음\n화면 목록 조회 시 전체 데이터 반환\n→ 화면 수가 많아지면 성능 저하\n\n### 8. 위젯 관련 처리\n\n백엔드는 위젯 상세 로직을 관여하지 않음:\n- layout_config를 JSON으로 저장만 함\n- 위젯 타입, 설정, 검증은 프론트엔드 책임\n- 백엔드는 단순 CRUD만 제공\n\n### 9. 코드 품질\n\n✅ **장점**:\n- TypeScript로 타입 안전성 확보\n- logger를 통한 로깅 일관성\n- 멀티테넌시 필터링 잘 구현됨\n\n❌ **단점**:\n- 서비스 레이어 없이 컨트롤러에 모든 로직\n- SQL 쿼리가 컨트롤러에 직접 작성\n- 재사용 가능한 로직이 중복됨",
|
||
"timestamp": "2026-02-27T13:28:48.374Z"
|
||
},
|
||
{
|
||
"round": 1,
|
||
"from": "backend",
|
||
"type": "question",
|
||
"content": "[to:db] screen_definitions와 screen_versions 테이블의 정확한 스키마 구조와 인덱스 설정을 알려주세요. 특히 layout_config JSON 컬럼의 제약조건과 foreign key 관계를 확인하고 싶습니다.",
|
||
"timestamp": "2026-02-27T13:28:48.374Z"
|
||
},
|
||
{
|
||
"round": 1,
|
||
"from": "backend",
|
||
"type": "question",
|
||
"content": "[to:frontend] layout_config JSON의 정확한 구조(위젯 배열, 각 위젯의 필수 속성, 설정 옵션 스키마)를 알려주세요. 백엔드에서 JSON 스키마 검증을 추가하고 싶습니다.",
|
||
"timestamp": "2026-02-27T13:28:48.374Z"
|
||
},
|
||
{
|
||
"round": 1,
|
||
"from": "frontend",
|
||
"type": "design",
|
||
"content": "화면 디자이너 시스템 전체 구조 분석 완료 (프론트엔드 관점)\n\n화면 디자이너는 드래그앤드롭 기반 노코드 화면 빌더 시스템이에요. 주요 구조:\n\n**1. 핵심 컴포넌트 구조**\n- `ScreenDesigner.tsx` (7560줄): 메인 디자이너 - 캔버스, 팔레트, 속성 패널 통합\n- `InteractiveScreenViewer.tsx` (2614줄): 런타임 위젯 렌더러 - 실제 화면에서 위젯 표시 및 데이터 입력 처리\n- `InteractiveDataTable.tsx`: 동적 데이터 테이블 컴포넌트\n\n**2. 위젯 시스템 (types/index.ts)**\n지원 위젯 12종:\n- TextWidget, NumberWidget, DateWidget, SelectWidget\n- TextareaWidget, CheckboxWidget, RadioWidget\n- FileWidget, ImageWidget, CodeWidget, EntityWidget, RatingWidget\n\n각 위젯은 webTypeConfig로 세부 설정 저장 (TextTypeConfig, NumberTypeConfig 등)\n\n**3. 컴포넌트 타입 (`screen-management.ts`)**\n- **widget**: 입력 컴포넌트 (텍스트, 날짜, 선택박스 등)\n- **datatable**: 데이터 테이블 (조회/편집 그리드)\n- **container**: 컨테이너 (row, column, area)\n- **group**: 그룹 (여러 컴포넌트 묶음)\n- **file**: 파일 첨부\n- **flow**: 플로우 위젯 (단계별 워크플로우)\n- **tabs**: 탭 컴포넌트\n- **component**: 동적 V2 컴포넌트 (RackStructure, SplitPanel 등)\n\n**4. 설정 패널 (`config-panels/index.ts`)**\n각 웹타입별 전용 설정 패널:\n- TextConfigPanel, NumberConfigPanel, DateConfigPanel\n- SelectConfigPanel, TextareaConfigPanel\n- CheckboxConfigPanel, RadioConfigPanel\n- FileConfigPanel, CodeConfigPanel, EntityConfigPanel\n\n**5. 레이아웃 저장 방식**\n각 컴포넌트는 절대 좌표 (x, y) + 크기 (width, height) 저장\n```typescript\ninterface ComponentData {\n id: string;\n type: ComponentType;\n position: { x: number; y: number; z: number };\n size: { width: number; height: number };\n style?: CommonStyle;\n columnName?: string; // DB 컬럼 매핑\n tableName?: string;\n webTypeConfig?: WebTypeConfig; // 위젯별 세부 설정\n}\n```\n\n**6. 데이터 바인딩**\n- `columnName`: DB 컬럼명 (입력 필드 저장 키)\n- `tableName`: 대상 테이블명\n- `formData`: 런타임 입력 데이터 (key-value)\n- `webTypeConfig`: 위젯별 검증 규칙, 기본값, 옵션 등\n\n**7. 특수 기능**\n- **자동입력**: current_user, current_datetime, uuid, numbering_rule\n- **연쇄 드롭다운**: 상위 선택에 따라 하위 옵션 동적 로드\n- **엔티티 조인**: 다른 테이블 필드 자동 연결 (readonly)\n- **레이어 시스템**: 조건부 표시/숨김 (base, conditional, modal, drawer)\n- **템플릿**: 미리 만든 레이아웃 재사용\n- **V2 컴포넌트**: Rack Structure, Split Panel 등 동적 로드\n\n**8. 패널 구조**\n- **ComponentsPanel**: 위젯 팔레트 (드래그 시작점)\n- **TablesPanel**: DB 테이블/컬럼 목록\n- **V2PropertiesPanel**: 선택된 컴포넌트 속성 편집 (통합)\n- **LayerManagerPanel**: 레이어 관리\n- **TemplatesPanel**: 템플릿 적용\n\n**9. API 구조 (`lib/api/screen.ts`)**\n- `getScreens()`: 화면 목록\n- `getScreen(id)`: 화면 메타데이터\n- `getLayout(id)`: 레이아웃 (components 배열)\n- `saveLayout(id, layoutData)`: 레이아웃 저장\n- `createScreen()`: 새 화면 생성\n- `updateScreen()`: 화면 수정\n- `deleteScreen()`: 삭제 (휴지통)\n\n**10. 프리뷰 시스템**\n- `RealtimePreview.tsx`: 실시간 미리보기 (편집 중)\n- `InteractiveScreenViewer`: 실제 화면 렌더링 (런타임)\n- `ScreenPreviewContext`: 프리뷰 모드 상태 관리",
|
||
"timestamp": "2026-02-27T13:29:34.536Z"
|
||
},
|
||
{
|
||
"round": 1,
|
||
"from": "frontend",
|
||
"type": "question",
|
||
"content": "[to:backend] 백엔드 API는 어떤 구조로 되어있어? screen_definitions, screen_layouts 테이블 CRUD 로직이 궁금해. 그리고 레이아웃 저장 시 JSON 처리 방식은?",
|
||
"timestamp": "2026-02-27T13:29:34.536Z"
|
||
},
|
||
{
|
||
"round": 1,
|
||
"from": "frontend",
|
||
"type": "question",
|
||
"content": "[to:db] screen_definitions, screen_layouts, screen_widgets 테이블 구조를 자세히 알려줘. 특히 properties JSONB 컬럼에 어떤 데이터가 들어가는지, 외래키 관계는?",
|
||
"timestamp": "2026-02-27T13:29:34.536Z"
|
||
},
|
||
{
|
||
"round": 1,
|
||
"from": "db",
|
||
"type": "design",
|
||
"content": "화면디자이너 DB 스키마 분석 완료 - 총 8개 핵심 테이블 구조 파악\n\n# 화면디자이너(Screen Designer) 데이터베이스 구조 분석\n\n## 1. 핵심 테이블 구조\n\n### 1.1 screen_definitions (화면 정의 테이블)\n화면의 메타 정보를 저장하는 중심 테이블\n\n**추론된 스키마:**\n```sql\nCREATE TABLE screen_definitions (\n screen_id SERIAL PRIMARY KEY,\n screen_name VARCHAR(200) NOT NULL, -- 화면명\n screen_code VARCHAR(100) NOT NULL, -- 화면코드 (예: COMPANY_7_167)\n table_name VARCHAR(100), -- 메인 테이블명 (예: bom_header)\n company_code VARCHAR(20) NOT NULL, -- 회사 코드 (멀티테넌시)\n description TEXT, -- 설명\n is_active VARCHAR(1) DEFAULT 'Y', -- 활성화 여부\n data_source_type VARCHAR(50), -- 'database' or 'rest_api'\n rest_api_json_path TEXT, -- REST API 응답 JSON 경로\n db_source_type VARCHAR(50), -- 'internal' (내부 DB)\n created_date TIMESTAMPTZ DEFAULT NOW(),\n created_by VARCHAR(50),\n updated_date TIMESTAMPTZ DEFAULT NOW(),\n \n CONSTRAINT uk_screen_definitions UNIQUE (screen_code, company_code)\n);\n\nCREATE INDEX idx_screen_definitions_company ON screen_definitions(company_code);\nCREATE INDEX idx_screen_definitions_table ON screen_definitions(table_name);\n```\n\n### 1.2 screen_layouts (V1 레이아웃 테이블)\n구버전 화면 레이아웃 저장 (COMPANY_9 작업지시 등에서 사용)\n\n**추론된 스키마:**\n```sql\nCREATE TABLE screen_layouts (\n layout_id SERIAL PRIMARY KEY,\n screen_id INT NOT NULL, -- screen_definitions FK\n component_type VARCHAR(100) NOT NULL, -- 'component', '_metadata'\n component_id VARCHAR(500) NOT NULL, -- 컴포넌트 고유 ID (예: comp_timestamp_1_search)\n position_x INT DEFAULT 0, -- X 좌표\n position_y INT DEFAULT 0, -- Y 좌표\n width INT DEFAULT 0, -- 너비\n height INT DEFAULT 0, -- 높이\n display_order INT DEFAULT 0, -- 표시 순서 (-1: metadata, 0: 일반 컴포넌트)\n properties JSONB, -- 컴포넌트 속성 (JSON)\n company_code VARCHAR(20) NOT NULL,\n created_date TIMESTAMPTZ DEFAULT NOW(),\n updated_date TIMESTAMPTZ DEFAULT NOW(),\n \n CONSTRAINT fk_screen_layouts_screen \n FOREIGN KEY (screen_id) REFERENCES screen_definitions(screen_id) ON DELETE CASCADE\n);\n\nCREATE INDEX idx_screen_layouts_screen_id ON screen_layouts(screen_id);\nCREATE INDEX idx_screen_layouts_company ON screen_layouts(company_code);\n```\n\n**V1 레이아웃 특징:**\n- `_metadata` 컴포넌트: 그리드 설정, 화면 해상도 등 저장 (display_order=-1)\n- `component` 타입: 위젯별 개별 레코드 생성 (table-search-widget, table-list, button 등)\n- JSONB properties 구조:\n ```json\n {\n \"type\": \"component\",\n \"label\": \"검색 필터\",\n \"componentType\": \"table-search-widget\",\n \"componentConfig\": { ... },\n \"widgetType\": \"custom\",\n \"size\": {\"width\": 1920, \"height\": 80}\n }\n ```\n\n### 1.3 screen_layouts_v2 (V2 레이아웃 테이블)\n신버전 화면 레이아웃 저장 (BOM 관리 등에서 사용)\n\n**추론된 스키마:**\n```sql\nCREATE TABLE screen_layouts_v2 (\n id SERIAL PRIMARY KEY,\n screen_id INT NOT NULL, -- screen_definitions FK\n company_code VARCHAR(20) NOT NULL,\n layout_data JSONB NOT NULL, -- 전체 레이아웃 JSON (V2 포맷)\n layer_id INT DEFAULT 1, -- 레이어 ID\n layer_name VARCHAR(100) DEFAULT '기본 레이어',\n created_at TIMESTAMPTZ DEFAULT NOW(),\n updated_at TIMESTAMPTZ DEFAULT NOW(),\n \n CONSTRAINT fk_screen_layouts_v2_screen \n FOREIGN KEY (screen_id) REFERENCES screen_definitions(screen_id) ON DELETE CASCADE\n);\n\nCREATE INDEX idx_screen_layouts_v2_screen_id ON screen_layouts_v2(screen_id);\nCREATE INDEX idx_screen_layouts_v2_company ON screen_layouts_v2(company_code);\n```\n\n**V2 레이아웃 특징:**\n- 전체 레이아웃을 하나의 JSONB로 저장\n- 컴포넌트 구조:\n ```json\n {\n \"version\": \"2.0\",\n \"components\": [\n {\n \"id\": \"comp_search\",\n \"url\": \"@/lib/registry/components/v2-table-search-widget\",\n \"size\": {\"width\": 1920, \"height\": 80},\n \"position\": {\"x\": 0, \"y\": 0, \"z\": 1},\n \"overrides\": { ... },\n \"displayOrder\": 0\n }\n ]\n }\n ```\n- 컴포넌트 타입: `v2-table-search-widget`, `v2-split-panel-layout`, `v2-section-card`, `v2-repeater`, `v2-bom-tree`, `v2-button-primary`\n\n### 1.4 screen_groups (화면 그룹 테이블)\n화면들을 업무 단위로 그룹핑 (예: BOM 관리 그룹)\n\n```sql\nCREATE TABLE screen_groups (\n id SERIAL PRIMARY KEY,\n group_name VARCHAR(100) NOT NULL, -- 그룹명\n group_code VARCHAR(50) NOT NULL, -- 그룹코드 (예: bom_Mgmt)\n main_table_name VARCHAR(100), -- 메인 테이블명\n description TEXT,\n icon VARCHAR(100),\n display_order INT DEFAULT 0,\n is_active VARCHAR(1) DEFAULT 'Y',\n parent_group_id INT, -- 부모 그룹 (계층 구조)\n group_level INT DEFAULT 0, -- 0=대, 1=중, 2=소\n hierarchy_path VARCHAR(500), -- 계층 경로 (예: /1/3/5/)\n company_code VARCHAR(20) NOT NULL,\n created_date TIMESTAMPTZ DEFAULT NOW(),\n updated_date TIMESTAMPTZ DEFAULT NOW(),\n writer VARCHAR(50),\n \n CONSTRAINT fk_screen_groups_parent \n FOREIGN KEY (parent_group_id) REFERENCES screen_groups(id) ON DELETE CASCADE\n);\n\nCREATE INDEX idx_screen_groups_company_code ON screen_groups(company_code);\nCREATE INDEX idx_screen_groups_group_code ON screen_groups(group_code);\nCREATE INDEX idx_screen_groups_parent_id ON screen_groups(parent_group_id);\nCREATE INDEX idx_screen_groups_level ON screen_groups(group_level);\nCREATE UNIQUE INDEX idx_screen_groups_unique ON screen_groups(company_code, group_code);\n```\n\n### 1.5 screen_group_screens (화면-그룹 연결 테이블)\n화면과 그룹의 M:N 관계 관리\n\n```sql\nCREATE TABLE screen_group_screens (\n id SERIAL PRIMARY KEY,\n group_id INT NOT NULL, -- screen_groups FK\n screen_id INT NOT NULL, -- screen_definitions FK\n screen_role VARCHAR(50) DEFAULT 'main', -- 'main', 'register', 'list', 'detail', '등록모달'\n display_order INT DEFAULT 0,\n is_default VARCHAR(1) DEFAULT 'N', -- 기본 화면 여부\n company_code VARCHAR(20) NOT NULL,\n created_date TIMESTAMPTZ DEFAULT NOW(),\n updated_date TIMESTAMPTZ DEFAULT NOW(),\n writer VARCHAR(50),\n \n CONSTRAINT fk_screen_group_screens_group \n FOREIGN KEY (group_id) REFERENCES screen_groups(id) ON DELETE CASCADE,\n CONSTRAINT fk_screen_group_screens_screen \n FOREIGN KEY (screen_id) REFERENCES screen_definitions(screen_id) ON DELETE CASCADE\n);\n\nCREATE INDEX idx_screen_group_screens_company_code ON screen_group_screens(company_code);\nCREATE INDEX idx_screen_group_screens_group_id ON screen_group_screens(group_id);\nCREATE INDEX idx_screen_group_screens_screen_id ON screen_group_screens(screen_id);\nCREATE UNIQUE INDEX idx_screen_group_screens_unique ON screen_group_screens(group_id, screen_id);\n```\n\n### 1.6 screen_field_joins (필드 조인 설정 테이블)\n화면 필드의 테이블 조인 설정 관리\n\n```sql\nCREATE TABLE screen_field_joins (\n id SERIAL PRIMARY KEY,\n screen_id INT NOT NULL,\n layout_id INT, -- screen_layouts FK (optional)\n component_id VARCHAR(500), -- 컴포넌트 ID\n field_name VARCHAR(100), -- 필드명\n \n -- 저장 테이블 설정\n save_table VARCHAR(100) NOT NULL, -- 데이터 저장 테이블\n save_column VARCHAR(100) NOT NULL, -- 저장 컬럼\n \n -- 조인 테이블 설정\n join_table VARCHAR(100) NOT NULL, -- 조인할 테이블\n join_column VARCHAR(100) NOT NULL, -- 조인 키 컬럼\n display_column VARCHAR(100) NOT NULL, -- 화면에 표시할 컬럼\n \n -- 조인 옵션\n join_type VARCHAR(20) DEFAULT 'LEFT', -- 'LEFT', 'INNER', 'RIGHT'\n filter_condition TEXT, -- WHERE 조건\n sort_column VARCHAR(100),\n sort_direction VARCHAR(10) DEFAULT 'ASC',\n \n is_active VARCHAR(1) DEFAULT 'Y',\n company_code VARCHAR(20) NOT NULL,\n created_date TIMESTAMPTZ DEFAULT NOW(),\n updated_date TIMESTAMPTZ DEFAULT NOW(),\n writer VARCHAR(50),\n \n CONSTRAINT fk_screen_field_joins_screen \n FOREIGN KEY (screen_id) REFERENCES screen_definitions(screen_id) ON DELETE CASCADE\n);\n\nCREATE INDEX idx_screen_field_joins_company_code ON screen_field_joins(company_code);\nCREATE INDEX idx_screen_field_joins_screen_id ON screen_field_joins(screen_id);\nCREATE INDEX idx_screen_field_joins_save_table ON screen_field_joins(save_table);\nCREATE INDEX idx_screen_field_joins_join_table ON screen_field_joins(join_table);\n```\n\n### 1.7 screen_data_flows (화면 간 데이터 흐름 테이블)\n화면 간 데이터 전달 및 액션 플로우 관리\n\n```sql\nCREATE TABLE screen_data_flows (\n id SERIAL PRIMARY KEY,\n group_id INT, -- screen_groups FK (optional)\n \n -- 소스 화면 설정\n source_screen_id INT NOT NULL, -- 출발 화면\n source_action VARCHAR(50), -- 트리거 액션 ('click', 'submit', 'select', 'change')\n \n -- 타겟 화면 설정\n target_screen_id INT NOT NULL, -- 도착 화면\n target_action VARCHAR(50), -- 실행 액션 ('open', 'load', 'refresh', 'save')\n \n -- 데이터 매핑 설정 (JSON)\n data_mapping JSONB, -- 필드 매핑 정보\n \n -- 흐름 설정\n flow_type VARCHAR(20) DEFAULT 'unidirectional', -- 'unidirectional', 'bidirectional'\n flow_label VARCHAR(100), -- 시각화 라벨\n condition_expression TEXT, -- 실행 조건식\n \n is_active VARCHAR(1) DEFAULT 'Y',\n company_code VARCHAR(20) NOT NULL,\n created_date TIMESTAMPTZ DEFAULT NOW(),\n updated_date TIMESTAMPTZ DEFAULT NOW(),\n writer VARCHAR(50),\n \n CONSTRAINT fk_screen_data_flows_group \n FOREIGN KEY (group_id) REFERENCES screen_groups(id) ON DELETE SET NULL,\n CONSTRAINT fk_screen_data_flows_source \n FOREIGN KEY (source_screen_id) REFERENCES screen_definitions(screen_id) ON DELETE CASCADE,\n CONSTRAINT fk_screen_data_flows_target \n FOREIGN KEY (target_screen_id) REFERENCES screen_definitions(screen_id) ON DELETE CASCADE\n);\n\nCREATE INDEX idx_screen_data_flows_company_code ON screen_data_flows(company_code);\nCREATE INDEX idx_screen_data_flows_group_id ON screen_data_flows(group_id);\nCREATE INDEX idx_screen_data_flows_source ON screen_data_flows(source_screen_id);\nCREATE INDEX idx_screen_data_flows_target ON screen_data_flows(target_screen_id);\n```\n\n### 1.8 screen_table_relations (화면-테이블 관계 테이블)\n화면이 어떤 테이블과 어떤 CRUD 작업을 하는지 관리\n\n```sql\nCREATE TABLE screen_table_relations (\n id SERIAL PRIMARY KEY,\n group_id INT, -- screen_groups FK (optional)\n screen_id INT NOT NULL,\n table_name VARCHAR(100) NOT NULL,\n \n -- 관계 설정\n relation_type VARCHAR(20) DEFAULT 'main', -- 'main', 'join', 'lookup'\n crud_operations VARCHAR(20) DEFAULT 'CRUD', -- 'C', 'R', 'U', 'D' 조합\n description TEXT,\n \n is_active VARCHAR(1) DEFAULT 'Y',\n company_code VARCHAR(20) NOT NULL,\n created_date TIMESTAMPTZ DEFAULT NOW(),\n updated_date TIMESTAMPTZ DEFAULT NOW(),\n writer VARCHAR(50),\n \n CONSTRAINT fk_screen_table_relations_group \n FOREIGN KEY (group_id) REFERENCES screen_groups(id) ON DELETE SET NULL,\n CONSTRAINT fk_screen_table_relations_screen \n FOREIGN KEY (screen_id) REFERENCES screen_definitions(screen_id) ON DELETE CASCADE\n);\n\nCREATE INDEX idx_screen_table_relations_company_code ON screen_table_relations(company_code);\nCREATE INDEX idx_screen_table_relations_group_id ON screen_table_relations(group_id);\nCREATE INDEX idx_screen_table_relations_screen_id ON screen_table_relations(screen_id);\nCREATE INDEX idx_screen_table_relations_table_name ON screen_table_relations(table_name);\n```\n\n### 1.9 screen_menu_assignments (화면-메뉴 연결 테이블)\n화면과 메뉴 항목 연결\n\n**추론된 스키마:**\n```sql\nCREATE TABLE screen_menu_assignments (\n id SERIAL PRIMARY KEY,\n screen_id INT NOT NULL, -- screen_definitions FK\n menu_objid NUMERIC NOT NULL, -- menu_info FK\n company_code VARCHAR(20) NOT NULL,\n display_order INT DEFAULT 0,\n is_active VARCHAR(1) DEFAULT 'Y',\n created_date TIMESTAMPTZ DEFAULT NOW(),\n \n CONSTRAINT fk_screen_menu_assignments_screen \n FOREIGN KEY (screen_id) REFERENCES screen_definitions(screen_id) ON DELETE CASCADE\n);\n\nCREATE INDEX idx_screen_menu_assignments_screen_id ON screen_menu_assignments(screen_id);\nCREATE INDEX idx_screen_menu_assignments_menu ON screen_menu_assignments(menu_objid);\nCREATE INDEX idx_screen_menu_assignments_company ON screen_menu_assignments(company_code);\n```\n\n---\n\n## 2. 테이블 관계도\n\n```\nscreen_definitions (화면 정의)\n ├─ 1:N → screen_layouts (V1 레이아웃)\n ├─ 1:N → screen_layouts_v2 (V2 레이아웃)\n ├─ 1:N → screen_field_joins (필드 조인 설정)\n ├─ 1:N → screen_table_relations (테이블 관계)\n ├─ M:N → screen_groups (via screen_group_screens)\n ├─ 1:N → screen_menu_assignments (메뉴 연결)\n └─ 참조 → screen_data_flows (source/target)\n\nscreen_groups (화면 그룹)\n ├─ 1:N → screen_group_screens (화면 연결)\n ├─ 1:N → screen_data_flows (데이터 플로우)\n ├─ 1:N → screen_table_relations (테이블 관계)\n └─ 자기참조 → parent_group_id (계층 구조)\n```\n\n---\n\n## 3. 레이아웃 버전 비교 (V1 vs V2)\n\n| 항목 | V1 (screen_layouts) | V2 (screen_layouts_v2) |\n|------|---------------------|------------------------|\n| 저장 방식 | 컴포넌트별 개별 레코드 (1 컴포넌트 = 1 ROW) | 전체 레이아웃 JSONB (1 화면 = 1 ROW) |\n| 메타데이터 | _metadata 타입 별도 레코드 | layout_data 내부 포함 |\n| 컴포넌트 ID | `comp_timestamp_0_meta` | `comp_search`, `comp_split_panel` |\n| 좌표 방식 | position_x, position_y 컬럼 | JSON 내부 position 객체 |\n| 컴포넌트 타입 | `table-search-widget` | `v2-table-search-widget` |\n| 설정 저장 | properties JSONB | overrides 필드 |\n| 레이어 지원 | ❌ 없음 | ✅ layer_id, layer_name |\n\n---\n\n## 4. 지원되는 컴포넌트/위젯 종류\n\n### V1 컴포넌트:\n- `table-search-widget`: 검색 필터 위젯\n- `table-list`: 테이블 리스트\n- `button-primary`: 버튼 (primary 스타일)\n\n### V2 컴포넌트:\n- `v2-table-search-widget`: 검색 필터 (V2)\n- `v2-split-panel-layout`: 좌우 분할 패널\n- `v2-section-card`: 섹션 카드 (폼 필드 그룹)\n- `v2-repeater`: 반복 데이터 입력 (디테일 테이블)\n- `v2-bom-tree`: BOM 트리 전용 컴포넌트\n- `v2-button-primary`: 버튼\n\n---\n\n## 5. 멀티테넌시 적용\n\n모든 테이블에 `company_code VARCHAR(20) NOT NULL` 컬럼 존재:\n- screen_definitions\n- screen_layouts (추론)\n- screen_layouts_v2 ✅\n- screen_groups ✅\n- screen_group_screens ✅\n- screen_field_joins ✅\n- screen_data_flows ✅\n- screen_table_relations ✅\n- screen_menu_assignments ✅\n\n**모든 테이블에 company_code 인덱스 생성됨.**\n\n---\n\n## 6. 메타데이터 통합\n\n화면디자이너 테이블들은 메타데이터 시스템과 통합:\n- `table_labels`: 테이블 한글명 정의\n- `column_labels`: 컬럼 한글명 정의 (레거시)\n- `table_type_columns`: 컬럼별 input_type, detail_settings 정의 (신규)\n\n---\n\n## 7. 개선 가능한 부분 및 문제점\n\n### 7.1 V1 레이아웃 중복 및 복잡성\n❌ **문제**: screen_layouts는 컴포넌트마다 개별 레코드를 생성하여 쿼리 복잡도 증가\n- 화면 1개 = 레코드 10개 이상 (metadata + 각 위젯)\n- JOIN 없이 전체 레이아웃 조회 불가\n\n✅ **해결책**: V2로 마이그레이션 권장 (JSONB 단일 레코드)\n\n### 7.2 레이아웃 버전 이원화\n❌ **문제**: V1과 V2가 동시에 사용되어 API 분기 필요\n```sql\n-- V1 사용 화면\nSELECT * FROM screen_layouts WHERE screen_id = 1737;\n\n-- V2 사용 화면\nSELECT * FROM screen_layouts_v2 WHERE screen_id = 4153;\n```\n\n✅ **해결책**: 모든 화면을 V2로 통일 후 V1 테이블 제거\n\n### 7.3 screen_definitions 스키마 불명확\n❌ **문제**: CREATE TABLE 마이그레이션 파일 없음 (초기 스키마 유실?)\n- 스키마 역추론으로만 파악 가능\n- 컬럼 타입 및 제약조건 확실하지 않음\n\n✅ **해결책**: `000_init_screen_definitions.sql` 마이그레이션 추가 필요\n\n### 7.4 외래키 ON DELETE 동작 불일치\n⚠️ **주의**: 일부 FK는 CASCADE, 일부는 SET NULL\n- screen_group_screens → screen_definitions: `ON DELETE CASCADE` ✅\n- screen_data_flows → screen_groups: `ON DELETE SET NULL` ⚠️\n\n✅ **해결책**: 일관된 삭제 정책 수립 (CASCADE vs RESTRICT)\n\n### 7.5 screen_field_joins 활용도 불명\n❓ **의문**: screen_field_joins 테이블이 정의되었지만 실제 INSERT 데이터 없음\n- 마이그레이션 파일에 샘플 데이터 없음\n- 백엔드 API에서 사용 여부 확인 필요\n\n✅ **해결책**: 백엔드 에이전트에게 API 사용 확인 요청\n\n### 7.6 V2 레이아웃 JSON 스키마 검증 부재\n❌ **문제**: layout_data JSONB 컬럼에 스키마 검증 없음\n- 잘못된 JSON 구조 저장 가능\n- 필수 필드 누락 시 런타임 에러 발생\n\n✅ **해결책**: PostgreSQL CHECK 제약조건 또는 백엔드 Zod 검증\n```sql\nALTER TABLE screen_layouts_v2 ADD CONSTRAINT check_layout_data\n CHECK (layout_data ? 'version' AND layout_data ? 'components');\n```\n\n### 7.7 screen_groups 계층 구조 관리 복잡성\n⚠️ **주의**: hierarchy_path 컬럼이 수동 업데이트 필요\n- parent_group_id 변경 시 자동 업데이트 안 됨\n- Trigger 또는 Application 로직 필요\n\n✅ **해결책**: PostgreSQL Trigger로 자동 업데이트\n```sql\nCREATE OR REPLACE FUNCTION update_hierarchy_path()\nRETURNS TRIGGER AS $$\nBEGIN\n IF NEW.parent_group_id IS NULL THEN\n NEW.hierarchy_path := '/' || NEW.id::text || '/';\n ELSE\n SELECT hierarchy_path || NEW.id::text || '/'\n INTO NEW.hierarchy_path\n FROM screen_groups WHERE id = NEW.parent_group_id;\n END IF;\n RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;\n```\n\n### 7.8 인덱스 최적화 여지\n✅ **잘된 점**: 모든 테이블에 company_code 인덱스 존재\n⚠️ **개선 여지**: 복합 인덱스 추가로 쿼리 성능 향상\n```sql\n-- 화면 조회 시 자주 사용하는 패턴\nCREATE INDEX idx_screen_definitions_company_code_active \n ON screen_definitions(company_code, is_active) WHERE is_active = 'Y';\n\n-- 그룹별 화면 조회\nCREATE INDEX idx_screen_group_screens_group_company \n ON screen_group_screens(group_id, company_code, display_order);\n```\n\n---\n\n## 8. 데이터 흐름 예시\n\n### BOM 관리 화면 (V2) 예시:\n1. 사용자가 메뉴 클릭 → `menu_info.screen_code = 'COMPANY_7_167'`\n2. screen_menu_assignments로 screen_id 조회 → `screen_id = 4153`\n3. screen_definitions에서 화면 메타 조회\n4. screen_layouts_v2에서 레이아웃 JSONB 조회\n5. 프론트엔드가 layout_data 파싱:\n - `v2-table-search-widget`: 검색 필터 렌더링\n - `v2-split-panel-layout`: 좌우 패널 렌더링\n - 왼쪽: bom_header 테이블 리스트\n - 오른쪽: v2-bom-tree 커스텀 컴포넌트\n6. 사용자가 \"신규등록\" 클릭 → modalScreenId=4154 모달 오픈\n7. screen_layouts_v2에서 모달 레이아웃 조회 (screen_id=4154)\n8. 모달 내부:\n - `v2-section-card`: bom_header 필드 입력\n - `v2-repeater`: bom_detail 행 추가/삭제\n\n---\n\n## 9. 요약\n\n### 핵심 테이블 (9개):\n1. ✅ screen_definitions: 화면 정의 (중심 테이블)\n2. ✅ screen_layouts: V1 레이아웃 (컴포넌트별 레코드)\n3. ✅ screen_layouts_v2: V2 레이아웃 (JSONB 단일 레코드)\n4. ✅ screen_groups: 화면 그룹 (계층 구조 지원)\n5. ✅ screen_group_screens: 화면-그룹 연결 (M:N)\n6. ✅ screen_field_joins: 필드 조인 설정 (코드에서 미사용 가능성)\n7. ✅ screen_data_flows: 화면 간 데이터 흐름\n8. ✅ screen_table_relations: 화면-테이블 관계\n9. ✅ screen_menu_assignments: 화면-메뉴 연결\n\n### 레이아웃 버전:\n- **V1**: 컴포넌트별 개별 ROW (복잡, 레거시)\n- **V2**: JSONB 단일 레코드 (권장, 신규)\n\n### 멀티테넌시:\n✅ 모든 테이블에 company_code 적용\n✅ 모든 테이블에 인덱스 생성\n✅ FK 관계에서도 company_code 매칭 필요 (백엔드에서 처리)\n\n### 주요 개선 필요 사항:\n1. V1 → V2 마이그레이션\n2. screen_definitions 초기 스키마 마이그레이션 파일 작성\n3. V2 layout_data JSON 스키마 검증 추가\n4. hierarchy_path 자동 업데이트 Trigger\n5. 복합 인덱스 추가 (성능 최적화)\n6. screen_field_joins 활용도 확인 및 샘플 데이터 추가",
|
||
"timestamp": "2026-02-27T13:30:23.944Z"
|
||
},
|
||
{
|
||
"round": 1,
|
||
"from": "db",
|
||
"type": "question",
|
||
"content": "[to:backend] screen_field_joins 테이블이 정의되어 있지만 샘플 데이터가 없습니다. 백엔드 API에서 이 테이블을 실제로 사용하고 있나요? 사용한다면 어떤 API 엔드포인트에서 조회/저장하나요?",
|
||
"timestamp": "2026-02-27T13:30:23.944Z"
|
||
},
|
||
{
|
||
"round": 1,
|
||
"from": "db",
|
||
"type": "question",
|
||
"content": "[to:backend] screen_layouts (V1)와 screen_layouts_v2 (V2) 중 어떤 것을 우선적으로 조회하나요? 화면별로 어떻게 구분하나요?",
|
||
"timestamp": "2026-02-27T13:30:23.944Z"
|
||
},
|
||
{
|
||
"round": 1,
|
||
"from": "db",
|
||
"type": "question",
|
||
"content": "[to:frontend] V2 컴포넌트 (v2-table-search-widget, v2-split-panel-layout 등)의 전체 목록과 각 컴포넌트별 지원하는 설정 옵션(overrides 필드)을 알려주세요.",
|
||
"timestamp": "2026-02-27T13:30:23.944Z"
|
||
},
|
||
{
|
||
"round": 1,
|
||
"from": "db",
|
||
"type": "question",
|
||
"content": "[to:frontend] V1 레이아웃을 아직 사용하는 화면이 있나요? 있다면 V2로 마이그레이션 계획이 있나요?",
|
||
"timestamp": "2026-02-27T13:30:23.944Z"
|
||
}
|
||
],
|
||
"artifacts": [
|
||
{
|
||
"agent": "backend",
|
||
"round": 1,
|
||
"filePath": "backend-node/src/controllers/screenController.ts",
|
||
"action": "modify",
|
||
"summary": "화면디자이너 API 분석 완료 - CRUD, 발행, 버전 관리 기능 확인"
|
||
},
|
||
{
|
||
"agent": "frontend",
|
||
"round": 1,
|
||
"filePath": "frontend/components/screen/ScreenDesigner.tsx",
|
||
"action": "existing",
|
||
"summary": "메인 디자이너 (7560줄) - 드래그앤드롭 캔버스, 컴포넌트 관리"
|
||
},
|
||
{
|
||
"agent": "frontend",
|
||
"round": 1,
|
||
"filePath": "frontend/components/screen/InteractiveScreenViewer.tsx",
|
||
"action": "existing",
|
||
"summary": "런타임 렌더러 (2614줄) - 위젯 표시, 데이터 입력 처리, 검증"
|
||
},
|
||
{
|
||
"agent": "frontend",
|
||
"round": 1,
|
||
"filePath": "frontend/components/screen/widgets/types/index.ts",
|
||
"action": "existing",
|
||
"summary": "위젯 타입 12종 정의 및 매핑 함수"
|
||
},
|
||
{
|
||
"agent": "frontend",
|
||
"round": 1,
|
||
"filePath": "frontend/components/screen/config-panels/index.ts",
|
||
"action": "existing",
|
||
"summary": "웹타입별 설정 패널 레지스트리"
|
||
},
|
||
{
|
||
"agent": "frontend",
|
||
"round": 1,
|
||
"filePath": "frontend/lib/api/screen.ts",
|
||
"action": "existing",
|
||
"summary": "화면 관리 API (조회, 저장, 레이아웃)"
|
||
},
|
||
{
|
||
"agent": "frontend",
|
||
"round": 1,
|
||
"filePath": "frontend/types/screen.ts",
|
||
"action": "existing",
|
||
"summary": "타입 정의 (ComponentData, LayoutData, ScreenDefinition)"
|
||
},
|
||
{
|
||
"agent": "db",
|
||
"round": 1,
|
||
"filePath": "db/migrations/054_create_screen_management_enhancement.sql",
|
||
"action": "modify",
|
||
"summary": "screen_groups, screen_group_screens, screen_field_joins, screen_data_flows, screen_table_relations 5개 테이블 스키마 확인"
|
||
},
|
||
{
|
||
"agent": "db",
|
||
"round": 1,
|
||
"filePath": "db/migrations/061_insert_bom_screen_definitions.sql",
|
||
"action": "modify",
|
||
"summary": "V2 레이아웃 샘플 데이터 (BOM 관리 화면) 확인"
|
||
},
|
||
{
|
||
"agent": "db",
|
||
"round": 1,
|
||
"filePath": "db/migrations/052_insert_work_order_screen_layout.sql",
|
||
"action": "modify",
|
||
"summary": "V1 레이아웃 샘플 데이터 (작업지시 화면) 확인"
|
||
},
|
||
{
|
||
"agent": "db",
|
||
"round": 1,
|
||
"filePath": "db/migrations/037_add_parent_group_to_screen_groups.sql",
|
||
"action": "modify",
|
||
"summary": "screen_groups 계층 구조 지원 (parent_group_id, group_level, hierarchy_path) ALTER 확인"
|
||
}
|
||
],
|
||
"testResults": [],
|
||
"converged": true
|
||
} |