Skip to main content

검증과 예제

검증 체크리스트

JSON 파일 작성 완료 후 반드시 아래 항목을 검증합니다.

구조 검증

  • 유효한 JSON 형식 (파싱 오류 없음)
  • 모든 필수 최상위 필드 존재 (key, displayName, tables, relationships, screens, menuGroups)
  • key가 기존 솔루션 및 다른 프리셋과 중복되지 않음

테이블 검증

  • 모든 테이블의 refId === tableName
  • 접두사 일관성 — 모든 테이블이 동일 접두사 사용
  • 기존 솔루션 541개 테이블과 이름 충돌 없음
  • 다른 고객 프리셋과 테이블명 충돌 없음

FK 검증 (가장 중요!)

  • 모든 FK 컬럼의 fkRef.targetTableRef가 같은 JSON 내 테이블 refId에 존재
  • 모든 FK 컬럼의 fkRef.displayColumn이 대상 테이블의 실제 컬럼 name에 존재
  • FK 타입이 "UUID"
  • constraints"FK" 포함

SYSTEM_REF 검증

  • systemRef 값이 "users" 또는 "departments"만 사용
  • constraints"SYSTEM_REF" 포함
  • 타입이 "UUID"

ENUM 검증

  • type: "ENUM" 인 컬럼에 enumValues 배열 존재
  • defaultValueenumValues 중 하나이거나 null

화면 검증

  • 모든 화면의 boundTableRef가 존재하는 테이블 refId
  • 모든 화면의 parentMenuRef가 존재하는 menuGroups 또는 popMenuGroupsrefId
  • POP 화면(screenType: "pop")에 popConfig 정의 존재

트리/그래프 화면 검증

  • screenType: "tree" 화면에 treeConfig 정의 존재
  • screenType: "graph" 화면에 graphConfig 정의 존재
  • treeConfig.parentColumnboundTableRef 테이블의 실제 FK 컬럼명과 일치
  • treeConfig.childColumnboundTableRef 테이블의 실제 FK 컬럼명과 일치
  • treeConfig.labelColumn이 마스터 테이블의 실제 컬럼명과 일치
  • graphConfig.sourceColumnboundTableRef 테이블의 실제 FK 컬럼명과 일치
  • graphConfig.targetColumnboundTableRef 테이블의 실제 FK 컬럼명과 일치
  • graphConfig.labelColumn이 마스터 테이블의 실제 컬럼명과 일치
  • treeConfig.direction"TB", "LR", "BT" 중 하나 (선택 필드)
  • graphConfig.layout"elk", "force", "circular" 중 하나 (선택 필드)
  • treeConfig/graphConfig 없는 tree/graph 화면은 lot_genealogy 기본값으로 동작 확인

POP 전용 노드 검증

  • POP 화면이 있으면 popMenuGroups 배열 존재
  • screenMappings의 모든 screenRefscreens 내 POP 화면 refId와 일치
  • screenMappings의 모든 categoryIdcategoriesid와 일치
  • 모든 필수 필드 존재 (enableFullscreen, theme, fontScale, touchOptimized, categories, screenMappings)

계산 컬럼 검증

  • isComputed: true 컬럼에 computedConfig 정의 존재
  • computedConfig.type"formula", "aggregation", "reference" 중 하나
  • formula 타입: dependsOn 배열의 모든 컬럼이 같은 테이블에 존재
  • aggregation 타입: sourceTable이 같은 JSON 내 테이블 refId에 존재
  • aggregation 타입: fkColumnsourceTable의 실제 FK 컬럼에 존재
  • reference 타입: sourceTablefkColumn이 유효
  • isComputed 컬럼의 시드 데이터 값이 수식 결과와 정확히 일치
  • supportsAutoNumbering 컬럼의 시드 데이터 값이 채번 패턴과 일치

sourceDocumentConfig 검증

  • sources[].sourceTable이 같은 JSON 내 테이블 refId에 존재
  • sources[].targetTable이 같은 JSON 내 테이블 refId에 존재
  • fieldMappingsourceField가 소스 테이블 컬럼에 존재
  • fieldMappingtargetField가 대상 테이블 컬럼에 존재
  • displayColumns의 FK 참조(fkTable, fkColumn)가 유효

formulas 검증

  • formulas[].targetField가 부모 테이블(boundTableRef) 컬럼에 존재
  • formulas[].subtableRefformConfig.subtables[].tableRef에 존재
  • formulas[].sourceField가 서브테이블의 실제 컬럼에 존재

관계 검증

  • 모든 sourceRef, targetRef가 존재하는 테이블 refId
  • FK가 있는 컬럼에 대응하는 관계 정의 존재
  • relationType"1:1", "1:N", "M:N" 중 하나

