# Vexplor 구조 다이어그램

> 생성일: 2026-01-22 | 총 테이블: 164개 | 코드 기반 관계 분석 완료

---

## 1. 테이블 JOIN 관계도 (핵심)

### 1-1. 사용자/권한 시스템 JOIN 관계

| CRUD | 테이블 순서 | 설명 |
|------|-------------|------|
| **C** | `user_info` → `user_dept` → `authority_sub_user` | 사용자 생성 → 부서 배정 → 권한 부여 |
| **R** | `user_info` + `company_mng` + `authority_sub_user` + `authority_master` JOIN | 로그인/조회 시 회사+권한 JOIN |
| **U** | `user_info` / `user_dept` / `authority_sub_user` 개별 | 각 테이블 독립 수정 |
| **D** | 각각 독립 삭제 (별도 API) | user_dept, authority_sub_user, user_info 각각 삭제 |

```mermaid
erDiagram
    company_mng {
        varchar company_code PK "회사코드"
        varchar company_name "회사명"
    }
    
    user_info {
        varchar user_id PK "사용자ID"
        varchar company_code "회사코드 (멀티테넌시)"
        varchar user_name "사용자명"
        varchar user_type "SUPER_ADMIN | COMPANY_ADMIN | USER"
    }
    
    dept_info {
        varchar dept_code PK "부서코드"
        varchar company_code "회사코드"
        varchar dept_name "부서명"
    }
    
    user_dept {
        varchar user_id "사용자ID"
        varchar dept_code "부서코드"
        varchar company_code "회사코드"
    }
    
    authority_master {
        int objid PK "권한그룹ID"
        varchar company_code "회사코드"
        varchar auth_group_name "권한그룹명"
    }
    
    authority_sub_user {
        int master_objid "권한그룹ID"
        varchar user_id "사용자ID"
        varchar company_code "회사코드"
    }

    company_mng ||--o{ user_info : "company_code = company_code"
    company_mng ||--o{ dept_info : "company_code = company_code"
    user_info ||--o{ user_dept : "user_id = user_id"
    dept_info ||--o{ user_dept : "dept_code = dept_code"
    authority_master ||--o{ authority_sub_user : "objid = master_objid"
    user_info ||--o{ authority_sub_user : "user_id = user_id"
```

**실제 코드 JOIN 예시:**
```sql
-- 사용자 권한 조회 (authService.ts:158)
SELECT am.auth_group_name, am.objid
FROM authority_sub_user asu
INNER JOIN authority_master am ON asu.master_objid = am.objid
WHERE asu.user_id = $1
```

### 1-2. 메뉴/권한 시스템 JOIN 관계

| CRUD | 테이블 순서 | 설명 |
|------|-------------|------|
| **C** | `menu_info` → `rel_menu_auth` | 메뉴 생성 → 권한그룹에 메뉴 할당 |
| **R** | `authority_master` → `rel_menu_auth` → `menu_info` | 사용자 권한으로 접근 가능 메뉴 필터링 |
| **U** | `menu_info` 단독 / `rel_menu_auth` 삭제 후 재생성 | 메뉴 수정 or 권한 재할당 |
| **D** | `rel_menu_auth` → `menu_info` | 권한 매핑 먼저 삭제 → 메뉴 삭제 |

```mermaid
erDiagram
    menu_info {
        int objid PK "메뉴ID"
        varchar company_code "회사코드"
        varchar menu_name_kor "메뉴명"
        varchar menu_url "메뉴URL"
        int parent_obj_id "상위메뉴ID"
    }
    
    rel_menu_auth {
        int menu_objid "메뉴ID"
        int auth_objid "권한그룹ID"
        varchar company_code "회사코드"
    }
    
    authority_master {
        int objid PK "권한그룹ID"
        varchar company_code "회사코드"
    }

    menu_info ||--o{ rel_menu_auth : "objid = menu_objid"
    authority_master ||--o{ rel_menu_auth : "objid = auth_objid"
```

**실제 코드 JOIN 예시:**
```sql
-- 사용자 메뉴 조회 (adminService.ts)
SELECT mi.*
FROM menu_info mi
JOIN rel_menu_auth rma ON mi.objid = rma.menu_objid
WHERE rma.auth_objid IN (사용자권한목록)
AND mi.company_code = $companyCode
```

### 1-3. 화면 시스템 JOIN 관계

| CRUD | 테이블 순서 | 설명 |
|------|-------------|------|
| **C** | `screen_definitions` → `screen_layouts` → `screen_menu_assignments` | 화면 정의 → 레이아웃 → 메뉴 연결 |
| **R** | `menu_info` → `screen_menu_assignments` → `screen_definitions` + `screen_layouts` JOIN | 메뉴에서 화면+레이아웃 JOIN |
| **U** | `screen_definitions` / `screen_layouts` 개별 (같은 screen_id) | 정의와 레이아웃 각각 수정 |
| **D** | `screen_layouts` → `screen_menu_assignments` → `screen_definitions` | 레이아웃 → 메뉴연결 → 정의 순서 |

