# 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` | 선택적 정규화 |