워크플로우 검증

  • 모든 flowConnectionssourceRef, targetRefprocessNodesrefId에 존재
  • exclusiveGateway에서 나가는 연결에 label 존재
  • 각 워크플로우 그룹에 startEventendEvent 존재

audit_trail 검증

  • entity_type이 프리셋 내 정의된 테이블명과 일치하는지 확인 (CascadeUpdate 대상 테이블)
  • entity_id가 실제 존재하는 행의 UUID를 참조하는지 확인
  • old_valuenew_value가 해당 컬럼의 데이터 타입에 맞는 값인지 확인
  • source_entity_type + source_entity_id로 변경 원인을 역추적할 수 있는지 확인
  • changed_by가 유효한 사용자 ID인지 확인
  • 시드 데이터에 audit_trail 행을 포함하지 않았는지 확인 (런타임 자동 생성)

actionButtons 검증

  • 동일 화면 내 label중복되지 않음
  • action 값이 유효한 타입: approve, reject, complete, submit, custom
  • style 값이 primary, default, danger 중 하나
  • visibleWhen의 필드명이 boundTableRef 테이블의 실제 컬럼에 존재
  • visibleWhen의 값 배열이 해당 컬럼의 enumValues에 포함됨
  • order 값이 음수가 아닌지 확인
  • confirmMessage가 사용자에게 명확한 한글 메시지인지 확인

cascadeUpdate 검증

  • 트리거 테이블과 대상 테이블 간 FK 관계가 relationships에 정의되어 있음
  • 집계 대상 컬럼(good_qty, defect_qty 등)이 실제 테이블에 존재
  • FOR UPDATE 잠금이 적용되어 동시성 안전성 보장
  • 트랜잭션 실패 시 전체 롤백이 수행되는지 확인
  • 연쇄 규칙이 순환 참조를 만들지 않는지 확인 (A→B→C→A 방지)
  • audit_trail INSERT가 각 cascade 단계마다 올바르게 생성되는지 확인
  • 시드 데이터의 집계 컬럼 값이 하위 테이블 시드 행의 실제 합계와 정확히 일치

documentConversion 검증

  • 전환 대상 actionButtonsvisibleWhen 상태가 소스 테이블 ENUM에 존재
  • 전환 후 소스 상태 값(수주전환, 생산의뢰 등)이 소스 테이블 ENUM에 포함
  • 소스→대상 필드 매핑이 양쪽 테이블 컬럼과 정확히 일치
  • 전환 API 엔드포인트(/_convert-quotation 등)가 컨트롤러에 등록되어 있음
  • 전환 시 생성되는 대상 레코드의 FK가 올바른 테이블을 참조

실제 예제

최소 프리셋 (3테이블, 3화면, 1메뉴)