> **그룹**: `screen_groups` → `screen_group_screens`는 별도 API로 관리 (복사/그룹화 용도)

```mermaid
erDiagram
    screen_definitions {
        uuid screen_id PK "화면ID"
        varchar company_code "회사코드"
        varchar screen_name "화면명"
        varchar table_name "연결테이블"
    }
    
    screen_layouts {
        uuid screen_id PK "화면ID"
        jsonb layout_metadata "레이아웃JSON"
    }
    
    screen_menu_assignments {
        uuid screen_id "화면ID"
        int menu_objid "메뉴ID"
        varchar company_code "회사코드"
    }
    
    screen_groups {
        int id PK "그룹ID"
        varchar company_code "회사코드"
        varchar group_name "그룹명"
    }
    
    screen_group_screens {
        int group_id "그룹ID"
        uuid screen_id "화면ID"
        varchar company_code "회사코드"
    }

    screen_definitions ||--|| screen_layouts : "screen_id = screen_id"
    screen_definitions ||--o{ screen_menu_assignments : "screen_id = screen_id"
    menu_info ||--o{ screen_menu_assignments : "objid = menu_objid"
    screen_groups ||--o{ screen_group_screens : "id = group_id"
    screen_definitions ||--o{ screen_group_screens : "screen_id = screen_id"
```

**실제 코드 JOIN 예시:**
```sql
-- 화면 정의 + 레이아웃 조회 (screenGroupController.ts:1272)
SELECT sd.*, sl.layout_metadata
FROM screen_definitions sd
JOIN screen_layouts sl ON sd.screen_id = sl.screen_id
WHERE sd.screen_id = $1
```

### 1-4. 테이블 타입/메타데이터 JOIN 관계

| CRUD | 테이블 순서 | 설명 |
|------|-------------|------|
| **C** | 각 테이블 독립 생성 | DDL 실행 시 자동 생성, 또는 개별 등록 |
| **R** | `table_type_columns` + `table_labels` + `table_relationships` LEFT JOIN | 화면 로딩 시 메타데이터 조합 |
| **U** | 각 테이블 개별 (table_name + column_name + company_code 기준) | 컬럼 정의/라벨/관계 각각 수정 |
| **D** | 각 테이블 독립 삭제 | 테이블 삭제 시 관련 메타데이터 개별 삭제 |

> **코드값 조회**: `table_column_category_values` → `code_category` → `code_info` (드롭다운 옵션)

```mermaid
erDiagram
    table_type_columns {
        varchar table_name PK "테이블명"
        varchar column_name PK "컬럼명"
        varchar company_code PK "회사코드"
        varchar display_name "표시명"
        varchar data_type "데이터타입"
        varchar reference_table "참조테이블"
        varchar reference_column "참조컬럼"
    }
    
    table_labels {
        varchar table_name PK "테이블명"
        varchar company_code PK "회사코드"
        varchar display_name "테이블표시명"
    }
    
    table_column_category_values {
        varchar table_name "테이블명"
        varchar column_name "컬럼명"
        varchar category_code "카테고리코드"
        varchar company_code "회사코드"
    }
    
    table_relationships {
        varchar table_name "테이블명"
        varchar source_column "소스컬럼"
        varchar target_table "타겟테이블"
        varchar target_column "타겟컬럼"
        varchar company_code "회사코드"
    }
    
    code_category {
        varchar category_code PK "카테고리코드"
        varchar company_code PK "회사코드"
        varchar category_name "카테고리명"
    }
    
    code_info {
        varchar category_code "카테고리코드"
        varchar code_value PK "코드값"
        varchar company_code PK "회사코드"
        varchar code_name "코드명"
    }

    table_type_columns ||--o{ table_labels : "table_name = table_name"
    table_type_columns ||--o{ table_column_category_values : "table_name, column_name"
    table_type_columns ||--o{ table_relationships : "table_name = table_name"
    code_category ||--o{ code_info : "category_code = category_code"
    table_column_category_values }o--|| code_category : "category_code = category_code"
```

**실제 코드 JOIN 예시:**
```sql
-- 테이블 컬럼 정보 조회 (tableManagementService.ts:210)
SELECT ttc.*, cl.display_name as column_label
FROM table_type_columns ttc
LEFT JOIN column_labels cl 
  ON ttc.table_name = cl.table_name 
  AND ttc.column_name = cl.column_name
WHERE ttc.table_name = $1
AND ttc.company_code = $2
```

### 1-5. 플로우 시스템 JOIN 관계

