700 lines
24 KiB
Markdown
700 lines
24 KiB
Markdown
|
|
# 레벨 기반 연쇄 드롭다운 시스템 설계
|
||
|
|
|
||
|
|
## 1. 개요
|
||
|
|
|
||
|
|
### 1.1 목적
|
||
|
|
다양한 계층 구조를 지원하는 범용 연쇄 드롭다운 시스템 구축
|
||
|
|
|
||
|
|
### 1.2 지원하는 계층 유형
|
||
|
|
|
||
|
|
| 유형 | 설명 | 예시 |
|
||
|
|
|------|------|------|
|
||
|
|
| **MULTI_TABLE** | 각 레벨이 다른 테이블 | 국가 → 시/도 → 구/군 → 동 |
|
||
|
|
| **SELF_REFERENCE** | 같은 테이블 내 자기참조 | 대분류 → 중분류 → 소분류 |
|
||
|
|
| **BOM** | BOM 구조 (수량 등 속성 포함) | 제품 → 어셈블리 → 부품 |
|
||
|
|
| **TREE** | 무한 깊이 트리 | 조직도, 메뉴 구조 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. 데이터베이스 설계
|
||
|
|
|
||
|
|
### 2.1 테이블 구조
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────┐
|
||
|
|
│ cascading_hierarchy_group │ ← 계층 그룹 정의
|
||
|
|
├─────────────────────────────────────┤
|
||
|
|
│ group_code (PK) │
|
||
|
|
│ group_name │
|
||
|
|
│ hierarchy_type │ ← MULTI_TABLE/SELF_REFERENCE/BOM/TREE
|
||
|
|
│ max_levels │
|
||
|
|
│ is_fixed_levels │
|
||
|
|
│ self_ref_* (자기참조 설정) │
|
||
|
|
│ bom_* (BOM 설정) │
|
||
|
|
│ company_code │
|
||
|
|
└─────────────────────────────────────┘
|
||
|
|
│
|
||
|
|
│ 1:N (MULTI_TABLE 유형만)
|
||
|
|
▼
|
||
|
|
┌─────────────────────────────────────┐
|
||
|
|
│ cascading_hierarchy_level │ ← 레벨별 테이블/컬럼 정의
|
||
|
|
├─────────────────────────────────────┤
|
||
|
|
│ group_code (FK) │
|
||
|
|
│ level_order │ ← 1, 2, 3...
|
||
|
|
│ level_name │
|
||
|
|
│ table_name │
|
||
|
|
│ value_column │
|
||
|
|
│ label_column │
|
||
|
|
│ parent_key_column │ ← 부모 테이블 참조 컬럼
|
||
|
|
│ company_code │
|
||
|
|
└─────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2.2 기존 시스템과의 관계
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────┐
|
||
|
|
│ cascading_relation │ ← 기존 2단계 관계 (유지)
|
||
|
|
│ (2단계 전용) │
|
||
|
|
└─────────────────────────────────────┘
|
||
|
|
│
|
||
|
|
│ 호환성 뷰
|
||
|
|
▼
|
||
|
|
┌─────────────────────────────────────┐
|
||
|
|
│ v_cascading_as_hierarchy │ ← 기존 관계를 계층 형태로 변환
|
||
|
|
└─────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. 계층 유형별 상세 설계
|
||
|
|
|
||
|
|
### 3.1 MULTI_TABLE (다중 테이블 계층)
|
||
|
|
|
||
|
|
**사용 사례**: 국가 → 시/도 → 구/군 → 동
|
||
|
|
|
||
|
|
**테이블 구조**:
|
||
|
|
```
|
||
|
|
country_info province_info city_info district_info
|
||
|
|
├─ country_code (PK) ├─ province_code (PK) ├─ city_code (PK) ├─ district_code (PK)
|
||
|
|
├─ country_name ├─ province_name ├─ city_name ├─ district_name
|
||
|
|
├─ country_code (FK) ├─ province_code (FK) ├─ city_code (FK)
|
||
|
|
```
|
||
|
|
|
||
|
|
**설정 예시**:
|
||
|
|
```sql
|
||
|
|
-- 그룹 정의
|
||
|
|
INSERT INTO cascading_hierarchy_group (
|
||
|
|
group_code, group_name, hierarchy_type, max_levels, company_code
|
||
|
|
) VALUES (
|
||
|
|
'REGION_HIERARCHY', '지역 계층', 'MULTI_TABLE', 4, 'EMAX'
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 레벨 정의
|
||
|
|
INSERT INTO cascading_hierarchy_level VALUES
|
||
|
|
(1, 'REGION_HIERARCHY', 'EMAX', 1, '국가', 'country_info', 'country_code', 'country_name', NULL),
|
||
|
|
(2, 'REGION_HIERARCHY', 'EMAX', 2, '시/도', 'province_info', 'province_code', 'province_name', 'country_code'),
|
||
|
|
(3, 'REGION_HIERARCHY', 'EMAX', 3, '구/군', 'city_info', 'city_code', 'city_name', 'province_code'),
|
||
|
|
(4, 'REGION_HIERARCHY', 'EMAX', 4, '동', 'district_info', 'district_code', 'district_name', 'city_code');
|
||
|
|
```
|
||
|
|
|
||
|
|
**API 호출 흐름**:
|
||
|
|
```
|
||
|
|
1. 레벨 1 (국가): GET /api/cascading-hierarchy/options/REGION_HIERARCHY/1
|
||
|
|
→ [{ value: 'KR', label: '대한민국' }, { value: 'US', label: '미국' }]
|
||
|
|
|
||
|
|
2. 레벨 2 (시/도): GET /api/cascading-hierarchy/options/REGION_HIERARCHY/2?parentValue=KR
|
||
|
|
→ [{ value: 'SEOUL', label: '서울특별시' }, { value: 'BUSAN', label: '부산광역시' }]
|
||
|
|
|
||
|
|
3. 레벨 3 (구/군): GET /api/cascading-hierarchy/options/REGION_HIERARCHY/3?parentValue=SEOUL
|
||
|
|
→ [{ value: 'GANGNAM', label: '강남구' }, { value: 'SEOCHO', label: '서초구' }]
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3.2 SELF_REFERENCE (자기참조 계층)
|
||
|
|
|
||
|
|
**사용 사례**: 제품 카테고리 (대분류 → 중분류 → 소분류)
|
||
|
|
|
||
|
|
**테이블 구조** (code_info 활용):
|
||
|
|
```
|
||
|
|
code_info
|
||
|
|
├─ code_category = 'PRODUCT_CATEGORY'
|
||
|
|
├─ code_value (PK) = 'ELEC', 'ELEC_TV', 'ELEC_TV_LED'
|
||
|
|
├─ code_name = '전자제품', 'TV', 'LED TV'
|
||
|
|
├─ parent_code = NULL, 'ELEC', 'ELEC_TV' ← 자기참조
|
||
|
|
├─ level = 1, 2, 3
|
||
|
|
├─ sort_order
|
||
|
|
```
|
||
|
|
|
||
|
|
**설정 예시**:
|
||
|
|
```sql
|
||
|
|
INSERT INTO cascading_hierarchy_group (
|
||
|
|
group_code, group_name, hierarchy_type, max_levels,
|
||
|
|
self_ref_table, self_ref_id_column, self_ref_parent_column,
|
||
|
|
self_ref_value_column, self_ref_label_column, self_ref_level_column,
|
||
|
|
self_ref_filter_column, self_ref_filter_value,
|
||
|
|
company_code
|
||
|
|
) VALUES (
|
||
|
|
'PRODUCT_CATEGORY', '제품 카테고리', 'SELF_REFERENCE', 3,
|
||
|
|
'code_info', 'code_value', 'parent_code',
|
||
|
|
'code_value', 'code_name', 'level',
|
||
|
|
'code_category', 'PRODUCT_CATEGORY',
|
||
|
|
'EMAX'
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
**API 호출 흐름**:
|
||
|
|
```
|
||
|
|
1. 레벨 1 (대분류): GET /api/cascading-hierarchy/options/PRODUCT_CATEGORY/1
|
||
|
|
→ WHERE parent_code IS NULL AND code_category = 'PRODUCT_CATEGORY'
|
||
|
|
→ [{ value: 'ELEC', label: '전자제품' }, { value: 'FURN', label: '가구' }]
|
||
|
|
|
||
|
|
2. 레벨 2 (중분류): GET /api/cascading-hierarchy/options/PRODUCT_CATEGORY/2?parentValue=ELEC
|
||
|
|
→ WHERE parent_code = 'ELEC' AND code_category = 'PRODUCT_CATEGORY'
|
||
|
|
→ [{ value: 'ELEC_TV', label: 'TV' }, { value: 'ELEC_REF', label: '냉장고' }]
|
||
|
|
|
||
|
|
3. 레벨 3 (소분류): GET /api/cascading-hierarchy/options/PRODUCT_CATEGORY/3?parentValue=ELEC_TV
|
||
|
|
→ WHERE parent_code = 'ELEC_TV' AND code_category = 'PRODUCT_CATEGORY'
|
||
|
|
→ [{ value: 'ELEC_TV_LED', label: 'LED TV' }, { value: 'ELEC_TV_OLED', label: 'OLED TV' }]
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3.3 BOM (Bill of Materials)
|
||
|
|
|
||
|
|
**사용 사례**: 제품 BOM 구조
|
||
|
|
|
||
|
|
**테이블 구조**:
|
||
|
|
```
|
||
|
|
klbom_tbl (BOM 관계) item_info (품목 마스터)
|
||
|
|
├─ id (자식 품목) ├─ item_code (PK)
|
||
|
|
├─ pid (부모 품목) ├─ item_name
|
||
|
|
├─ qty (수량) ├─ item_spec
|
||
|
|
├─ aylevel (레벨) ├─ unit
|
||
|
|
├─ bom_report_objid
|
||
|
|
```
|
||
|
|
|
||
|
|
**설정 예시**:
|
||
|
|
```sql
|
||
|
|
INSERT INTO cascading_hierarchy_group (
|
||
|
|
group_code, group_name, hierarchy_type, max_levels, is_fixed_levels,
|
||
|
|
bom_table, bom_parent_column, bom_child_column,
|
||
|
|
bom_item_table, bom_item_id_column, bom_item_label_column,
|
||
|
|
bom_qty_column, bom_level_column,
|
||
|
|
company_code
|
||
|
|
) VALUES (
|
||
|
|
'PRODUCT_BOM', '제품 BOM', 'BOM', NULL, 'N',
|
||
|
|
'klbom_tbl', 'pid', 'id',
|
||
|
|
'item_info', 'item_code', 'item_name',
|
||
|
|
'qty', 'aylevel',
|
||
|
|
'EMAX'
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
**API 호출 흐름**:
|
||
|
|
```
|
||
|
|
1. 루트 품목 (레벨 1): GET /api/cascading-hierarchy/bom/PRODUCT_BOM/roots
|
||
|
|
→ WHERE pid IS NULL OR pid = ''
|
||
|
|
→ [{ value: 'PROD001', label: '완제품 A', level: 1 }]
|
||
|
|
|
||
|
|
2. 하위 품목: GET /api/cascading-hierarchy/bom/PRODUCT_BOM/children?parentValue=PROD001
|
||
|
|
→ WHERE pid = 'PROD001'
|
||
|
|
→ [
|
||
|
|
{ value: 'ASSY001', label: '어셈블리 A', qty: 1, level: 2 },
|
||
|
|
{ value: 'ASSY002', label: '어셈블리 B', qty: 2, level: 2 }
|
||
|
|
]
|
||
|
|
|
||
|
|
3. 더 하위: GET /api/cascading-hierarchy/bom/PRODUCT_BOM/children?parentValue=ASSY001
|
||
|
|
→ WHERE pid = 'ASSY001'
|
||
|
|
→ [
|
||
|
|
{ value: 'PART001', label: '부품 A', qty: 4, level: 3 },
|
||
|
|
{ value: 'PART002', label: '부품 B', qty: 2, level: 3 }
|
||
|
|
]
|
||
|
|
```
|
||
|
|
|
||
|
|
**BOM 전용 응답 형식**:
|
||
|
|
```typescript
|
||
|
|
interface BomOption {
|
||
|
|
value: string; // 품목 코드
|
||
|
|
label: string; // 품목명
|
||
|
|
qty: number; // 수량
|
||
|
|
level: number; // BOM 레벨
|
||
|
|
hasChildren: boolean; // 하위 품목 존재 여부
|
||
|
|
spec?: string; // 규격 (선택)
|
||
|
|
unit?: string; // 단위 (선택)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3.4 TREE (무한 깊이 트리)
|
||
|
|
|
||
|
|
**사용 사례**: 조직도, 메뉴 구조
|
||
|
|
|
||
|
|
**테이블 구조**:
|
||
|
|
```
|
||
|
|
dept_info
|
||
|
|
├─ dept_code (PK)
|
||
|
|
├─ dept_name
|
||
|
|
├─ parent_dept_code ← 자기참조 (무한 깊이)
|
||
|
|
├─ sort_order
|
||
|
|
├─ is_active
|
||
|
|
```
|
||
|
|
|
||
|
|
**설정 예시**:
|
||
|
|
```sql
|
||
|
|
INSERT INTO cascading_hierarchy_group (
|
||
|
|
group_code, group_name, hierarchy_type, max_levels, is_fixed_levels,
|
||
|
|
self_ref_table, self_ref_id_column, self_ref_parent_column,
|
||
|
|
self_ref_value_column, self_ref_label_column, self_ref_order_column,
|
||
|
|
company_code
|
||
|
|
) VALUES (
|
||
|
|
'ORG_CHART', '조직도', 'TREE', NULL, 'N',
|
||
|
|
'dept_info', 'dept_code', 'parent_dept_code',
|
||
|
|
'dept_code', 'dept_name', 'sort_order',
|
||
|
|
'EMAX'
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
**API 호출 흐름** (BOM과 유사):
|
||
|
|
```
|
||
|
|
1. 루트 노드: GET /api/cascading-hierarchy/tree/ORG_CHART/roots
|
||
|
|
→ WHERE parent_dept_code IS NULL
|
||
|
|
→ [{ value: 'HQ', label: '본사', hasChildren: true }]
|
||
|
|
|
||
|
|
2. 하위 노드: GET /api/cascading-hierarchy/tree/ORG_CHART/children?parentValue=HQ
|
||
|
|
→ WHERE parent_dept_code = 'HQ'
|
||
|
|
→ [
|
||
|
|
{ value: 'DIV1', label: '사업부1', hasChildren: true },
|
||
|
|
{ value: 'DIV2', label: '사업부2', hasChildren: true }
|
||
|
|
]
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. API 설계
|
||
|
|
|
||
|
|
### 4.1 계층 그룹 관리 API
|
||
|
|
|
||
|
|
```
|
||
|
|
GET /api/cascading-hierarchy/groups # 그룹 목록
|
||
|
|
POST /api/cascading-hierarchy/groups # 그룹 생성
|
||
|
|
GET /api/cascading-hierarchy/groups/:code # 그룹 상세
|
||
|
|
PUT /api/cascading-hierarchy/groups/:code # 그룹 수정
|
||
|
|
DELETE /api/cascading-hierarchy/groups/:code # 그룹 삭제
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4.2 레벨 관리 API (MULTI_TABLE용)
|
||
|
|
|
||
|
|
```
|
||
|
|
GET /api/cascading-hierarchy/groups/:code/levels # 레벨 목록
|
||
|
|
POST /api/cascading-hierarchy/groups/:code/levels # 레벨 추가
|
||
|
|
PUT /api/cascading-hierarchy/groups/:code/levels/:order # 레벨 수정
|
||
|
|
DELETE /api/cascading-hierarchy/groups/:code/levels/:order # 레벨 삭제
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4.3 옵션 조회 API
|
||
|
|
|
||
|
|
```
|
||
|
|
# MULTI_TABLE / SELF_REFERENCE
|
||
|
|
GET /api/cascading-hierarchy/options/:groupCode/:level
|
||
|
|
?parentValue=xxx # 부모 값 (레벨 2 이상)
|
||
|
|
&companyCode=xxx # 회사 코드 (선택)
|
||
|
|
|
||
|
|
# BOM / TREE
|
||
|
|
GET /api/cascading-hierarchy/tree/:groupCode/roots # 루트 노드
|
||
|
|
GET /api/cascading-hierarchy/tree/:groupCode/children # 자식 노드
|
||
|
|
?parentValue=xxx
|
||
|
|
GET /api/cascading-hierarchy/tree/:groupCode/path # 경로 조회
|
||
|
|
?value=xxx
|
||
|
|
GET /api/cascading-hierarchy/tree/:groupCode/search # 검색
|
||
|
|
?keyword=xxx
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. 프론트엔드 컴포넌트 설계
|
||
|
|
|
||
|
|
### 5.1 CascadingHierarchyDropdown
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface CascadingHierarchyDropdownProps {
|
||
|
|
groupCode: string; // 계층 그룹 코드
|
||
|
|
level: number; // 현재 레벨 (1, 2, 3...)
|
||
|
|
parentValue?: string; // 부모 값 (레벨 2 이상)
|
||
|
|
value?: string; // 선택된 값
|
||
|
|
onChange: (value: string, option: HierarchyOption) => void;
|
||
|
|
placeholder?: string;
|
||
|
|
disabled?: boolean;
|
||
|
|
required?: boolean;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 사용 예시 (지역 계층)
|
||
|
|
<CascadingHierarchyDropdown groupCode="REGION_HIERARCHY" level={1} onChange={setCountry} />
|
||
|
|
<CascadingHierarchyDropdown groupCode="REGION_HIERARCHY" level={2} parentValue={country} onChange={setProvince} />
|
||
|
|
<CascadingHierarchyDropdown groupCode="REGION_HIERARCHY" level={3} parentValue={province} onChange={setCity} />
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5.2 CascadingHierarchyGroup (자동 연결)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface CascadingHierarchyGroupProps {
|
||
|
|
groupCode: string;
|
||
|
|
values: Record<number, string>; // { 1: 'KR', 2: 'SEOUL', 3: 'GANGNAM' }
|
||
|
|
onChange: (level: number, value: string) => void;
|
||
|
|
layout?: 'horizontal' | 'vertical';
|
||
|
|
}
|
||
|
|
|
||
|
|
// 사용 예시
|
||
|
|
<CascadingHierarchyGroup
|
||
|
|
groupCode="REGION_HIERARCHY"
|
||
|
|
values={regionValues}
|
||
|
|
onChange={(level, value) => {
|
||
|
|
setRegionValues(prev => ({ ...prev, [level]: value }));
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5.3 BomTreeSelect (BOM 전용)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface BomTreeSelectProps {
|
||
|
|
groupCode: string;
|
||
|
|
value?: string;
|
||
|
|
onChange: (value: string, path: BomOption[]) => void;
|
||
|
|
showQty?: boolean; // 수량 표시
|
||
|
|
showLevel?: boolean; // 레벨 표시
|
||
|
|
maxDepth?: number; // 최대 깊이 제한
|
||
|
|
}
|
||
|
|
|
||
|
|
// 사용 예시
|
||
|
|
<BomTreeSelect
|
||
|
|
groupCode="PRODUCT_BOM"
|
||
|
|
value={selectedPart}
|
||
|
|
onChange={(value, path) => {
|
||
|
|
setSelectedPart(value);
|
||
|
|
console.log('선택 경로:', path); // [완제품 → 어셈블리 → 부품]
|
||
|
|
}}
|
||
|
|
showQty
|
||
|
|
/>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. 화면관리 시스템 통합
|
||
|
|
|
||
|
|
### 6.1 컴포넌트 설정 확장
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface SelectBasicConfig {
|
||
|
|
// 기존 설정
|
||
|
|
cascadingEnabled?: boolean;
|
||
|
|
cascadingRelationCode?: string; // 기존 2단계 관계
|
||
|
|
cascadingRole?: 'parent' | 'child';
|
||
|
|
cascadingParentField?: string;
|
||
|
|
|
||
|
|
// 🆕 레벨 기반 계층 설정
|
||
|
|
hierarchyEnabled?: boolean;
|
||
|
|
hierarchyGroupCode?: string; // 계층 그룹 코드
|
||
|
|
hierarchyLevel?: number; // 이 컴포넌트의 레벨
|
||
|
|
hierarchyParentField?: string; // 부모 레벨 필드명
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6.2 설정 UI 확장
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────┐
|
||
|
|
│ 연쇄 드롭다운 설정 │
|
||
|
|
├─────────────────────────────────────────┤
|
||
|
|
│ ○ 2단계 관계 (기존) │
|
||
|
|
│ └─ 관계 선택: [창고-위치 ▼] │
|
||
|
|
│ └─ 역할: [부모] [자식] │
|
||
|
|
│ │
|
||
|
|
│ ● 다단계 계층 (신규) │
|
||
|
|
│ └─ 계층 그룹: [지역 계층 ▼] │
|
||
|
|
│ └─ 레벨: [2 - 시/도 ▼] │
|
||
|
|
│ └─ 부모 필드: [country_code] (자동감지) │
|
||
|
|
└─────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7. 구현 우선순위
|
||
|
|
|
||
|
|
### Phase 1: 기반 구축
|
||
|
|
1. ✅ 기존 2단계 연쇄 드롭다운 완성
|
||
|
|
2. 📋 데이터베이스 마이그레이션 (066_create_cascading_hierarchy.sql)
|
||
|
|
3. 📋 백엔드 API 구현 (계층 그룹 CRUD)
|
||
|
|
|
||
|
|
### Phase 2: MULTI_TABLE 지원
|
||
|
|
1. 📋 레벨 관리 API
|
||
|
|
2. 📋 옵션 조회 API
|
||
|
|
3. 📋 프론트엔드 컴포넌트
|
||
|
|
|
||
|
|
### Phase 3: SELF_REFERENCE 지원
|
||
|
|
1. 📋 자기참조 쿼리 로직
|
||
|
|
2. 📋 code_info 기반 카테고리 계층
|
||
|
|
|
||
|
|
### Phase 4: BOM/TREE 지원
|
||
|
|
1. 📋 BOM 전용 API
|
||
|
|
2. 📋 트리 컴포넌트
|
||
|
|
3. 📋 무한 깊이 지원
|
||
|
|
|
||
|
|
### Phase 5: 화면관리 통합
|
||
|
|
1. 📋 설정 UI 확장
|
||
|
|
2. 📋 자동 연결 기능
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. 성능 고려사항
|
||
|
|
|
||
|
|
### 8.1 쿼리 최적화
|
||
|
|
- 인덱스: `(group_code, company_code, level_order)`
|
||
|
|
- 캐싱: 자주 조회되는 옵션 목록 Redis 캐싱
|
||
|
|
- Lazy Loading: 하위 레벨은 필요 시에만 로드
|
||
|
|
|
||
|
|
### 8.2 BOM 재귀 쿼리
|
||
|
|
```sql
|
||
|
|
-- PostgreSQL WITH RECURSIVE 활용
|
||
|
|
WITH RECURSIVE bom_tree AS (
|
||
|
|
-- 루트 노드
|
||
|
|
SELECT id, pid, qty, 1 AS level
|
||
|
|
FROM klbom_tbl
|
||
|
|
WHERE pid IS NULL
|
||
|
|
|
||
|
|
UNION ALL
|
||
|
|
|
||
|
|
-- 하위 노드
|
||
|
|
SELECT b.id, b.pid, b.qty, t.level + 1
|
||
|
|
FROM klbom_tbl b
|
||
|
|
JOIN bom_tree t ON b.pid = t.id
|
||
|
|
WHERE t.level < 10 -- 최대 깊이 제한
|
||
|
|
)
|
||
|
|
SELECT * FROM bom_tree;
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8.3 트리 최적화 전략
|
||
|
|
- Materialized Path: `/HQ/DIV1/DEPT1/TEAM1`
|
||
|
|
- Nested Set: left/right 값으로 범위 쿼리
|
||
|
|
- Closure Table: 별도 관계 테이블
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9. 추가 연쇄 패턴
|
||
|
|
|
||
|
|
### 9.1 조건부 연쇄 (Conditional Cascading)
|
||
|
|
|
||
|
|
**사용 사례**: 특정 조건에 따라 다른 옵션 목록 표시
|
||
|
|
|
||
|
|
```
|
||
|
|
입고유형: [구매입고] → 창고: [원자재창고, 부품창고] 만 표시
|
||
|
|
입고유형: [생산입고] → 창고: [완제품창고, 반제품창고] 만 표시
|
||
|
|
```
|
||
|
|
|
||
|
|
**테이블**: `cascading_condition`
|
||
|
|
|
||
|
|
```sql
|
||
|
|
INSERT INTO cascading_condition (
|
||
|
|
relation_code, condition_name,
|
||
|
|
condition_field, condition_operator, condition_value,
|
||
|
|
filter_column, filter_values, company_code
|
||
|
|
) VALUES
|
||
|
|
('WAREHOUSE_LOCATION', '구매입고 창고',
|
||
|
|
'inbound_type', 'EQ', 'PURCHASE',
|
||
|
|
'warehouse_type', 'RAW_MATERIAL,PARTS', 'EMAX');
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 9.2 다중 부모 연쇄 (Multi-Parent Cascading)
|
||
|
|
|
||
|
|
**사용 사례**: 여러 부모 필드의 조합으로 자식 필터링
|
||
|
|
|
||
|
|
```
|
||
|
|
회사: [A사] + 사업부: [영업부문] → 부서: [영업1팀, 영업2팀]
|
||
|
|
```
|
||
|
|
|
||
|
|
**테이블**: `cascading_multi_parent`, `cascading_multi_parent_source`
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- 관계 정의
|
||
|
|
INSERT INTO cascading_multi_parent (
|
||
|
|
relation_code, relation_name,
|
||
|
|
child_table, child_value_column, child_label_column, company_code
|
||
|
|
) VALUES (
|
||
|
|
'COMPANY_DIVISION_DEPT', '회사-사업부-부서',
|
||
|
|
'dept_info', 'dept_code', 'dept_name', 'EMAX'
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 부모 소스 정의
|
||
|
|
INSERT INTO cascading_multi_parent_source (
|
||
|
|
relation_code, company_code, parent_order, parent_name,
|
||
|
|
parent_table, parent_value_column, child_filter_column
|
||
|
|
) VALUES
|
||
|
|
('COMPANY_DIVISION_DEPT', 'EMAX', 1, '회사', 'company_info', 'company_code', 'company_code'),
|
||
|
|
('COMPANY_DIVISION_DEPT', 'EMAX', 2, '사업부', 'division_info', 'division_code', 'division_code');
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 9.3 자동 입력 그룹 (Auto-Fill Group)
|
||
|
|
|
||
|
|
**사용 사례**: 마스터 선택 시 여러 필드 자동 입력
|
||
|
|
|
||
|
|
```
|
||
|
|
고객사 선택 → 담당자, 연락처, 주소, 결제조건 자동 입력
|
||
|
|
```
|
||
|
|
|
||
|
|
**테이블**: `cascading_auto_fill_group`, `cascading_auto_fill_mapping`
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- 그룹 정의
|
||
|
|
INSERT INTO cascading_auto_fill_group (
|
||
|
|
group_code, group_name,
|
||
|
|
master_table, master_value_column, master_label_column, company_code
|
||
|
|
) VALUES (
|
||
|
|
'CUSTOMER_AUTO_FILL', '고객사 정보 자동입력',
|
||
|
|
'customer_info', 'customer_code', 'customer_name', 'EMAX'
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 필드 매핑
|
||
|
|
INSERT INTO cascading_auto_fill_mapping (
|
||
|
|
group_code, company_code, source_column, target_field, target_label
|
||
|
|
) VALUES
|
||
|
|
('CUSTOMER_AUTO_FILL', 'EMAX', 'contact_person', 'contact_name', '담당자'),
|
||
|
|
('CUSTOMER_AUTO_FILL', 'EMAX', 'contact_phone', 'contact_phone', '연락처'),
|
||
|
|
('CUSTOMER_AUTO_FILL', 'EMAX', 'address', 'delivery_address', '배송주소');
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 9.4 상호 배제 (Mutual Exclusion)
|
||
|
|
|
||
|
|
**사용 사례**: 같은 값 선택 불가
|
||
|
|
|
||
|
|
```
|
||
|
|
출발 창고: [창고A] → 도착 창고: [창고B, 창고C] (창고A 제외)
|
||
|
|
```
|
||
|
|
|
||
|
|
**테이블**: `cascading_mutual_exclusion`
|
||
|
|
|
||
|
|
```sql
|
||
|
|
INSERT INTO cascading_mutual_exclusion (
|
||
|
|
exclusion_code, exclusion_name, field_names,
|
||
|
|
source_table, value_column, label_column,
|
||
|
|
error_message, company_code
|
||
|
|
) VALUES (
|
||
|
|
'WAREHOUSE_TRANSFER', '창고간 이동',
|
||
|
|
'from_warehouse_code,to_warehouse_code',
|
||
|
|
'warehouse_info', 'warehouse_code', 'warehouse_name',
|
||
|
|
'출발 창고와 도착 창고는 같을 수 없습니다',
|
||
|
|
'EMAX'
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 9.5 역방향 조회 (Reverse Lookup)
|
||
|
|
|
||
|
|
**사용 사례**: 자식에서 부모 방향으로 조회
|
||
|
|
|
||
|
|
```
|
||
|
|
품목: [부품A] 선택 → 사용처 BOM: [제품X, 제품Y, 제품Z]
|
||
|
|
```
|
||
|
|
|
||
|
|
**테이블**: `cascading_reverse_lookup`
|
||
|
|
|
||
|
|
```sql
|
||
|
|
INSERT INTO cascading_reverse_lookup (
|
||
|
|
lookup_code, lookup_name,
|
||
|
|
source_table, source_value_column, source_label_column,
|
||
|
|
target_table, target_value_column, target_label_column, target_link_column,
|
||
|
|
company_code
|
||
|
|
) VALUES (
|
||
|
|
'ITEM_USED_IN_BOM', '품목 사용처 BOM',
|
||
|
|
'item_info', 'item_code', 'item_name',
|
||
|
|
'klbom_tbl', 'pid', 'ayupgname', 'id',
|
||
|
|
'EMAX'
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10. 전체 테이블 구조 요약
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────────────────────────────┐
|
||
|
|
│ 연쇄 드롭다운 시스템 구조 │
|
||
|
|
├─────────────────────────────────────────────────────────────────┤
|
||
|
|
│ │
|
||
|
|
│ [기존 - 2단계] │
|
||
|
|
│ cascading_relation ─────────────────────────────────────────── │
|
||
|
|
│ │
|
||
|
|
│ [신규 - 다단계 계층] │
|
||
|
|
│ cascading_hierarchy_group ──┬── cascading_hierarchy_level │
|
||
|
|
│ │ (MULTI_TABLE용) │
|
||
|
|
│ │ │
|
||
|
|
│ [신규 - 조건부] │
|
||
|
|
│ cascading_condition ────────┴── 조건에 따른 필터링 │
|
||
|
|
│ │
|
||
|
|
│ [신규 - 다중 부모] │
|
||
|
|
│ cascading_multi_parent ─────┬── cascading_multi_parent_source │
|
||
|
|
│ │ (여러 부모 조합) │
|
||
|
|
│ │
|
||
|
|
│ [신규 - 자동 입력] │
|
||
|
|
│ cascading_auto_fill_group ──┬── cascading_auto_fill_mapping │
|
||
|
|
│ │ (마스터→다중 필드) │
|
||
|
|
│ │
|
||
|
|
│ [신규 - 상호 배제] │
|
||
|
|
│ cascading_mutual_exclusion ─┴── 같은 값 선택 불가 │
|
||
|
|
│ │
|
||
|
|
│ [신규 - 역방향] │
|
||
|
|
│ cascading_reverse_lookup ───┴── 자식→부모 조회 │
|
||
|
|
│ │
|
||
|
|
└─────────────────────────────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 11. 마이그레이션 가이드
|
||
|
|
|
||
|
|
### 11.1 기존 데이터 마이그레이션
|
||
|
|
```sql
|
||
|
|
-- 기존 cascading_relation → cascading_hierarchy_group 변환
|
||
|
|
INSERT INTO cascading_hierarchy_group (
|
||
|
|
group_code, group_name, hierarchy_type, max_levels, company_code
|
||
|
|
)
|
||
|
|
SELECT
|
||
|
|
'LEGACY_' || relation_code,
|
||
|
|
relation_name,
|
||
|
|
'MULTI_TABLE',
|
||
|
|
2,
|
||
|
|
company_code
|
||
|
|
FROM cascading_relation
|
||
|
|
WHERE is_active = 'Y';
|
||
|
|
```
|
||
|
|
|
||
|
|
### 11.2 호환성 유지
|
||
|
|
- 기존 `cascading_relation` 테이블 유지
|
||
|
|
- 기존 API 엔드포인트 유지
|
||
|
|
- 점진적 마이그레이션 지원
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 12. 구현 우선순위 (업데이트)
|
||
|
|
|
||
|
|
| Phase | 기능 | 복잡도 | 우선순위 |
|
||
|
|
|-------|------|--------|----------|
|
||
|
|
| 1 | 기존 2단계 연쇄 (cascading_relation) | 완료 | 완료 |
|
||
|
|
| 2 | 다단계 계층 - MULTI_TABLE | 중 | 높음 |
|
||
|
|
| 3 | 다단계 계층 - SELF_REFERENCE | 중 | 높음 |
|
||
|
|
| 4 | 자동 입력 그룹 (Auto-Fill) | 낮음 | 높음 |
|
||
|
|
| 5 | 조건부 연쇄 | 중 | 중간 |
|
||
|
|
| 6 | 상호 배제 | 낮음 | 중간 |
|
||
|
|
| 7 | 다중 부모 연쇄 | 높음 | 낮음 |
|
||
|
|
| 8 | BOM/TREE 구조 | 높음 | 낮음 |
|
||
|
|
| 9 | 역방향 조회 | 중 | 낮음 |
|
||
|
|
|