{
"key": "example_factory",
"displayName": "예제 공장",
"description": "최소 구성 예제",
"icon": "RocketOutlined",
"color": "#1890ff",
"version": "1.0.0",

"tables": [
{
"refId": "ex_item_categories",
"tableName": "ex_item_categories",
"displayName": "품목분류",
"isHierarchical": true,
"connectUsers": true,
"columns": [
{
"name": "category_code",
"displayName": "분류코드",
"type": "VARCHAR",
"constraints": ["NOT_NULL", "UNIQUE"]
},
{
"name": "category_name",
"displayName": "분류명",
"type": "VARCHAR",
"constraints": ["NOT_NULL"]
},
{
"name": "is_active",
"displayName": "사용여부",
"type": "BOOLEAN",
"constraints": [],
"defaultValue": "true"
}
]
},
{
"refId": "ex_items",
"tableName": "ex_items",
"displayName": "품목마스터",
"connectUsers": true,
"columns": [
{
"name": "item_code",
"displayName": "품목코드",
"type": "VARCHAR",
"constraints": ["NOT_NULL", "UNIQUE"]
},
{
"name": "item_name",
"displayName": "품목명",
"type": "VARCHAR",
"constraints": ["NOT_NULL"]
},
{
"name": "item_type",
"displayName": "품목유형",
"type": "ENUM",
"constraints": ["NOT_NULL"],
"enumValues": ["원자재", "반제품", "완제품"]
},
{
"name": "category_id",
"displayName": "품목분류",
"type": "UUID",
"constraints": ["FK"],
"fkRef": { "targetTableRef": "ex_item_categories", "displayColumn": "category_name" }
},
{
"name": "unit_price",
"displayName": "단가",
"type": "DECIMAL",
"constraints": [],
"defaultValue": "0"
},
{
"name": "safety_stock",
"displayName": "안전재고",
"type": "INT",
"constraints": [],
"defaultValue": "0"
},
{
"name": "manager_id",
"displayName": "담당자",
"type": "UUID",
"constraints": ["SYSTEM_REF"],
"systemRef": "users"
},
{ "name": "remark", "displayName": "비고", "type": "TEXT", "constraints": [] }
]
},
{
"refId": "ex_work_orders",
"tableName": "ex_work_orders",
"displayName": "작업지시",
"connectUsers": true,
"columns": [
{
"name": "wo_number",
"displayName": "작업지시번호",
"type": "VARCHAR",
"constraints": ["NOT_NULL", "UNIQUE"]
},
{
"name": "item_id",
"displayName": "품목",
"type": "UUID",
"constraints": ["FK", "NOT_NULL"],
"fkRef": { "targetTableRef": "ex_items", "displayColumn": "item_name" }
},
{
"name": "order_qty",
"displayName": "지시수량",
"type": "INT",
"constraints": ["NOT_NULL"]
},
{
"name": "status",
"displayName": "상태",
"type": "ENUM",
"constraints": ["NOT_NULL"],
"enumValues": ["대기", "진행중", "완료", "취소"],
"defaultValue": "대기"
},
{
"name": "planned_date",
"displayName": "계획일",
"type": "DATE",
"constraints": ["NOT_NULL"]
},
{ "name": "remark", "displayName": "비고", "type": "TEXT", "constraints": [] }
]
}
],

"relationships": [
{
"sourceRef": "ex_item_categories",
"targetRef": "ex_items",
"relationType": "1:N",
"relationName": "분류→품목"
},
{
"sourceRef": "ex_items",
"targetRef": "ex_work_orders",
"relationType": "1:N",
"relationName": "품목→작업지시"
}
],

"screens": [
{
"refId": "scr_ex_item_categories",
"label": "품목분류",
"screenType": "list",
"menuIcon": "FolderOutlined",
"menuOrder": 1,
"boundTableRef": "ex_item_categories",
"parentMenuRef": "mg_ex_master"
},
{
"refId": "scr_ex_items",
"label": "품목관리",
"screenType": "list",
"menuIcon": "AppstoreOutlined",
"menuOrder": 2,
"boundTableRef": "ex_items",
"parentMenuRef": "mg_ex_master"
},
{
"refId": "scr_ex_work_orders",
"label": "작업지시",
"screenType": "list",
"menuIcon": "ScheduleOutlined",
"menuOrder": 1,
"boundTableRef": "ex_work_orders",
"parentMenuRef": "mg_ex_production"
}
],

"menuGroups": [
{
"refId": "mg_ex_master",
"label": "기초정보",
"icon": "DatabaseOutlined",
"menuOrder": 1,
"businessFunction": "admin"
},
{
"refId": "mg_ex_production",
"label": "생산관리",
"icon": "ExperimentOutlined",
"menuOrder": 2,
"businessFunction": "production"
}
],

"popMenuGroups": [],
"processNodes": [],
"flowConnections": [],
"dashboardWidgets": [],
"dashboardPreset": null,
"actionScreens": [],
"seedData": [],
"requiredSolutions": []
}

POP 포함 예제 (위 예제에 POP 추가)

{
"추가할 screens": [
{
"refId": "pop_ex_result_input",
"label": "실적입력 POP",
"screenType": "pop",
"menuIcon": "MobileOutlined",
"menuOrder": 1,
"boundTableRef": "ex_work_orders",
"parentMenuRef": "menu_ex_pop",
"popConfig": {
"layout": "input-form",
"fontScale": 1.3,
"headerFields": [
{ "columnName": "wo_number", "label": "작업지시번호", "order": 1, "size": "large" }
],
"actionFields": [
{
"columnName": "order_qty",
"label": "수량",
"fieldType": "number-pad",
"required": true,
"order": 1
}
],
"quickActions": [
{
"label": "실적등록",
"action": "submit",
"color": "#52c41a",
"size": "large",
"order": 1
}
]
}
}
],

"추가할 menuGroups": [
{
"refId": "menu_ex_pop",
"label": "현장POP",
"icon": "MobileOutlined",
"menuOrder": 3,
"businessFunction": "production"
}
],

"추가할 popMenuGroups": [
{
"refId": "pop_menu_ex",
"label": "현장POP",
"icon": "MobileOutlined",
"menuOrder": 100,
"enableFullscreen": true,
"theme": "light",
"fontScale": 1.3,
"touchOptimized": true,
"categories": [
{
"id": "cat_ex_production",
"label": "생산",
"icon": "ExperimentOutlined",
"order": 1,
"color": "#1890ff"
}
],
"screenMappings": [{ "screenRef": "pop_ex_result_input", "categoryId": "cat_ex_production" }]
}
]
}

이전: 시드 데이터와 네이밍 | 다음: 변경 이력 (changeLog)