| CRUD | 테이블 순서 | 설명 |
|------|-------------|------|
| **C** | `flow_definition` → `flow_step` → `flow_step_connection` → `flow_data_mapping` | 플로우 → 스텝 → 연결선 → 매핑 |
| **R** | `flow_definition` + `flow_step` + `flow_step_connection` JOIN | 플로우 화면 렌더링 |
| **U** | 각 테이블 개별 (definition_id/step_id 기준) | 정의/스텝/연결 각각 수정 |
| **D** | 각 테이블 독립 삭제 (DB CASCADE 의존) | step/connection/definition 각각 삭제 API |

> **데이터 이동**: `flow_data_mapping`(컬럼 변환) → 소스→타겟 INSERT → `flow_audit_log`(자동 기록)

```mermaid
erDiagram
    flow_definition {
        int id PK "플로우ID"
        varchar company_code "회사코드"
        varchar name "플로우명"
    }
    
    flow_step {
        int id PK "스텝ID"
        int definition_id "플로우ID"
        varchar company_code "회사코드"
        varchar step_name "스텝명"
        varchar table_name "연결테이블"
        int step_order "순서"
    }
    
    flow_step_connection {
        int id PK "연결ID"
        int from_step_id "출발스텝ID"
        int to_step_id "도착스텝ID"
        int definition_id "플로우ID"
    }
    
    flow_data_mapping {
        int from_step_id "출발스텝ID"
        int to_step_id "도착스텝ID"
        varchar source_column "소스컬럼"
        varchar target_column "타겟컬럼"
    }
    
    flow_audit_log {
        int id PK "로그ID"
        int definition_id "플로우ID"
        int from_step_id "출발스텝ID"
        int to_step_id "도착스텝ID"
        int data_id "데이터ID"
        timestamp moved_at "이동시간"
    }

    flow_definition ||--o{ flow_step : "id = definition_id"
    flow_step ||--o{ flow_step_connection : "id = from_step_id"
    flow_step ||--o{ flow_step_connection : "id = to_step_id"
    flow_step ||--o{ flow_data_mapping : "id = from_step_id"
    flow_step ||--o{ flow_audit_log : "id = from_step_id"
```

**실제 코드 JOIN 예시:**
```sql
-- 플로우 감사로그 조회 (flowDataMoveService.ts:461)
SELECT fal.*, 
       fs_from.step_name as from_step_name,
       fs_to.step_name as to_step_name
FROM flow_audit_log fal
LEFT JOIN flow_step fs_from ON fal.from_step_id = fs_from.id
LEFT JOIN flow_step fs_to ON fal.to_step_id = fs_to.id
WHERE fal.definition_id = $1
```

### 1-6. 배치/수집 시스템 JOIN 관계

| CRUD | 테이블 순서 | 설명 |
|------|-------------|------|
| **C** | `external_db_connections` → `batch_configs` → `batch_mappings` | 외부DB 연결 → 배치 설정 → 매핑 규칙 |
| **R** | `batch_configs` + `external_db_connections` + `batch_mappings` JOIN | 배치 실행 시 전체 설정 조회 |
| **U** | `batch_mappings` 삭제 후 재생성 / `batch_configs` 개별 수정 | 매핑은 전체 교체 방식 |
| **D** | `batch_configs` 삭제 시 `batch_mappings` CASCADE 삭제 | 설정만 삭제하면 매핑 자동 삭제 |

> **실행 시**: 크론 → 외부DB 조회 → 내부 테이블 동기화 → `batch_execution_logs`(결과 기록)

```mermaid
erDiagram
    external_db_connections {
        int id PK "연결ID"
        varchar company_code "회사코드"
        varchar connection_name "연결명"
        varchar db_type "postgresql|mysql|mssql"
        varchar host "호스트"
        int port "포트"
    }
    
    batch_configs {
        int id PK "배치ID"
        varchar company_code "회사코드"
        varchar batch_name "배치명"
        varchar cron_expression "크론식"
        int connection_id "연결ID"
        varchar is_active "Y|N"
    }
    
    batch_mappings {
        int id PK "매핑ID"
        int batch_config_id "배치ID"
        varchar source_table "소스테이블"
        varchar source_column "소스컬럼"
        varchar target_table "타겟테이블"
        varchar target_column "타겟컬럼"
    }
    
    batch_execution_logs {
        int id PK "로그ID"
        int batch_config_id "배치ID"
        timestamp started_at "시작시간"
        timestamp finished_at "종료시간"
        varchar status "SUCCESS|FAILED"
    }

    external_db_connections ||--o{ batch_configs : "id = connection_id"
    batch_configs ||--o{ batch_mappings : "id = batch_config_id"
    batch_configs ||--o{ batch_execution_logs : "id = batch_config_id"
```

