ERP-node/docs/screen-implementation-guide/화면개발_표준_가이드.md

707 lines
17 KiB
Markdown

# 화면 개발 표준 가이드
> **목적**: 어떤 개발자/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` |