품목정보 (Item Info)
Screen ID: /screens/140
메뉴 경로: 기준정보 > 품목정보
테이블: item_info
⚠️ 문서 사용 안내
이 문서는 "품목정보" 화면의 구현 예시입니다.
📌 중요: JSON 데이터는 참고용입니다!
이 문서에 포함된 JSON 설정(레이아웃, 컴포넌트 구성 등)은 품목정보 화면에 특화된 예시입니다.
다른 화면을 구현할 때:
- 이 JSON을 그대로 복사해서 사용하지 마세요
- 해당 화면의 테이블 구조를 먼저 분석하세요
- 화면의 요구사항과 기능을 파악하세요
- 분석 결과에 맞는 새로운 JSON 구조를 작성하세요
참고해야 할 항목
- ✅ 문서 구조 및 작성 형식
- ✅ V2 컴포넌트 종류 및 사용법
- ✅ API 호출 방식 및 DB INSERT 절차
- ✅ 컴포넌트 설정 패턴 (position, size, overrides 구조)
복사하면 안 되는 항목
- ❌ 테이블명 (
item_info → 해당 화면의 테이블로 변경)
- ❌ 컬럼 설정 (해당 테이블의 컬럼에 맞게 작성)
- ❌ 필드명 (
fieldName, columnName 등)
- ❌ 화면명, screen_code, company_code
- ❌ screen_id, targetScreenId (동적 생성되는 값)
🚨 컴포넌트 부족 시 필수 명시 사항
화면 분석 결과, 현재 V2 컴포넌트로 구현이 불가능한 기능이 있을 경우:
- 문서에 "구현 불가 항목" 섹션을 반드시 추가
- 다음 형식으로 명시:
## 🚫 구현 불가 항목 (컴포넌트 개발 필요)
| 기능 | 필요한 컴포넌트 | 현재 상태 | 비고 |
|------|-----------------|-----------|------|
| 트리 구조 표시 | v2-tree-view | 미구현 | 계층형 데이터 표시 필요 |
| 드래그 앤 드롭 | v2-drag-drop | 미구현 | 순서 변경 기능 |
- 컴포넌트 개발 우선순위/중요도 명시
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 컴포넌트 목록
📌 V2 컴포넌트 전체 목록 - 화면 구성 시 사용 가능한 컴포넌트
| 컴포넌트 ID |
설명 |
카테고리 |
v2-input |
텍스트, 숫자, 비밀번호, 이메일 등 입력 |
입력 |
v2-select |
드롭다운, 콤보박스, 라디오, 체크박스 |
입력 |
v2-date |
날짜/시간 입력 |
입력 |
v2-button-primary |
버튼 |
액션 |
v2-table-list |
테이블 리스트 (CRUD) |
테이블 |
v2-table-search-widget |
테이블 검색/필터 위젯 |
유틸리티 |
v2-pivot-grid |
피벗 그리드 (다차원 분석) |
테이블 |
v2-aggregation-widget |
집계 위젯 |
위젯 |
v2-text-display |
텍스트 표시 (읽기 전용) |
표시 |
v2-card-display |
카드 표시 |
표시 |
v2-divider-line |
구분선 |
레이아웃 |
v2-section-card |
섹션 카드 (그룹핑) |
레이아웃 |
v2-section-paper |
섹션 페이퍼 (그룹핑) |
레이아웃 |
v2-split-panel-layout |
분할 패널 레이아웃 |
레이아웃 |
v2-repeat-container |
반복 컨테이너 |
레이아웃 |
v2-repeater |
리피터 (동적 행) |
레이아웃 |
v2-category-manager |
카테고리 관리 |
특수 |
v2-numbering-rule |
채번규칙 |
특수 |
v2-media |
미디어 (이미지/영상) |
미디어 |
v2-rack-structure |
랙 구조 (창고) |
특수 |
v2-location-swap-selector |
위치 스왑 선택기 |
특수 |
2.3 이 화면에서 사용하는 컴포넌트
| 컴포넌트 타입 |
역할 |
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 |
삭제 |
v2-input |
모달 - 텍스트 입력 필드 |
v2-select |
모달 - 선택 필드 |
3. 화면 디자이너 설정 가이드
3.1 v2-table-search-widget (검색 필터) 설정
- 좌측 컴포넌트 패널에서
v2-table-search-widget 드래그하여 화면 상단에 배치
- 대상 테이블로 아래에 배치할 테이블 리스트 선택
💡 참고: 검색 필터는 사용자가 런타임에서 원하는 필드를 직접 추가/삭제하여 사용할 수 있습니다. 별도의 필드 설정이 필요 없습니다.
3.2 v2-table-list (품목 테이블) 설정
Step 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: 새 화면 생성
- 화면 관리에서 [+ 새 화면] 클릭
- 화면 정보 입력:
- 화면명:
품목 등록/수정
- 테이블:
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: 버튼에 화면 연결
- 메인 화면(품목정보)으로 돌아가기
- 등록 버튼 선택 → 설정 패널에서:
- 액션 타입:
modal
- 연결 화면:
품목 등록/수정 선택
- 모달 제목:
품목 등록
- 복사 버튼 선택 → 설정 패널에서:
- 액션 타입:
copy
- 연결 화면:
품목 등록/수정 선택
- 선택 필수: ✅ 체크
- 동작: 선택된 데이터를 복사하여 폼에 채움 (신규 등록)
- 수정 버튼 선택 → 설정 패널에서:
- 액션 타입:
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. 검증 체크리스트
기본 기능
테이블 기능
검색 위젯 연동
그룹핑 기능
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)으로 저장합니다.
⚠️ 주의: 아래 JSON은 "품목정보" 화면 전용 예시입니다!
다른 화면 구현 시:
- 테이블 분석 필수 - 해당 화면이 사용하는 테이블 구조 파악
- 컬럼 재정의 -
columns, fieldName 등을 해당 테이블에 맞게 작성
- 기능 요구사항 반영 - 버튼, 모달, 액션 등을 화면 요구사항에 맞게 구성
- ID 값 동적 처리 -
screen_id, targetScreenId는 생성 시 할당되는 값 사용
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}_{순번} 형식)
필수 입력 필드:
{
"screenName": "품목정보",
"tableName": "item_info",
"companyCode": "COMPANY_7",
"description": "품목 기본정보 관리 화면"
}
전체 필드 (자동 생성 포함):
{
"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으로 저장
{
"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": 1
},
{
"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" }
},
"displayOrder": 2
},
{
"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": 3
},
{
"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": 4
},
{
"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": "lg",
"modalTitle": "품목 등록",
"targetScreenId": {{modal_screen_id}},
"errorMessage": "저장 중 오류가 발생했습니다.",
"successMessage": "저장되었습니다."
},
"variant": "primary",
"actionType": "button",
"webTypeConfig": { "variant": "default", "actionType": "custom" }
},
"displayOrder": 5
},
{
"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",
"modalSize": "lg",
"modalTitle": "품목 복사",
"targetScreenId": {{modal_screen_id}},
"errorMessage": "복사 중 오류가 발생했습니다.",
"successMessage": "복사되었습니다."
},
"variant": "primary",
"actionType": "button",
"webTypeConfig": { "variant": "default", "actionType": "custom" }
},
"displayOrder": 6
},
{
"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",
"modalSize": "lg",
"modalTitle": "품목 수정",
"targetScreenId": {{modal_screen_id}},
"errorMessage": "수정 중 오류가 발생했습니다.",
"successMessage": "수정되었습니다."
},
"variant": "primary",
"actionType": "button",
"webTypeConfig": { "variant": "default", "actionType": "custom" }
},
"displayOrder": 7
},
{
"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": "danger",
"actionType": "button",
"webTypeConfig": { "variant": "default", "actionType": "custom" }
},
"displayOrder": 8
}
]
}
8.3 모달 화면 (품목 등록/수정)
화면 정의 (필수 입력)
{
"screenName": "품목 등록/수정",
"tableName": "item_info",
"companyCode": "COMPANY_7",
"description": "품목 등록/수정 폼 화면"
}
레이아웃 데이터 (screen_layouts_v2.layout_data)
📌 실제 적용된 레이아웃 - 품목 등록/수정 폼 (모달용)
{
"version": "2.0",
"components": [
{
"id": "comp_item_number",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 20, "y": 20, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "품번코드",
"fieldName": "item_number",
"placeholder": "품번코드를 입력하세요",
"required": true
},
"displayOrder": 0
},
{
"id": "comp_item_name",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 340, "y": 20, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "품명",
"fieldName": "item_name",
"placeholder": "품명을 입력하세요",
"required": true
},
"displayOrder": 1
},
{
"id": "comp_size",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 20, "y": 100, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "규격",
"fieldName": "size",
"placeholder": "규격을 입력하세요"
},
"displayOrder": 2
},
{
"id": "comp_material",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 340, "y": 100, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "재질",
"fieldName": "material",
"placeholder": "재질을 입력하세요"
},
"displayOrder": 3
},
{
"id": "comp_inventory_unit",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 20, "y": 180, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "재고단위",
"fieldName": "inventory_unit",
"placeholder": "재고단위를 입력하세요"
},
"displayOrder": 4
},
{
"id": "comp_weight",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 340, "y": 180, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "중량",
"fieldName": "weight",
"placeholder": "중량을 입력하세요"
},
"displayOrder": 5
},
{
"id": "comp_unit",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 20, "y": 260, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "단위",
"fieldName": "unit",
"placeholder": "단위를 입력하세요"
},
"displayOrder": 6
},
{
"id": "comp_division",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 340, "y": 260, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "구분",
"fieldName": "division",
"placeholder": "구분을 입력하세요"
},
"displayOrder": 7
},
{
"id": "comp_type",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 20, "y": 340, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "유형",
"fieldName": "type",
"placeholder": "유형을 입력하세요"
},
"displayOrder": 8
},
{
"id": "comp_status",
"url": "@/lib/registry/components/v2-select",
"size": { "width": 300, "height": 60 },
"position": { "x": 340, "y": 340, "z": 1 },
"overrides": {
"type": "v2-select",
"label": "상태",
"fieldName": "status",
"config": {
"mode": "dropdown",
"source": "static",
"options": [
{ "value": "정상", "label": "정상" },
{ "value": "품절", "label": "품절" },
{ "value": "대기", "label": "대기" },
{ "value": "단종", "label": "단종" }
]
}
},
"displayOrder": 9
},
{
"id": "comp_btn_save",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 100, "height": 40 },
"position": { "x": 540, "y": 420, "z": 1 },
"overrides": {
"text": "저장",
"type": "v2-button-primary",
"label": "저장 버튼",
"action": {
"type": "save",
"closeModalAfterSave": true,
"refreshParentTable": true,
"successMessage": "저장되었습니다.",
"errorMessage": "저장 중 오류가 발생했습니다."
},
"variant": "primary",
"actionType": "button"
},
"displayOrder": 10
}
]
}
8.4 API 호출 방식
📌 실제 화면 생성은 API를 통해 진행됩니다. screen_code는 서버에서 자동 생성됩니다.
Step 1: 화면 코드 자동 생성 API
GET /api/screens/generate-code?companyCode=COMPANY_7
응답:
{
"success": true,
"data": { "screenCode": "COMPANY_7_4" }
}
Step 2: 화면 생성 API
POST /api/screens
Content-Type: application/json
Authorization: Bearer {{token}}
{
"screenName": "품목정보",
"screenCode": "COMPANY_7_4",
"tableName": "item_info",
"companyCode": "COMPANY_7",
"description": "품목 기본정보 관리 화면"
}
응답:
{
"success": true,
"data": {
"screenId": 141,
"screenCode": "COMPANY_7_4",
"screenName": "품목정보"
}
}
Step 3: 레이아웃 저장 API
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 (실제 적용 방식)
📌 실제 테스트 완료된 방식입니다. Docker psql을 통해 직접 DB에 삽입합니다.
Step 1: 모달 화면 먼저 생성 (등록/수정 폼)
-- 모달 화면 정의
INSERT INTO screen_definitions (
screen_code, screen_name, table_name, company_code, description, is_active
) VALUES (
'COMPANY_19_ITEM_MODAL', 'Item Register/Edit Modal', 'item_info', 'COMPANY_19',
'Item registration and edit form modal', 'Y'
) RETURNING screen_id;
-- 예: screen_id = 3731 반환됨
Step 2: 모달 레이아웃 저장
-- 레이아웃 JSON을 파일로 저장 후 INSERT (한글 인코딩 문제 방지)
INSERT INTO screen_layouts_v2 (screen_id, company_code, layout_data, created_at, updated_at)
VALUES (
3731, -- Step 1에서 반환된 screen_id
'COMPANY_19',
'{"version":"2.0","components":[/* 8.3의 모달 components 배열 */]}'::jsonb,
NOW(), NOW()
);
Step 3: 메인 화면 생성
-- 메인 화면 정의
INSERT INTO screen_definitions (
screen_code, screen_name, table_name, company_code, description, is_active
) VALUES (
'COMPANY_19_ITEM_INFO', 'Item Info', 'item_info', 'COMPANY_19',
'Item master data management', 'Y'
) RETURNING screen_id;
-- 예: screen_id = 3730 반환됨
Step 4: 메인 레이아웃 저장 (모달 연결)
-- targetScreenId를 Step 1에서 생성한 모달 screen_id로 치환
INSERT INTO screen_layouts_v2 (screen_id, company_code, layout_data, created_at, updated_at)
VALUES (
3730, -- Step 3에서 반환된 screen_id
'COMPANY_19',
'{"version":"2.0","components":[/* 8.2의 components 배열, targetScreenId: 3731 */]}'::jsonb,
NOW(), NOW()
);
Step 5: 메뉴에 화면 연결 (선택사항)
-- 기존 메뉴에 화면 연결 (screen_menu_assignments 테이블 사용)
INSERT INTO screen_menu_assignments (screen_id, menu_id, company_code, display_order)
VALUES (3730, 55566, 'COMPANY_19', 1);
8.6 화면 생성 순서 (중요!)
1. 모달 화면 생성 (screen_definitions INSERT)
│
▼
2. 모달 레이아웃 저장 (screen_layouts_v2 INSERT)
│
▼
3. 메인 화면 생성 (screen_definitions INSERT)
│
▼
4. 메인 레이아웃 저장 (screen_layouts_v2 INSERT)
└── targetScreenId에 모달 screen_id 사용!
│
▼
5. (선택) 메뉴에 화면 연결
8.7 주의사항
| 항목 |
설명 |
screen_code |
회사별 고유, 형식: {COMPANY_CODE}_{용도} (예: COMPANY_19_ITEM_INFO) |
screen_id |
AUTO INCREMENT, INSERT 후 RETURNING으로 획득 |
component.id |
comp_ prefix 권장, 화면 내 중복 불가 |
component.url |
@/lib/registry/components/v2-xxx 형식 정확히 사용 |
component.type |
overrides.type과 URL 마지막 부분 일치 필요 |
targetScreenId |
숫자 (문자열 아님), 모달 화면 먼저 생성 필요 |
version |
반드시 "2.0" 사용 |
layout_data |
JSONB 타입, 복잡한 JSON은 파일로 저장 후 -f 옵션으로 실행 |
| UNIQUE 제약 |
(screen_id, company_code) 조합이 유니크 |
| 한글 처리 |
Docker psql에서 한글 직접 입력 시 인코딩 문제 → 영문 사용 또는 파일 사용 |
8.8 컴포넌트 타입 레퍼런스
| 컴포넌트 |
URL |
type (overrides) |
| 텍스트 입력 |
v2-input |
v2-input |
| 선택 (드롭다운) |
v2-select |
v2-select |
| 날짜 입력 |
v2-date |
v2-date |
| 버튼 |
v2-button-primary |
v2-button-primary |
| 테이블 리스트 |
v2-table-list |
v2-table-list |
| 검색 위젯 |
v2-table-search-widget |
v2-table-search-widget |
| 텍스트 표시 |
v2-text-display |
v2-text-display |
| 구분선 |
v2-divider-line |
v2-divider-line |
| 섹션 카드 |
v2-section-card |
v2-section-card |
9. 화면 구현 체크리스트
📋 새로운 화면을 구현할 때 아래 체크리스트를 순서대로 확인하세요.
9.1 분석 단계
| 체크 |
항목 |
설명 |
| ☐ |
테이블 구조 분석 |
해당 화면이 사용할 테이블 스키마 확인 (컬럼명, 타입, 필수 여부) |
| ☐ |
화면 기능 파악 |
조회/등록/수정/삭제, 검색, 필터, 그룹핑 등 필요 기능 목록화 |
| ☐ |
컴포넌트 매핑 |
필요 기능 → V2 컴포넌트 매핑 (2.2 목록 참조) |
| ☐ |
구현 불가 항목 확인 |
현재 V2 컴포넌트로 구현 불가능한 기능 파악 |
| ☐ |
대체 방안 검토 |
구현 불가 항목에 대해 기존 컴포넌트 조합으로 대체 가능 여부 확인 |
9.2 문서 작성 단계
| 체크 |
항목 |
설명 |
| ☐ |
테이블 정의 작성 |
1.1~1.2 형식으로 테이블/컬럼 정보 작성 |
| ☐ |
레이아웃 배치도 작성 |
2.1 형식으로 ASCII 다이어그램 작성 |
| ☐ |
사용 컴포넌트 목록 |
2.3 형식으로 이 화면에서 사용할 컴포넌트 정리 |
| ☐ |
모달 화면 정의 |
등록/수정 모달이 필요하면 별도 레이아웃 작성 |
| ☐ |
JSON 설정 작성 |
8.2~8.3 형식으로 layout_data JSON 작성 |
| ☐ |
구현 불가/대체 방안 명시 |
해당 사항 있으면 문서에 섹션 추가 |
9.3 INSERT 전 확인
| 체크 |
항목 |
설명 |
| ☐ |
company_code 확인 |
대상 회사 코드 정확한지 확인 |
| ☐ |
screen_code 형식 |
{COMPANY_CODE}_{용도} 형식 준수 |
| ☐ |
모달 먼저 생성 |
모달 화면이 있으면 반드시 먼저 INSERT |
| ☐ |
JSON 파일 준비 |
복잡한 JSON은 파일로 저장 (한글 인코딩 대비) |
| ☐ |
컴포넌트 ID 고유성 |
comp_ prefix, 화면 내 중복 없는지 확인 |
| ☐ |
컴포넌트 URL/type 일치 |
url의 마지막 부분과 overrides.type 동일한지 확인 |
| ☐ |
targetScreenId 치환 |
모달 screen_id를 숫자로 정확히 입력 |
9.4 INSERT 후 검증
| 체크 |
항목 |
설명 |
| ☐ |
화면 접속 테스트 |
/screens/{screen_id} URL로 접속 |
| ☐ |
컴포넌트 렌더링 확인 |
모든 컴포넌트가 "미구현" 없이 표시되는지 확인 |
| ☐ |
검색 기능 테스트 |
검색 위젯 동작 확인 |
| ☐ |
테이블 데이터 로드 |
테이블에 데이터 표시되는지 확인 |
| ☐ |
버튼 동작 테스트 |
등록/수정/삭제 버튼 클릭 시 모달/액션 동작 확인 |
| ☐ |
모달 폼 테스트 |
모달 열림, 입력 필드 표시, 저장 동작 확인 |
| ☐ |
메뉴 연결 확인 |
(연결한 경우) 메뉴에서 화면 접근 가능한지 확인 |
9.5 문제 발생 시 확인 사항
| 증상 |
확인 사항 |
| 화면이 안 보임 |
screen_layouts_v2에 데이터 있는지 확인, company_code 일치 여부 |
| "미구현 컴포넌트" 표시 |
url과 overrides.type 일치 여부, 컴포넌트명 오타 확인 |
| 모달이 안 열림 |
targetScreenId가 숫자인지, 해당 screen_id 존재하는지 확인 |
| 테이블 데이터 없음 |
selectedTable 값 확인, 테이블에 데이터 존재 여부 |
| 버튼 동작 안 함 |
action.type 값 확인, actionType: "button" 설정 여부 |