**실제 코드 JOIN 예시:**
```sql
-- 배치 설정 + 매핑 조회 (batchService.ts:143)
SELECT bc.*, bm.*
FROM batch_configs bc
LEFT JOIN batch_mappings bm ON bc.id = bm.batch_config_id
WHERE bc.id = $1
AND bc.company_code = $2
ORDER BY bm.mapping_order
```

---

## 2. 로직 플로우 요약

> 위 JOIN 관계가 **언제** 사용되는지 간략 설명

### 2-1. 로그인 → 화면 접근 순서

| 단계 | 테이블 | JOIN 관계 | 설명 |
|------|--------|-----------|------|
| 1 | `user_info` | - | user_id, password 확인 |
| 2 | `user_info` | - | company_code 조회 → 멀티테넌시 분기 |
| 3 | `company_mng` | user_info.company_code = company_mng.company_code | 회사명 조회 |
| 4 | `authority_sub_user` → `authority_master` | asu.master_objid = am.objid | 사용자 권한 조회 |
| 5 | `menu_info` → `rel_menu_auth` | mi.objid = rma.menu_objid | 권한별 메뉴 필터 |
| 6 | `screen_menu_assignments` → `screen_definitions` | sma.screen_id = sd.screen_id | 메뉴-화면 연결 |
| 7 | `screen_definitions` → `screen_layouts` | sd.screen_id = sl.screen_id | 화면+레이아웃 |
| 8 | `table_type_columns` | WHERE table_name = $1 | 컬럼 메타데이터 |

### 2-2. 데이터 조회 순서

| 단계 | 테이블 | JOIN 관계 | 설명 |
|------|--------|-----------|------|
| 1 | `table_type_columns` | - | 컬럼 정의 조회 |
| 2 | `table_labels` | ttc.table_name = tl.table_name | 테이블 표시명 |
| 3 | `table_column_category_values` | ttc.table_name, column_name | 카테고리 값 |
| 4 | `table_relationships` | ttc.table_name = tr.table_name | 참조 관계 |
| 5 | `code_category` → `code_info` | cc.category_code = ci.category_code | 코드값 조회 |
| 6 | 비즈니스 테이블 | LEFT JOIN (table_relationships 기반) | 실제 데이터 |

### 2-3. 플로우 데이터 이동 순서

| 단계 | 테이블 | JOIN 관계 | 설명 |
|------|--------|-----------|------|
| 1 | `flow_definition` | - | 플로우 정의 |
| 2 | `flow_step` | fs.definition_id = fd.id | 스텝 목록 |
| 3 | `flow_step_connection` | fsc.from_step_id = fs.id | 연결 관계 |
| 4 | `flow_data_mapping` | fdm.from_step_id, to_step_id | 컬럼 매핑 |
| 5 | 소스 테이블 | - | 데이터 조회 |
| 6 | 타겟 테이블 | - | 데이터 INSERT |
| 7 | `flow_audit_log` | - | 이동 기록 |

### 2-4. 배치 실행 순서

| 단계 | 테이블 | JOIN 관계 | 설명 |
|------|--------|-----------|------|
| 1 | `batch_configs` | - | 활성 배치 조회 |
| 2 | `external_db_connections` | bc.connection_id = edc.id | 외부 DB 정보 |
| 3 | `batch_mappings` | bm.batch_config_id = bc.id | 매핑 규칙 |
| 4 | 외부 DB | - | 데이터 조회 |
| 5 | 내부 테이블 | - | 데이터 동기화 |
| 6 | `batch_execution_logs` | bel.batch_config_id = bc.id | 실행 로그 |

---

## 3. 멀티테넌시 (company_code) 적용 요약

| 테이블 | company_code 필터 | 비고 |
|--------|------------------|------|
| `user_info` | O | 사용자별 회사 구분 |
| `menu_info` | O | 회사별 메뉴 |
| `screen_definitions` | O | 회사별 화면 |
| `table_type_columns` | O | 회사별 컬럼 정의 |
| `flow_definition` | O | 회사별 플로우 |
| `batch_configs` | O | 회사별 배치 |
| 모든 비즈니스 테이블 | O | 자동 필터 적용 |
| `company_mng` | X (PK) | 회사 마스터 |

**company_code = '*'**: 최고관리자, 모든 회사 데이터 접근 가능

---

## 4. 비효율성 분석

> 상세 내용: [DB_INEFFICIENCY_ANALYSIS.md](./DB_INEFFICIENCY_ANALYSIS.md)

| 심각도 | 항목 | 권장 조치 |
|--------|------|-----------|
| 🔴 | `screen_definitions.layout_metadata` | 미사용 컬럼 삭제 |
| 🔴 | `user_dept` 비정규화 | 정규화 리팩토링 |
| 🟡 | 히스토리 테이블 39개 | 통합 감사 테이블 |
| 🟡 | cascading 미사용 3개 | 테이블 삭제 |
| 🟢 | `dept_info.company_name` | 선택적 정규화 |
