# 화면 개발 표준 가이드 > **목적**: 어떤 개발자/AI가 화면을 개발하든 동일한 결과물이 나오도록 하는 표준 가이드 > **대상**: 개발자, AI 에이전트 (Cursor 등) > **버전**: 1.0.0 --- ## 1. 개요 이 문서는 WACE 솔루션에서 화면을 개발할 때 반드시 따라야 하는 표준입니다. 비즈니스 로직을 어떻게 설명하든, 최종 결과물은 이 가이드대로 생성되어야 합니다. ### 핵심 원칙 1. **V2 컴포넌트만 사용**: `v2-` 접두사가 붙은 컴포넌트만 사용 2. **UI와 로직 분리**: UI는 `screen_layouts_v2`, 비즈니스 로직은 `dataflow_diagrams` 3. **멀티테넌시 필수**: 모든 쿼리에 `company_code` 필터링 --- ## 2. 사용 가능한 V2 컴포넌트 목록 (23개) ### 입력 컴포넌트 | ID | 이름 | 용도 | |----|------|------| | `v2-input` | 입력 | 텍스트, 숫자, 비밀번호, 이메일 등 | | `v2-select` | 선택 | 드롭다운, 콤보박스, 라디오, 체크박스 | | `v2-date` | 날짜 | 날짜, 시간, 날짜범위 | ### 표시 컴포넌트 | ID | 이름 | 용도 | |----|------|------| | `v2-text-display` | 텍스트 표시 | 라벨, 제목 | | `v2-card-display` | 카드 디스플레이 | 테이블 데이터를 카드 형태로 표시 | | `v2-aggregation-widget` | 집계 위젯 | 합계, 평균, 개수 등 | ### 테이블/데이터 컴포넌트 | ID | 이름 | 용도 | |----|------|------| | `v2-table-list` | 테이블 리스트 | 데이터 조회/편집 테이블 | | `v2-table-search-widget` | 검색 필터 | 테이블 검색/필터 | | `v2-pivot-grid` | 피벗 그리드 | 다차원 분석 | ### 레이아웃 컴포넌트 | ID | 이름 | 용도 | |----|------|------| | `v2-split-panel-layout` | 분할 패널 | 마스터-디테일 좌우 분할 | | `v2-tabs-widget` | 탭 위젯 | 탭 전환 | | `v2-section-card` | 섹션 카드 | 제목+테두리 그룹화 | | `v2-section-paper` | 섹션 페이퍼 | 배경색 그룹화 | | `v2-divider-line` | 구분선 | 영역 구분 | | `v2-repeat-container` | 리피터 컨테이너 | 데이터 반복 렌더링 | | `v2-repeater` | 리피터 | 반복 컨트롤 | | `v2-repeat-screen-modal` | 반복 화면 모달 | 모달 반복 | ### 액션/특수 컴포넌트 | ID | 이름 | 용도 | |----|------|------| | `v2-button-primary` | 기본 버튼 | 저장, 삭제 등 액션 | | `v2-numbering-rule` | 채번규칙 | 자동 코드 생성 | | `v2-category-manager` | 카테고리 관리자 | 카테고리 관리 | | `v2-location-swap-selector` | 위치 교환 | 위치 선택/교환 | | `v2-rack-structure` | 랙 구조 | 창고 랙 시각화 | | `v2-media` | 미디어 | 이미지/동영상 표시 | --- ## 3. 화면 패턴 (5가지) ### 패턴 A: 기본 마스터 화면 **사용 조건**: 단일 테이블 CRUD **컴포넌트 구성**: ``` v2-table-search-widget (검색) v2-table-list (테이블) v2-button-primary (저장/삭제) ``` **레이아웃**: ``` ┌─────────────────────────────────────────────────┐ │ [검색필드들] [조회] [엑셀] │ ← v2-table-search-widget ├─────────────────────────────────────────────────┤ │ 제목 [신규] [삭제] │ │ ─────────────────────────────────────────────── │ │ □ | 코드 | 이름 | 상태 | 등록일 | │ ← v2-table-list └─────────────────────────────────────────────────┘ ``` --- ### 패턴 B: 마스터-디테일 화면 **사용 조건**: 마스터 테이블 선택 → 디테일 테이블 표시 **컴포넌트 구성**: ``` v2-split-panel-layout (분할) ├─ 좌측: v2-table-list (마스터) └─ 우측: v2-table-list (디테일) ``` **레이아웃**: ``` ┌──────────────────┬──────────────────────────────┐ │ 마스터 리스트 │ 디테일 리스트 │ │ ─────────────── │ │ │ □ A001 항목1 │ [디테일 테이블] │ │ □ A002 항목2 ← │ │ └──────────────────┴──────────────────────────────┘ v2-split-panel-layout ``` **필수 설정**: ```json { "leftPanel": { "tableName": "마스터_테이블명" }, "rightPanel": { "tableName": "디테일_테이블명", "relation": { "type": "detail", "foreignKey": "master_id" } }, "splitRatio": 30 } ``` --- ### 패턴 C: 마스터-디테일 + 탭 **사용 조건**: 마스터 선택 → 여러 탭으로 상세 정보 표시 **컴포넌트 구성**: ``` v2-split-panel-layout (분할) ├─ 좌측: v2-table-list (마스터) └─ 우측: v2-tabs-widget (탭) ├─ 탭1: v2-table-list ├─ 탭2: v2-table-list └─ 탭3: 폼 컴포넌트들 ``` --- ### 패턴 D: 카드 뷰 **사용 조건**: 이미지+정보 카드 형태 표시 **컴포넌트 구성**: ``` v2-table-search-widget (검색) v2-card-display (카드) ``` **필수 설정**: ```json { "cardsPerRow": 3, "columnMapping": { "title": "name", "subtitle": "code", "image": "image_url", "status": "status" } } ``` --- ### 패턴 E: 피벗 분석 **사용 조건**: 다차원 집계/분석 **컴포넌트 구성**: ``` v2-pivot-grid (피벗) ``` **필수 설정**: ```json { "fields": [ { "field": "region", "area": "row" }, { "field": "year", "area": "column" }, { "field": "amount", "area": "data", "summaryType": "sum" } ] } ``` --- ## 4. 데이터베이스 구조 ### 화면 정의 테이블 ```sql -- screen_definitions: 화면 기본 정보 INSERT INTO screen_definitions ( screen_id, screen_name, screen_code, description, table_name, company_code ) VALUES (...); -- screen_layouts_v2: UI 레이아웃 (JSON) INSERT INTO screen_layouts_v2 ( screen_id, company_code, layout_data -- JSON: 컴포넌트 배치 정보 ) VALUES (...); -- screen_menu_assignments: 메뉴 연결 INSERT INTO screen_menu_assignments ( screen_id, menu_objid, company_code ) VALUES (...); ``` ### 제어관리 테이블 ```sql -- dataflow_diagrams: 비즈니스 로직 INSERT INTO dataflow_diagrams ( diagram_name, company_code, control, -- JSON: 조건 설정 plan -- JSON: 실행 계획 ) VALUES (...); ``` --- ## 5. UI 설정 vs 비즈니스 로직 설정 ### UI 설정 (화면 디자이너에서 처리) | 항목 | 저장 위치 | |------|----------| | 컴포넌트 배치 | screen_layouts_v2.layout_data | | 테이블명 | layout_data 내 tableName | | 컬럼 표시/숨김 | layout_data 내 columns | | 검색 필드 | layout_data 내 searchFields | | 기본 저장/삭제 | 버튼 config.action.type | ### 비즈니스 로직 (제어관리에서 처리) | 항목 | 저장 위치 | |------|----------| | 조건부 실행 | dataflow_diagrams.control | | 다중 테이블 저장 | dataflow_diagrams.plan | | 버튼 전/후 트리거 | dataflow_diagrams.control.triggerType | | 필드 매핑 | dataflow_diagrams.plan.mappings | --- ## 6. 비즈니스 로직 설정 표준 형식 ### 기본 구조 ```json { "control": { "actionType": "update", "triggerType": "after", "conditions": [ { "id": "조건ID", "type": "condition", "field": "status", "operator": "=", "value": "대기", "dataType": "string" } ] }, "plan": { "mappings": [ { "id": "매핑ID", "sourceField": "소스필드", "targetField": "타겟필드", "targetTable": "타겟테이블", "valueType": "field" } ] } } ``` ### 조건 연산자 | 연산자 | 설명 | |--------|------| | `=` | 같음 | | `!=` | 다름 | | `>` | 큼 | | `<` | 작음 | | `>=` | 크거나 같음 | | `<=` | 작거나 같음 | | `LIKE` | 포함 | | `IN` | 목록에 포함 | | `IS NULL` | NULL 값 | ### 액션 타입 | 타입 | 설명 | |------|------| | `insert` | 새 데이터 삽입 | | `update` | 기존 데이터 수정 | | `delete` | 데이터 삭제 | ### 트리거 타입 | 타입 | 설명 | |------|------| | `before` | 버튼 클릭 전 실행 | | `after` | 버튼 클릭 후 실행 | --- ## 7. 화면 개발 순서 ### Step 1: 요구사항 분석 ``` 1. 어떤 테이블을 사용하는가? 2. 테이블 간 관계는? (FK) 3. 어떤 패턴인가? (A/B/C/D/E) 4. 어떤 버튼이 필요한가? 5. 각 버튼의 비즈니스 로직은? ``` ### Step 2: screen_definitions INSERT ```sql INSERT INTO screen_definitions ( screen_name, screen_code, description, table_name, company_code, created_at ) VALUES ( '화면명', 'SCREEN_CODE', '화면 설명', '메인테이블명', '회사코드', NOW() ) RETURNING screen_id; ``` ### Step 3: screen_layouts_v2 INSERT ```sql INSERT INTO screen_layouts_v2 ( screen_id, company_code, layout_data ) VALUES ( 위에서_받은_screen_id, '회사코드', '{"components": [...], "layout": {...}}'::jsonb ); ``` ### Step 4: dataflow_diagrams INSERT (비즈니스 로직 있는 경우) ```sql INSERT INTO dataflow_diagrams ( diagram_name, company_code, control, plan ) VALUES ( '화면명_제어', '회사코드', '{"조건설정"}'::jsonb, '{"실행계획"}'::jsonb ); ``` ### Step 5: screen_menu_assignments INSERT ```sql INSERT INTO screen_menu_assignments ( screen_id, menu_objid, company_code ) VALUES ( screen_id, 메뉴ID, '회사코드' ); ``` --- ## 8. 예시: 수주관리 화면 ### 요구사항 ``` 화면명: 수주관리 패턴: B (마스터-디테일) 테이블: - 마스터: order_master - 디테일: order_detail 버튼: - [저장]: order_master에 저장 - [확정]: - 조건: status = '대기' - 동작: status를 '확정'으로 변경 - 추가: order_history에 이력 저장 ``` ### screen_definitions ```sql INSERT INTO screen_definitions ( screen_name, screen_code, description, table_name, company_code ) VALUES ( '수주관리', 'ORDER_MNG', '수주를 관리하는 화면', 'order_master', 'COMPANY_A' ); ``` ### screen_layouts_v2 (layout_data) ```json { "components": [ { "id": "search-1", "componentType": "v2-table-search-widget", "position": {"x": 0, "y": 0}, "size": {"width": 1920, "height": 80} }, { "id": "split-1", "componentType": "v2-split-panel-layout", "position": {"x": 0, "y": 80}, "size": {"width": 1920, "height": 800}, "componentConfig": { "leftPanel": { "tableName": "order_master" }, "rightPanel": { "tableName": "order_detail", "relation": { "type": "detail", "foreignKey": "order_id" } }, "splitRatio": 30 } }, { "id": "btn-save", "componentType": "v2-button-primary", "componentConfig": { "text": "저장", "action": {"type": "save"} } }, { "id": "btn-confirm", "componentType": "v2-button-primary", "componentConfig": { "text": "확정", "enableDataflowControl": true, "dataflowDiagramId": 123 } } ] } ``` ### dataflow_diagrams (확정 버튼 로직) ```json { "control": { "actionType": "update", "triggerType": "after", "conditions": [ { "id": "cond-1", "type": "condition", "field": "status", "operator": "=", "value": "대기", "dataType": "string" } ] }, "plan": { "actions": [ { "id": "action-1", "actionType": "update", "targetTable": "order_master", "fieldMappings": [ {"targetField": "status", "defaultValue": "확정"} ] }, { "id": "action-2", "actionType": "insert", "targetTable": "order_history", "fieldMappings": [ {"sourceField": "order_no", "targetField": "order_no"}, {"sourceField": "customer_name", "targetField": "customer_name"}, {"defaultValue": "#NOW", "targetField": "confirmed_at"} ] } ] } } ``` --- ## 9. 지원하지 않는 UI (별도 개발 필요) | UI 유형 | 상태 | 대안 | |---------|------|------| | 트리 뷰 | ❌ 미지원 | 테이블로 대체 or `v2-tree-view` 개발 필요 | | 그룹화 테이블 | ❌ 미지원 | 일반 테이블로 대체 or `v2-grouped-table` 개발 필요 | | 간트 차트 | ❌ 미지원 | 별도 개발 필요 | | 드래그앤드롭 | ❌ 미지원 | 순서 컬럼으로 대체 | --- ## 10. 체크리스트 ### 화면 생성 시 ``` □ screen_definitions INSERT 완료 □ screen_layouts_v2 INSERT 완료 □ screen_menu_assignments INSERT 완료 (메뉴 연결 필요 시) □ company_code 필터링 적용 □ 사용한 컴포넌트가 모두 v2- 접두사인지 확인 ``` ### 비즈니스 로직 설정 시 ``` □ 기본 액션 (저장/삭제)만 → 화면 디자이너에서 설정 □ 조건부/다중테이블 → dataflow_diagrams INSERT □ 버튼 config에 dataflowDiagramId 연결 □ control.conditions 설정 □ plan.actions 또는 plan.mappings 설정 ``` --- ## 11. 비즈니스 로직 요청 양식 (필수) > **경고**: 양식대로 안 쓰면 처리 안 함. 병신아 제대로 써. > 대충 쓰면 대충 만들어지고, 안 쓰면 안 만들어줌. ### 11.1 양식 템플릿 ``` === 비즈니스 로직 요청서 === 【화면 정보】 - 화면명: - 회사코드: - 메뉴ID (있으면): 【테이블 정보】 - 메인 테이블: - 디테일 테이블 (있으면): - 관계 FK (있으면): 【버튼 목록】 버튼1: - 버튼명: - 동작 유형: (저장/삭제/수정/조회/기타) - 조건 (있으면): - 대상 테이블: - 추가 동작 (있으면): 버튼2: - 버튼명: - ... 【추가 요구사항】 - ``` ### 11.2 작성 예시 (올바른 예시) ``` === 비즈니스 로직 요청서 === 【화면 정보】 - 화면명: 수주관리 - 회사코드: ssalmeog - 메뉴ID: 55566 【테이블 정보】 - 메인 테이블: order_master - 디테일 테이블: order_detail - 관계 FK: order_id 【버튼 목록】 버튼1: - 버튼명: 저장 - 동작 유형: 저장 - 조건: 없음 - 대상 테이블: order_master, order_detail - 추가 동작: 없음 버튼2: - 버튼명: 확정 - 동작 유형: 수정 - 조건: status = '대기' - 대상 테이블: order_master - 추가 동작: 1. status를 '확정'으로 변경 2. order_history에 이력 INSERT (order_no, customer_name, confirmed_at=현재시간) 버튼3: - 버튼명: 삭제 - 동작 유형: 삭제 - 조건: status != '확정' - 대상 테이블: order_master, order_detail (cascade) - 추가 동작: 없음 【추가 요구사항】 - 확정된 수주는 수정/삭제 불가 - 수주번호 자동채번 (ORDER-YYYYMMDD-0001) ``` ### 11.3 잘못된 예시 (이렇게 쓰면 안 됨) ``` ❌ "수주관리 화면 만들어줘" → 테이블이 뭔데? 버튼은? 로직은? ❌ "저장 버튼 누르면 저장해줘" → 어떤 테이블에? 조건은? ❌ "확정하면 재고 처리해줘" → 어떤 테이블? 증가? 감소? 얼마나? ❌ "이전 화면이랑 비슷하게" → 이전 화면이 뭔데? ``` ### 11.4 복잡한 로직 추가 양식 조건이 여러 개이거나 복잡한 경우: ``` 【복잡한 버튼 로직】 버튼명: 출고확정 실행 조건: 조건1: status = '출고대기' AND 조건2: qty > 0 AND 조건3: warehouse_id IS NOT NULL 실행 동작 (순서대로): 1. shipment_master.status → '출고완료' 2. inventory에서 qty만큼 감소 (WHERE item_code = 현재행.item_code) 3. shipment_history에 INSERT: - shipment_no ← 현재행.shipment_no - shipped_qty ← 현재행.qty - shipped_at ← 현재시간 - shipped_by ← 현재사용자 실패 시: - 재고 부족: "재고가 부족합니다" 메시지 - 조건 불충족: "출고대기 상태만 확정 가능합니다" 메시지 ``` --- ## 12. 참고 경로 | 항목 | 경로/테이블 | |------|------------| | 제어관리 페이지 | `/admin/systemMng/dataflow` | | 화면 정의 테이블 | `screen_definitions` | | 레이아웃 테이블 | `screen_layouts_v2` | | 제어관리 테이블 | `dataflow_diagrams` | | 메뉴 연결 테이블 | `screen_menu_assignments` |