ERP-node/docs/screen-implementation-guide/01_master-data/item-info.md

40 KiB

품목정보 (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}_{순번} 형식)

필수 입력 필드:

{
  "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": 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 모달 화면 (품목 등록/수정)

화면 정의 (필수 입력)

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

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 (참고용)

⚠️ 일반적으로 API를 사용하지만, 대량 마이그레이션 시 직접 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) 조합이 유니크