V2 이벤트 시스템 통합 및 데이터 전달 인터페이스 구현: UnifiedRepeater 컴포넌트에 데이터 제공 및 수신 인터페이스를 추가하여 다른 컴포넌트와의 데이터 연동을 개선하였습니다. 또한, AggregationWidgetComponent와 RepeatContainerComponent에서 V2 표준 이벤트를 구독하여 데이터 변경 이벤트를 효율적으로 처리하도록 수정하였습니다. 이를 통해 컴포넌트 간의 데이터 흐름과 사용자 경험을 향상시켰습니다.

This commit is contained in:
juseok2
2026-01-29 23:20:23 +09:00
parent 8cdb8a3047
commit 3803b7dce1
16 changed files with 4654 additions and 85 deletions
@@ -0,0 +1,974 @@
# 품목정보 (Item Info)
> Screen ID: /screens/140
> 메뉴 경로: 기준정보 > 품목정보
> 테이블: `item_info`
## 1. 테이블 선택 및 화면 구조
### 1.1 사용 테이블
| 테이블명 | 용도 | 비고 |
|----------|------|------|
| `item_info` | 품목 기본정보 | 주 테이블 |
### 1.2 테이블 컬럼 정의 (실제 DB 기준)
| 컬럼명 | 표시명 | 타입 | 필수 | 설명 |
|--------|--------|------|------|------|
| `id` | ID | varchar(500) | PK | UUID 자동 생성 |
| `item_number` | 품번코드 | varchar(500) | | 품목 고유 코드 |
| `item_name` | 품명 | varchar(500) | | 품목명 |
| `status` | 상태 | varchar(500) | | 정상, 품절, 대기, 단종 |
| `size` | 규격 | varchar(500) | | 규격 정보 |
| `material` | 재질 | varchar(500) | | 재질 정보 |
| `inventory_unit` | 재고단위 | varchar(500) | | EA, kg, L, Sheet, Box |
| `weight` | 중량 | varchar(500) | | 중량 값 |
| `unit` | 단위 | varchar(500) | | g, kg, kg/L, t |
| `image` | 이미지 | varchar(500) | | 품목 이미지 경로 |
| `division` | 구분 | varchar(500) | | 원자재, 중간재, 완제품, 포장재 (카테고리 코드) |
| `type` | 유형 | varchar(500) | | 용도별 유형 |
| `meno` | 메모 | varchar(500) | | 비고 (오타: memo) |
| `selling_price` | 판매가 | varchar(500) | | 기본값 '0' |
| `standard_price` | 기준가 | varchar(500) | | 기본값 '0' |
| `currency_code` | 통화코드 | varchar(500) | | 기본값 'KRW' |
| `writer` | 등록자 | varchar(500) | | 작성자 ID |
| `company_code` | 회사코드 | varchar(500) | | 멀티테넌시 |
| `created_date` | 등록일 | timestamp | | 자동 생성 |
| `updated_date` | 수정일 | timestamp | | 자동 갱신 |
### 1.3 화면 구조 개요
- **화면 유형**: 목록형 (단일 테이블 CRUD)
- **주요 기능**:
- 품목 조회/검색/필터링
- 품목 등록/수정/삭제
- 그룹핑 (Group By)
- 코드 변경/합병
- 엑셀 업로드
- 컬럼 표시/숨기기 설정
---
## 2. 컴포넌트 배치도
### 2.1 전체 레이아웃
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ [검색 영역] │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ v2-table-search-widget │ │
│ │ ┌───────────┐ ┌───────────────┐ ┌───────────────┐ ┌─────────┐ │ │
│ │ │ 상태 │ │ 품번코드 │ │ 품명 │ │ [검색] │ │ │
│ │ │ (select) │ │ (text) │ │ (text) │ │ │ │ │
│ │ └───────────┘ └───────────────┘ └───────────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────────┤
│ [테이블 헤더 + 액션 버튼] │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ [코드변경][업로드][다운로드] [등록][복사][수정][삭제] │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────────┤
│ [데이터 테이블] │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ v2-table-list │ │
│ │ ┌──┬────┬────────┬────────┬──────┬──────┬────────┬─────┬─────┬────────┐ │ │
│ │ │☐ │상태│품번코드│품명 │규격 │재질 │재고단위│중량 │단위 │구분 │ │ │
│ │ ├──┼────┼────────┼────────┼──────┼──────┼────────┼─────┼─────┼────────┤ │ │
│ │ │☐ │정상│R_001 │테스트A │100mm │SUS304│EA │1.5 │kg │원자재 │ │ │
│ │ │☐ │대기│R_002 │테스트B │200mm │AL │kg │2.0 │kg │완제품 │ │ │
│ │ └──┴────┴────────┴────────┴──────┴──────┴────────┴─────┴─────┴────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### 2.2 컴포넌트 목록
| 컴포넌트 타입 | 역할 |
|---------------|------|
| v2-table-search-widget | 검색 필터 |
| v2-table-list | 품목 데이터 테이블 |
| v2-button-primary | 코드변경 |
| v2-button-primary | 업로드 (엑셀) |
| v2-button-primary | 다운로드 (엑셀) |
| v2-button-primary | 등록 (모달 열기) |
| v2-button-primary | 복사 (모달 열기) |
| v2-button-primary | 수정 (모달 열기) |
| v2-button-primary | 삭제 |
---
## 3. 화면 디자이너 설정 가이드
### 3.1 v2-table-search-widget (검색 필터) 설정
1. 좌측 컴포넌트 패널에서 `v2-table-search-widget` 드래그하여 화면 상단에 배치
2. 대상 테이블로 아래에 배치할 테이블 리스트 선택
> 💡 **참고**: 검색 필터는 사용자가 런타임에서 원하는 필드를 직접 추가/삭제하여 사용할 수 있습니다. 별도의 필드 설정이 필요 없습니다.
---
### 3.2 v2-table-list (품목 테이블) 설정
#### Step 1: 컴포넌트 추가
1. 좌측 컴포넌트 패널에서 `v2-table-list` 드래그하여 검색 필터 아래에 배치
#### Step 2: 데이터 소스 설정
| 설정 항목 | 설정 값 |
|-----------|---------|
| 테이블 선택 | `item_info` |
| 자동 컬럼 생성 | ✅ 체크 (테이블 컬럼 자동 로드) |
#### Step 3: 컬럼 설정
**[컬럼 설정]** 패널에서 표시할 컬럼 선택 및 순서 조정:
| 순서 | 컬럼 | 표시명 | 너비 | 정렬 | 표시 | 특수 설정 |
|------|------|--------|------|------|------|-----------|
| 1 | `status` | 상태 | 80 | 중앙 | ✅ | 뱃지 스타일 (색상별) |
| 2 | `item_number` | 품번코드 | 140 | 좌측 | ✅ | |
| 3 | `item_name` | 품명 | 200 | 좌측 | ✅ | 굵게 표시 |
| 4 | `size` | 규격 | 150 | 좌측 | ✅ | |
| 5 | `material` | 재질 | 150 | 좌측 | ✅ | |
| 6 | `inventory_unit` | 재고단위 | 100 | 중앙 | ✅ | |
| 7 | `weight` | 중량 | 80 | 우측 | ✅ | |
| 8 | `unit` | 단위 | 80 | 중앙 | ✅ | |
| 9 | `image` | 이미지 | 80 | 중앙 | ✅ | 이미지 미리보기 |
| 10 | `division` | 구분 | 100 | 중앙 | ✅ | 카테고리 표시 |
| 11 | `type` | 유형 | 100 | 중앙 | ✅ | |
| 12 | `selling_price` | 판매가 | 100 | 우측 | ☐ | 숫자 포맷 |
| 13 | `standard_price` | 기준가 | 100 | 우측 | ☐ | 숫자 포맷 |
| 14 | `meno` | 메모 | 180 | 좌측 | ☐ | |
| 15 | `writer` | 등록자 | 100 | 좌측 | ☐ | 읽기 전용 |
| 16 | `created_date` | 등록일 | 120 | 중앙 | ☐ | 읽기 전용 |
| 17 | `updated_date` | 수정일 | 120 | 중앙 | ☐ | 읽기 전용 |
#### Step 4: 기능 설정
| 설정 항목 | 설정 값 | 설명 |
|-----------|---------|------|
| 체크박스 | ✅ 사용 | 다중 선택 활성화 |
| 페이지네이션 | ✅ 사용 | |
| 페이지 크기 | 20 | 기본 표시 행 수 |
| 정렬 | ✅ 사용 | 컬럼 헤더 클릭 정렬 |
| 컬럼 리사이즈 | ✅ 사용 | 컬럼 너비 조정 |
| 그룹핑 | ✅ 사용 | Group By 기능 |
#### Step 5: 그룹핑 옵션 설정
Group By 드롭다운에 표시할 컬럼 선택:
-`status` (상태)
-`division` (구분)
-`type` (유형)
-`inventory_unit` (재고단위)
-`writer` (등록자)
---
### 3.3 버튼 설정
#### 좌측 버튼 그룹
##### 코드변경 버튼
| 설정 항목 | 설정 값 |
|-----------|---------|
| 라벨 | `코드변경` |
| 액션 타입 | `code_merge` |
| 스타일 | `secondary` |
| 선택 필수 | ✅ 체크 (복수 선택) |
| 병합 대상 컬럼 | `item_number` |
| 데이터플로우 연결 | 품번코드 통합 (flow_id: 18) |
##### 업로드 버튼
| 설정 항목 | 설정 값 |
|-----------|---------|
| 라벨 | `업로드` |
| 액션 타입 | `excel_upload` |
| 스타일 | `secondary` |
| 대상 테이블 | `item_info` |
##### 다운로드 버튼
| 설정 항목 | 설정 값 |
|-----------|---------|
| 라벨 | `다운로드` |
| 액션 타입 | `excel_download` |
| 스타일 | `secondary` |
| 대상 | 현재 테이블 리스트 |
#### 우측 버튼 그룹
##### 등록 버튼
| 설정 항목 | 설정 값 |
|-----------|---------|
| 라벨 | `등록` |
| 액션 타입 | `modal` |
| 스타일 | `default` |
| 연결 화면 | 품목 등록/수정 화면 (아래 3.4 참조) |
| 모달 제목 | 품목 등록 |
| 모달 사이즈 | `md` |
##### 복사 버튼
| 설정 항목 | 설정 값 |
|-----------|---------|
| 라벨 | `복사` |
| 액션 타입 | `copy` |
| 스타일 | `default` |
| 선택 필수 | ✅ 체크 (1개만) |
| 연결 화면 | 품목 등록/수정 화면 (아래 3.4 참조) |
| 동작 | 선택된 데이터를 복사하여 신규 등록 폼에 채움 |
##### 수정 버튼
| 설정 항목 | 설정 값 |
|-----------|---------|
| 라벨 | `수정` |
| 액션 타입 | `edit` |
| 스타일 | `default` |
| 선택 필수 | ✅ 체크 (1개만) |
| 연결 화면 | 품목 등록/수정 화면 (아래 3.4 참조) |
| 동작 | 선택된 데이터 수정 모드로 폼 열기 |
##### 삭제 버튼
| 설정 항목 | 설정 값 |
|-----------|---------|
| 라벨 | `삭제` |
| 액션 타입 | `delete` |
| 스타일 | `default` |
| 선택 필수 | ✅ 체크 (복수 선택 가능) |
| 확인 메시지 | 선택한 품목을 삭제하시겠습니까? |
| 삭제 후 동작 | 테이블 새로고침 |
---
### 3.4 품목 등록/수정 화면 (모달용 화면)
> 📌 **별도 화면 생성 필요**: 등록/복사/수정 버튼에 연결할 모달 화면을 새로 생성합니다.
>
> 💡 **동일 화면 공유**: 등록, 복사, 수정 버튼 모두 동일한 폼 화면을 사용합니다.
> - **등록**: 빈 폼으로 열림
> - **복사**: 선택된 데이터가 채워진 상태로 열림 (신규 등록)
> - **수정**: 선택된 데이터가 채워진 상태로 열림 (기존 데이터 업데이트)
#### Step 1: 새 화면 생성
1. 화면 관리에서 **[+ 새 화면]** 클릭
2. 화면 정보 입력:
- 화면명: `품목 등록/수정`
- 테이블: `item_info`
- 화면 유형: `모달`
#### Step 2: 폼 필드 배치
**모달 레이아웃 배치도**:
```
┌─────────────────────────────────────────────────────────────┐
│ 품목 등록/수정 [✕] │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ 품번코드 * │ │ 품명 * │ │
│ │ [____________________] │ │ [____________________] │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ 규격 │ │ 재질 │ │
│ │ [____________________] │ │ [____________________] │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ ┌───────────┐ ┌───────────┐ │
│ │ 재고단위 * │ │ 중량 │ │ 중량단위 │ │
│ │ [EA ▼] │ │ [_______] │ │ [kg ▼] │ │
│ └─────────────────────────┘ └───────────┘ └───────────┘ │
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ 구분 * │ │ 유형 │ │
│ │ [원자재 ▼] │ │ [반도체용 ▼] │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ 판매가 │ │ 기준가 │ │
│ │ [____________________] │ │ [____________________] │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 메모 │ │
│ │ [__________________________________________________]│ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ 상태 * │ │
│ │ [정상 ▼] │ │
│ └─────────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────┤
│ [취소] [💾 저장] │
└─────────────────────────────────────────────────────────────┘
```
**필드 목록**:
| 순서 | 필드 (컬럼명) | 라벨 | 입력 타입 | 필수 | 비고 |
|------|---------------|------|-----------|------|------|
| 1 | `item_number` | 품번코드 | text | ✅ | |
| 2 | `item_name` | 품명 | text | ✅ | |
| 3 | `size` | 규격 | text | | |
| 4 | `material` | 재질 | text | | |
| 5 | `inventory_unit` | 재고단위 | select | ✅ | 옵션: EA, kg, L, Sheet, Box |
| 6 | `weight` | 중량 | number | | |
| 7 | `unit` | 중량단위 | select | | 옵션: g, kg, kg/L, t |
| 8 | `division` | 구분 | category | ✅ | 품목 구분 카테고리 |
| 9 | `type` | 유형 | select | | 옵션: 반도체용, 태양광용, 산업용, 의료용, 건축용, 사출용, 화장품용 |
| 10 | `selling_price` | 판매가 | number | | |
| 11 | `standard_price` | 기준가 | number | | |
| 12 | `meno` | 메모 | text | | |
| 13 | `status` | 상태 | select | ✅ | 옵션: 정상, 품절, 대기, 단종 |
#### Step 3: 버튼 배치
| 버튼 | 액션 타입 | 스타일 | 설정 |
|------|-----------|--------|------|
| 저장 | `저장` | primary | 저장 후 모달 닫기, 부모 화면 테이블 새로고침 |
| 취소 | `모달 닫기` | secondary | |
#### Step 4: 버튼에 화면 연결
1. 메인 화면(품목정보)으로 돌아가기
2. **등록 버튼** 선택 → 설정 패널에서:
- 액션 타입: `modal`
- 연결 화면: `품목 등록/수정` 선택
- 모달 제목: `품목 등록`
3. **복사 버튼** 선택 → 설정 패널에서:
- 액션 타입: `copy`
- 연결 화면: `품목 등록/수정` 선택
- 선택 필수: ✅ 체크
- 동작: 선택된 데이터를 복사하여 폼에 채움 (신규 등록)
4. **수정 버튼** 선택 → 설정 패널에서:
- 액션 타입: `edit`
- 연결 화면: `품목 등록/수정` 선택
- 선택 필수: ✅ 체크
- 동작: 선택된 데이터를 수정 모드로 폼에 채움
> 💡 **참고**: 컬럼별 스타일(뱃지 색상, 카테고리 표시 등)은 컴포넌트 기본 스타일을 따릅니다. 필요시 테이블 관리에서 컬럼별 상세 설정을 조정할 수 있습니다.
---
## 4. 컴포넌트 연동 설정
### 4.1 이벤트 흐름
```
[검색 입력]
v2-table-search-widget
│ onFilterChange
v2-table-list (자동 재조회)
[데이터 표시]
[등록/복사/수정 버튼 클릭]
[모달 열기] → [폼 입력] → [저장]
│ │
│ ▼
│ refreshTable 이벤트
│ │
└────────────────────────┘
v2-table-list (재조회)
```
### 4.2 연동 설정
| 소스 컴포넌트 | 이벤트/액션 | 대상 컴포넌트 | 동작 |
|---------------|-------------|---------------|------|
| 검색 위젯 | onFilterChange | 테이블 리스트 | 필터 적용, 재조회 |
| 등록 버튼 | click | 모달 | 빈 폼으로 모달 열기 |
| 복사 버튼 | click | 모달 | 선택 데이터가 채워진 폼 열기 (신규) |
| 수정 버튼 | click | 모달 | 선택 데이터가 채워진 폼 열기 (수정) |
| 삭제 버튼 | click | 테이블 리스트 | 선택 항목 삭제 |
| 모달 저장 | afterSave | 테이블 리스트 | refreshTable |
### 4.3 TableOptionsContext 연동
```
v2-table-search-widget ──── TableOptionsContext ──── v2-table-list
│ │ │
│ registeredTables에서 │ │
│ item-table 참조 │ │
│ │ │
└── onFilterChange() ───────┼──────────────────────┘
필터 조건 전달 & 재조회
```
---
## 5. 사용자 사용 예시 시나리오
### 시나리오 1: 품목 조회
| 단계 | 사용자 동작 | 기대 결과 |
|------|-------------|-----------|
| 1 | 화면 진입 | 전체 품목 목록 표시 |
| 2 | 상태 필터를 "정상"으로 선택 | 자동 필터링 |
| 3 | 품명에 "폴리머" 입력 후 검색 | 품명에 "폴리머" 포함된 품목 표시 |
| 4 | Group by에서 "구분" 선택 | division별 그룹핑 |
### 시나리오 2: 품목 등록
| 단계 | 사용자 동작 | 기대 결과 |
|------|-------------|-----------|
| 1 | [등록] 버튼 클릭 | 빈 폼 모달 표시 |
| 2 | 데이터 입력 (품번코드, 품명, 규격 등) | 입력 필드 채움 |
| 3 | [저장] 버튼 클릭 | 저장 완료, 모달 닫힘, 목록 갱신 |
### 시나리오 3: 품목 복사
| 단계 | 사용자 동작 | 기대 결과 |
|------|-------------|-----------|
| 1 | 테이블에서 복사할 행 체크박스 선택 | 행 선택 표시 |
| 2 | [복사] 버튼 클릭 | 선택된 데이터가 채워진 폼 모달 표시 |
| 3 | 필요시 데이터 수정 (품번코드 등) | 필드 값 변경 |
| 4 | [저장] 버튼 클릭 | 신규 등록 완료, 목록 갱신 |
### 시나리오 4: 품목 수정
| 단계 | 사용자 동작 | 기대 결과 |
|------|-------------|-----------|
| 1 | 테이블에서 행 체크박스 선택 | 행 선택 표시 |
| 2 | [수정] 버튼 클릭 | 수정 모달 표시 (기존 데이터 로드) |
| 3 | 데이터 수정 | 필드 값 변경 |
| 4 | [저장] 버튼 클릭 | 저장 완료, 목록 갱신 |
### 시나리오 5: 품목 삭제
| 단계 | 사용자 동작 | 기대 결과 |
|------|-------------|-----------|
| 1 | 삭제할 행 체크박스 선택 (다중 가능) | 행 선택 표시 |
| 2 | [삭제] 버튼 클릭 | 삭제 확인 다이얼로그 표시 |
| 3 | 확인 | 삭제 완료, 목록 갱신 |
---
## 6. 검증 체크리스트
### 기본 기능
- [ ] 데이터 조회가 정상 동작하는가?
- [ ] 검색 필터 (상태, 품번코드, 품명)가 정상 동작하는가?
- [ ] 신규 등록이 정상 동작하는가?
- [ ] 복사 기능이 정상 동작하는가?
- [ ] 수정이 정상 동작하는가?
- [ ] 삭제가 정상 동작하는가?
- [ ] 코드변경이 정상 동작하는가?
- [ ] 엑셀 업로드가 정상 동작하는가?
- [ ] 엑셀 다운로드가 정상 동작하는가?
### 테이블 기능
- [ ] 페이지네이션이 정상 동작하는가?
- [ ] 정렬이 정상 동작하는가?
- [ ] 컬럼 너비 조정이 정상 동작하는가?
- [ ] 체크박스 선택이 정상 동작하는가?
### 검색 위젯 연동
- [ ] v2-table-search-widget과 v2-table-list 연동이 정상 동작하는가?
- [ ] 필터 변경 시 자동 재조회가 동작하는가?
- [ ] 초기화 버튼이 정상 동작하는가?
### 그룹핑 기능
- [ ] Group by 선택 시 그룹핑이 정상 동작하는가?
- [ ] 다중 그룹핑이 정상 동작하는가?
---
## 7. 참고 사항
### 관련 테이블
- `customer_item_mapping` - 거래처별 품목 매핑
- `supplier_item_mapping` - 공급업체별 품목 매핑
- `item_inspection_info` - 품목 검사 정보
- `item_routing_version` - 품목별 공정 버전
- `item_routing_detail` - 품목별 공정 상세
### 특이 사항
- `division` 컬럼은 카테고리 코드 (예: CATEGORY_191259)로 저장됨
- `meno` 컬럼은 오타로 보임 (원래 memo)
- `selling_price`, `standard_price`는 varchar로 저장됨 (숫자 형식 문자열)
- `company_code`는 멀티테넌시용 회사 코드
---
## 8. DB INSERT용 JSON 설정 (screen_layouts_v2 방식)
> 📌 실제 화면 저장은 `screen_definitions` + `screen_layouts_v2` 테이블을 사용합니다.
> `screen_layouts_v2`는 전체 레이아웃을 하나의 JSON (`layout_data`)으로 저장합니다.
### 8.1 테이블 구조
#### screen_definitions
| 컬럼명 | 타입 | 필수 | 기본값 | 설명 |
|--------|------|------|--------|------|
| `screen_id` | integer | PK | 자동 생성 (시퀀스) | 화면 고유 ID |
| `screen_name` | varchar(100) | ✅ | - | 화면명 |
| `screen_code` | varchar(50) | ✅ | **자동 생성** | `{company_code}_{순번}` 형식 |
| `table_name` | varchar(100) | | - | 기본 테이블명 |
| `company_code` | varchar(50) | ✅ | - | 회사 코드 |
| `description` | text | | - | 화면 설명 |
| `is_active` | char(1) | | `'Y'` | Y=활성, N=비활성, D=삭제 |
| `created_date` | timestamp | | `CURRENT_TIMESTAMP` | 생성일시 |
| `db_source_type` | varchar(10) | | `'internal'` | internal/external |
| `data_source_type` | varchar(20) | | `'database'` | database/rest_api |
#### screen_layouts_v2
| 컬럼명 | 타입 | 필수 | 기본값 | 설명 |
|--------|------|------|--------|------|
| `layout_id` | integer | PK | 자동 생성 (시퀀스) | 레이아웃 고유 ID |
| `screen_id` | integer | ✅ | - | 화면 ID (FK) |
| `company_code` | varchar(20) | ✅ | - | 회사 코드 |
| `layout_data` | jsonb | ✅ | `'{}'` | 전체 레이아웃 JSON |
| `created_at` | timestamp | | `now()` | 생성일시 |
| `updated_at` | timestamp | | `now()` | 수정일시 |
### 8.2 화면 정의 (screen_definitions)
> ⚠️ `screen_code`는 API 호출 시 자동 생성됩니다. (`{company_code}_{순번}` 형식)
**필수 입력 필드:**
```json
{
"screenName": "품목정보",
"tableName": "item_info",
"companyCode": "COMPANY_7",
"description": "품목 기본정보 관리 화면"
}
```
**전체 필드 (자동 생성 포함):**
```json
{
"screen_id": 140,
"screen_name": "품목정보",
"screen_code": "COMPANY_7_3",
"table_name": "item_info",
"company_code": "COMPANY_7",
"description": "품목 기본정보 관리 화면",
"is_active": "Y",
"db_source_type": "internal",
"data_source_type": "database",
"created_date": "2025-01-29T00:00:00.000Z"
}
```
### 8.2 레이아웃 데이터 (screen_layouts_v2.layout_data)
> 전체 레이아웃을 하나의 JSON으로 저장
```json
{
"version": "2.0",
"components": [
{
"id": "comp_search",
"url": "@/lib/registry/components/v2-table-search-widget",
"size": { "width": 1920, "height": 80 },
"position": { "x": 0, "y": 20, "z": 1 },
"overrides": {
"type": "v2-table-search-widget",
"label": "검색 필터",
"webTypeConfig": {}
},
"displayOrder": 0
},
{
"id": "comp_table",
"url": "@/lib/registry/components/v2-table-list",
"size": { "width": 1920, "height": 930 },
"position": { "x": 0, "y": 150, "z": 1 },
"overrides": {
"type": "v2-table-list",
"label": "테이블 리스트",
"filter": { "enabled": true, "filters": [] },
"height": "auto",
"actions": { "actions": [], "bulkActions": false, "showActions": false },
"columns": [
{ "align": "left", "order": 0, "format": "text", "visible": true, "sortable": true, "columnName": "status", "searchable": true, "displayName": "status" },
{ "align": "left", "order": 1, "format": "text", "visible": true, "sortable": true, "columnName": "item_number", "searchable": true, "displayName": "item_number" },
{ "align": "left", "order": 2, "format": "text", "visible": true, "sortable": true, "columnName": "item_name", "searchable": true, "displayName": "item_name" },
{ "align": "left", "order": 3, "format": "text", "visible": true, "sortable": true, "columnName": "size", "searchable": true, "displayName": "size" },
{ "align": "left", "order": 4, "format": "text", "visible": true, "sortable": true, "columnName": "material", "searchable": true, "displayName": "material" },
{ "align": "left", "order": 5, "format": "text", "visible": true, "sortable": true, "columnName": "inventory_unit", "searchable": true, "displayName": "inventory_unit" },
{ "align": "left", "order": 6, "format": "text", "visible": true, "sortable": true, "columnName": "weight", "searchable": true, "displayName": "weight" },
{ "align": "left", "order": 7, "format": "text", "visible": true, "sortable": true, "columnName": "unit", "searchable": true, "displayName": "unit" },
{ "align": "left", "order": 8, "format": "text", "visible": true, "sortable": true, "columnName": "division", "searchable": true, "displayName": "division" },
{ "align": "left", "order": 9, "format": "text", "visible": true, "sortable": true, "columnName": "type", "searchable": true, "displayName": "type" },
{ "align": "left", "order": 10, "format": "text", "visible": true, "sortable": true, "columnName": "writer", "searchable": true, "displayName": "writer" }
],
"autoLoad": true,
"checkbox": { "enabled": true, "multiple": true, "position": "left", "selectAll": true },
"pagination": { "enabled": true, "pageSize": 20, "showPageInfo": true, "pageSizeOptions": [10, 20, 50, 100], "showSizeSelector": true },
"showFooter": true,
"showHeader": true,
"tableStyle": { "theme": "default", "rowHeight": "normal", "borderStyle": "light", "headerStyle": "default", "hoverEffect": true, "alternateRows": true },
"displayMode": "table",
"stickyHeader": false,
"selectedTable": "item_info",
"webTypeConfig": {},
"horizontalScroll": { "enabled": true, "maxColumnWidth": 300, "minColumnWidth": 100, "maxVisibleColumns": 8 }
},
"displayOrder": 0
},
{
"id": "comp_btn_code_merge",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 88, "height": 40 },
"position": { "x": 10, "y": 100, "z": 1 },
"overrides": {
"text": "코드변경",
"type": "v2-button-primary",
"label": "기본 버튼",
"action": {
"type": "code_merge",
"errorMessage": "저장 중 오류가 발생했습니다.",
"successMessage": "저장되었습니다.",
"mergeColumnName": "item_number"
},
"variant": "primary",
"actionType": "button",
"webTypeConfig": {
"variant": "default",
"actionType": "custom",
"dataflowConfig": {
"flowConfig": { "flowId": 18, "flowName": "품번코드 통합", "contextData": {}, "executionTiming": "after" },
"selectedDiagramId": 18
}
}
},
"displayOrder": 0
},
{
"id": "comp_btn_upload",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 88, "height": 40 },
"position": { "x": 110, "y": 100, "z": 1 },
"overrides": {
"text": "업로드",
"type": "v2-button-primary",
"label": "기본 버튼",
"action": {
"type": "excel_upload",
"errorMessage": "저장 중 오류가 발생했습니다.",
"successMessage": "저장되었습니다."
},
"variant": "primary",
"actionType": "button",
"webTypeConfig": { "variant": "default", "actionType": "custom" }
},
"displayOrder": 0
},
{
"id": "comp_btn_download",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 88, "height": 40 },
"position": { "x": 210, "y": 100, "z": 1 },
"overrides": {
"text": "다운로드",
"type": "v2-button-primary",
"label": "기본 버튼",
"action": {
"type": "excel_download",
"errorMessage": "저장 중 오류가 발생했습니다.",
"successMessage": "저장되었습니다."
},
"variant": "primary",
"actionType": "button",
"webTypeConfig": { "variant": "default", "actionType": "custom" }
},
"displayOrder": 0
},
{
"id": "comp_btn_register",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 80, "height": 40 },
"position": { "x": 1550, "y": 100, "z": 1 },
"overrides": {
"text": "등록",
"type": "v2-button-primary",
"label": "기본 버튼",
"action": {
"type": "modal",
"modalSize": "md",
"modalTitle": "품목 등록",
"errorMessage": "저장 중 오류가 발생했습니다.",
"successMessage": "저장되었습니다.",
"targetScreenId": "{{modal_screen_id}}"
},
"variant": "primary",
"actionType": "button",
"webTypeConfig": { "variant": "default", "actionType": "custom" }
},
"displayOrder": 0
},
{
"id": "comp_btn_copy",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 80, "height": 40 },
"position": { "x": 1640, "y": 100, "z": 1 },
"overrides": {
"text": "복사",
"type": "v2-button-primary",
"label": "기본 버튼",
"action": {
"type": "copy",
"errorMessage": "저장 중 오류가 발생했습니다.",
"successMessage": "저장되었습니다.",
"targetScreenId": "{{modal_screen_id}}"
},
"variant": "primary",
"actionType": "button",
"webTypeConfig": { "variant": "default", "actionType": "custom" }
},
"displayOrder": 0
},
{
"id": "comp_btn_edit",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 80, "height": 40 },
"position": { "x": 1730, "y": 100, "z": 1 },
"overrides": {
"text": "수정",
"type": "v2-button-primary",
"label": "기본 버튼",
"action": {
"type": "edit",
"errorMessage": "저장 중 오류가 발생했습니다.",
"successMessage": "저장되었습니다.",
"targetScreenId": "{{modal_screen_id}}"
},
"variant": "primary",
"actionType": "button",
"webTypeConfig": { "variant": "default", "actionType": "custom" }
},
"displayOrder": 0
},
{
"id": "comp_btn_delete",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 80, "height": 40 },
"position": { "x": 1820, "y": 100, "z": 1 },
"overrides": {
"text": "삭제",
"type": "v2-button-primary",
"label": "기본 버튼",
"action": {
"type": "delete",
"errorMessage": "저장 중 오류가 발생했습니다.",
"successMessage": "저장되었습니다."
},
"variant": "primary",
"actionType": "button",
"webTypeConfig": { "variant": "default", "actionType": "custom" }
},
"displayOrder": 0
}
]
}
```
### 8.3 모달 화면 (품목 등록/수정)
#### 화면 정의 (필수 입력)
```json
{
"screenName": "품목 등록/수정",
"tableName": "item_info",
"companyCode": "COMPANY_7",
"description": "품목 등록/수정 폼 화면"
}
```
#### 레이아웃 데이터 (screen_layouts_v2.layout_data)
```json
{
"version": "2.0",
"components": [
{
"id": "comp_item_number",
"url": "@/lib/registry/components/v2-text-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 20, "y": 20, "z": 1 },
"overrides": {
"type": "v2-text-input",
"label": "품번코드",
"fieldName": "item_number",
"placeholder": "품번코드를 입력하세요",
"required": true
},
"displayOrder": 0
},
{
"id": "comp_item_name",
"url": "@/lib/registry/components/v2-text-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 340, "y": 20, "z": 1 },
"overrides": {
"type": "v2-text-input",
"label": "품명",
"fieldName": "item_name",
"placeholder": "품명을 입력하세요",
"required": true
},
"displayOrder": 1
},
{
"id": "comp_status",
"url": "@/lib/registry/components/v2-select-basic",
"size": { "width": 300, "height": 60 },
"position": { "x": 20, "y": 100, "z": 1 },
"overrides": {
"type": "v2-select-basic",
"label": "상태",
"fieldName": "status",
"options": ["정상", "품절", "대기", "단종"]
},
"displayOrder": 2
},
{
"id": "comp_btn_save",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 80, "height": 40 },
"position": { "x": 400, "y": 500, "z": 1 },
"overrides": {
"text": "저장",
"type": "v2-button-primary",
"label": "저장 버튼",
"action": {
"type": "save",
"closeModalAfterSave": true,
"refreshParentTable": true,
"successMessage": "저장되었습니다.",
"errorMessage": "저장 중 오류가 발생했습니다."
},
"variant": "primary",
"actionType": "button"
},
"displayOrder": 20
}
]
}
```
### 8.4 API 호출 방식
> 📌 실제 화면 생성은 API를 통해 진행됩니다. `screen_code`는 서버에서 자동 생성됩니다.
#### Step 1: 화면 코드 자동 생성 API
```http
GET /api/screens/generate-code?companyCode=COMPANY_7
```
**응답:**
```json
{
"success": true,
"data": { "screenCode": "COMPANY_7_4" }
}
```
#### Step 2: 화면 생성 API
```http
POST /api/screens
Content-Type: application/json
Authorization: Bearer {{token}}
{
"screenName": "",
"screenCode": "COMPANY_7_4",
"tableName": "item_info",
"companyCode": "COMPANY_7",
"description": " "
}
```
**응답:**
```json
{
"success": true,
"data": {
"screenId": 141,
"screenCode": "COMPANY_7_4",
"screenName": "품목정보"
}
}
```
#### Step 3: 레이아웃 저장 API
```http
PUT /api/screens/141/layout-v2
Content-Type: application/json
Authorization: Bearer {{token}}
{
"layoutData": {
"version": "2.0",
"components": [ /* 8.2 components */ ]
}
}
```
### 8.5 SQL 직접 INSERT (참고용)
> ⚠️ 일반적으로 API를 사용하지만, 대량 마이그레이션 시 직접 SQL 사용 가능
```sql
-- Step 1: 화면 정의 (screen_code는 수동 지정 필요)
INSERT INTO screen_definitions (
screen_name, screen_code, table_name, company_code, description
) VALUES (
'품목정보', 'COMPANY_7_4', 'item_info', 'COMPANY_7', '품목 기본정보 관리 화면'
) RETURNING screen_id;
-- Step 2: 레이아웃 저장 (screen_id 사용)
INSERT INTO screen_layouts_v2 (screen_id, company_code, layout_data)
VALUES (
141, -- 위에서 반환된 screen_id
'COMPANY_7',
'{"version": "2.0", "components": [...]}'::jsonb
);
```
### 8.6 주의사항
| 항목 | 설명 |
|------|------|
| `screen_code` | API 사용 시 `generateScreenCode` 먼저 호출, 형식: `{company_code}_{순번}` |
| `screen_id` | 화면 생성 후 반환되는 값, 레이아웃 저장 시 필요 |
| `component.id` | 고유 ID (UUID 또는 `comp_` prefix), 중복 불가 |
| `component.url` | `@/lib/registry/components/v2-xxx` 형식 |
| `{{modal_screen_id}}` | 모달 화면 먼저 생성 후 실제 ID로 치환 |
| `version` | 반드시 `"2.0"` 사용 |
| UNIQUE 제약 | `screen_layouts_v2``(screen_id, company_code)` 조합이 유니크 |
+126
View File
@@ -0,0 +1,126 @@
# 화면 구현 가이드
V2 컴포넌트를 활용한 ERP 화면 구현 가이드입니다.
## 폴더 구조
```
screen-implementation-guide/
├── 01_master-data/ # 기준정보
│ ├── company-info.md # 회사정보
│ ├── department.md # 부서관리
│ ├── item-info.md # 품목정보
│ └── options.md # 옵션설정
├── 02_sales/ # 영업관리
│ ├── quotation.md # 견적관리
│ ├── order.md # 수주관리
│ ├── customer.md # 거래처관리
│ ├── sales-item.md # 판매품목정보
│ └── options.md # 영업옵션설정
├── 03_production/ # 생산관리
│ ├── production-plan.md # 생산계획
│ ├── work-order.md # 작업지시
│ ├── production-result.md # 생산실적
│ ├── process-info.md # 공정정보관리
│ ├── bom.md # BOM관리
│ └── options.md # 생산옵션설정
├── 04_purchase/ # 구매관리
│ ├── purchase-order.md # 발주관리
│ ├── purchase-item.md # 구매품목정보
│ ├── supplier.md # 공급업체관리
│ ├── receiving.md # 입고관리
│ └── options.md # 구매옵션설정
├── 05_equipment/ # 설비관리
│ ├── equipment-info.md # 설비정보
│ └── options.md # 설비옵션설정
├── 06_logistics/ # 물류관리
│ ├── logistics-info.md # 물류정보관리
│ ├── inout.md # 입출고관리
│ ├── inventory.md # 재고현황
│ ├── warehouse.md # 창고정보관리
│ ├── shipping.md # 출고관리
│ └── options.md # 물류옵션설정
├── 07_quality/ # 품질관리
│ ├── inspection-info.md # 검사정보관리
│ ├── item-inspection.md # 품목검사정보
│ └── options.md # 품질옵션설정
└── README.md
```
## 문서 작성 형식
각 화면별 문서는 다음 구조로 작성됩니다:
### 1. 테이블 선택 및 화면 구조
- 사용할 데이터베이스 테이블
- 테이블 간 관계 (FK, 조인)
- 화면 전체 레이아웃
### 2. 컴포넌트 배치도
- ASCII 다이어그램으로 컴포넌트 배치
- 각 영역별 사용 컴포넌트 명시
### 3. 각 컴포넌트별 설정
- 컴포넌트 타입
- 상세 설정 (config)
- 연동 설정
### 4. 사용자 사용 예시 시나리오
- 테스트 시나리오
- 기대 동작
- 검증 포인트
## 메뉴별 Screen ID 매핑
| 메뉴 | Screen ID | 상태 |
|------|-----------|------|
| **기준정보** | | |
| 회사정보 | /screens/138 | 활성화 |
| 부서관리 | /screens/1487 | 활성화 |
| 품목정보 | /screens/140 | 활성화 |
| 옵션설정 | /screens/1421 | 활성화 |
| **영업관리** | | |
| 견적관리 | - | 활성화 |
| 수주관리 | /screens/156 | 활성화 |
| 거래처관리 | - | 활성화 |
| 판매품목정보 | - | 활성화 |
| 영업옵션설정 | /screens/1552 | 활성화 |
| **생산관리** | | |
| 생산계획 | - | 활성화 |
| 작업지시 | - | 활성화 |
| 생산실적 | - | 활성화 |
| 공정정보관리 | /screens/1599 | 활성화 |
| BOM관리 | - | 활성화 |
| 생산옵션설정 | /screens/1606 | 활성화 |
| **구매관리** | | |
| 발주관리 | /screens/1244 | 활성화 |
| 구매품목정보 | /screens/1061 | 활성화 |
| 공급업체관리 | /screens/1053 | 활성화 |
| 입고관리 | /screens/1064 | 활성화 |
| 구매옵션설정 | /screens/1057 | 활성화 |
| **설비관리** | | |
| 설비정보 | /screens/1253 | 활성화 |
| 설비옵션설정 | /screens/1264 | 활성화 |
| **물류관리** | | |
| 물류정보관리 | /screens/1556 | 활성화 |
| 입출고관리 | - | 활성화 |
| 재고현황 | /screens/1587 | 활성화 |
| 창고정보관리 | /screens/1562 | 활성화 |
| 출고관리 | /screens/2296 | 활성화 |
| 물류옵션설정 | /screens/1559 | 활성화 |
| **품질관리** | | |
| 검사정보관리 | /screens/1616 | 활성화 |
| 품목검사정보 | /screens/2089 | 활성화 |
| 품질옵션설정 | /screens/1622 | 활성화 |
## 참고 문서
- [V2 컴포넌트 분석 가이드](../V2_컴포넌트_분석_가이드.md)
- [V2 컴포넌트 연동 가이드](../V2_컴포넌트_연동_가이드.md)
@@ -0,0 +1,212 @@
# [화면명]
> Screen ID: /screens/XXX
> 메뉴 경로: [L2 메뉴] > [L3 메뉴]
## 1. 테이블 선택 및 화면 구조
### 1.1 사용 테이블
| 테이블명 | 용도 | 비고 |
|----------|------|------|
| `table_name` | 마스터 데이터 | 주 테이블 |
| `detail_table` | 디테일 데이터 | FK: master_id |
### 1.2 테이블 관계
```
┌─────────────────┐ ┌─────────────────┐
│ master_table │ │ detail_table │
├─────────────────┤ ├─────────────────┤
│ id (PK) │──1:N──│ master_id (FK) │
│ name │ │ id (PK) │
│ ... │ │ ... │
└─────────────────┘ └─────────────────┘
```
### 1.3 화면 구조 개요
- **화면 유형**: [목록형 / 마스터-디테일 / 단일 폼 / 복합]
- **주요 기능**: [CRUD / 조회 / 집계 등]
---
## 2. 컴포넌트 배치도
### 2.1 전체 레이아웃
```
┌─────────────────────────────────────────────────────────────┐
│ [검색 영역] v2-table-search-widget │
├─────────────────────────────────────────────────────────────┤
│ │
│ [메인 테이블] v2-table-list │
│ │
├─────────────────────────────────────────────────────────────┤
│ [버튼 영역] v2-button-primary (신규, 저장, 삭제) │
└─────────────────────────────────────────────────────────────┘
```
### 2.2 컴포넌트 목록
| 컴포넌트 ID | 컴포넌트 타입 | 역할 |
|-------------|---------------|------|
| `search-widget` | v2-table-search-widget | 검색 필터 |
| `main-table` | v2-table-list | 데이터 목록 |
| `btn-new` | v2-button-primary | 신규 등록 |
| `btn-save` | v2-button-primary | 저장 |
| `btn-delete` | v2-button-primary | 삭제 |
---
## 3. 각 컴포넌트별 설정
### 3.1 v2-table-search-widget
```json
{
"targetTableId": "main-table",
"searchFields": [
{
"field": "name",
"label": "이름",
"type": "text"
},
{
"field": "status",
"label": "상태",
"type": "select",
"options": [
{ "value": "active", "label": "활성" },
{ "value": "inactive", "label": "비활성" }
]
}
]
}
```
### 3.2 v2-table-list
```json
{
"tableName": "master_table",
"columns": [
{
"field": "id",
"headerName": "ID",
"width": 80,
"visible": false
},
{
"field": "name",
"headerName": "이름",
"width": 150
},
{
"field": "status",
"headerName": "상태",
"width": 100
}
],
"features": {
"checkbox": true,
"pagination": true,
"sorting": true
},
"pagination": {
"pageSize": 20
}
}
```
### 3.3 v2-button-primary (저장)
```json
{
"label": "저장",
"actionType": "save",
"variant": "default",
"afterSaveActions": ["refreshTable"]
}
```
---
## 4. 컴포넌트 연동 설정
### 4.1 이벤트 흐름
```
[검색 입력]
v2-table-search-widget
│ onFilterChange
v2-table-list (자동 재조회)
[데이터 표시]
```
### 4.2 연동 설정
| 소스 컴포넌트 | 이벤트/액션 | 대상 컴포넌트 | 동작 |
|---------------|-------------|---------------|------|
| search-widget | onFilterChange | main-table | 필터 적용 |
| btn-save | click | main-table | refreshTable |
---
## 5. 사용자 사용 예시 시나리오
### 시나리오 1: 데이터 조회
| 단계 | 사용자 동작 | 기대 결과 |
|------|-------------|-----------|
| 1 | 화면 진입 | 전체 목록 표시 |
| 2 | 검색어 입력 | 필터링된 결과 표시 |
| 3 | 정렬 클릭 | 정렬 순서 변경 |
### 시나리오 2: 데이터 등록
| 단계 | 사용자 동작 | 기대 결과 |
|------|-------------|-----------|
| 1 | [신규] 버튼 클릭 | 등록 모달/폼 표시 |
| 2 | 데이터 입력 | 입력 필드 채움 |
| 3 | [저장] 버튼 클릭 | 저장 완료, 목록 갱신 |
### 시나리오 3: 데이터 수정
| 단계 | 사용자 동작 | 기대 결과 |
|------|-------------|-----------|
| 1 | 행 더블클릭 | 수정 모달/폼 표시 |
| 2 | 데이터 수정 | 필드 값 변경 |
| 3 | [저장] 버튼 클릭 | 저장 완료, 목록 갱신 |
### 시나리오 4: 데이터 삭제
| 단계 | 사용자 동작 | 기대 결과 |
|------|-------------|-----------|
| 1 | 행 체크박스 선택 | 선택 표시 |
| 2 | [삭제] 버튼 클릭 | 삭제 확인 다이얼로그 |
| 3 | 확인 | 삭제 완료, 목록 갱신 |
---
## 6. 검증 체크리스트
- [ ] 데이터 조회가 정상 동작하는가?
- [ ] 검색 필터가 정상 동작하는가?
- [ ] 신규 등록이 정상 동작하는가?
- [ ] 수정이 정상 동작하는가?
- [ ] 삭제가 정상 동작하는가?
- [ ] 페이지네이션이 정상 동작하는가?
- [ ] 정렬이 정상 동작하는가?
---
## 7. 참고 사항
- 관련 화면: [관련 화면명](./related-screen.md)
- 특이 사항: 없음