{ "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 }