diff --git a/docs/DB_ARCHITECTURE_ANALYSIS.md b/docs/DB_ARCHITECTURE_ANALYSIS.md
new file mode 100644
index 00000000..084c6940
--- /dev/null
+++ b/docs/DB_ARCHITECTURE_ANALYSIS.md
@@ -0,0 +1,2188 @@
+# WACE ERP 데이터베이스 아키텍처 분석 보고서
+
+> 📅 작성일: 2026-01-20
+> 🎯 목적: WACE ERP 시스템 전체 워크플로우 문서화를 위한 DB 구조 분석
+> 📊 DB 엔진: PostgreSQL 16.8
+
+---
+
+## 📋 목차
+
+1. [개요](#1-개요)
+2. [전체 테이블 목록](#2-전체-테이블-목록)
+3. [멀티테넌시 아키텍처](#3-멀티테넌시-아키텍처)
+4. [핵심 시스템 테이블](#4-핵심-시스템-테이블)
+5. [메타데이터 관리 시스템](#5-메타데이터-관리-시스템)
+6. [화면 관리 시스템](#6-화면-관리-시스템)
+7. [비즈니스 도메인별 테이블](#7-비즈니스-도메인별-테이블)
+8. [플로우 및 데이터 통합](#8-플로우-및-데이터-통합)
+9. [인덱스 전략](#9-인덱스-전략)
+10. [동적 테이블 생성 패턴](#10-동적-테이블-생성-패턴)
+11. [마이그레이션 히스토리](#11-마이그레이션-히스토리)
+
+---
+
+## 1. 개요
+
+### 1.1 데이터베이스 통계
+
+```
+- 총 테이블 수: 약 280개
+- 총 함수 수: 약 50개
+- 총 트리거 수: 약 30개
+- 총 시퀀스 수: 약 100개
+- 뷰 수: 약 20개
+```
+
+### 1.2 아키텍처 특징
+
+- **멀티테넌시**: 모든 테이블에 `company_code` 컬럼으로 회사별 데이터 격리
+- **동적 스키마**: 런타임에 테이블 생성/수정 가능
+- **메타데이터 드리븐**: UI 컴포넌트가 메타데이터 테이블을 기반으로 동적 렌더링
+- **이력 관리**: 주요 테이블에 `_log` 테이블로 변경 이력 추적
+- **외부 연동**: 외부 DB 및 REST API 연결 지원
+- **플로우 기반**: 화면 간 데이터 흐름을 정의하고 실행
+
+---
+
+## 2. 전체 테이블 목록
+
+### 2.1 테이블 분류 체계
+
+```
+시스템 관리 (약 30개)
+├── 사용자/권한 (10개)
+├── 메뉴 관리 (5개)
+├── 회사 관리 (3개)
+└── 공통 코드 (5개)
+
+메타데이터 시스템 (약 20개)
+├── 테이블/컬럼 정의 (8개)
+├── 화면 정의 (10개)
+└── 레이아웃/컴포넌트 (5개)
+
+비즈니스 도메인 (약 200개)
+├── 영업/수주 (30개)
+├── 구매/발주 (25개)
+├── 재고/창고 (20개)
+├── 생산/작업 (25개)
+├── 품질/검사 (15개)
+├── 물류/운송 (20개)
+├── PLM/설계 (30개)
+├── 회계/원가 (20개)
+└── 기타 (15개)
+
+통합/플로우 (약 30개)
+├── 데이터플로우 (10개)
+├── 배치 작업 (8개)
+└── 외부 연동 (12개)
+```
+
+### 2.2 주요 테이블 목록 (알파벳순)
+
+
+전체 테이블 목록 보기 (280개)
+
+```
+approval
+attach_file_info
+auth_tokens
+authority_master
+authority_master_history
+authority_sub_user
+batch_configs
+batch_execution_logs
+batch_job_executions
+batch_job_parameters
+batch_jobs
+batch_mappings
+batch_schedules
+button_action_standards
+carrier_contract_mng
+carrier_contract_mng_log
+carrier_mng
+carrier_mng_log
+carrier_vehicle_mng
+carrier_vehicle_mng_log
+cascading_auto_fill_group
+cascading_auto_fill_mapping
+cascading_condition
+cascading_hierarchy_group
+cascading_hierarchy_level
+cascading_multi_parent
+cascading_multi_parent_source
+cascading_mutual_exclusion
+cascading_relation
+cascading_reverse_lookup
+category_column_mapping
+category_value_cascading_group
+category_value_cascading_mapping
+chartmgmt
+check_report_mng
+code_category
+code_info
+collection_batch_executions
+collection_batch_management
+column_labels
+comm_code
+comm_code_history
+comm_exchange_rate
+comments
+company_code_sequence
+company_mng
+component_standards
+contract_mgmt
+contract_mgmt_option
+counselingmgmt
+customer_item
+customer_item_alias
+customer_item_mapping
+customer_item_price
+customer_mng
+customer_service_mgmt
+customer_service_part
+customer_service_workingtime
+dashboard_elements
+dashboard_shares
+dashboard_slider_items
+dashboard_sliders
+dashboards
+data_collection_configs
+data_collection_history
+data_collection_jobs
+data_relationship_bridge
+dataflow_diagrams
+dataflow_external_calls
+ddl_execution_log
+defect_standard_mng
+defect_standard_mng_log
+delivery_destination
+delivery_history
+delivery_history_defect
+delivery_part_price
+delivery_route_mng
+delivery_route_mng_log
+delivery_status
+dept_info
+dept_info_history
+digital_twin_layout
+digital_twin_layout_template
+digital_twin_location_layout
+digital_twin_objects
+digital_twin_zone_layout
+drivers
+dtg_contracts
+dtg_maintenance_history
+dtg_management
+dtg_management_log
+dtg_monthly_settlements
+dynamic_form_data
+equipment_consumable
+equipment_consumable_log
+equipment_inspection_item
+equipment_inspection_item_log
+equipment_mng
+equipment_mng_log
+estimate_mgmt
+excel_mapping_template
+expense_detail
+expense_master
+external_call_configs
+external_call_logs
+external_connection_permission
+external_db_connection
+external_db_connections
+external_rest_api_connections
+external_work_review_info
+facility_assembly_plan
+file_down_log
+flow_audit_log
+flow_data_mapping
+flow_data_status
+flow_definition
+flow_external_connection_permission
+flow_external_db_connection
+flow_integration_log
+flow_step
+flow_step_connection
+fund_mgmt
+grid_standards
+inbound_mng
+inboxtask
+injection_cost
+input_cost_goal
+input_resource
+inspection_equipment_mng
+inspection_equipment_mng_log
+inspection_standard
+inventory_history
+inventory_stock
+item_info
+item_inspection_info
+item_routing_detail
+item_routing_version
+klbom_tbl
+language_master
+layout_instances
+layout_standards
+login_access_log
+logistics_cost_mng
+logistics_cost_mng_log
+mail_log
+maintenance_schedules
+material_cost
+material_detail_mgmt
+material_master_mgmt
+material_mng
+material_release
+menu_info
+menu_screen_group_items
+menu_screen_groups
+mold_dev_request_info
+multi_lang_category
+multi_lang_key_master
+multi_lang_text
+node_flows
+numbering_rule_parts
+numbering_rules
+oem_factory_mng
+oem_milestone_mng
+oem_mng
+option_mng
+option_price_history
+order_mgmt
+order_mng_master
+order_mng_sub
+order_plan_mgmt
+order_plan_result_error
+order_spec_mng
+order_spec_mng_history
+outbound_mng
+part_bom_qty
+part_bom_report
+part_distribution_list
+part_mgmt
+part_mng
+part_mng_history
+planning_issue
+pms_invest_cost_mng
+pms_pjt_concept_info
+pms_pjt_info
+pms_pjt_year_goal
+pms_rel_pjt_concept_milestone
+pms_rel_pjt_concept_prod
+pms_rel_pjt_prod
+pms_rel_prod_ref_dept
+pms_wbs_task
+pms_wbs_task_confirm
+pms_wbs_task_info
+pms_wbs_task_standard
+pms_wbs_template
+problem_mng
+process_equipment
+process_mng
+procurement_standard
+product_group_mng
+product_kind_spec
+product_kind_spec_main
+product_mgmt
+product_mgmt_model
+product_mgmt_price_history
+product_mgmt_upg_detail
+product_mgmt_upg_master
+product_mng
+product_spec
+production_issue
+production_record
+production_task
+profit_loss
+profit_loss_coefficient
+profit_loss_coolingtime
+profit_loss_depth
+profit_loss_lossrate
+profit_loss_machine
+profit_loss_pretime
+profit_loss_srrate
+profit_loss_total
+profit_loss_total_addlist
+profit_loss_weight
+project
+project_mgmt
+purchase_detail
+purchase_order
+purchase_order_master
+purchase_order_mng
+purchase_order_multi
+purchase_order_part
+ratecal_mgmt
+receive_history
+receiving
+rel_menu_auth
+report_layout
+report_master
+report_menu_mapping
+report_query
+report_template
+safety_budget_execution
+safety_incidents
+safety_inspections
+safety_inspections_log
+sales_bom_part_qty
+sales_bom_report
+sales_bom_report_part
+sales_long_delivery
+sales_long_delivery_input
+sales_long_delivery_predict
+sales_order_detail
+sales_order_detail_log
+sales_order_mng
+sales_part_chg
+sales_request_master
+sales_request_part
+sample_supply
+screen_data_flows
+screen_data_transfer
+screen_definitions
+screen_embedding
+screen_field_joins
+screen_group_members
+screen_group_screens
+screen_groups
+screen_layouts
+screen_menu_assignments
+screen_split_panel
+screen_table_relations
+screen_templates
+screen_widgets
+shipment_detail
+shipment_header
+shipment_instruction
+shipment_instruction_item
+shipment_pallet
+shipment_plan
+standard_doc_info
+structural_review_proposal
+style_templates
+supplier_item
+supplier_item_alias
+supplier_item_mapping
+supplier_item_price
+supplier_mng
+supplier_mng_log
+supply_charger_mng
+supply_mng
+supply_mng_history
+table_column_category_values
+table_labels
+table_log_config
+table_relationships
+table_type_columns
+tax_invoice
+tax_invoice_item
+time_sheet
+transport_logs
+transport_statistics
+transport_vehicle_locations
+used_mng
+user_dept
+user_dept_sub
+user_info
+user_info_history
+vehicle_location_history
+vehicle_locations
+vehicle_trip_summary
+vehicles
+warehouse_info
+warehouse_location
+web_type_standards
+work_instruction
+work_instruction_detail
+work_instruction_detail_log
+work_instruction_log
+work_order
+work_orders
+work_orders_detail
+work_request
+yard_layout
+yard_material_placement
+```
+
+
+
+---
+
+## 3. 멀티테넌시 아키텍처
+
+### 3.1 company_code 패턴
+
+**모든 테이블에 필수적으로 포함되는 컬럼:**
+
+```sql
+company_code VARCHAR(20) NOT NULL
+```
+
+**의미:**
+- 하나의 데이터베이스에서 여러 회사의 데이터를 격리
+- 모든 쿼리는 반드시 `company_code` 필터 포함 필요
+
+### 3.2 특별한 company_code 값
+
+#### `company_code = "*"` 의미
+
+```sql
+-- ❌ 잘못된 이해: 모든 회사가 공유하는 공통 데이터
+-- ✅ 올바른 이해: 슈퍼 관리자 전용 데이터
+
+-- 일반 회사는 "*" 데이터를 볼 수 없음
+SELECT * FROM table_name
+WHERE company_code = 'COMPANY_A'
+ AND company_code != '*'; -- 필수!
+```
+
+**용도:**
+- 시스템 관리자용 메타데이터
+- 전역 설정 값
+- 기본 템플릿
+
+### 3.3 멀티테넌시 쿼리 패턴
+
+```sql
+-- ✅ 표준 SELECT 패턴
+SELECT * FROM table_name
+WHERE company_code = $1
+ AND company_code != '*'
+ORDER BY created_date DESC;
+
+-- ✅ JOIN 패턴 (company_code 매칭 필수!)
+SELECT a.*, b.name
+FROM table_a a
+LEFT JOIN table_b b
+ ON a.ref_id = b.id
+ AND a.company_code = b.company_code -- 필수!
+WHERE a.company_code = $1;
+
+-- ✅ 서브쿼리 패턴
+SELECT *
+FROM orders o
+WHERE company_code = $1
+ AND product_id IN (
+ SELECT id FROM products
+ WHERE company_code = $1 -- 서브쿼리에도 필수!
+ );
+
+-- ✅ 집계 패턴
+SELECT
+ product_type,
+ COUNT(*) as total,
+ SUM(amount) as total_amount
+FROM sales
+WHERE company_code = $1
+GROUP BY product_type;
+```
+
+### 3.4 company_code 인덱스 전략
+
+**모든 테이블에 필수 인덱스:**
+
+```sql
+CREATE INDEX idx_{table_name}_company_code
+ON {table_name}(company_code);
+
+-- 복합 인덱스 예시
+CREATE INDEX idx_sales_company_date
+ON sales(company_code, sale_date DESC);
+```
+
+---
+
+## 4. 핵심 시스템 테이블
+
+### 4.1 사용자 관리
+
+#### user_info (사용자 정보)
+
+```sql
+CREATE TABLE user_info (
+ sabun VARCHAR(1024), -- 사번
+ user_id VARCHAR(1024) PRIMARY KEY,-- 사용자 ID
+ user_password VARCHAR(1024), -- 암호화된 비밀번호
+ user_name VARCHAR(1024), -- 한글명
+ user_name_eng VARCHAR(1024), -- 영문명
+ user_name_cn VARCHAR(1024), -- 중문명
+ dept_code VARCHAR(1024), -- 부서 코드
+ dept_name VARCHAR(1024), -- 부서명
+ position_code VARCHAR(1024), -- 직위 코드
+ position_name VARCHAR(1024), -- 직위명
+ email VARCHAR(1024), -- 이메일
+ tel VARCHAR(1024), -- 전화번호
+ cell_phone VARCHAR(1024), -- 휴대폰
+ user_type VARCHAR(1024), -- 사용자 유형 코드
+ user_type_name VARCHAR(1024), -- 사용자 유형명
+ company_code VARCHAR(50), -- 회사 코드 (멀티테넌시)
+ status VARCHAR(32), -- active/inactive
+ license_number VARCHAR(50), -- 면허번호
+ vehicle_number VARCHAR(50), -- 차량번호
+ signup_type VARCHAR(20), -- 가입 유형
+ branch_name VARCHAR(100), -- 지점명
+ regdate TIMESTAMP, -- 등록일
+ end_date TIMESTAMP -- 종료일
+);
+```
+
+**관련 테이블:**
+- `user_info_history`: 사용자 정보 변경 이력
+- `user_dept`: 사용자-부서 관계
+- `user_dept_sub`: 사용자 하위 부서
+
+#### auth_tokens (인증 토큰)
+
+```sql
+CREATE TABLE auth_tokens (
+ id SERIAL PRIMARY KEY,
+ user_id VARCHAR(255) NOT NULL,
+ token VARCHAR(500) NOT NULL,
+ refresh_token VARCHAR(500),
+ expires_at TIMESTAMP NOT NULL,
+ created_at TIMESTAMP DEFAULT NOW(),
+ ip_address VARCHAR(50),
+ user_agent TEXT
+);
+```
+
+### 4.2 권한 관리
+
+#### authority_master (권한 그룹)
+
+```sql
+CREATE TABLE authority_master (
+ objid NUMERIC PRIMARY KEY,
+ auth_code VARCHAR(64), -- 권한 코드
+ auth_name VARCHAR(64), -- 권한명
+ company_code VARCHAR(50), -- 회사 코드
+ status VARCHAR(32), -- 상태
+ writer VARCHAR(32), -- 작성자
+ regdate TIMESTAMP -- 등록일
+);
+```
+
+**관련 테이블:**
+- `authority_master_history`: 권한 변경 이력
+- `authority_sub_user`: 권한-사용자 매핑
+- `rel_menu_auth`: 권한-메뉴 매핑
+
+### 4.3 메뉴 관리
+
+#### menu_info (메뉴 정보)
+
+```sql
+CREATE TABLE menu_info (
+ objid NUMERIC PRIMARY KEY,
+ menu_type NUMERIC, -- 0=일반, 1=시스템관리, 2=동적생성
+ parent_obj_id NUMERIC, -- 부모 메뉴 ID
+ menu_name_kor VARCHAR(64), -- 한글 메뉴명
+ menu_name_eng VARCHAR(64), -- 영문 메뉴명
+ menu_code VARCHAR(50), -- 메뉴 코드
+ menu_url VARCHAR(256), -- 메뉴 URL
+ seq NUMERIC, -- 순서
+ screen_code VARCHAR(50), -- 화면 코드 (동적 생성 시)
+ screen_group_id INTEGER, -- 화면 그룹 ID
+ company_code VARCHAR(50), -- 회사 코드
+ status VARCHAR(32), -- active/inactive
+ lang_key VARCHAR(100), -- 다국어 키
+ source_menu_objid BIGINT, -- 원본 메뉴 ID (복사 시)
+ writer VARCHAR(32),
+ regdate TIMESTAMP
+);
+```
+
+**특징:**
+- `menu_type = 2`: 화면 생성 시 자동으로 생성되는 메뉴
+- 트리거: `auto_create_menu_for_screen()` - 화면 생성 시 자동 메뉴 추가
+
+**관련 테이블:**
+- `menu_screen_groups`: 메뉴 화면 그룹
+- `menu_screen_group_items`: 그룹-화면 연결
+
+### 4.4 회사 관리
+
+#### company_mng (회사 정보)
+
+```sql
+CREATE TABLE company_mng (
+ company_code VARCHAR(32) PRIMARY KEY,
+ company_name VARCHAR(64),
+ business_registration_number VARCHAR(20), -- 사업자등록번호
+ representative_name VARCHAR(100), -- 대표자명
+ representative_phone VARCHAR(20), -- 대표 연락처
+ email VARCHAR(255), -- 회사 이메일
+ website VARCHAR(500), -- 웹사이트
+ address VARCHAR(500), -- 주소
+ status VARCHAR(32),
+ writer VARCHAR(32),
+ regdate TIMESTAMP
+);
+```
+
+**관련 테이블:**
+- `company_code_sequence`: 회사별 시퀀스 관리
+
+### 4.5 부서 관리
+
+#### dept_info (부서 정보)
+
+```sql
+CREATE TABLE dept_info (
+ dept_code VARCHAR(1024) PRIMARY KEY,
+ dept_name VARCHAR(1024),
+ parent_dept_code VARCHAR(1024), -- 상위 부서
+ company_code VARCHAR(50),
+ status VARCHAR(32),
+ writer VARCHAR(32),
+ regdate TIMESTAMP
+);
+```
+
+**관련 테이블:**
+- `dept_info_history`: 부서 정보 변경 이력
+
+---
+
+## 5. 메타데이터 관리 시스템
+
+WACE ERP의 핵심 특징은 **메타데이터 드리븐 아키텍처**입니다. 화면, 테이블, 컬럼 정보를 메타데이터 테이블에서 관리하고, 프론트엔드가 이를 기반으로 동적 렌더링합니다.
+
+### 5.1 테이블 메타데이터
+
+#### table_labels (테이블 정의)
+
+```sql
+CREATE TABLE table_labels (
+ table_name VARCHAR(100) PRIMARY KEY, -- 테이블명 (물리명)
+ table_label VARCHAR(200), -- 테이블 한글명
+ description TEXT, -- 설명
+ use_log_table VARCHAR(1) DEFAULT 'N', -- 이력 테이블 사용 여부
+ created_date TIMESTAMP,
+ updated_date TIMESTAMP
+);
+```
+
+**역할:**
+- 동적으로 생성된 모든 테이블의 메타정보 저장
+- 화면 생성 시 테이블 선택 목록 제공
+- 데이터 딕셔너리로 활용
+
+#### table_type_columns (컬럼 타입 정의)
+
+```sql
+CREATE TABLE table_type_columns (
+ id SERIAL PRIMARY KEY,
+ table_name VARCHAR(255) NOT NULL,
+ column_name VARCHAR(255) NOT NULL,
+ company_code VARCHAR(20) NOT NULL, -- 회사별 컬럼 설정
+ input_type VARCHAR(50) DEFAULT 'text',-- 입력 타입
+ detail_settings TEXT DEFAULT '{}', -- JSON 상세 설정
+ is_nullable VARCHAR(10) DEFAULT 'Y',
+ display_order INTEGER DEFAULT 0, -- 표시 순서
+ created_date TIMESTAMP,
+ updated_date TIMESTAMP,
+
+ UNIQUE(table_name, column_name, company_code)
+);
+```
+
+**input_type 종류:**
+- `text`: 일반 텍스트
+- `number`: 숫자
+- `date`: 날짜
+- `select`: 드롭다운 (options 필요)
+- `textarea`: 여러 줄 텍스트
+- `entity`: 참조 테이블 (referenceTable, referenceColumn 필요)
+- `checkbox`: 체크박스
+- `radio`: 라디오 버튼
+
+**detail_settings 예시:**
+
+```json
+// select 타입
+{
+ "options": [
+ {"label": "일반", "value": "normal"},
+ {"label": "긴급", "value": "urgent"}
+ ]
+}
+
+// entity 타입
+{
+ "referenceTable": "customer_mng",
+ "referenceColumn": "customer_code",
+ "displayColumn": "customer_name"
+}
+```
+
+#### column_labels (컬럼 라벨 - 레거시)
+
+```sql
+CREATE TABLE column_labels (
+ table_name VARCHAR(100) NOT NULL,
+ column_name VARCHAR(100) NOT NULL,
+ column_label VARCHAR(200), -- 한글 라벨
+ input_type VARCHAR(50),
+ detail_settings TEXT,
+ description TEXT,
+ display_order INTEGER,
+ is_visible BOOLEAN DEFAULT true,
+ created_date TIMESTAMP,
+ updated_date TIMESTAMP,
+
+ PRIMARY KEY (table_name, column_name)
+);
+```
+
+**참고:**
+- 레거시 호환을 위해 유지
+- 새로운 컬럼은 `table_type_columns` 사용 권장
+- `table_type_columns`는 회사별 설정, `column_labels`는 전역 설정
+
+### 5.2 카테고리 값 관리
+
+#### table_column_category_values (컬럼 카테고리 값)
+
+```sql
+CREATE TABLE table_column_category_values (
+ id SERIAL PRIMARY KEY,
+ table_name VARCHAR(255) NOT NULL,
+ column_name VARCHAR(255) NOT NULL,
+ company_code VARCHAR(20) NOT NULL,
+ category_value VARCHAR(500) NOT NULL, -- 카테고리 값
+ display_label VARCHAR(500), -- 표시 라벨
+ display_order INTEGER DEFAULT 0,
+ is_active VARCHAR(1) DEFAULT 'Y',
+ parent_value VARCHAR(500), -- 부모 카테고리 (계층 구조)
+ created_date TIMESTAMP,
+ updated_date TIMESTAMP,
+
+ UNIQUE(table_name, column_name, company_code, category_value)
+);
+```
+
+**용도:**
+- 동적 드롭다운 값 관리
+- 계층형 카테고리 지원 (parent_value)
+- 회사별 카테고리 값 커스터마이징
+
+**관련 테이블:**
+- `category_column_mapping`: 카테고리-컬럼 매핑
+- `category_value_cascading_group`: 카테고리 캐스케이딩 그룹
+- `category_value_cascading_mapping`: 캐스케이딩 매핑
+
+### 5.3 테이블 관계 관리
+
+#### table_relationships (테이블 관계)
+
+```sql
+CREATE TABLE table_relationships (
+ id SERIAL PRIMARY KEY,
+ parent_table VARCHAR(100), -- 부모 테이블
+ parent_column VARCHAR(100), -- 부모 컬럼
+ child_table VARCHAR(100), -- 자식 테이블
+ child_column VARCHAR(100), -- 자식 컬럼
+ relationship_type VARCHAR(20), -- one-to-many, many-to-one 등
+ created_date TIMESTAMP
+);
+```
+
+---
+
+## 6. 화면 관리 시스템
+
+WACE ERP는 코드 작성 없이 화면을 동적으로 생성/수정할 수 있는 **Low-Code 플랫폼** 기능을 제공합니다.
+
+### 6.1 화면 정의
+
+#### screen_definitions (화면 정의)
+
+```sql
+CREATE TABLE screen_definitions (
+ screen_id SERIAL PRIMARY KEY,
+ screen_name VARCHAR(100) NOT NULL, -- 화면명
+ screen_code VARCHAR(50) NOT NULL, -- 화면 코드 (URL용)
+ table_name VARCHAR(100) NOT NULL, -- 메인 테이블
+ company_code VARCHAR(50) NOT NULL,
+ description TEXT,
+ is_active CHAR(1) DEFAULT 'Y', -- Y=활성, N=비활성, D=삭제
+ layout_metadata JSONB, -- 레이아웃 JSON
+
+ -- 외부 데이터 소스 지원
+ db_source_type VARCHAR(10) DEFAULT 'internal', -- internal/external
+ db_connection_id INTEGER, -- 외부 DB 연결 ID
+ data_source_type VARCHAR(20) DEFAULT 'database', -- database/rest_api
+ rest_api_connection_id INTEGER, -- REST API 연결 ID
+ rest_api_endpoint VARCHAR(500), -- API 엔드포인트
+ rest_api_json_path VARCHAR(200) DEFAULT 'data', -- JSON 응답 경로
+
+ source_screen_id INTEGER, -- 원본 화면 ID (복사 시)
+
+ created_date TIMESTAMP NOT NULL DEFAULT NOW(),
+ created_by VARCHAR(50),
+ updated_date TIMESTAMP NOT NULL DEFAULT NOW(),
+ updated_by VARCHAR(50),
+ deleted_date TIMESTAMP, -- 휴지통 이동 시점
+ deleted_by VARCHAR(50),
+ delete_reason TEXT,
+
+ UNIQUE(screen_code, company_code)
+);
+```
+
+**화면 생성 플로우:**
+1. 관리자가 화면 설정 페이지에서 테이블 선택
+2. `screen_definitions` 레코드 생성
+3. 트리거 `auto_create_menu_for_screen()` 실행 → `menu_info` 자동 생성
+4. 프론트엔드가 `/screen/{screen_code}` 경로로 접근 시 동적 렌더링
+
+#### screen_layouts (화면 레이아웃 - 레거시)
+
+```sql
+CREATE TABLE screen_layouts (
+ layout_id SERIAL PRIMARY KEY,
+ screen_id INTEGER REFERENCES screen_definitions(screen_id),
+ layout_name VARCHAR(100),
+ layout_type VARCHAR(50), -- grid, form, split, tab 등
+ layout_config JSONB, -- 레이아웃 설정
+ display_order INTEGER,
+ is_active CHAR(1) DEFAULT 'Y',
+ company_code VARCHAR(50),
+ created_date TIMESTAMP,
+ updated_date TIMESTAMP
+);
+```
+
+### 6.2 화면 그룹 관리
+
+#### screen_groups (화면 그룹)
+
+```sql
+CREATE TABLE screen_groups (
+ id SERIAL PRIMARY KEY,
+ group_name VARCHAR(100) NOT NULL, -- 그룹명
+ group_code VARCHAR(50) NOT NULL, -- 그룹 코드
+ main_table_name VARCHAR(100), -- 메인 테이블
+ description TEXT,
+ icon VARCHAR(100), -- 아이콘
+ display_order INT DEFAULT 0,
+ is_active VARCHAR(1) DEFAULT 'Y',
+ company_code VARCHAR(20) NOT NULL,
+
+ -- 계층 구조 지원 (037 마이그레이션에서 추가)
+ parent_group_id INTEGER REFERENCES screen_groups(id) ON DELETE CASCADE,
+ group_level INTEGER DEFAULT 0, -- 0=대, 1=중, 2=소
+ hierarchy_path VARCHAR(500), -- 예: /1/3/5/
+
+ created_date TIMESTAMPTZ DEFAULT NOW(),
+ updated_date TIMESTAMPTZ DEFAULT NOW(),
+ writer VARCHAR(50),
+
+ UNIQUE(company_code, group_code)
+);
+
+CREATE INDEX idx_screen_groups_company_code ON screen_groups(company_code);
+CREATE INDEX idx_screen_groups_parent_id ON screen_groups(parent_group_id);
+CREATE INDEX idx_screen_groups_hierarchy_path ON screen_groups(hierarchy_path);
+```
+
+#### screen_group_screens (화면-그룹 연결)
+
+```sql
+CREATE TABLE screen_group_screens (
+ id SERIAL PRIMARY KEY,
+ group_id INT NOT NULL REFERENCES screen_groups(id) ON DELETE CASCADE,
+ screen_id INT NOT NULL REFERENCES screen_definitions(screen_id) ON DELETE CASCADE,
+ screen_role VARCHAR(50) DEFAULT 'main', -- main, register, list, detail 등
+ display_order INT DEFAULT 0,
+ is_default VARCHAR(1) DEFAULT 'N', -- 기본 화면 여부
+ company_code VARCHAR(20) NOT NULL,
+ created_date TIMESTAMPTZ DEFAULT NOW(),
+ updated_date TIMESTAMPTZ DEFAULT NOW(),
+ writer VARCHAR(50),
+
+ UNIQUE(group_id, screen_id)
+);
+```
+
+**용도:**
+- 관련 화면들을 그룹으로 묶어 관리
+- 예: "영업 관리" 그룹 → 견적 화면, 수주 화면, 출하 화면
+
+### 6.3 화면 필드 조인
+
+#### screen_field_joins (화면 필드 조인 설정)
+
+```sql
+CREATE TABLE screen_field_joins (
+ id SERIAL PRIMARY KEY,
+ screen_id INT NOT NULL REFERENCES screen_definitions(screen_id) ON DELETE CASCADE,
+ layout_id INT,
+ component_id VARCHAR(500),
+ field_name VARCHAR(100),
+
+ -- 저장 테이블 설정
+ save_table VARCHAR(100) NOT NULL,
+ save_column VARCHAR(100) NOT NULL,
+
+ -- 조인 테이블 설정
+ join_table VARCHAR(100) NOT NULL,
+ join_column VARCHAR(100) NOT NULL,
+ display_column VARCHAR(100) NOT NULL,
+
+ -- 조인 옵션
+ join_type VARCHAR(20) DEFAULT 'LEFT',
+ filter_condition TEXT,
+ sort_column VARCHAR(100),
+ sort_direction VARCHAR(10) DEFAULT 'ASC',
+
+ is_active VARCHAR(1) DEFAULT 'Y',
+ company_code VARCHAR(20) NOT NULL,
+ created_date TIMESTAMPTZ DEFAULT NOW(),
+ updated_date TIMESTAMPTZ DEFAULT NOW(),
+ writer VARCHAR(50)
+);
+```
+
+**예시:**
+```json
+{
+ "save_table": "sales_order",
+ "save_column": "customer_code",
+ "join_table": "customer_mng",
+ "join_column": "customer_code",
+ "display_column": "customer_name"
+}
+```
+
+### 6.4 화면 간 데이터 흐름
+
+#### screen_data_flows (화면 간 데이터 흐름)
+
+```sql
+CREATE TABLE screen_data_flows (
+ id SERIAL PRIMARY KEY,
+ group_id INT REFERENCES screen_groups(id) ON DELETE SET NULL,
+
+ -- 소스 화면
+ source_screen_id INT NOT NULL REFERENCES screen_definitions(screen_id) ON DELETE CASCADE,
+ source_action VARCHAR(50), -- click, submit, select 등
+
+ -- 타겟 화면
+ target_screen_id INT NOT NULL REFERENCES screen_definitions(screen_id) ON DELETE CASCADE,
+ target_action VARCHAR(50), -- open, load, refresh 등
+
+ -- 데이터 매핑 설정
+ data_mapping JSONB, -- 필드 매핑 정보
+
+ -- 흐름 설정
+ flow_type VARCHAR(20) DEFAULT 'unidirectional', -- unidirectional/bidirectional
+ flow_label VARCHAR(100), -- 시각화 라벨
+ condition_expression TEXT, -- 실행 조건식
+
+ is_active VARCHAR(1) DEFAULT 'Y',
+ company_code VARCHAR(20) NOT NULL,
+ created_date TIMESTAMPTZ DEFAULT NOW(),
+ updated_date TIMESTAMPTZ DEFAULT NOW(),
+ writer VARCHAR(50)
+);
+```
+
+**data_mapping 예시:**
+
+```json
+{
+ "customer_code": "customer_code",
+ "customer_name": "customer_name",
+ "selected_date": "order_date"
+}
+```
+
+#### screen_table_relations (화면-테이블 관계)
+
+```sql
+CREATE TABLE screen_table_relations (
+ id SERIAL PRIMARY KEY,
+ group_id INT REFERENCES screen_groups(id) ON DELETE SET NULL,
+ screen_id INT NOT NULL REFERENCES screen_definitions(screen_id) ON DELETE CASCADE,
+ table_name VARCHAR(100) NOT NULL,
+
+ relation_type VARCHAR(20) DEFAULT 'main', -- main, join, lookup
+ crud_operations VARCHAR(20) DEFAULT 'CRUD',-- CRUD 조합
+ description TEXT,
+
+ is_active VARCHAR(1) DEFAULT 'Y',
+ company_code VARCHAR(20) NOT NULL,
+ created_date TIMESTAMPTZ DEFAULT NOW(),
+ updated_date TIMESTAMPTZ DEFAULT NOW(),
+ writer VARCHAR(50)
+);
+```
+
+### 6.5 컴포넌트 표준
+
+#### component_standards (컴포넌트 표준)
+
+```sql
+CREATE TABLE component_standards (
+ component_code VARCHAR(50) PRIMARY KEY,
+ component_name VARCHAR(100) NOT NULL,
+ component_name_eng VARCHAR(100),
+ description TEXT,
+ category VARCHAR(50) NOT NULL, -- input, layout, display 등
+ icon_name VARCHAR(50),
+ default_size JSON, -- {width, height}
+ component_config JSON NOT NULL, -- 컴포넌트 설정
+ preview_image VARCHAR(255),
+ sort_order INTEGER DEFAULT 0,
+ is_active CHAR(1) DEFAULT 'Y',
+ is_public CHAR(1) DEFAULT 'Y',
+ company_code VARCHAR(50) NOT NULL,
+ created_date TIMESTAMP,
+ updated_date TIMESTAMP
+);
+```
+
+#### layout_standards (레이아웃 표준)
+
+```sql
+CREATE TABLE layout_standards (
+ layout_code VARCHAR(50) PRIMARY KEY,
+ layout_name VARCHAR(100) NOT NULL,
+ layout_type VARCHAR(50), -- grid, form, split, tab
+ default_config JSON,
+ is_active CHAR(1) DEFAULT 'Y',
+ company_code VARCHAR(50),
+ created_date TIMESTAMP,
+ updated_date TIMESTAMP
+);
+```
+
+---
+
+## 7. 비즈니스 도메인별 테이블
+
+### 7.1 영업/수주 관리
+
+#### 수주 관리 (Order Management)
+
+```
+sales_order_mng -- 수주 마스터
+├── sales_order_detail -- 수주 상세
+├── sales_order_detail_log -- 수주 상세 이력
+├── sales_request_master -- 영업 요청 마스터
+├── sales_request_part -- 영업 요청 부품
+└── sales_part_chg -- 영업 부품 변경
+```
+
+**sales_order_mng:**
+- 고객별 수주 정보
+- 납기, 금액, 상태 관리
+
+**sales_order_detail:**
+- 수주 라인 아이템
+- 품목, 수량, 단가 정보
+
+#### 견적 관리
+
+```
+estimate_mgmt -- 견적 관리
+contract_mgmt -- 계약 관리
+├── contract_mgmt_option -- 계약 옵션
+```
+
+#### BOM 관리
+
+```
+sales_bom_report -- 영업 BOM 리포트
+├── sales_bom_report_part -- 영업 BOM 부품
+└── sales_bom_part_qty -- 영업 BOM 부품 수량
+```
+
+### 7.2 구매/발주 관리
+
+```
+purchase_order_master -- 발주 마스터
+├── purchase_order -- 발주 상세
+├── purchase_order_part -- 발주 부품
+├── purchase_order_multi -- 다중 발주
+└── purchase_detail -- 구매 상세
+
+supplier_mng -- 공급업체 관리
+├── supplier_mng_log -- 공급업체 이력
+├── supplier_item -- 공급업체 품목
+├── supplier_item_alias -- 공급업체 품목 별칭
+├── supplier_item_mapping -- 공급업체 품목 매핑
+└── supplier_item_price -- 공급업체 품목 가격
+```
+
+### 7.3 재고/창고 관리
+
+```
+inventory_stock -- 재고 현황
+inventory_history -- 재고 이력
+warehouse_info -- 창고 정보
+warehouse_location -- 창고 위치
+inbound_mng -- 입고 관리
+outbound_mng -- 출고 관리
+receiving -- 입하
+receive_history -- 입하 이력
+```
+
+### 7.4 생산/작업 관리
+
+```
+work_orders -- 작업지시 (신규)
+├── work_orders_detail -- 작업지시 상세
+work_order -- 작업지시 (레거시)
+work_instruction -- 작업 지시서
+├── work_instruction_detail -- 작업 지시서 상세
+├── work_instruction_detail_log
+└── work_instruction_log
+
+production_record -- 생산 실적
+production_task -- 생산 작업
+production_issue -- 생산 이슈
+work_request -- 작업 요청
+```
+
+#### work_orders (작업지시 - 050 마이그레이션)
+
+```sql
+CREATE TABLE work_orders (
+ id VARCHAR(500) PRIMARY KEY DEFAULT gen_random_uuid()::text,
+ created_date TIMESTAMP DEFAULT NOW(),
+ updated_date TIMESTAMP DEFAULT NOW(),
+ writer VARCHAR(500),
+ company_code VARCHAR(500),
+
+ -- 작업지시 정보
+ wo_number VARCHAR(500), -- WO-20250130-001
+ product_code VARCHAR(500),
+ product_name VARCHAR(500),
+ spec VARCHAR(500),
+ order_qty VARCHAR(500),
+ completed_qty VARCHAR(500),
+ start_date VARCHAR(500),
+ due_date VARCHAR(500),
+ status VARCHAR(500), -- normal/urgent
+ progress_status VARCHAR(500), -- pending/in-progress/completed
+ equipment VARCHAR(500),
+ routing VARCHAR(500),
+ work_team VARCHAR(500), -- DAY/NIGHT
+ worker VARCHAR(500),
+ shift VARCHAR(500), -- DAY/NIGHT
+ remark VARCHAR(500)
+);
+
+CREATE INDEX idx_work_orders_company_code ON work_orders(company_code);
+CREATE INDEX idx_work_orders_wo_number ON work_orders(wo_number);
+```
+
+### 7.5 품질/검사 관리
+
+```
+inspection_standard -- 검사 기준
+item_inspection_info -- 품목 검사 정보
+inspection_equipment_mng -- 검사 설비 관리
+├── inspection_equipment_mng_log
+defect_standard_mng -- 불량 기준 관리
+├── defect_standard_mng_log
+check_report_mng -- 검사 성적서 관리
+safety_inspections -- 안전 점검
+└── safety_inspections_log
+```
+
+### 7.6 물류/운송 관리
+
+```
+vehicles -- 차량 정보
+├── vehicle_locations -- 차량 위치
+├── vehicle_location_history -- 차량 위치 이력
+├── vehicle_trip_summary -- 차량 운행 요약
+drivers -- 운전자 정보
+transport_logs -- 운송 로그
+transport_statistics -- 운송 통계
+transport_vehicle_locations -- 차량 위치
+
+carrier_mng -- 운송사 관리
+├── carrier_mng_log
+├── carrier_contract_mng -- 운송사 계약
+├── carrier_contract_mng_log
+├── carrier_vehicle_mng -- 운송사 차량
+└── carrier_vehicle_mng_log
+
+delivery_route_mng -- 배송 경로 관리
+├── delivery_route_mng_log
+delivery_destination -- 배송지
+delivery_status -- 배송 상태
+delivery_history -- 배송 이력
+├── delivery_history_defect -- 배송 불량
+delivery_part_price -- 배송 부품 가격
+```
+
+#### DTG 관리 (디지털 타코그래프)
+
+```
+dtg_management -- DTG 관리
+├── dtg_management_log
+dtg_contracts -- DTG 계약
+dtg_maintenance_history -- DTG 정비 이력
+dtg_monthly_settlements -- DTG 월별 정산
+```
+
+### 7.7 PLM/설계 관리
+
+```
+part_mng -- 부품 관리 (메인)
+├── part_mng_history
+part_mgmt -- 부품 관리 (서브)
+part_bom_qty -- 부품 BOM 수량
+part_bom_report -- 부품 BOM 리포트
+part_distribution_list -- 부품 배포 목록
+
+item_info -- 품목 정보
+item_routing_version -- 품목 라우팅 버전
+item_routing_detail -- 품목 라우팅 상세
+
+product_mng -- 제품 관리
+product_mgmt -- 제품 관리 (메인)
+├── product_mgmt_model -- 제품 모델
+├── product_mgmt_price_history -- 제품 가격 이력
+├── product_mgmt_upg_master -- 제품 업그레이드 마스터
+└── product_mgmt_upg_detail -- 제품 업그레이드 상세
+
+product_kind_spec -- 제품 종류 사양
+product_kind_spec_main -- 제품 종류 사양 메인
+product_spec -- 제품 사양
+product_group_mng -- 제품 그룹 관리
+
+mold_dev_request_info -- 금형 개발 요청
+structural_review_proposal -- 구조 검토 제안
+```
+
+### 7.8 프로젝트 관리 (PMS)
+
+```
+pms_pjt_info -- 프로젝트 정보
+├── pms_pjt_concept_info -- 프로젝트 개념 정보
+├── pms_pjt_year_goal -- 프로젝트 연도 목표
+pms_wbs_task -- WBS 작업
+├── pms_wbs_task_info -- WBS 작업 정보
+├── pms_wbs_task_confirm -- WBS 작업 확인
+├── pms_wbs_task_standard -- WBS 작업 표준
+└── pms_wbs_template -- WBS 템플릿
+
+pms_rel_pjt_concept_milestone -- 프로젝트 개념-마일스톤 관계
+pms_rel_pjt_concept_prod -- 프로젝트 개념-제품 관계
+pms_rel_pjt_prod -- 프로젝트-제품 관계
+pms_rel_prod_ref_dept -- 제품-참조부서 관계
+
+pms_invest_cost_mng -- 투자 비용 관리
+project_mgmt -- 프로젝트 관리
+problem_mng -- 문제 관리
+planning_issue -- 계획 이슈
+```
+
+### 7.9 회계/원가 관리
+
+```
+tax_invoice -- 세금계산서
+├── tax_invoice_item -- 세금계산서 항목
+fund_mgmt -- 자금 관리
+expense_master -- 비용 마스터
+├── expense_detail -- 비용 상세
+
+profit_loss -- 손익 계산
+├── profit_loss_total -- 손익 합계
+├── profit_loss_coefficient -- 손익 계수
+├── profit_loss_machine -- 손익 기계
+├── profit_loss_weight -- 손익 무게
+├── profit_loss_depth -- 손익 깊이
+├── profit_loss_pretime -- 손익 사전 시간
+├── profit_loss_coolingtime -- 손익 냉각 시간
+├── profit_loss_lossrate -- 손익 손실률
+└── profit_loss_srrate -- 손익 SR률
+
+material_cost -- 자재 비용
+injection_cost -- 사출 비용
+logistics_cost_mng -- 물류 비용 관리
+└── logistics_cost_mng_log
+
+input_cost_goal -- 투입 비용 목표
+input_resource -- 투입 자원
+```
+
+### 7.10 고객/협력사 관리
+
+```
+customer_mng -- 고객 관리
+customer_item -- 고객 품목
+├── customer_item_alias -- 고객 품목 별칭
+├── customer_item_mapping -- 고객 품목 매핑
+└── customer_item_price -- 고객 품목 가격
+
+customer_service_mgmt -- 고객 서비스 관리
+├── customer_service_part -- 고객 서비스 부품
+└── customer_service_workingtime -- 고객 서비스 작업시간
+
+oem_mng -- OEM 관리
+├── oem_factory_mng -- OEM 공장 관리
+└── oem_milestone_mng -- OEM 마일스톤 관리
+```
+
+### 7.11 설비/장비 관리
+
+```
+equipment_mng -- 설비 관리
+├── equipment_mng_log
+equipment_consumable -- 설비 소모품
+├── equipment_consumable_log
+equipment_inspection_item -- 설비 검사 항목
+└── equipment_inspection_item_log
+
+process_equipment -- 공정 설비
+process_mng -- 공정 관리
+maintenance_schedules -- 정비 일정
+```
+
+### 7.12 기타
+
+```
+approval -- 결재
+comments -- 댓글
+inboxtask -- 수신함 작업
+time_sheet -- 작업 시간
+attach_file_info -- 첨부 파일
+file_down_log -- 파일 다운로드 로그
+login_access_log -- 로그인 접근 로그
+```
+
+---
+
+## 8. 플로우 및 데이터 통합
+
+### 8.1 플로우 정의
+
+#### flow_definition (플로우 정의)
+
+```sql
+CREATE TABLE flow_definition (
+ flow_id SERIAL PRIMARY KEY,
+ flow_name VARCHAR(200) NOT NULL,
+ flow_code VARCHAR(100) NOT NULL,
+ flow_type VARCHAR(50), -- data_transfer, approval, batch 등
+ description TEXT,
+ trigger_type VARCHAR(50), -- manual, schedule, event
+ trigger_config JSONB,
+ is_active VARCHAR(1) DEFAULT 'Y',
+ company_code VARCHAR(20),
+ created_date TIMESTAMPTZ DEFAULT NOW(),
+ updated_date TIMESTAMPTZ DEFAULT NOW(),
+ created_by VARCHAR(50),
+ updated_by VARCHAR(50),
+
+ UNIQUE(flow_code, company_code)
+);
+```
+
+#### flow_step (플로우 단계)
+
+```sql
+CREATE TABLE flow_step (
+ step_id SERIAL PRIMARY KEY,
+ flow_id INTEGER REFERENCES flow_definition(flow_id) ON DELETE CASCADE,
+ step_name VARCHAR(200) NOT NULL,
+ step_type VARCHAR(50) NOT NULL, -- query, transform, api_call, condition 등
+ step_order INTEGER NOT NULL,
+ step_config JSONB NOT NULL,
+ error_handling_config JSONB,
+ is_active VARCHAR(1) DEFAULT 'Y',
+ company_code VARCHAR(20),
+ created_date TIMESTAMPTZ DEFAULT NOW(),
+ updated_date TIMESTAMPTZ DEFAULT NOW()
+);
+```
+
+#### flow_step_connection (플로우 단계 연결)
+
+```sql
+CREATE TABLE flow_step_connection (
+ connection_id SERIAL PRIMARY KEY,
+ flow_id INTEGER REFERENCES flow_definition(flow_id) ON DELETE CASCADE,
+ source_step_id INTEGER REFERENCES flow_step(step_id) ON DELETE CASCADE,
+ target_step_id INTEGER REFERENCES flow_step(step_id) ON DELETE CASCADE,
+ condition_expression TEXT, -- 조건부 실행
+ connection_type VARCHAR(20) DEFAULT 'sequential', -- sequential, parallel, conditional
+ company_code VARCHAR(20),
+ created_date TIMESTAMPTZ DEFAULT NOW()
+);
+```
+
+### 8.2 데이터플로우
+
+#### dataflow_diagrams (데이터플로우 다이어그램)
+
+```sql
+CREATE TABLE dataflow_diagrams (
+ diagram_id SERIAL PRIMARY KEY,
+ diagram_name VARCHAR(200) NOT NULL,
+ diagram_type VARCHAR(50),
+ diagram_json JSONB NOT NULL, -- 다이어그램 시각화 정보
+ description TEXT,
+ is_active VARCHAR(1) DEFAULT 'Y',
+ company_code VARCHAR(20),
+ created_date TIMESTAMPTZ DEFAULT NOW(),
+ updated_date TIMESTAMPTZ DEFAULT NOW(),
+ created_by VARCHAR(50),
+ updated_by VARCHAR(50)
+);
+```
+
+#### flow_data_mapping (플로우 데이터 매핑)
+
+```sql
+CREATE TABLE flow_data_mapping (
+ mapping_id SERIAL PRIMARY KEY,
+ flow_id INTEGER REFERENCES flow_definition(flow_id) ON DELETE CASCADE,
+ source_type VARCHAR(20), -- table, api, flow
+ source_identifier VARCHAR(200), -- 테이블명 또는 API 엔드포인트
+ source_field VARCHAR(100),
+ target_type VARCHAR(20),
+ target_identifier VARCHAR(200),
+ target_field VARCHAR(100),
+ transformation_rule TEXT, -- 변환 규칙 (JavaScript 표현식)
+ company_code VARCHAR(20),
+ created_date TIMESTAMPTZ DEFAULT NOW()
+);
+```
+
+#### flow_data_status (플로우 데이터 상태)
+
+```sql
+CREATE TABLE flow_data_status (
+ status_id SERIAL PRIMARY KEY,
+ flow_id INTEGER REFERENCES flow_definition(flow_id),
+ execution_id VARCHAR(100),
+ source_table VARCHAR(100),
+ source_record_id VARCHAR(500),
+ target_table VARCHAR(100),
+ target_record_id VARCHAR(500),
+ status VARCHAR(20), -- pending, processing, completed, failed
+ error_message TEXT,
+ processed_at TIMESTAMPTZ,
+ company_code VARCHAR(20),
+ created_date TIMESTAMPTZ DEFAULT NOW()
+);
+```
+
+### 8.3 외부 연동
+
+#### external_db_connections (외부 DB 연결)
+
+```sql
+CREATE TABLE external_db_connections (
+ id SERIAL PRIMARY KEY,
+ connection_name VARCHAR(200) NOT NULL,
+ connection_code VARCHAR(100) NOT NULL,
+ db_type VARCHAR(50) NOT NULL, -- postgresql, mysql, mssql, oracle
+ host VARCHAR(255) NOT NULL,
+ port INTEGER NOT NULL,
+ database_name VARCHAR(100) NOT NULL,
+ username VARCHAR(100),
+ password_encrypted TEXT,
+ ssl_enabled BOOLEAN DEFAULT false,
+ connection_options JSONB,
+ is_active VARCHAR(1) DEFAULT 'Y',
+ company_code VARCHAR(20),
+ created_date TIMESTAMPTZ DEFAULT NOW(),
+ updated_date TIMESTAMPTZ DEFAULT NOW(),
+ created_by VARCHAR(50),
+
+ UNIQUE(connection_code, company_code)
+);
+```
+
+#### external_rest_api_connections (외부 REST API 연결)
+
+```sql
+CREATE TABLE external_rest_api_connections (
+ id SERIAL PRIMARY KEY,
+ connection_name VARCHAR(200) NOT NULL,
+ connection_code VARCHAR(100) NOT NULL,
+ base_url VARCHAR(500) NOT NULL,
+ auth_type VARCHAR(50), -- none, basic, bearer, api_key
+ auth_config JSONB,
+ default_headers JSONB,
+ timeout_seconds INTEGER DEFAULT 30,
+ retry_count INTEGER DEFAULT 0,
+ is_active VARCHAR(1) DEFAULT 'Y',
+ company_code VARCHAR(20),
+ created_date TIMESTAMPTZ DEFAULT NOW(),
+ updated_date TIMESTAMPTZ DEFAULT NOW(),
+
+ UNIQUE(connection_code, company_code)
+);
+```
+
+#### external_call_configs (외부 호출 설정)
+
+```sql
+CREATE TABLE external_call_configs (
+ id SERIAL PRIMARY KEY,
+ config_name VARCHAR(200) NOT NULL,
+ config_code VARCHAR(100) NOT NULL,
+ connection_id INTEGER, -- external_rest_api_connections 참조
+ http_method VARCHAR(10), -- GET, POST, PUT, DELETE
+ endpoint_path VARCHAR(500),
+ request_mapping JSONB, -- 요청 데이터 매핑
+ response_mapping JSONB, -- 응답 데이터 매핑
+ error_handling JSONB,
+ is_active VARCHAR(1) DEFAULT 'Y',
+ company_code VARCHAR(20),
+ created_date TIMESTAMPTZ DEFAULT NOW(),
+ updated_date TIMESTAMPTZ DEFAULT NOW()
+);
+```
+
+### 8.4 배치 작업
+
+#### batch_jobs (배치 작업 정의)
+
+```sql
+CREATE TABLE batch_jobs (
+ job_id SERIAL PRIMARY KEY,
+ job_name VARCHAR(200) NOT NULL,
+ job_code VARCHAR(100) NOT NULL,
+ job_type VARCHAR(50), -- data_collection, aggregation, sync 등
+ source_type VARCHAR(50), -- database, api, file
+ source_config JSONB,
+ target_config JSONB,
+ schedule_config JSONB,
+ is_active VARCHAR(1) DEFAULT 'Y',
+ company_code VARCHAR(20),
+ created_date TIMESTAMPTZ DEFAULT NOW(),
+ updated_date TIMESTAMPTZ DEFAULT NOW(),
+
+ UNIQUE(job_code, company_code)
+);
+```
+
+#### batch_execution_logs (배치 실행 로그)
+
+```sql
+CREATE TABLE batch_execution_logs (
+ execution_id SERIAL PRIMARY KEY,
+ job_id INTEGER REFERENCES batch_jobs(job_id),
+ execution_status VARCHAR(20), -- running, completed, failed
+ start_time TIMESTAMPTZ,
+ end_time TIMESTAMPTZ,
+ records_processed INTEGER,
+ records_failed INTEGER,
+ error_message TEXT,
+ execution_details JSONB,
+ company_code VARCHAR(20),
+ created_date TIMESTAMPTZ DEFAULT NOW()
+);
+```
+
+---
+
+## 9. 인덱스 전략
+
+### 9.1 필수 인덱스
+
+**모든 테이블에 적용:**
+
+```sql
+-- company_code 인덱스 (멀티테넌시 성능)
+CREATE INDEX idx_{table_name}_company_code ON {table_name}(company_code);
+
+-- 복합 인덱스 (company_code + 주요 검색 컬럼)
+CREATE INDEX idx_{table_name}_company_{column}
+ON {table_name}(company_code, {column});
+```
+
+### 9.2 화면 관련 인덱스
+
+```sql
+-- screen_definitions
+CREATE INDEX idx_screen_definitions_company_code ON screen_definitions(company_code);
+CREATE INDEX idx_screen_definitions_table_name ON screen_definitions(table_name);
+CREATE INDEX idx_screen_definitions_is_active ON screen_definitions(is_active);
+CREATE UNIQUE INDEX idx_screen_definitions_code_company
+ON screen_definitions(screen_code, company_code);
+
+-- screen_groups
+CREATE INDEX idx_screen_groups_company_code ON screen_groups(company_code);
+CREATE INDEX idx_screen_groups_parent_id ON screen_groups(parent_group_id);
+CREATE INDEX idx_screen_groups_hierarchy_path ON screen_groups(hierarchy_path);
+
+-- screen_field_joins
+CREATE INDEX idx_screen_field_joins_screen_id ON screen_field_joins(screen_id);
+CREATE INDEX idx_screen_field_joins_save_table ON screen_field_joins(save_table);
+CREATE INDEX idx_screen_field_joins_join_table ON screen_field_joins(join_table);
+```
+
+### 9.3 메타데이터 인덱스
+
+```sql
+-- table_type_columns
+CREATE UNIQUE INDEX idx_table_type_columns_unique
+ON table_type_columns(table_name, column_name, company_code);
+
+-- column_labels
+CREATE INDEX idx_column_labels_table ON column_labels(table_name);
+```
+
+### 9.4 비즈니스 테이블 인덱스 예시
+
+```sql
+-- sales_order_mng
+CREATE INDEX idx_sales_order_company_code ON sales_order_mng(company_code);
+CREATE INDEX idx_sales_order_customer ON sales_order_mng(customer_code);
+CREATE INDEX idx_sales_order_date ON sales_order_mng(order_date DESC);
+CREATE INDEX idx_sales_order_status ON sales_order_mng(order_status);
+
+-- work_orders
+CREATE INDEX idx_work_orders_company_code ON work_orders(company_code);
+CREATE INDEX idx_work_orders_wo_number ON work_orders(wo_number);
+CREATE INDEX idx_work_orders_start_date ON work_orders(start_date);
+CREATE INDEX idx_work_orders_product_code ON work_orders(product_code);
+```
+
+### 9.5 플로우 관련 인덱스
+
+```sql
+-- flow_definition
+CREATE UNIQUE INDEX idx_flow_definition_code_company
+ON flow_definition(flow_code, company_code);
+
+-- flow_step
+CREATE INDEX idx_flow_step_flow_id ON flow_step(flow_id);
+CREATE INDEX idx_flow_step_order ON flow_step(flow_id, step_order);
+
+-- flow_data_status
+CREATE INDEX idx_flow_data_status_flow_execution
+ON flow_data_status(flow_id, execution_id);
+CREATE INDEX idx_flow_data_status_source
+ON flow_data_status(source_table, source_record_id);
+```
+
+---
+
+## 10. 동적 테이블 생성 패턴
+
+WACE ERP의 핵심 기능 중 하나는 **런타임에 테이블을 동적으로 생성**할 수 있다는 것입니다.
+
+### 10.1 표준 컬럼 구조
+
+**모든 동적 생성 테이블의 기본 컬럼:**
+
+```sql
+CREATE TABLE {dynamic_table_name} (
+ -- 시스템 기본 컬럼 (자동 포함)
+ id VARCHAR(500) PRIMARY KEY DEFAULT gen_random_uuid()::text,
+ created_date TIMESTAMP DEFAULT NOW(),
+ updated_date TIMESTAMP DEFAULT NOW(),
+ writer VARCHAR(500),
+ company_code VARCHAR(500),
+
+ -- 사용자 정의 컬럼 (모두 VARCHAR(500))
+ {user_column_1} VARCHAR(500),
+ {user_column_2} VARCHAR(500),
+ ...
+);
+
+-- 필수 인덱스
+CREATE INDEX idx_{table_name}_company_code ON {table_name}(company_code);
+```
+
+### 10.2 메타데이터 등록 프로세스
+
+동적 테이블 생성 시 반드시 수행해야 하는 작업:
+
+#### 1단계: 테이블 생성
+
+```sql
+CREATE TABLE {table_name} (
+ -- 위의 표준 구조 참조
+);
+```
+
+#### 2단계: table_labels 등록
+
+```sql
+INSERT INTO table_labels (table_name, table_label, description, created_date, updated_date)
+VALUES ('{table_name}', '{한글명}', '{설명}', NOW(), NOW())
+ON CONFLICT (table_name)
+DO UPDATE SET
+ table_label = EXCLUDED.table_label,
+ description = EXCLUDED.description,
+ updated_date = NOW();
+```
+
+#### 3단계: table_type_columns 등록
+
+```sql
+-- 기본 컬럼 등록 (display_order: -5 ~ -1)
+INSERT INTO table_type_columns (
+ table_name, column_name, company_code, input_type, detail_settings,
+ is_nullable, display_order, created_date, updated_date
+) VALUES
+ ('{table_name}', 'id', '{company_code}', 'text', '{}', 'Y', -5, NOW(), NOW()),
+ ('{table_name}', 'created_date', '{company_code}', 'date', '{}', 'Y', -4, NOW(), NOW()),
+ ('{table_name}', 'updated_date', '{company_code}', 'date', '{}', 'Y', -3, NOW(), NOW()),
+ ('{table_name}', 'writer', '{company_code}', 'text', '{}', 'Y', -2, NOW(), NOW()),
+ ('{table_name}', 'company_code', '{company_code}', 'text', '{}', 'Y', -1, NOW(), NOW())
+ON CONFLICT (table_name, column_name, company_code)
+DO UPDATE SET
+ input_type = EXCLUDED.input_type,
+ display_order = EXCLUDED.display_order,
+ updated_date = NOW();
+
+-- 사용자 정의 컬럼 등록 (display_order: 0부터 시작)
+INSERT INTO table_type_columns (
+ table_name, column_name, company_code, input_type, detail_settings,
+ is_nullable, display_order, created_date, updated_date
+) VALUES
+ ('{table_name}', '{column_1}', '{company_code}', 'text', '{}', 'Y', 0, NOW(), NOW()),
+ ('{table_name}', '{column_2}', '{company_code}', 'number', '{}', 'Y', 1, NOW(), NOW()),
+ ...
+```
+
+#### 4단계: column_labels 등록 (레거시 호환용)
+
+```sql
+INSERT INTO column_labels (
+ table_name, column_name, column_label, input_type, detail_settings,
+ description, display_order, is_visible, created_date, updated_date
+) VALUES
+ ('{table_name}', 'id', 'ID', 'text', '{}', '기본키', -5, true, NOW(), NOW()),
+ ...
+ON CONFLICT (table_name, column_name)
+DO UPDATE SET
+ column_label = EXCLUDED.column_label,
+ input_type = EXCLUDED.input_type,
+ updated_date = NOW();
+```
+
+### 10.3 마이그레이션 예시
+
+**050_create_work_orders_table.sql:**
+
+```sql
+-- ============================================
+-- 1. 테이블 생성
+-- ============================================
+CREATE TABLE work_orders (
+ id VARCHAR(500) PRIMARY KEY DEFAULT gen_random_uuid()::text,
+ created_date TIMESTAMP DEFAULT NOW(),
+ updated_date TIMESTAMP DEFAULT NOW(),
+ writer VARCHAR(500),
+ company_code VARCHAR(500),
+
+ wo_number VARCHAR(500),
+ product_code VARCHAR(500),
+ product_name VARCHAR(500),
+ ...
+);
+
+CREATE INDEX idx_work_orders_company_code ON work_orders(company_code);
+CREATE INDEX idx_work_orders_wo_number ON work_orders(wo_number);
+
+-- ============================================
+-- 2. table_labels 등록
+-- ============================================
+INSERT INTO table_labels (table_name, table_label, description, created_date, updated_date)
+VALUES ('work_orders', '작업지시', '작업지시 관리 테이블', NOW(), NOW())
+ON CONFLICT (table_name) DO UPDATE SET ...;
+
+-- ============================================
+-- 3. table_type_columns 등록
+-- ============================================
+INSERT INTO table_type_columns (...) VALUES (...);
+
+-- ============================================
+-- 4. column_labels 등록
+-- ============================================
+INSERT INTO column_labels (...) VALUES (...);
+
+-- ============================================
+-- 5. 코멘트 추가
+-- ============================================
+COMMENT ON TABLE work_orders IS '작업지시 관리 테이블';
+COMMENT ON COLUMN work_orders.wo_number IS '작업지시번호 (WO-YYYYMMDD-XXX)';
+```
+
+---
+
+## 11. 마이그레이션 히스토리
+
+### 11.1 마이그레이션 파일 목록
+
+```
+db/migrations/
+├── 037_add_parent_group_to_screen_groups.sql -- 화면 그룹 계층 구조
+├── 050_create_work_orders_table.sql -- 작업지시 테이블
+├── 051_insert_work_order_screen_definition.sql -- 작업지시 화면 정의
+├── 052_insert_work_order_screen_layout.sql -- 작업지시 화면 레이아웃
+├── 054_create_screen_management_enhancement.sql -- 화면 관리 기능 확장
+└── plm_schema_20260120.sql -- 전체 스키마 덤프
+```
+
+### 11.2 주요 마이그레이션 내용
+
+#### 037: 화면 그룹 계층 구조
+
+- `screen_groups`에 계층 구조 지원 추가
+- `parent_group_id`, `group_level`, `hierarchy_path` 컬럼 추가
+- 대/중/소 분류 지원
+
+#### 050~052: 작업지시 시스템
+
+- `work_orders` 테이블 생성
+- 메타데이터 등록 (table_labels, table_type_columns, column_labels)
+- 화면 정의 및 레이아웃 생성
+
+#### 054: 화면 관리 기능 확장
+
+- `screen_groups`: 화면 그룹
+- `screen_group_screens`: 화면-그룹 연결
+- `screen_field_joins`: 화면 필드 조인 설정
+- `screen_data_flows`: 화면 간 데이터 흐름
+- `screen_table_relations`: 화면-테이블 관계
+
+### 11.3 마이그레이션 실행 가이드
+
+**마이그레이션 문서:**
+- `RUN_027_MIGRATION.md`
+- `RUN_043_MIGRATION.md`
+- `RUN_044_MIGRATION.md`
+- `RUN_046_MIGRATION.md`
+- `RUN_063_064_MIGRATION.md`
+- `RUN_065_MIGRATION.md`
+- `RUN_078_MIGRATION.md`
+
+---
+
+## 12. 데이터베이스 함수 및 트리거
+
+### 12.1 주요 함수
+
+#### 화면 관련
+
+```sql
+-- 화면 생성 시 메뉴 자동 생성
+CREATE FUNCTION auto_create_menu_for_screen() RETURNS TRIGGER;
+
+-- 화면 삭제 시 메뉴 비활성화
+CREATE FUNCTION auto_deactivate_menu_for_screen() RETURNS TRIGGER;
+```
+
+#### 통계 집계
+
+```sql
+-- 일일 운송 통계 집계
+CREATE FUNCTION aggregate_daily_transport_statistics(target_date DATE) RETURNS INTEGER;
+```
+
+#### 거리 계산
+
+```sql
+-- Haversine 거리 계산
+CREATE FUNCTION calculate_distance(lat1 NUMERIC, lng1 NUMERIC, lat2 NUMERIC, lng2 NUMERIC)
+RETURNS NUMERIC;
+
+-- 이전 위치로부터 거리 계산
+CREATE FUNCTION calculate_distance_from_prev() RETURNS TRIGGER;
+```
+
+#### 비즈니스 로직
+
+```sql
+-- 수주 잔량 계산
+CREATE FUNCTION calculate_order_balance() RETURNS TRIGGER;
+
+-- 세금계산서 합계 계산
+CREATE FUNCTION calculate_tax_invoice_total() RETURNS TRIGGER;
+
+-- 영업에서 프로젝트 자동 생성
+CREATE FUNCTION auto_create_project_from_sales(p_sales_no VARCHAR) RETURNS VARCHAR;
+```
+
+#### 로그 관리
+
+```sql
+-- 테이블 변경 로그 트리거 함수
+CREATE FUNCTION carrier_contract_mng_log_trigger_func() RETURNS TRIGGER;
+CREATE FUNCTION carrier_mng_log_trigger_func() RETURNS TRIGGER;
+-- ... 각 테이블별 로그 트리거 함수
+```
+
+### 12.2 주요 트리거
+
+```sql
+-- 화면 생성 시 메뉴 자동 생성
+CREATE TRIGGER trg_auto_create_menu_for_screen
+AFTER INSERT ON screen_definitions
+FOR EACH ROW
+EXECUTE FUNCTION auto_create_menu_for_screen();
+
+-- 화면 삭제 시 메뉴 비활성화
+CREATE TRIGGER trg_auto_deactivate_menu_for_screen
+AFTER UPDATE ON screen_definitions
+FOR EACH ROW
+EXECUTE FUNCTION auto_deactivate_menu_for_screen();
+
+-- 차량 위치 이력 거리 계산
+CREATE TRIGGER trg_calculate_distance_from_prev
+BEFORE INSERT ON vehicle_location_history
+FOR EACH ROW
+EXECUTE FUNCTION calculate_distance_from_prev();
+```
+
+---
+
+## 13. 뷰 (Views)
+
+### 13.1 시스템 뷰
+
+```sql
+-- 권한 그룹 요약
+CREATE VIEW v_authority_group_summary AS
+SELECT
+ am.objid,
+ am.auth_name,
+ am.auth_code,
+ am.company_code,
+ (SELECT COUNT(*) FROM authority_sub_user WHERE master_objid = am.objid) AS member_count,
+ (SELECT COUNT(*) FROM rel_menu_auth WHERE auth_objid = am.objid) AS menu_count
+FROM authority_master am;
+```
+
+---
+
+## 14. 데이터베이스 보안 및 암호화
+
+### 14.1 암호화 컬럼
+
+```sql
+-- external_db_connections
+password_encrypted TEXT -- AES 암호화된 비밀번호
+
+-- external_rest_api_connections
+auth_config JSONB -- 암호화된 인증 정보
+```
+
+### 14.2 접근 제어
+
+- PostgreSQL 롤 기반 접근 제어
+- 회사별 데이터 격리 (company_code)
+- 사용자별 권한 관리 (authority_master, rel_menu_auth)
+
+---
+
+## 15. 성능 최적화 전략
+
+### 15.1 인덱스 최적화
+
+- **company_code 인덱스**: 모든 테이블에 필수
+- **복합 인덱스**: 자주 함께 조회되는 컬럼 조합
+- **부분 인덱스**: 특정 조건의 데이터만 인덱싱
+
+### 15.2 쿼리 최적화
+
+- **서브쿼리 최소화**: JOIN으로 대체
+- **EXPLAIN ANALYZE** 활용
+- **인덱스 힌트** 사용
+
+### 15.3 캐싱 전략
+
+- **참조 데이터 캐싱**: referenceCacheService.ts
+- **Redis 캐싱**: 자주 조회되는 메타데이터
+
+### 15.4 파티셔닝
+
+- 대용량 이력 테이블 파티셔닝 고려
+- 날짜 기반 파티셔닝 (vehicle_location_history 등)
+
+---
+
+## 16. 백업 및 복구
+
+### 16.1 백업 전략
+
+```bash
+# 전체 스키마 백업
+pg_dump -h host -U user -d database > plm_schema_YYYYMMDD.sql
+
+# 데이터 포함 백업
+pg_dump -h host -U user -d database --data-only > data_YYYYMMDD.sql
+
+# 특정 테이블 백업
+pg_dump -h host -U user -d database -t table_name > table_backup.sql
+```
+
+### 16.2 마이그레이션 롤백
+
+- DDL 작업 전 백업 필수
+- 트랜잭션 기반 마이그레이션
+- 롤백 스크립트 준비
+
+---
+
+## 17. 모니터링 및 로깅
+
+### 17.1 시스템 로그 테이블
+
+```
+login_access_log -- 로그인 접근 로그
+ddl_execution_log -- DDL 실행 로그
+batch_execution_logs -- 배치 실행 로그
+flow_audit_log -- 플로우 감사 로그
+flow_integration_log -- 플로우 통합 로그
+external_call_logs -- 외부 호출 로그
+mail_log -- 메일 발송 로그
+file_down_log -- 파일 다운로드 로그
+```
+
+### 17.2 변경 이력 테이블
+
+**패턴:** `{원본테이블}_log` 또는 `{원본테이블}_history`
+
+```
+user_info_history
+dept_info_history
+authority_master_history
+comm_code_history
+carrier_mng_log
+supplier_mng_log
+equipment_mng_log
+...
+```
+
+---
+
+## 18. 결론
+
+### 18.1 핵심 아키텍처 특징
+
+1. **멀티테넌시**: company_code로 완벽한 데이터 격리
+2. **메타데이터 드리븐**: 동적 화면/테이블 생성
+3. **Low-Code 플랫폼**: 코드 없이 화면 구축
+4. **플로우 기반**: 시각적 데이터 흐름 설계
+5. **외부 연동**: DB/API 통합 지원
+6. **이력 관리**: 완벽한 변경 이력 추적
+
+### 18.2 확장성
+
+- **수평 확장**: 멀티테넌시로 무한한 회사 추가 가능
+- **수직 확장**: 동적 테이블/컬럼 추가
+- **기능 확장**: 플로우/배치 작업으로 비즈니스 로직 추가
+
+### 18.3 유지보수성
+
+- **표준화된 구조**: 모든 테이블이 동일한 패턴
+- **자동화**: 트리거/함수로 반복 작업 자동화
+- **문서화**: 메타데이터 테이블 자체가 문서
+
+---
+
+## 부록 A: 백엔드 서비스 매핑
+
+### 주요 서비스와 테이블 매핑
+
+```typescript
+// backend-node/src/services/
+
+screenManagementService.ts → screen_definitions, screen_layouts
+tableManagementService.ts → table_labels, table_type_columns, column_labels
+menuService.ts → menu_info, menu_screen_groups
+categoryTreeService.ts → table_column_category_values
+flowDefinitionService.ts → flow_definition, flow_step
+flowExecutionService.ts → flow_data_status, flow_audit_log
+dataflowService.ts → dataflow_diagrams, screen_data_flows
+externalDbConnectionService.ts → external_db_connections
+externalRestApiConnectionService.ts → external_rest_api_connections
+batchService.ts → batch_jobs, batch_execution_logs
+authService.ts → user_info, auth_tokens
+roleService.ts → authority_master, rel_menu_auth
+```
+
+---
+
+## 부록 B: SQL 쿼리 예시
+
+### 멀티테넌시 표준 쿼리
+
+```sql
+-- ✅ 단일 테이블 조회
+SELECT * FROM sales_order_mng
+WHERE company_code = $1
+ AND company_code != '*'
+ AND order_date >= $2
+ORDER BY order_date DESC
+LIMIT 100;
+
+-- ✅ JOIN 쿼리
+SELECT
+ so.order_no,
+ so.order_date,
+ c.customer_name,
+ p.product_name,
+ so.quantity,
+ so.unit_price
+FROM sales_order_mng so
+LEFT JOIN customer_mng c
+ ON so.customer_code = c.customer_code
+ AND so.company_code = c.company_code
+LEFT JOIN product_mng p
+ ON so.product_code = p.product_code
+ AND so.company_code = p.company_code
+WHERE so.company_code = $1
+ AND so.company_code != '*'
+ AND so.order_date BETWEEN $2 AND $3;
+
+-- ✅ 집계 쿼리
+SELECT
+ customer_code,
+ COUNT(*) as order_count,
+ SUM(total_amount) as total_sales
+FROM sales_order_mng
+WHERE company_code = $1
+ AND company_code != '*'
+ AND order_date >= DATE_TRUNC('month', CURRENT_DATE)
+GROUP BY customer_code
+HAVING COUNT(*) >= 5
+ORDER BY total_sales DESC;
+```
+
+---
+
+## 부록 C: 참고 문서
+
+```
+docs/
+├── DDD1542/
+│ ├── DB_STRUCTURE_DIAGRAM.md -- DB 구조 다이어그램
+│ ├── DB_INEFFICIENCY_ANALYSIS.md -- DB 비효율성 분석
+│ ├── COMPONENT_URL_SYSTEM_IMPLEMENTATION.md
+│ ├── V2_마이그레이션_학습노트_DDD1542.md
+│ └── 본서버_개발서버_마이그레이션_가이드.md
+├── backend-architecture-analysis.md -- 백엔드 아키텍처 분석
+└── screen-implementation-guide/ -- 화면 구현 가이드
+```
+
+---
+
+**문서 작성자**: Cursor AI (DB Specialist Agent)
+**문서 버전**: 1.0
+**마지막 업데이트**: 2026-01-20
+**스키마 버전**: plm_schema_20260120.sql
+
+---
diff --git a/docs/backend-architecture-analysis.md b/docs/backend-architecture-analysis.md
new file mode 100644
index 00000000..c5c2b549
--- /dev/null
+++ b/docs/backend-architecture-analysis.md
@@ -0,0 +1,1424 @@
+# WACE ERP 백엔드 아키텍처 상세 분석
+
+> **작성일**: 2026-02-06
+> **분석 대상**: ERP-node/backend-node
+> **Stack**: Node.js + Express + TypeScript + PostgreSQL Raw Query
+
+---
+
+## 📋 목차
+
+1. [전체 디렉토리 구조](#1-전체-디렉토리-구조)
+2. [API 라우트 목록 및 역할](#2-api-라우트-목록-및-역할)
+3. [인증/인가 워크플로우](#3-인증인가-워크플로우)
+4. [비즈니스 도메인별 모듈 분류](#4-비즈니스-도메인별-모듈-분류)
+5. [미들웨어 스택 구성](#5-미들웨어-스택-구성)
+6. [서비스 레이어 패턴](#6-서비스-레이어-패턴)
+7. [멀티테넌시 구현 방식](#7-멀티테넌시-구현-방식)
+8. [에러 핸들링 전략](#8-에러-핸들링-전략)
+9. [파일 업로드/다운로드 처리](#9-파일-업로드다운로드-처리)
+10. [외부 연동](#10-외부-연동)
+11. [배치/스케줄 처리](#11-배치스케줄-처리)
+12. [컨트롤러/서비스 상세 역할](#12-컨트롤러서비스-상세-역할)
+
+---
+
+## 1. 전체 디렉토리 구조
+
+```
+backend-node/
+├── src/
+│ ├── app.ts # Express 앱 진입점, 라우트 등록, 미들웨어 설정
+│ ├── config/
+│ │ └── environment.ts # 환경변수 관리 (PORT, DB, JWT, CORS 등)
+│ ├── controllers/ # 69개 컨트롤러 (요청 처리 및 응답)
+│ ├── services/ # 87개 서비스 (비즈니스 로직)
+│ ├── routes/ # 77개 라우터 (엔드포인트 정의)
+│ ├── middleware/ # 4개 미들웨어 (인증, 권한, 에러 핸들링)
+│ ├── database/ # DB 연결 풀, 커넥터, 마이그레이션
+│ ├── utils/ # 16개 유틸리티 (JWT, 암호화, 로거 등)
+│ ├── types/ # 26개 TypeScript 타입 정의
+│ ├── interfaces/ # 인터페이스 정의
+│ └── tests/ # 테스트 파일
+├── scripts/ # 배치 및 유틸리티 스크립트
+├── data/ # JSON 기반 설정 데이터
+├── uploads/ # 파일 업로드 디렉토리
+└── package.json # 의존성 관리
+```
+
+### 주요 특징
+- **Layered Architecture**: Controller → Service → Database 3계층 구조
+- **TypeScript Strict Mode**: 타입 안전성 보장
+- **Raw Query 기반**: Prisma → PostgreSQL Raw Query 전환 완료
+- **Connection Pool**: pg 라이브러리 기반 연결 풀 관리
+- **마이크로서비스 지향**: 도메인별 명확한 분리
+
+---
+
+## 2. API 라우트 목록 및 역할
+
+### 2.1 인증 및 관리자 (Auth & Admin)
+
+| 엔드포인트 | 메서드 | 역할 | 인증 |
+|-----------|--------|------|------|
+| `/api/auth/login` | POST | 로그인 (JWT 토큰 발급) | ❌ |
+| `/api/auth/signup` | POST | 회원가입 (공차중계) | ❌ |
+| `/api/auth/me` | GET | 현재 사용자 정보 조회 | ✅ |
+| `/api/auth/logout` | POST | 로그아웃 | ✅ |
+| `/api/auth/refresh` | POST | JWT 토큰 갱신 | ✅ |
+| `/api/auth/switch-company` | POST | 관리자 전용: 회사 전환 | ✅ |
+| `/api/admin/menus` | GET | 메뉴 목록 조회 | ✅ |
+| `/api/admin/users` | GET/POST/PUT | 사용자 관리 (CRUD) | ✅ |
+| `/api/admin/companies` | GET/POST/PUT/DELETE | 회사 관리 (CRUD) | ✅ |
+| `/api/admin/departments` | GET | 부서 목록 조회 | ✅ |
+
+### 2.2 테이블 및 데이터 관리
+
+| 엔드포인트 | 메서드 | 역할 | 인증 |
+|-----------|--------|------|------|
+| `/api/table-management/tables` | GET | 테이블 목록 조회 | ✅ |
+| `/api/table-management/columns` | GET | 컬럼 정보 조회 | ✅ |
+| `/api/table-management/entity-joins` | GET/POST | 테이블 조인 설정 | ✅ |
+| `/api/data/*` | GET/POST/PUT/DELETE | 동적 테이블 데이터 CRUD | ✅ |
+| `/api/ddl/*` | POST | DDL 실행 (테이블 생성/수정/삭제) | ✅ (Super Admin) |
+
+### 2.3 화면 및 폼 관리
+
+| 엔드포인트 | 메서드 | 역할 | 인증 |
+|-----------|--------|------|------|
+| `/api/screen-management/*` | GET/POST/PUT/DELETE | 화면 메타데이터 관리 | ✅ |
+| `/api/screen-groups/*` | GET/POST/PUT/DELETE | 화면 그룹 관리 | ✅ |
+| `/api/dynamic-form/*` | GET/POST | 동적 폼 생성 및 렌더링 | ✅ |
+| `/api/admin/web-types` | GET/POST | 웹 컴포넌트 타입 표준 관리 | ✅ |
+| `/api/admin/button-actions` | GET/POST | 버튼 액션 표준 관리 | ✅ |
+| `/api/admin/template-standards` | GET/POST | 템플릿 표준 관리 | ✅ |
+| `/api/admin/component-standards` | GET/POST | 컴포넌트 표준 관리 | ✅ |
+
+### 2.4 플로우 및 데이터플로우
+
+| 엔드포인트 | 메서드 | 역할 | 인증 |
+|-----------|--------|------|------|
+| `/api/flow/definitions` | GET/POST/PUT/DELETE | 플로우 정의 관리 | ✅ |
+| `/api/flow/definitions/:id/steps` | GET/POST | 플로우 단계 관리 | ✅ |
+| `/api/flow/connections` | GET/POST/DELETE | 플로우 연결 관리 | ✅ |
+| `/api/flow/move` | POST | 데이터 이동 실행 | ✅ |
+| `/api/flow/audit/:flowId` | GET | 플로우 오딧 로그 조회 | ✅ |
+| `/api/dataflow/*` | GET/POST/PUT/DELETE | 데이터플로우 관계 관리 | ✅ |
+| `/api/dataflow-diagrams/*` | GET/POST/PUT/DELETE | 데이터플로우 다이어그램 | ✅ |
+| `/api/dataflow/execute` | POST | 데이터플로우 실행 | ✅ |
+
+### 2.5 배치 관리
+
+| 엔드포인트 | 메서드 | 역할 | 인증 |
+|-----------|--------|------|------|
+| `/api/batch-configs` | GET/POST/PUT/DELETE | 배치 설정 관리 | ✅ |
+| `/api/batch-configs/connections` | GET | 사용 가능한 커넥션 목록 | ✅ |
+| `/api/batch-configs/:id/execute` | POST | 배치 수동 실행 | ✅ |
+| `/api/batch-management/*` | GET/POST | 배치 실행 관리 | ✅ |
+| `/api/batch-execution-logs` | GET | 배치 실행 이력 조회 | ✅ |
+
+### 2.6 외부 연동
+
+| 엔드포인트 | 메서드 | 역할 | 인증 |
+|-----------|--------|------|------|
+| `/api/external-db-connections` | GET/POST/PUT/DELETE | 외부 DB 연결 관리 | ✅ |
+| `/api/external-db-connections/:id/test` | POST | 외부 DB 연결 테스트 | ✅ |
+| `/api/external-rest-api-connections` | GET/POST/PUT/DELETE | 외부 REST API 연결 | ✅ |
+| `/api/external-calls/*` | GET/POST | 외부 API 호출 설정 | ✅ |
+| `/api/multi-connection/query` | POST | 멀티 DB 통합 쿼리 | ✅ |
+
+### 2.7 메일 관리
+
+| 엔드포인트 | 메서드 | 역할 | 인증 |
+|-----------|--------|------|------|
+| `/api/mail/accounts` | GET/POST/PUT/DELETE | 메일 계정 관리 | ✅ |
+| `/api/mail/templates-file` | GET/POST/PUT/DELETE | 메일 템플릿 관리 | ✅ |
+| `/api/mail/send` | POST | 메일 발송 (단일/대량) | ✅ |
+| `/api/mail/sent` | GET | 발송 이력 조회 | ✅ |
+| `/api/mail/receive` | GET | 메일 수신함 조회 | ✅ |
+
+### 2.8 기타 도메인
+
+| 엔드포인트 | 메서드 | 역할 | 인증 |
+|-----------|--------|------|------|
+| `/api/dashboards/*` | GET/POST | 대시보드 관리 | ✅ |
+| `/api/admin/reports/*` | GET/POST | 리포트 생성 및 조회 | ✅ |
+| `/api/files/*` | POST | 파일 업로드/다운로드 | ✅ |
+| `/api/delivery/*` | GET/POST | 배송/화물 관리 | ✅ |
+| `/api/risk-alerts/*` | GET/POST | 리스크/알림 관리 | ✅ |
+| `/api/todos/*` | GET/POST/PUT/DELETE | To-Do 관리 | ✅ |
+| `/api/bookings/*` | GET/POST | 예약 관리 | ✅ |
+| `/api/digital-twin/*` | GET/POST | 디지털 트윈 (야드 관제) | ✅ |
+| `/api/schedule/*` | GET/POST | 스케줄 자동 생성 | ✅ |
+| `/api/work-history/*` | GET | 작업 이력 조회 | ✅ |
+| `/api/table-history/*` | GET | 테이블 변경 이력 조회 | ✅ |
+| `/api/roles/*` | GET/POST | 권한 그룹 관리 | ✅ |
+| `/api/numbering-rules/*` | GET/POST | 채번 규칙 관리 | ✅ |
+| `/api/entity-search/*` | GET | 엔티티 검색 | ✅ |
+| `/api/cascading-*` | GET/POST | 연쇄 드롭다운 관계 | ✅ |
+| `/api/category-tree/*` | GET/POST | 카테고리 트리 | ✅ |
+| `/api/vehicle/*` | GET/POST | 차량 운행 이력 | ✅ |
+| `/api/tax-invoice/*` | GET/POST | 세금계산서 관리 | ✅ |
+
+**총 77개 라우터 파일, 200개 이상의 엔드포인트 제공**
+
+---
+
+## 3. 인증/인가 워크플로우
+
+### 3.1 인증 메커니즘
+
+```
+로그인 요청 (userId, password)
+ ↓
+1. AuthController.login()
+ ↓
+2. AuthService.processLogin()
+ ├─ 비밀번호 검증 (BCrypt + 마스터 패스워드)
+ ├─ 사용자 정보 조회 (user_info 테이블)
+ ├─ 로그인 로그 기록 (LOGIN_ACCESS_LOG)
+ └─ JWT 토큰 생성 (JwtUtils.generateToken)
+ ↓
+3. JWT 토큰 응답
+ ├─ accessToken (24시간 유효)
+ ├─ refreshToken (7일 유효)
+ └─ userInfo (userId, userName, companyCode, userType)
+```
+
+### 3.2 JWT 토큰 구조
+
+```typescript
+// JWT Payload
+{
+ userId: string; // 사용자 ID
+ userName: string; // 사용자 이름
+ companyCode: string; // 회사 코드 (멀티테넌시 핵심)
+ userType: string; // 사용자 유형 (SUPER_ADMIN, COMPANY_ADMIN, USER)
+ userLang?: string; // 사용자 언어
+ iat: number; // 발급 시간
+ exp: number; // 만료 시간
+}
+```
+
+### 3.3 미들웨어 체인
+
+```
+1. refreshTokenIfNeeded (자동 토큰 갱신)
+ ↓
+2. authenticateToken (JWT 검증 및 사용자 정보 설정)
+ ↓
+3. 권한 미들웨어 (선택적)
+ ├─ requireSuperAdmin (회사코드 '*' 필수)
+ ├─ requireAdmin (회사관리자 이상)
+ ├─ requireCompanyAccess (회사 데이터 접근 권한)
+ ├─ requireDDLPermission (DDL 실행 권한)
+ └─ requireUserManagement (사용자 관리 권한)
+ ↓
+4. Controller 실행
+ ↓
+5. errorHandler (에러 발생 시)
+```
+
+### 3.4 권한 레벨 (3단계)
+
+| 레벨 | companyCode | userType | 권한 범위 |
+|------|-------------|----------|----------|
+| **Super Admin** | `*` | `SUPER_ADMIN` | 전체 시스템 접근, DDL 실행, 회사 생성/삭제 |
+| **Company Admin** | 회사코드 | `COMPANY_ADMIN` | 자사 데이터 관리, 사용자 관리, 설정 변경 |
+| **일반 사용자** | 회사코드 | `USER` | 자사 데이터 조회/수정 (권한 범위 내) |
+
+### 3.5 토큰 갱신 전략
+
+- **자동 갱신**: 토큰이 1시간 이내 만료 시 응답 헤더(`X-New-Token`)에 새 토큰 포함
+- **명시적 갱신**: `/api/auth/refresh` 엔드포인트 호출
+- **만료 처리**: 만료된 토큰은 401 Unauthorized 응답 (`TOKEN_EXPIRED`)
+
+---
+
+## 4. 비즈니스 도메인별 모듈 분류
+
+### 4.1 관리자 영역 (Admin)
+
+**파일**:
+- `adminController.ts`, `adminService.ts`, `adminRoutes.ts`
+
+**주요 기능**:
+- 메뉴 관리 (CRUD, 복사, 상태 토글, 일괄 삭제)
+- 사용자 관리 (등록, 수정, 상태 변경, 비밀번호 초기화)
+- 회사 관리 (등록, 수정, 삭제, 조회)
+- 부서 관리 (조회, 사용자-부서 통합 저장)
+- 로케일 설정 (다국어 지원)
+- 테이블 스키마 조회 (엑셀 매핑용)
+
+**특징**:
+- 멀티테넌시 기반 회사별 데이터 격리
+- Super Admin만 회사 생성/삭제 가능
+- 사용자 변경 이력 추적
+
+### 4.2 테이블 및 데이터 관리 (Table Management & Data)
+
+**파일**:
+- `tableManagementController.ts`, `tableManagementService.ts`
+- `dataController.ts`, `dataService.ts`
+- `entityJoinController.ts`, `entityJoinService.ts`
+
+**주요 기능**:
+- 테이블 목록 조회 (PostgreSQL information_schema 활용)
+- 컬럼 정보 조회 (타입, 라벨, 제약조건, 참조 관계)
+- 동적 테이블 데이터 CRUD (Raw Query 기반)
+- 테이블 조인 설정 및 실행 (1:N, N:M 관계)
+- 컬럼 라벨 및 설정 관리 (table_type_columns)
+
+**특징**:
+- 캐시 기반 성능 최적화 (테이블/컬럼 정보)
+- 멀티테넌시 자동 필터링 (`company_code` 조건)
+- 코드 타입 컬럼 자동 처리 (공통 코드 연동)
+
+### 4.3 화면 관리 (Screen Management)
+
+**파일**:
+- `screenManagementController.ts`, `screenManagementService.ts`
+- `screenGroupController.ts`, `screenEmbeddingController.ts`
+
+**주요 기능**:
+- 화면 메타데이터 관리 (테이블 연결, 컬럼 설정, 레이아웃)
+- 화면 그룹 관리 (폴더 구조)
+- 화면 임베딩 (부모-자식 화면 데이터 전달)
+- 동적 폼 생성 (JSON 기반 폼 설정 → React 컴포넌트)
+
+**특징**:
+- Low-Code 화면 구성
+- 웹 컴포넌트 타입 표준 기반 렌더링
+- 버튼 액션 표준 지원 (저장, 삭제, 조회, 커스텀)
+
+### 4.4 플로우 관리 (Flow Management)
+
+**파일**:
+- `flowController.ts`, `flowService.ts`
+- `flowExecutionService.ts`, `flowStepService.ts`
+- `flowConnectionService.ts`, `flowDataMoveService.ts`
+
+**주요 기능**:
+- 플로우 정의 관리 (작업 흐름 설계)
+- 플로우 단계 관리 (스텝 생성, 수정, 삭제)
+- 플로우 연결 관리 (스텝 간 조건부 연결)
+- 데이터 이동 실행 (스텝 간 데이터 이동)
+- 오딧 로그 조회 (변경 이력 추적)
+
+**특징**:
+- 비주얼 워크플로우 엔진
+- 조건부 분기 지원
+- 배치 데이터 이동 지원
+
+### 4.5 데이터플로우 (Dataflow)
+
+**파일**:
+- `dataflowController.ts`, `dataflowService.ts`
+- `dataflowDiagramController.ts`, `dataflowDiagramService.ts`
+- `dataflowExecutionController.ts`
+
+**주요 기능**:
+- 테이블 관계 정의 (1:1, 1:N, N:M)
+- 데이터플로우 다이어그램 생성 (ERD 같은 시각화)
+- 데이터플로우 실행 (자동 데이터 동기화)
+- 관계 기반 데이터 조회 (조인 쿼리 자동 생성)
+
+**특징**:
+- 그래프 기반 데이터 관계 모델링
+- 다이어그램별 관계 그룹화
+
+### 4.6 배치 관리 (Batch Management)
+
+**파일**:
+- `batchController.ts`, `batchService.ts`
+- `batchSchedulerService.ts`, `batchExecutionLogService.ts`
+- `batchExternalDbService.ts`
+
+**주요 기능**:
+- 배치 설정 관리 (CRUD)
+- Cron 기반 스케줄링 (node-cron)
+- 배치 수동/자동 실행
+- 실행 이력 조회 (성공/실패 로그)
+- 외부 DB 연동 배치 지원
+
+**특징**:
+- 실시간 스케줄러 업데이트
+- 다중 DB 간 데이터 동기화
+- 실행 시간 제한 및 오류 알림
+
+### 4.7 외부 연동 (External Integration)
+
+**파일**:
+- `externalDbConnectionController.ts`, `externalDbConnectionService.ts`
+- `externalRestApiConnectionController.ts`, `externalRestApiConnectionService.ts`
+- `externalCallController.ts`, `externalCallService.ts`
+- `multiConnectionQueryService.ts`
+
+**주요 기능**:
+- 외부 DB 연결 관리 (PostgreSQL, MySQL, MSSQL, Oracle, MariaDB)
+- 외부 REST API 연결 관리
+- 멀티 DB 통합 쿼리 실행
+- 연결 테스트 및 상태 확인
+- 크레덴셜 암호화 저장
+
+**특징**:
+- 5종 DB 지원 (PostgreSQL, MySQL, MSSQL, Oracle, MariaDB)
+- Connection Pool 기반 연결 관리
+- 비밀번호 암호화 (AES-256-CBC)
+
+### 4.8 메일 관리 (Mail Management)
+
+**파일**:
+- `mailAccountFileController.ts`, `mailAccountFileService.ts`
+- `mailTemplateFileController.ts`, `mailTemplateFileService.ts`
+- `mailSendSimpleController.ts`, `mailSendSimpleService.ts`
+- `mailSentHistoryController.ts`, `mailSentHistoryService.ts`
+- `mailReceiveBasicController.ts`, `mailReceiveBasicService.ts`
+
+**주요 기능**:
+- 메일 계정 관리 (SMTP/IMAP 설정)
+- 메일 템플릿 관리 (JSON 기반 컴포넌트 조합)
+- 메일 발송 (단일/대량, 첨부파일 지원)
+- 발송 이력 조회 (30일 자동 삭제)
+- 메일 수신함 조회 (IMAP)
+
+**특징**:
+- Nodemailer 기반 발송
+- 템플릿 변수 치환
+- 대량 발송 지원 (100건/배치)
+- 메일 예약 발송
+
+### 4.9 대시보드 (Dashboard)
+
+**파일**:
+- `DashboardController.ts`, `DashboardService.ts`
+
+**주요 기능**:
+- 대시보드 위젯 관리 (차트, 테이블, 카드)
+- 실시간 데이터 조회 (집계 쿼리)
+- 사용자별 대시보드 설정
+
+**특징**:
+- JSON 기반 위젯 설정
+- 캐시 기반 성능 최적화
+
+### 4.10 기타 도메인
+
+**파일 및 주요 기능**:
+- **리포트**: 리포트 생성 및 조회 (`reportController.ts`, `reportService.ts`)
+- **파일**: 파일 업로드/다운로드 (`fileController.ts`, Multer)
+- **배송/화물**: 배송 관리, 화물 추적 (`deliveryController.ts`)
+- **리스크/알림**: 리스크 알림, 캐시 기반 자동 갱신 (`riskAlertController.ts`)
+- **To-Do**: 할 일 관리 (`todoController.ts`)
+- **예약**: 예약 요청 관리 (`bookingController.ts`)
+- **디지털 트윈**: 야드 관제, 3D 레이아웃 (`digitalTwinController.ts`)
+- **스케줄**: 스케줄 자동 생성 (`scheduleController.ts`)
+- **작업 이력**: 작업 로그 조회 (`workHistoryController.ts`)
+- **권한 그룹**: 권한 그룹 관리 (`roleController.ts`)
+- **채번 규칙**: 자동 채번 규칙 (`numberingRuleController.ts`)
+- **엔티티 검색**: 동적 엔티티 검색 (`entitySearchController.ts`)
+- **연쇄 드롭다운**: 조건부 드롭다운 (`cascadingController.ts` 시리즈)
+
+---
+
+## 5. 미들웨어 스택 구성
+
+### 5.1 미들웨어 실행 순서 (app.ts 기준)
+
+```
+1. 프로세스 레벨 예외 처리 (unhandledRejection, uncaughtException)
+2. 보안 헤더 (helmet)
+3. 압축 (compression)
+4. 바디 파싱 (express.json, express.urlencoded)
+5. 정적 파일 서빙 (/uploads)
+6. CORS (cors)
+7. Rate Limiting (express-rate-limit)
+8. 토큰 자동 갱신 (refreshTokenIfNeeded)
+9. [라우트별 미들웨어]
+ ├─ authenticateToken (모든 /api/* 라우트)
+ ├─ 권한 미들웨어 (선택적)
+ └─ 컨트롤러 실행
+10. 404 핸들러
+11. 에러 핸들러 (errorHandler)
+```
+
+### 5.2 미들웨어 파일
+
+#### `authMiddleware.ts`
+- **authenticateToken**: JWT 토큰 검증 및 사용자 정보 설정
+- **optionalAuth**: 선택적 인증 (토큰 없어도 통과)
+- **requireAdmin**: 관리자 권한 필수 (userId === 'plm_admin')
+- **refreshTokenIfNeeded**: 토큰 자동 갱신 (1시간 이내 만료 시)
+- **checkAuthStatus**: 인증 상태 확인 (유효성 검사만)
+
+#### `permissionMiddleware.ts`
+- **requireSuperAdmin**: 슈퍼관리자 권한 필수 (companyCode === '*')
+- **requireAdmin**: 관리자 이상 권한 필수 (Super Admin + Company Admin)
+- **requireCompanyAccess**: 회사 데이터 접근 권한 체크
+- **requireUserManagement**: 사용자 관리 권한 체크
+- **requireCompanySettingsManagement**: 회사 설정 변경 권한 체크
+- **requireCompanyManagement**: 회사 생성/삭제 권한 체크
+- **requireDDLPermission**: DDL 실행 권한 체크
+
+#### `superAdminMiddleware.ts`
+- **requireSuperAdmin**: 슈퍼관리자 권한 확인 (DDL 전용)
+- **validateDDLPermission**: DDL 실행 전 추가 보안 검증 (5초 간격 제한)
+- **isSuperAdmin**: 슈퍼관리자 여부 확인 유틸 함수
+- **checkDDLPermission**: DDL 권한 체크 (미들웨어 없이 사용)
+
+#### `errorHandler.ts`
+- **AppError**: 커스텀 에러 클래스 (statusCode, isOperational)
+- **errorHandler**: 전역 에러 핸들러 (PostgreSQL, JWT 에러 처리)
+- **notFoundHandler**: 404 에러 핸들러
+
+### 5.3 보안 설정
+
+```typescript
+// helmet: 보안 헤더 설정
+helmet({
+ contentSecurityPolicy: {
+ directives: {
+ 'frame-ancestors': ['self', 'http://localhost:9771', 'http://localhost:3000']
+ }
+ }
+})
+
+// Rate Limiting: 1분당 10,000 요청 (개발), 100 요청 (운영)
+rateLimit({
+ windowMs: 1 * 60 * 1000,
+ max: process.env.NODE_ENV === 'development' ? 10000 : 100,
+ skip: (req) => {
+ // 헬스 체크, 자주 호출되는 API는 제외
+ return req.path === '/health'
+ || req.path.includes('/table-management/')
+ || req.path.includes('/external-db-connections/')
+ }
+})
+
+// CORS: 환경별 origin 설정
+cors({
+ origin: process.env.NODE_ENV === 'development'
+ ? true
+ : ['http://localhost:9771', 'http://39.117.244.52:5555'],
+ credentials: true
+})
+```
+
+---
+
+## 6. 서비스 레이어 패턴
+
+### 6.1 서비스 레이어 구조
+
+```
+Controller (요청 처리)
+ ↓
+Service (비즈니스 로직)
+ ↓
+Database (Raw Query 실행)
+ ↓
+PostgreSQL (데이터 저장소)
+```
+
+### 6.2 데이터베이스 접근 방식
+
+#### `db.ts` - Raw Query 매니저
+
+```typescript
+// 기본 쿼리 실행
+async function query(text: string, params?: any[]): Promise
+
+// 단일 행 조회
+async function queryOne(text: string, params?: any[]): Promise
+
+// 트랜잭션
+async function transaction(callback: (client: PoolClient) => Promise): Promise
+
+// 연결 풀 상태
+function getPoolStatus(): { totalCount, idleCount, waitingCount }
+```
+
+#### Connection Pool 설정
+
+```typescript
+new Pool({
+ host: dbConfig.host,
+ port: dbConfig.port,
+ database: dbConfig.database,
+ user: dbConfig.user,
+ password: dbConfig.password,
+
+ // 연결 풀 설정
+ min: process.env.NODE_ENV === 'production' ? 5 : 2,
+ max: process.env.NODE_ENV === 'production' ? 20 : 10,
+
+ // 타임아웃 설정
+ connectionTimeoutMillis: 30000, // 30초
+ idleTimeoutMillis: 600000, // 10분
+ statement_timeout: 60000, // 60초
+ query_timeout: 60000,
+
+ application_name: 'WACE-PLM-Backend'
+})
+```
+
+### 6.3 서비스 패턴 예시
+
+#### 멀티테넌시 쿼리 패턴
+
+```typescript
+// Super Admin: 모든 데이터 조회
+if (companyCode === '*') {
+ query = 'SELECT * FROM table_name ORDER BY company_code';
+ params = [];
+}
+// 일반 사용자: 자사 데이터만 조회 (Super Admin 데이터 제외)
+else {
+ query = `
+ SELECT * FROM table_name
+ WHERE company_code = $1 AND company_code != '*'
+ ORDER BY created_at DESC
+ `;
+ params = [companyCode];
+}
+```
+
+#### 트랜잭션 패턴
+
+```typescript
+await transaction(async (client) => {
+ // 1. 부모 레코드 삽입
+ const parent = await client.query(
+ 'INSERT INTO parent_table (...) VALUES (...) RETURNING *',
+ [...]
+ );
+
+ // 2. 자식 레코드 삽입
+ await client.query(
+ 'INSERT INTO child_table (parent_id, ...) VALUES ($1, ...) RETURNING *',
+ [parent.rows[0].id, ...]
+ );
+
+ return { success: true };
+});
+```
+
+#### 캐시 패턴
+
+```typescript
+// 캐시 조회
+const cachedData = cache.get(CacheKeys.TABLE_LIST);
+if (cachedData) {
+ return cachedData;
+}
+
+// DB 조회
+const data = await query('SELECT ...');
+
+// 캐시 저장 (10분 TTL)
+cache.set(CacheKeys.TABLE_LIST, data, 10 * 60 * 1000);
+
+return data;
+```
+
+### 6.4 외부 DB 커넥터 패턴
+
+```typescript
+// DatabaseConnectorFactory.ts
+export class DatabaseConnectorFactory {
+ static createConnector(dbType: string, config: ConnectionConfig): DatabaseConnector {
+ switch (dbType) {
+ case 'postgresql': return new PostgreSQLConnector(config);
+ case 'mysql': return new MySQLConnector(config);
+ case 'mssql': return new MSSQLConnector(config);
+ case 'oracle': return new OracleConnector(config);
+ case 'mariadb': return new MariaDBConnector(config);
+ default: throw new Error(`Unsupported DB type: ${dbType}`);
+ }
+ }
+}
+
+// 사용 예시
+const connector = DatabaseConnectorFactory.createConnector('mysql', config);
+await connector.connect();
+const result = await connector.executeQuery('SELECT * FROM users');
+await connector.disconnect();
+```
+
+---
+
+## 7. 멀티테넌시 구현 방식
+
+### 7.1 핵심 원칙
+
+**CRITICAL PROJECT RULES**:
+1. **모든 쿼리는 company_code 필터 필수**
+2. **req.user!.companyCode 사용 (클라이언트 전송 값 신뢰 금지)**
+3. **Super Admin (company_code = '*')만 전체 데이터 조회**
+4. **일반 사용자는 company_code = '*' 데이터 조회 불가**
+
+### 7.2 쿼리 패턴
+
+```typescript
+const companyCode = req.user!.companyCode;
+
+// Super Admin: 모든 회사 데이터 조회
+if (companyCode === '*') {
+ query = 'SELECT * FROM users ORDER BY company_code';
+ params = [];
+}
+// 일반 사용자: 자사 데이터만 조회 (Super Admin 제외)
+else {
+ query = `
+ SELECT * FROM users
+ WHERE company_code = $1 AND company_code != '*'
+ `;
+ params = [companyCode];
+}
+```
+
+### 7.3 테이블 설계
+
+```sql
+-- 모든 비즈니스 테이블에 company_code 컬럼 필수
+CREATE TABLE table_name (
+ id SERIAL PRIMARY KEY,
+ company_code VARCHAR(50) NOT NULL, -- 회사 코드
+ ...
+ INDEX idx_company_code (company_code)
+);
+
+-- Super Admin 데이터: company_code = '*'
+-- 회사별 데이터: company_code = '회사코드'
+```
+
+### 7.4 회사 전환 (Super Admin 전용)
+
+```typescript
+// POST /api/auth/switch-company
+{
+ targetCompanyCode: "ILSHIN" // 전환할 회사 코드
+}
+
+// 응답
+{
+ success: true,
+ token: "새로운 JWT 토큰", // companyCode가 변경된 토큰
+ userInfo: { companyCode: "ILSHIN", ... }
+}
+```
+
+### 7.5 권한 체크
+
+```typescript
+// 회사 데이터 접근 권한 확인
+export function canAccessCompanyData(user: PersonBean, targetCompanyCode: string): boolean {
+ // Super Admin: 모든 회사 접근 가능
+ if (user.companyCode === '*') {
+ return true;
+ }
+
+ // 일반 사용자: 자사만 접근 가능
+ return user.companyCode === targetCompanyCode;
+}
+```
+
+---
+
+## 8. 에러 핸들링 전략
+
+### 8.1 에러 핸들링 구조
+
+```
+Controller (try-catch)
+ ↓ 에러 발생
+Service (throw error)
+ ↓
+errorHandler (미들웨어)
+ ├─ PostgreSQL 에러 처리
+ ├─ JWT 에러 처리
+ ├─ 커스텀 에러 처리 (AppError)
+ └─ 응답 전송 (JSON)
+```
+
+### 8.2 커스텀 에러 클래스
+
+```typescript
+export class AppError extends Error {
+ public statusCode: number;
+ public isOperational: boolean;
+
+ constructor(message: string, statusCode: number = 500) {
+ super(message);
+ this.statusCode = statusCode;
+ this.isOperational = true;
+ }
+}
+
+// 사용 예시
+throw new AppError('중복된 데이터가 존재합니다.', 400);
+```
+
+### 8.3 PostgreSQL 에러 처리
+
+```typescript
+// errorHandler.ts
+if (pgError.code === '23505') { // unique_violation
+ error = new AppError('중복된 데이터가 존재합니다.', 400);
+} else if (pgError.code === '23503') { // foreign_key_violation
+ error = new AppError('참조 무결성 제약 조건 위반입니다.', 400);
+} else if (pgError.code === '23502') { // not_null_violation
+ error = new AppError('필수 입력값이 누락되었습니다.', 400);
+}
+```
+
+### 8.4 에러 응답 형식
+
+```json
+{
+ "success": false,
+ "error": {
+ "code": "UNIQUE_VIOLATION",
+ "message": "중복된 데이터가 존재합니다.",
+ "details": "사용자 ID가 이미 존재합니다.",
+ "stack": "..." // 개발 환경에서만 포함
+ }
+}
+```
+
+### 8.5 에러 로깅
+
+```typescript
+// logger.ts (Winston 기반)
+logger.error({
+ message: error.message,
+ stack: error.stack,
+ url: req.url,
+ method: req.method,
+ ip: req.ip,
+ userAgent: req.get('User-Agent')
+});
+```
+
+### 8.6 프로세스 레벨 예외 처리
+
+```typescript
+// app.ts
+process.on('unhandledRejection', (reason, promise) => {
+ logger.error('⚠️ Unhandled Promise Rejection:', reason);
+ // 프로세스 종료하지 않고 로깅만 수행
+});
+
+process.on('uncaughtException', (error) => {
+ logger.error('🔥 Uncaught Exception:', error);
+ // 심각한 에러 시 graceful shutdown 고려
+});
+```
+
+---
+
+## 9. 파일 업로드/다운로드 처리
+
+### 9.1 파일 업로드
+
+**파일**: `fileController.ts`, `fileRoutes.ts`
+
+```typescript
+// Multer 설정
+const storage = multer.diskStorage({
+ destination: (req, file, cb) => {
+ cb(null, 'uploads/');
+ },
+ filename: (req, file, cb) => {
+ const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
+ cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
+ }
+});
+
+const upload = multer({
+ storage,
+ limits: { fileSize: 10 * 1024 * 1024 }, // 10MB
+ fileFilter: (req, file, cb) => {
+ // 허용된 확장자 체크
+ const allowedTypes = /jpeg|jpg|png|gif|pdf|doc|docx|xls|xlsx/;
+ const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
+ const mimetype = allowedTypes.test(file.mimetype);
+
+ if (mimetype && extname) {
+ return cb(null, true);
+ } else {
+ cb(new Error('허용되지 않는 파일 형식입니다.'));
+ }
+ }
+});
+
+// 라우트
+router.post('/upload', authenticateToken, upload.single('file'), uploadFile);
+router.post('/upload-multiple', authenticateToken, upload.array('files', 10), uploadMultipleFiles);
+```
+
+### 9.2 파일 다운로드
+
+```typescript
+// 정적 파일 서빙 (app.ts)
+app.use('/uploads',
+ (req, res, next) => {
+ res.setHeader('Access-Control-Allow-Origin', '*');
+ res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
+ res.setHeader('Cache-Control', 'public, max-age=3600');
+ next();
+ },
+ express.static(path.join(process.cwd(), 'uploads'))
+);
+
+// 다운로드 엔드포인트
+router.get('/download/:filename', authenticateToken, async (req, res) => {
+ const filename = req.params.filename;
+ const filePath = path.join(process.cwd(), 'uploads', filename);
+
+ if (!fs.existsSync(filePath)) {
+ return res.status(404).json({ error: '파일을 찾을 수 없습니다.' });
+ }
+
+ res.download(filePath);
+});
+```
+
+### 9.3 화면별 파일 관리
+
+**파일**: `screenFileController.ts`, `screenFileService.ts`
+
+```typescript
+// 화면별 파일 업로드
+router.post('/screens/:screenId/files', authenticateToken, upload.single('file'), uploadScreenFile);
+
+// 화면별 파일 목록 조회
+router.get('/screens/:screenId/files', authenticateToken, getScreenFiles);
+
+// 파일 삭제
+router.delete('/screens/:screenId/files/:fileId', authenticateToken, deleteScreenFile);
+```
+
+---
+
+## 10. 외부 연동
+
+### 10.1 외부 DB 연결
+
+**지원 DB**: PostgreSQL, MySQL, MSSQL, Oracle, MariaDB
+
+**파일**:
+- `externalDbConnectionService.ts`
+- `PostgreSQLConnector.ts`, `MySQLConnector.ts`, `MSSQLConnector.ts`, `OracleConnector.ts`, `MariaDBConnector.ts`
+
+```typescript
+// 외부 DB 연결 설정
+{
+ connection_name: "외부 ERP DB",
+ db_type: "mysql",
+ host: "192.168.0.100",
+ port: 3306,
+ database: "erp_db",
+ username: "erp_user",
+ password: "encrypted_password", // AES-256-CBC 암호화
+ company_code: "ILSHIN",
+ is_active: "Y"
+}
+
+// 연결 테스트
+POST /api/external-db-connections/:id/test
+{
+ success: true,
+ message: "연결 테스트 성공"
+}
+
+// 쿼리 실행
+POST /api/external-db-connections/:id/query
+{
+ query: "SELECT * FROM products WHERE category = ?",
+ params: ["전자제품"]
+}
+```
+
+### 10.2 외부 REST API 연결
+
+**파일**: `externalRestApiConnectionService.ts`
+
+```typescript
+// 외부 REST API 연결 설정
+{
+ connection_name: "날씨 API",
+ base_url: "https://api.weather.com",
+ auth_type: "bearer", // bearer, api-key, basic, oauth2
+ auth_credentials: {
+ token: "encrypted_token"
+ },
+ headers: {
+ "Content-Type": "application/json"
+ },
+ company_code: "ILSHIN"
+}
+
+// API 호출
+POST /api/external-rest-api-connections/:id/call
+{
+ method: "GET",
+ endpoint: "/weather",
+ params: { city: "Seoul" }
+}
+```
+
+### 10.3 멀티 DB 통합 쿼리
+
+**파일**: `multiConnectionQueryService.ts`
+
+```typescript
+// 여러 DB에서 동시 쿼리 실행
+POST /api/multi-connection/query
+{
+ connections: [
+ {
+ connectionId: 1,
+ query: "SELECT * FROM orders WHERE status = 'pending'"
+ },
+ {
+ connectionId: 2,
+ query: "SELECT * FROM inventory WHERE quantity < 10"
+ }
+ ]
+}
+
+// 응답
+{
+ success: true,
+ results: [
+ { connectionId: 1, data: [...], rowCount: 15 },
+ { connectionId: 2, data: [...], rowCount: 8 }
+ ]
+}
+```
+
+### 10.4 Open API Proxy
+
+**파일**: `openApiProxyController.ts`
+
+```typescript
+// 날씨 API
+GET /api/open-api/weather?city=Seoul
+
+// 환율 API
+GET /api/open-api/exchange-rate?from=USD&to=KRW
+```
+
+---
+
+## 11. 배치/스케줄 처리
+
+### 11.1 배치 스케줄러
+
+**파일**: `batchSchedulerService.ts`
+
+```typescript
+// 배치 설정
+{
+ batch_name: "일일 재고 동기화",
+ batch_type: "external_db", // external_db, rest_api, internal
+ cron_schedule: "0 2 * * *", // 매일 새벽 2시
+ source_connection_id: 1,
+ source_query: "SELECT * FROM inventory",
+ target_table: "inventory_sync",
+ mapping: {
+ product_id: "item_id",
+ quantity: "stock_qty"
+ },
+ is_active: "Y"
+}
+
+// 스케줄러 초기화 (서버 시작 시)
+await BatchSchedulerService.initializeScheduler();
+
+// 배치 수동 실행
+POST /api/batch-configs/:id/execute
+```
+
+### 11.2 Cron 기반 자동 실행
+
+```typescript
+// node-cron 사용
+const task = cron.schedule(
+ config.cron_schedule, // "0 2 * * *"
+ async () => {
+ logger.info(`배치 실행 시작: ${config.batch_name}`);
+ await executeBatchConfig(config);
+ },
+ { timezone: 'Asia/Seoul' }
+);
+
+// 스케줄 업데이트
+await BatchSchedulerService.updateBatchSchedule(configId);
+
+// 스케줄 제거
+await BatchSchedulerService.removeBatchSchedule(configId);
+```
+
+### 11.3 배치 실행 로그
+
+**파일**: `batchExecutionLogService.ts`
+
+```typescript
+// 배치 실행 이력
+{
+ batch_config_id: 1,
+ execution_status: "success", // success, failed, running
+ start_time: "2024-12-24T02:00:00Z",
+ end_time: "2024-12-24T02:05:23Z",
+ rows_processed: 1523,
+ rows_inserted: 1200,
+ rows_updated: 300,
+ rows_failed: 23,
+ error_message: null,
+ execution_log: "..."
+}
+
+// 배치 이력 조회
+GET /api/batch-execution-logs?batch_config_id=1&page=1&limit=10
+```
+
+### 11.4 자동 스케줄 작업
+
+**파일**: `app.ts`
+
+```typescript
+// 메일 자동 삭제 (매일 새벽 2시)
+cron.schedule('0 2 * * *', async () => {
+ logger.info('🗑️ 30일 지난 삭제된 메일 자동 삭제 시작...');
+ const deletedCount = await mailSentHistoryService.cleanupOldDeletedMails();
+ logger.info(`✅ 30일 지난 메일 ${deletedCount}개 자동 삭제 완료`);
+});
+
+// 리스크/알림 자동 갱신 (10분 간격)
+const cacheService = RiskAlertCacheService.getInstance();
+cacheService.startAutoRefresh();
+```
+
+---
+
+## 12. 컨트롤러/서비스 상세 역할
+
+### 12.1 인증 및 관리자
+
+#### `authController.ts` / `authService.ts`
+- **로그인**: 비밀번호 검증, JWT 토큰 발급, 로그인 로그 기록
+- **회원가입**: 공차중계 사용자 등록
+- **토큰 갱신**: accessToken, refreshToken 갱신
+- **현재 사용자 정보**: JWT 기반 사용자 정보 조회
+- **로그아웃**: 토큰 무효화 (클라이언트 측 처리)
+- **회사 전환**: Super Admin 전용 회사 전환
+
+#### `adminController.ts` / `adminService.ts`
+- **메뉴 관리**: 메뉴 트리 조회, 메뉴 CRUD, 메뉴 복사, 상태 토글, 일괄 삭제
+- **사용자 관리**: 사용자 목록, 사용자 CRUD, 상태 변경, 비밀번호 초기화, 변경 이력
+- **회사 관리**: 회사 목록, 회사 CRUD (Super Admin 전용)
+- **부서 관리**: 부서 목록, 사용자-부서 통합 저장
+- **로케일 설정**: 사용자 언어 설정 (ko, en)
+- **테이블 스키마**: 엑셀 업로드 컬럼 매핑용 스키마 조회
+
+### 12.2 테이블 및 데이터
+
+#### `tableManagementController.ts` / `tableManagementService.ts`
+- **테이블 목록**: PostgreSQL information_schema 조회
+- **컬럼 정보**: 컬럼 타입, 제약조건, 라벨, 참조 관계
+- **컬럼 라벨 관리**: 다국어 라벨, 표시 순서, 표시 여부
+- **컬럼 설정**: 입력 타입, 코드 카테고리, 필수 여부, 기본값
+- **테이블 조회 설정**: 조회 컬럼, 정렬 순서, 필터 조건
+- **캐시 관리**: 테이블/컬럼 정보 캐시 (10분 TTL)
+
+#### `dataController.ts` / `dataService.ts`
+- **동적 데이터 조회**: 테이블명 기반 데이터 조회 (페이지네이션, 필터, 정렬)
+- **동적 데이터 생성**: INSERT 쿼리 자동 생성
+- **동적 데이터 수정**: UPDATE 쿼리 자동 생성
+- **동적 데이터 삭제**: DELETE 쿼리 자동 생성 (논리 삭제 지원)
+- **멀티테넌시 자동 필터링**: company_code 자동 추가
+
+#### `entityJoinController.ts` / `entityJoinService.ts`
+- **조인 설정 관리**: 테이블 간 조인 관계 설정 (1:N, N:M)
+- **조인 쿼리 실행**: 설정된 조인 관계 기반 데이터 조회
+- **참조 데이터 캐싱**: 자주 사용되는 참조 데이터 캐시
+
+### 12.3 화면 관리
+
+#### `screenManagementController.ts` / `screenManagementService.ts`
+- **화면 메타데이터 관리**: 화면 설정 (테이블, 컬럼, 레이아웃)
+- **화면 목록 조회**: 회사별, 화면 그룹별 필터링
+- **화면 복사**: 화면 설정 복제
+- **화면 삭제**: 논리 삭제
+- **화면 설정 조회**: 화면 메타데이터 상세 조회
+
+#### `dynamicFormController.ts` / `dynamicFormService.ts`
+- **동적 폼 생성**: JSON 기반 폼 설정 → React 컴포넌트
+- **폼 유효성 검사**: 필수 입력, 데이터 타입, 길이 제한
+- **폼 제출**: 데이터 저장 (INSERT/UPDATE)
+
+#### `buttonActionStandardController.ts` / `buttonActionStandardService.ts`
+- **버튼 액션 표준**: 저장, 삭제, 조회, 엑셀 다운로드, 커스텀 액션
+- **버튼 액션 설정**: 액션 타입, 파라미터, 권한 설정
+
+### 12.4 플로우 및 데이터플로우
+
+#### `flowController.ts` / `flowService.ts`
+- **플로우 정의**: 플로우 생성, 수정, 삭제, 조회
+- **플로우 단계**: 단계 생성, 수정, 삭제, 순서 변경
+- **플로우 연결**: 단계 간 연결 (조건부 분기)
+- **데이터 이동**: 단계 간 데이터 이동 (단일/배치)
+- **오딧 로그**: 플로우 실행 이력 조회
+
+#### `dataflowController.ts` / `dataflowService.ts`
+- **테이블 관계 정의**: 테이블 간 관계 설정 (1:1, 1:N, N:M)
+- **데이터플로우 다이어그램**: ERD 같은 시각화
+- **데이터플로우 실행**: 관계 기반 데이터 동기화
+- **관계 조회**: 다이어그램별 관계 목록
+
+### 12.5 배치 관리
+
+#### `batchController.ts` / `batchService.ts`
+- **배치 설정 관리**: 배치 CRUD
+- **배치 실행**: 수동 실행, 스케줄 실행
+- **배치 이력**: 실행 로그, 성공/실패 통계
+- **커넥션 조회**: 사용 가능한 외부 DB/API 목록
+
+#### `batchSchedulerService.ts`
+- **스케줄러 초기화**: 서버 시작 시 활성 배치 등록
+- **스케줄 등록**: Cron 표현식 기반 스케줄 등록
+- **스케줄 업데이트**: 배치 설정 변경 시 스케줄 재등록
+- **스케줄 제거**: 배치 삭제 시 스케줄 제거
+
+#### `batchExternalDbService.ts`
+- **외부 DB 배치**: 외부 DB → 내부 DB 데이터 동기화
+- **컬럼 매핑**: 소스-타겟 컬럼 매핑
+- **데이터 변환**: 타입 변환, 값 변환
+
+### 12.6 외부 연동
+
+#### `externalDbConnectionController.ts` / `externalDbConnectionService.ts`
+- **외부 DB 연결 관리**: 연결 CRUD
+- **연결 테스트**: 연결 유효성 검증
+- **쿼리 실행**: 외부 DB 쿼리 실행
+- **테이블 목록**: 외부 DB 테이블 목록 조회
+- **컬럼 정보**: 외부 DB 컬럼 정보 조회
+
+#### `externalRestApiConnectionController.ts` / `externalRestApiConnectionService.ts`
+- **REST API 연결 관리**: API 연결 CRUD
+- **API 호출**: 외부 API 호출 (GET, POST, PUT, DELETE)
+- **인증 처리**: Bearer, API Key, Basic, OAuth2
+- **응답 캐싱**: API 응답 캐싱 (TTL 설정)
+
+#### `multiConnectionQueryService.ts`
+- **멀티 DB 통합 쿼리**: 여러 DB에서 동시 쿼리 실행
+- **결과 병합**: 여러 DB 쿼리 결과 병합
+- **오류 처리**: 부분 실패 시 에러 로그 기록
+
+### 12.7 메일 관리
+
+#### `mailAccountFileController.ts` / `mailAccountFileService.ts`
+- **메일 계정 관리**: SMTP/IMAP 계정 CRUD
+- **계정 테스트**: 연결 유효성 검증
+- **계정 상태**: 활성/비활성 상태 관리
+
+#### `mailTemplateFileController.ts` / `mailTemplateFileService.ts`
+- **메일 템플릿 관리**: 템플릿 CRUD
+- **템플릿 컴포넌트**: JSON 기반 컴포넌트 조합 (헤더, 본문, 버튼, 푸터)
+- **변수 치환**: {변수명} 형태의 변수 치환
+
+#### `mailSendSimpleController.ts` / `mailSendSimpleService.ts`
+- **메일 발송**: 단일 발송, 대량 발송 (100건/배치)
+- **첨부파일**: 다중 첨부파일 지원
+- **참조/숨은참조**: CC, BCC 지원
+- **발송 이력**: 발송 성공/실패 로그 기록
+
+#### `mailSentHistoryController.ts` / `mailSentHistoryService.ts`
+- **발송 이력 조회**: 페이지네이션, 필터링, 검색
+- **이력 삭제**: 논리 삭제 (30일 후 물리 삭제)
+- **자동 삭제**: Cron 기반 자동 삭제 (매일 새벽 2시)
+
+#### `mailReceiveBasicController.ts` / `mailReceiveBasicService.ts`
+- **메일 수신**: IMAP 기반 메일 수신
+- **메일 목록**: 수신함 목록 조회
+- **메일 읽기**: 메일 상세 조회, 첨부파일 다운로드
+
+### 12.8 대시보드 및 리포트
+
+#### `DashboardController.ts` / `DashboardService.ts`
+- **대시보드 위젯**: 차트, 테이블, 카드 위젯
+- **실시간 데이터**: 집계 쿼리 기반 실시간 데이터 조회
+- **사용자 설정**: 사용자별 대시보드 레이아웃 저장
+
+#### `reportController.ts` / `reportService.ts`
+- **리포트 생성**: 동적 리포트 생성 (PDF, Excel, Word)
+- **리포트 템플릿**: 템플릿 기반 리포트 생성
+- **리포트 스케줄**: 정기 리포트 자동 생성 및 메일 발송
+
+### 12.9 기타 도메인
+
+#### `deliveryController.ts` / `deliveryService.ts`
+- **배송 관리**: 배송 정보 등록, 조회, 수정
+- **화물 추적**: 배송 상태 추적
+
+#### `riskAlertController.ts` / `riskAlertService.ts` / `riskAlertCacheService.ts`
+- **리스크 알림**: 리스크 기준 설정, 알림 발생
+- **자동 갱신**: 10분 간격 자동 갱신 (캐시 기반)
+
+#### `todoController.ts` / `todoService.ts`
+- **To-Do 관리**: 할 일 CRUD, 상태 변경 (대기, 진행, 완료)
+
+#### `bookingController.ts` / `bookingService.ts`
+- **예약 관리**: 예약 요청 CRUD, 승인/거부
+
+#### `digitalTwinController.ts` / `digitalTwinLayoutController.ts` / `digitalTwinDataController.ts`
+- **디지털 트윈**: 야드 관제, 3D 레이아웃, 실시간 데이터 시각화
+
+#### `scheduleController.ts` / `scheduleService.ts`
+- **스케줄 자동 생성**: 작업 스케줄 자동 생성 (규칙 기반)
+
+#### `workHistoryController.ts` / `workHistoryService.ts`
+- **작업 이력**: 작업 로그 조회, 필터링, 검색
+
+#### `roleController.ts` / `roleService.ts`
+- **권한 그룹**: 권한 그룹 CRUD, 사용자-권한 매핑
+
+#### `numberingRuleController.ts` / `numberingRuleService.ts`
+- **채번 규칙**: 자동 채번 규칙 설정 (접두사, 연번, 접미사)
+
+#### `entitySearchController.ts` / `entitySearchService.ts`
+- **엔티티 검색**: 동적 엔티티 검색 (테이블, 컬럼, 조건 기반)
+
+#### `cascadingController.ts` 시리즈
+- **연쇄 드롭다운**: 조건부 드롭다운 관계 설정
+- **자동 입력**: 연쇄 자동 입력 관계 설정
+- **상호 배제**: 상호 배타적 선택 관계 설정
+- **다단계 계층**: 계층 구조 관계 설정
+
+---
+
+## 📊 통계 요약
+
+| 구분 | 개수 |
+|------|------|
+| **컨트롤러** | 69개 |
+| **서비스** | 87개 |
+| **라우터** | 77개 |
+| **미들웨어** | 4개 |
+| **엔드포인트** | 200개 이상 |
+| **데이터베이스 커넥터** | 5종 (PostgreSQL, MySQL, MSSQL, Oracle, MariaDB) |
+| **유틸리티** | 16개 |
+| **타입 정의** | 26개 |
+
+---
+
+## 🔧 기술 스택
+
+```json
+{
+ "런타임": "Node.js 20.10.0+",
+ "언어": "TypeScript 5.3.3",
+ "프레임워크": "Express 4.18.2",
+ "데이터베이스": "PostgreSQL (pg 8.16.3)",
+ "인증": "JWT (jsonwebtoken 9.0.2)",
+ "암호화": "BCrypt (bcryptjs 2.4.3)",
+ "로깅": "Winston 3.11.0",
+ "스케줄링": "node-cron 4.2.1",
+ "메일": "Nodemailer 6.10.1 + IMAP 0.8.19",
+ "파일 업로드": "Multer 1.4.5",
+ "보안": "Helmet 7.1.0",
+ "외부 DB": "mysql2, mssql, oracledb",
+ "캐싱": "In-Memory Cache (Map 기반)",
+ "압축": "compression 1.7.4",
+ "Rate Limiting": "express-rate-limit 7.1.5"
+}
+```
+
+---
+
+## 📁 핵심 파일 경로
+
+```
+backend-node/
+├── src/
+│ ├── app.ts # 앱 진입점
+│ ├── config/environment.ts # 환경변수 설정
+│ ├── database/
+│ │ ├── db.ts # Raw Query 매니저
+│ │ ├── DatabaseConnectorFactory.ts # DB 커넥터 팩토리
+│ │ └── [DB]Connector.ts # 각 DB별 커넥터
+│ ├── middleware/
+│ │ ├── authMiddleware.ts # JWT 인증
+│ │ ├── permissionMiddleware.ts # 권한 체크
+│ │ ├── superAdminMiddleware.ts # Super Admin 체크
+│ │ └── errorHandler.ts # 에러 핸들링
+│ ├── utils/
+│ │ ├── logger.ts # Winston 로거
+│ │ ├── jwtUtils.ts # JWT 유틸
+│ │ ├── encryptUtil.ts # BCrypt 암호화
+│ │ ├── passwordEncryption.ts # AES 암호화
+│ │ ├── cache.ts # 캐시 유틸
+│ │ └── permissionUtils.ts # 권한 유틸
+│ └── types/
+│ ├── auth.ts # 인증 타입
+│ ├── tableManagement.ts # 테이블 관리 타입
+│ └── ...
+└── package.json # 의존성 관리
+```
+
+---
+
+## 🚀 서버 시작 프로세스
+
+```
+1. dotenv 환경변수 로드
+2. Express 앱 생성
+3. 미들웨어 설정 (helmet, cors, compression, rate-limit)
+4. 데이터베이스 연결 풀 초기화
+5. 라우터 등록 (77개 라우터)
+6. 에러 핸들러 등록
+7. 서버 리스닝 (기본 포트: 8080)
+8. 데이터베이스 마이그레이션 실행
+9. 배치 스케줄러 초기화
+10. 리스크/알림 자동 갱신 시작
+11. 메일 자동 삭제 스케줄러 시작
+```
+
+---
+
+## 🔒 보안 고려사항
+
+1. **JWT 기반 인증**: 세션 없이 무상태(Stateless) 인증
+2. **비밀번호 암호화**: BCrypt (12 rounds)
+3. **외부 DB 크레덴셜 암호화**: AES-256-CBC
+4. **SQL Injection 방지**: Parameterized Query 필수
+5. **XSS 방지**: Helmet 보안 헤더
+6. **CSRF 방지**: CORS 설정 + JWT
+7. **Rate Limiting**: 1분당 요청 수 제한
+8. **DDL 실행 제한**: Super Admin만 가능 + 5초 간격 제한
+9. **멀티테넌시 격리**: company_code 자동 필터링
+10. **에러 정보 노출 방지**: 운영 환경에서 스택 트레이스 숨김
+
+---
+
+## 📝 추천 개선 사항
+
+1. **API 문서화**: Swagger/OpenAPI 자동 생성
+2. **단위 테스트**: Jest 기반 테스트 커버리지 확대
+3. **Redis 캐싱**: In-Memory 캐시 → Redis 전환
+4. **로그 중앙화**: Winston → ELK Stack 연동
+5. **성능 모니터링**: APM 도구 연동 (New Relic, Datadog)
+6. **Docker 컨테이너화**: Dockerfile 및 docker-compose 개선
+7. **CI/CD 파이프라인**: GitHub Actions, Jenkins 연동
+8. **API Rate Limiting 세분화**: 엔드포인트별 제한 설정
+9. **WebSocket 지원**: 실시간 알림 및 데이터 업데이트
+10. **GraphQL API**: REST API + GraphQL 병행 지원
+
+---
+
+**문서 작성자**: WACE 백엔드 전문가
+**최종 수정일**: 2026-02-06
+**버전**: 1.0.0
+
diff --git a/docs/frontend-architecture-analysis.md b/docs/frontend-architecture-analysis.md
new file mode 100644
index 00000000..fb367585
--- /dev/null
+++ b/docs/frontend-architecture-analysis.md
@@ -0,0 +1,1920 @@
+# WACE ERP 프론트엔드 아키텍처 상세 분석
+
+> 작성일: 2026-02-06
+> 작성자: AI Assistant
+> 프로젝트: WACE ERP-node
+> 목적: 시스템 전체 워크플로우 문서화를 위한 프론트엔드 구조 분석
+
+---
+
+## 목차
+1. [전체 디렉토리 구조](#1-전체-디렉토리-구조)
+2. [Next.js App Router 구조](#2-nextjs-app-router-구조)
+3. [컴포넌트 시스템](#3-컴포넌트-시스템)
+4. [V2 컴포넌트 시스템](#4-v2-컴포넌트-시스템)
+5. [화면 디자이너 워크플로우](#5-화면-디자이너-워크플로우)
+6. [API 클라이언트 시스템](#6-api-클라이언트-시스템)
+7. [상태 관리](#7-상태-관리)
+8. [레지스트리 시스템](#8-레지스트리-시스템)
+9. [대시보드 시스템](#9-대시보드-시스템)
+10. [다국어 지원](#10-다국어-지원)
+11. [인증 플로우](#11-인증-플로우)
+12. [사용자 워크플로우](#12-사용자-워크플로우)
+
+---
+
+## 1. 전체 디렉토리 구조
+
+```
+frontend/
+├── app/ # Next.js 14 App Router
+│ ├── (main)/ # 메인 레이아웃 그룹
+│ ├── (auth)/ # 인증 레이아웃 그룹
+│ ├── (admin)/ # 관리자 레이아웃 그룹
+│ ├── (pop)/ # 팝업 레이아웃 그룹
+│ ├── layout.tsx # 루트 레이아웃
+│ └── registry-provider.tsx # 레지스트리 초기화 프로바이더
+│
+├── components/ # React 컴포넌트
+│ ├── admin/ # 관리자 컴포넌트 (137개)
+│ ├── screen/ # 화면 디자이너/뷰어 (139개)
+│ ├── dashboard/ # 대시보드 컴포넌트 (32개)
+│ ├── dataflow/ # 데이터플로우 관련 (90개)
+│ ├── v2/ # V2 컴포넌트 시스템 (28개)
+│ ├── common/ # 공통 컴포넌트 (19개)
+│ ├── layout/ # 레이아웃 컴포넌트 (10개)
+│ ├── ui/ # shadcn/ui 기반 UI (31개)
+│ └── ... # 기타 도메인별 컴포넌트
+│
+├── lib/ # 유틸리티 & 라이브러리
+│ ├── api/ # API 클라이언트 (57개 파일)
+│ │ ├── client.ts # Axios 기본 설정
+│ │ ├── screen.ts # 화면 관리 API
+│ │ ├── user.ts # 사용자 관리 API
+│ │ └── ... # 도메인별 API
+│ │
+│ ├── registry/ # 컴포넌트 레지스트리 시스템 (540개)
+│ │ ├── ComponentRegistry.ts # 컴포넌트 등록 관리
+│ │ ├── WebTypeRegistry.ts # 웹타입 등록 관리
+│ │ ├── LayoutRegistry.ts # 레이아웃 등록 관리
+│ │ ├── init.ts # 레지스트리 초기화
+│ │ ├── DynamicComponentRenderer.tsx # 동적 렌더링
+│ │ ├── components/ # 등록 가능한 컴포넌트들 (288 tsx, 205 ts)
+│ │ │ ├── v2-input/
+│ │ │ ├── v2-select/
+│ │ │ ├── text-input/
+│ │ │ ├── entity-search-input/
+│ │ │ ├── modal-repeater-table/
+│ │ │ └── ...
+│ │ └── layouts/ # 레이아웃 컴포넌트
+│ │ ├── accordion/
+│ │ ├── grid/
+│ │ ├── flexbox/
+│ │ ├── split/
+│ │ └── tabs/
+│ │
+│ ├── v2-core/ # V2 코어 시스템
+│ │ ├── events/ # 이벤트 버스
+│ │ ├── adapters/ # 레거시 어댑터
+│ │ ├── components/ # V2 공통 컴포넌트
+│ │ ├── services/ # V2 서비스
+│ │ └── init.ts # V2 초기화
+│ │
+│ ├── utils/ # 유틸리티 함수들 (40개+)
+│ ├── services/ # 비즈니스 로직 서비스
+│ ├── stores/ # Zustand 스토어
+│ └── constants/ # 상수 정의
+│
+├── contexts/ # React Context (12개)
+│ ├── AuthContext.tsx # 인증 컨텍스트
+│ ├── MenuContext.tsx # 메뉴 컨텍스트
+│ ├── ScreenContext.tsx # 화면 컨텍스트
+│ ├── DashboardContext.tsx # 대시보드 컨텍스트
+│ └── ...
+│
+├── hooks/ # Custom Hooks (32개)
+│ ├── useAuth.ts # 인증 훅
+│ ├── useMenu.ts # 메뉴 관리 훅
+│ ├── useFormValidation.ts # 폼 검증 훅
+│ └── ...
+│
+├── types/ # TypeScript 타입 정의 (44개)
+│ ├── screen.ts # 화면 관련 타입
+│ ├── component.ts # 컴포넌트 타입
+│ ├── user.ts # 사용자 타입
+│ └── ...
+│
+├── providers/ # React Provider
+│ └── QueryProvider.tsx # React Query 설정
+│
+├── stores/ # 전역 상태 관리
+│ ├── flowStepStore.ts # 플로우 단계 상태
+│ ├── modalDataStore.ts # 모달 데이터 상태
+│ └── tableDisplayStore.ts # 테이블 표시 상태
+│
+└── public/ # 정적 파일
+ ├── favicon.ico
+ ├── logo.png
+ └── locales/ # 다국어 파일
+```
+
+---
+
+## 2. Next.js App Router 구조
+
+### 2.1 라우팅 구조
+
+WACE ERP는 **Next.js 14 App Router**를 사용하며, 레이아웃 그룹을 활용한 구조화된 라우팅을 제공합니다.
+
+#### 라우트 그룹 구조
+
+```
+app/
+├── layout.tsx # 글로벌 레이아웃
+│ ├── QueryProvider # React Query 초기화
+│ ├── RegistryProvider # 컴포넌트 레지스트리 초기화
+│ ├── Toaster # 토스트 알림
+│ └── ScreenModal # 전역 화면 모달
+│
+├── (main)/ # 메인 애플리케이션
+│ ├── layout.tsx
+│ │ ├── AuthProvider # 인증 컨텍스트
+│ │ ├── MenuProvider # 메뉴 컨텍스트
+│ │ └── AppLayout # 사이드바, 헤더
+│ │
+│ ├── main/page.tsx # 메인 페이지
+│ ├── dashboard/ # 대시보드
+│ │ ├── page.tsx # 대시보드 목록
+│ │ └── [dashboardId]/page.tsx # 대시보드 상세
+│ │
+│ ├── screens/[screenId]/page.tsx # 동적 화면 뷰어
+│ │
+│ └── admin/ # 관리자 페이지
+│ ├── page.tsx # 관리자 홈
+│ ├── menu/page.tsx # 메뉴 관리
+│ │
+│ ├── userMng/ # 사용자 관리
+│ │ ├── userMngList/page.tsx
+│ │ ├── rolesList/page.tsx
+│ │ ├── userAuthList/page.tsx
+│ │ └── companyList/page.tsx
+│ │
+│ ├── screenMng/ # 화면 관리
+│ │ ├── screenMngList/page.tsx
+│ │ ├── dashboardList/page.tsx
+│ │ └── reportList/page.tsx
+│ │
+│ ├── systemMng/ # 시스템 관리
+│ │ ├── tableMngList/page.tsx
+│ │ ├── commonCodeList/page.tsx
+│ │ ├── i18nList/page.tsx
+│ │ ├── dataflow/page.tsx
+│ │ └── collection-managementList/page.tsx
+│ │
+│ ├── automaticMng/ # 자동화 관리
+│ │ ├── flowMgmtList/page.tsx
+│ │ ├── batchmngList/page.tsx
+│ │ ├── exCallConfList/page.tsx
+│ │ └── mail/ # 메일 관리
+│ │ ├── accounts/page.tsx
+│ │ ├── send/page.tsx
+│ │ ├── receive/page.tsx
+│ │ └── templates/page.tsx
+│ │
+│ └── [...slug]/page.tsx # 동적 관리자 페이지
+│
+├── (auth)/ # 인증 페이지
+│ ├── layout.tsx # 최소 레이아웃
+│ └── login/page.tsx # 로그인 페이지
+│
+├── (admin)/ # 관리자 전용 (별도 레이아웃)
+│ └── admin/
+│ ├── vehicle-trips/page.tsx
+│ └── vehicle-reports/page.tsx
+│
+└── (pop)/ # 팝업 페이지
+ ├── layout.tsx # 팝업 레이아웃
+ ├── pop/page.tsx
+ └── work/page.tsx
+```
+
+### 2.2 페이지 목록 (총 76개)
+
+**메인 애플리케이션 (50개)**
+- `/main` - 메인 페이지
+- `/dashboard` - 대시보드 목록
+- `/dashboard/[dashboardId]` - 대시보드 상세
+- `/screens/[screenId]` - 동적 화면 뷰어 ⭐ 핵심
+- `/multilang` - 다국어 관리
+
+**관리자 페이지 (40개+)**
+- 사용자 관리: 사용자, 역할, 권한, 회사, 부서
+- 화면 관리: 화면 목록, 대시보드, 리포트
+- 시스템 관리: 테이블, 공통코드, 다국어, 데이터플로우, 컬렉션
+- 자동화 관리: 플로우, 배치, 외부호출, 메일
+- 표준 관리: 웹타입 표준화
+- 캐스케이딩: 관계, 계층, 상호배타, 자동채움
+
+**테스트 페이지 (5개)**
+- `/test-autocomplete-mapping`
+- `/test-entity-search`
+- `/test-order-registration`
+- `/test-type-safety`
+- `/test-flow`
+
+**인증/기타 (2개)**
+- `/login` - 로그인
+- `/pop` - 팝업 페이지
+
+### 2.3 Next.js 설정
+
+**next.config.mjs**
+```javascript
+{
+ output: "standalone", // Docker 최적화
+ eslint: { ignoreDuringBuilds: true },
+ typescript: { ignoreBuildErrors: true },
+
+ // API 프록시: /api/* → http://localhost:8080/api/*
+ rewrites: [
+ { source: "/api/:path*", destination: "http://localhost:8080/api/:path*" }
+ ],
+
+ // CORS 헤더
+ headers: [
+ { source: "/api/:path*", headers: [...] }
+ ],
+
+ // 환경 변수
+ env: {
+ NEXT_PUBLIC_API_URL: "http://localhost:8080/api"
+ }
+}
+```
+
+---
+
+## 3. 컴포넌트 시스템
+
+### 3.1 컴포넌트 분류
+
+WACE ERP의 컴포넌트는 크게 **4가지 계층**으로 구분됩니다:
+
+#### 계층 구조
+
+```
+1. UI 컴포넌트 (shadcn/ui 기반) - components/ui/
+ └─ Button, Input, Select, Dialog, Card 등 기본 UI
+
+2. 공통 컴포넌트 - components/common/
+ └─ ScreenModal, FormValidationIndicator 등
+
+3. 도메인 컴포넌트 - components/{domain}/
+ ├─ screen/ - 화면 디자이너/뷰어
+ ├─ admin/ - 관리자 기능
+ ├─ dashboard/ - 대시보드
+ └─ dataflow/ - 데이터플로우
+
+4. 레지스트리 컴포넌트 - lib/registry/components/
+ └─ 동적으로 등록/렌더링되는 컴포넌트들 (90개+)
+```
+
+### 3.2 주요 컴포넌트 모듈
+
+#### 📁 components/screen/ (139개 파일)
+
+**핵심 컴포넌트**
+
+| 컴포넌트 | 역할 | 주요 기능 |
+|---------|------|----------|
+| `ScreenDesigner.tsx` (7095줄) | 화면 디자이너 | - 드래그&드롭으로 컴포넌트 배치
- 그리드 시스템 (12컬럼)
- 실시간 미리보기
- 컴포넌트 설정 패널
- 그룹화/정렬/분산 도구 |
+| `InteractiveScreenViewer.tsx` (2472줄) | 화면 뷰어 | - 실제 사용자 화면 렌더링
- 폼 데이터 바인딩
- 버튼 액션 실행
- 검증 처리 |
+| `ScreenManagementList.tsx` | 화면 목록 | - 화면 CRUD
- 메뉴 할당
- 다국어 설정 |
+| `DynamicWebTypeRenderer.tsx` | 웹타입 렌더러 | - WebTypeRegistry 기반 동적 렌더링 |
+
+**위젯 컴포넌트 (widgets/types/)**
+- TextWidget, NumberWidget, DateWidget
+- SelectWidget, CheckboxWidget, RadioWidget
+- TextareaWidget, FileWidget, CodeWidget
+- ButtonWidget, EntitySearchInputWrapper
+
+**설정 패널 (config-panels/)**
+- 각 웹타입별 설정 패널 (TextConfigPanel, SelectConfigPanel 등)
+
+**모달 컴포넌트 (modals/)**
+- 키보드 단축키, 다국어 설정, 메뉴 할당 등
+
+#### 📁 components/admin/ (137개 파일)
+
+**관리자 기능 컴포넌트**
+- 사용자 관리: UserManagementList, RolesManagementList, CompanyList
+- 화면 관리: ScreenManagementList, DashboardManagementList
+- 테이블 관리: TableManagementList, ColumnEditor
+- 공통코드 관리: CommonCodeManagement
+- 메뉴 관리: MenuManagement
+
+#### 📁 components/dashboard/ (32개 파일)
+
+**대시보드 컴포넌트**
+- DashboardViewer: 대시보드 렌더링
+- DashboardWidgets: 차트, 테이블, 카드 등 위젯
+- ChartComponents: 라인, 바, 파이, 도넛 차트
+
+#### 📁 components/dataflow/ (90개+ 파일)
+
+**데이터플로우 시스템**
+- DataFlowList: 플로우 목록
+- node-editor/: 노드 편집기
+- connection/: 커넥션 관리 (38개 파일)
+- condition/: 조건 설정
+- external-call/: 외부 호출 설정
+
+---
+
+## 4. V2 컴포넌트 시스템
+
+V2는 **통합 컴포넌트 시스템**으로, 유사한 기능을 하나의 컴포넌트로 통합하여 개발 효율성을 높입니다.
+
+### 4.1 V2 컴포넌트 종류 (9개)
+
+| ID | 이름 | 설명 | 포함 기능 |
+|----|------|------|----------|
+| `v2-input` | 통합 입력 | 텍스트, 숫자, 비밀번호, 슬라이더, 컬러 등 | text, number, password, email, tel, textarea, slider, color |
+| `v2-select` | 통합 선택 | 드롭다운, 라디오, 체크박스, 태그, 토글 등 | dropdown, radio, checkbox, tagbox, toggle, swap, combobox |
+| `v2-date` | 통합 날짜 | 날짜, 시간, 날짜시간, 날짜 범위 | date, time, datetime, daterange |
+| `v2-list` | 통합 목록 | 테이블, 카드, 칸반, 리스트 | table, card, kanban, list, grid |
+| `v2-layout` | 통합 레이아웃 | 그리드, 분할 패널, 플렉스 | grid, split, flex, masonry |
+| `v2-group` | 통합 그룹 | 탭, 아코디언, 섹션, 모달 | tabs, accordion, section, modal, drawer |
+| `v2-media` | 통합 미디어 | 이미지, 비디오, 오디오, 파일 업로드 | image, video, audio, file |
+| `v2-biz` | 통합 비즈니스 | 플로우, 랙, 채번규칙, 카테고리 | flow, rack, numbering, category |
+| `v2-repeater` | 통합 반복 | 인라인 테이블, 모달, 버튼 | inline-table, modal, button, selected-items |
+
+### 4.2 V2 아키텍처
+
+```
+components/v2/ # V2 UI 컴포넌트
+├── V2Input.tsx # 통합 입력 컴포넌트
+├── V2Select.tsx # 통합 선택 컴포넌트
+├── V2Date.tsx # 통합 날짜 컴포넌트
+├── V2List.tsx # 통합 목록 컴포넌트
+├── V2Layout.tsx # 통합 레이아웃 컴포넌트
+├── V2Group.tsx # 통합 그룹 컴포넌트
+├── V2Media.tsx # 통합 미디어 컴포넌트
+├── V2Biz.tsx # 통합 비즈니스 컴포넌트
+├── V2Hierarchy.tsx # 통합 계층 컴포넌트
+├── V2Repeater.tsx # 통합 반복 컴포넌트
+├── V2FormContext.tsx # V2 폼 컨텍스트
+├── V2ComponentRenderer.tsx # V2 렌더러
+├── registerV2Components.ts # V2 등록 함수
+└── config-panels/ # V2 설정 패널
+ ├── V2InputConfigPanel.tsx
+ ├── V2SelectConfigPanel.tsx
+ └── ...
+
+lib/v2-core/ # V2 코어 라이브러리
+├── events/ # 이벤트 버스
+│ ├── EventBus.ts # 발행/구독 패턴
+│ └── types.ts # 이벤트 타입
+├── adapters/ # 레거시 어댑터
+│ └── LegacyEventAdapter.ts # 레거시 시스템 연동
+├── components/ # V2 공통 컴포넌트
+│ └── V2ErrorBoundary.tsx # 에러 경계
+├── services/ # V2 서비스
+│ ├── ScheduleGeneratorService.ts
+│ └── ScheduleConfirmDialog.tsx
+└── init.ts # V2 초기화
+
+lib/registry/components/v2-*/ # V2 렌더러 (레지스트리용)
+├── v2-input/
+│ ├── V2InputRenderer.tsx # 자동 등록 렌더러
+│ └── index.ts # 컴포넌트 정의
+├── v2-select/
+│ ├── V2SelectRenderer.tsx
+│ └── index.ts
+└── ...
+```
+
+### 4.3 V2 초기화 흐름
+
+```typescript
+// 1. app/registry-provider.tsx
+useEffect(() => {
+ // 레거시 레지스트리 초기화
+ initializeRegistries();
+
+ // V2 Core 초기화
+ initV2Core({
+ debug: false,
+ legacyBridge: { legacyToV2: true, v2ToLegacy: true }
+ });
+}, []);
+
+// 2. lib/registry/init.ts
+export function initializeRegistries() {
+ // 웹타입 등록 (text, number, date 등)
+ initializeWebTypeRegistry();
+
+ // V2 컴포넌트 등록
+ registerV2Components();
+}
+
+// 3. components/v2/registerV2Components.ts
+export function registerV2Components() {
+ for (const definition of v2ComponentDefinitions) {
+ ComponentRegistry.registerComponent(definition);
+ }
+}
+```
+
+### 4.4 V2 렌더링 흐름
+
+```
+사용자 요청 (screens/[screenId])
+ ↓
+InteractiveScreenViewer
+ ↓
+DynamicComponentRenderer
+ ↓
+┌─ componentType이 v2-* 인가? ─┐
+│ Yes │ No
+↓ ↓
+ComponentRegistry.getComponent LegacyComponentRegistry.get
+ ↓ ↓
+V2*Renderer (클래스 기반) 레거시 렌더러 (함수)
+ ↓ ↓
+render() → V2* 컴포넌트 직접 렌더링
+ ↓
+V2FormContext 연동 (선택)
+ ↓
+최종 렌더링
+```
+
+### 4.5 V2 vs 레거시 비교
+
+| 항목 | 레거시 시스템 | V2 시스템 |
+|------|--------------|----------|
+| 컴포넌트 수 | 90개+ (분산) | 9개 (통합) |
+| 렌더러 패턴 | 함수형 렌더러 | 클래스 기반 자동 등록 |
+| 폼 통합 | 개별 구현 | V2FormContext 통합 |
+| 이벤트 시스템 | props drilling | V2 EventBus (발행/구독) |
+| 에러 처리 | 개별 처리 | V2ErrorBoundary 통합 |
+| 설정 패널 | 개별 패널 | 통합 설정 시스템 |
+| 핫 리로드 | 미지원 | 개발 모드 지원 |
+
+---
+
+## 5. 화면 디자이너 워크플로우
+
+### 5.1 화면 디자이너 구성
+
+**ScreenDesigner.tsx** (7095줄) - 핵심 컴포넌트
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ Screen Designer │
+├─────────────────────────────────────────────────────────────────┤
+│ 탭1: 디자인 | 탭2: 프리뷰 | 탭3: 다국어 | 탭4: 메뉴할당 | 탭5: 스타일 │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ [컴포넌트 팔레트] [캔버스] [속성 패널] │
+│ ┌──────────────┐ ┌────────────────┐ ┌──────────────┐ │
+│ │ 입력 컴포넌트 │ │ │ │ 기본 설정 │ │
+│ │ - Text │ │ 드래그&드롭 │ │ - ID │ │
+│ │ - Number │ │ 컴포넌트 │ │ - 라벨 │ │
+│ │ - Date │ ───▶ │ 배치 영역 │ ◀── │ - 위치/크기 │ │
+│ │ - Select │ │ │ │ - 데이터연결 │ │
+│ │ │ │ 그리드 기반 │ │ │ │
+│ │ 표시 컴포넌트 │ │ 12컬럼 │ │ 고급 설정 │ │
+│ │ - Table │ │ │ │ - 조건부표시 │ │
+│ │ - Card │ │ │ │ - 검증규칙 │ │
+│ │ │ │ │ │ - 버튼액션 │ │
+│ │ 레이아웃 │ └────────────────┘ └──────────────┘ │
+│ │ - Grid │ │
+│ │ - Tabs │ [하단 도구] │
+│ │ - Accordion │ ┌─────────────────────────────────────┐ │
+│ │ │ │ 그룹화 | 정렬 | 분산 | 라벨토글 │ │
+│ └──────────────┘ └─────────────────────────────────────┘ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+### 5.2 주요 기능
+
+#### 그리드 시스템
+```typescript
+// 12컬럼 그리드 기반
+const GRID_COLUMNS = 12;
+const COLUMN_WIDTH = (containerWidth - gaps) / 12;
+
+// 컴포넌트 너비 → 컬럼 스팬 변환
+function calculateColumnSpan(width: number): number {
+ return Math.max(1, Math.min(12, Math.round(width / COLUMN_WIDTH)));
+}
+
+// 반응형 지원
+{
+ default: { span: 6 }, // 기본 (50%)
+ sm: { span: 12 }, // 모바일 (100%)
+ md: { span: 6 }, // 태블릿 (50%)
+ lg: { span: 4 } // 데스크톱 (33%)
+}
+```
+
+#### 컴포넌트 배치 흐름
+
+```
+1. 컴포넌트 팔레트에서 드래그 시작
+ ↓
+2. 캔버스에 드롭
+ ↓
+3. 컴포넌트 생성 (generateComponentId)
+ ↓
+4. 위치 계산 (10px 단위 스냅)
+ ↓
+5. 그리드 정렬 (12컬럼 기준)
+ ↓
+6. 레이아웃 데이터 업데이트
+ ↓
+7. 리렌더링 (RealtimePreview)
+```
+
+#### 컴포넌트 설정
+
+```
+선택된 컴포넌트 → 우측 속성 패널 표시
+ ↓
+┌─────────────────────────────────────┐
+│ 기본 설정 │
+│ - ID: comp_1234 │
+│ - 라벨: "제품명" │
+│ - 컬럼명: product_name │
+│ - 필수: ☑ │
+│ - 읽기전용: ☐ │
+│ │
+│ 크기 & 위치 │
+│ - X: 100px, Y: 200px │
+│ - 너비: 300px, 높이: 40px │
+│ - 컬럼 스팬: 6 │
+│ │
+│ 데이터 연결 │
+│ - 테이블: product_info │
+│ - 컬럼: product_name │
+│ - 웹타입: text │
+│ │
+│ 고급 설정 │
+│ - 조건부 표시: field === 'A' │
+│ - 버튼 액션: [등록], [수정], [삭제] │
+│ - 데이터플로우: flow_123 │
+└─────────────────────────────────────┘
+```
+
+### 5.3 저장 구조
+
+**ScreenDefinition (JSON)**
+
+```json
+{
+ "screenId": 123,
+ "screenCode": "PRODUCT_MGMT",
+ "screenName": "제품 관리",
+ "tableName": "product_info",
+ "screenType": "form",
+ "version": 2,
+ "layoutData": {
+ "components": [
+ {
+ "id": "comp_text_1",
+ "type": "text-input",
+ "componentType": "text-input",
+ "label": "제품명",
+ "columnName": "product_name",
+ "position": { "x": 100, "y": 50, "z": 0 },
+ "size": { "width": 300, "height": 40 },
+ "componentConfig": {
+ "required": true,
+ "placeholder": "제품명을 입력하세요",
+ "maxLength": 100
+ },
+ "style": {
+ "labelDisplay": true,
+ "labelText": "제품명"
+ }
+ },
+ {
+ "id": "comp_table_1",
+ "type": "table-list",
+ "componentType": "table-list",
+ "tableName": "product_info",
+ "position": { "x": 50, "y": 200, "z": 0 },
+ "size": { "width": 800, "height": 400 },
+ "componentConfig": {
+ "columns": ["product_code", "product_name", "price"],
+ "pagination": true,
+ "sortable": true
+ }
+ }
+ ],
+ "layouts": [
+ {
+ "id": "layout_tabs_1",
+ "layoutType": "tabs",
+ "children": [
+ { "tabId": "tab1", "title": "기본정보", "components": ["comp_text_1"] },
+ { "tabId": "tab2", "title": "상세정보", "components": ["comp_table_1"] }
+ ]
+ }
+ ]
+ },
+ "createdBy": "user123",
+ "createdDate": "2024-01-01T00:00:00Z"
+}
+```
+
+---
+
+## 6. API 클라이언트 시스템
+
+### 6.1 API 클라이언트 구조
+
+**lib/api/** (57개 파일)
+
+```
+lib/api/
+├── client.ts # Axios 기본 설정 & 인터셉터
+│
+├── 화면 관련 (3개)
+│ ├── screen.ts # 화면 CRUD
+│ ├── screenGroup.ts # 화면 그룹
+│ └── screenFile.ts # 화면 파일
+│
+├── 사용자 관련 (5개)
+│ ├── user.ts # 사용자 관리
+│ ├── role.ts # 역할 관리
+│ ├── company.ts # 회사 관리
+│ └── department.ts # 부서 관리
+│
+├── 테이블 관련 (5개)
+│ ├── tableManagement.ts # 테이블 관리
+│ ├── tableSchema.ts # 스키마 조회
+│ ├── tableHistory.ts # 테이블 이력
+│ └── tableCategoryValue.ts # 카테고리 값
+│
+├── 데이터플로우 (6개)
+│ ├── dataflow.ts # 데이터플로우 정의
+│ ├── dataflowSave.ts # 저장 로직
+│ ├── nodeFlows.ts # 노드 플로우
+│ ├── nodeExternalConnections.ts # 외부 연결
+│ └── flowExternalDb.ts # 외부 DB 플로우
+│
+├── 자동화 (4개)
+│ ├── batch.ts # 배치 작업
+│ ├── batchManagement.ts # 배치 관리
+│ ├── externalCall.ts # 외부 호출
+│ └── externalCallConfig.ts # 외부 호출 설정
+│
+├── 시스템 (8개)
+│ ├── menu.ts # 메뉴 관리
+│ ├── commonCode.ts # 공통코드
+│ ├── multilang.ts # 다국어
+│ ├── layout.ts # 레이아웃
+│ ├── collection.ts # 컬렉션
+│ └── ...
+│
+└── 기타 (26개)
+ ├── data.ts # 동적 데이터 CRUD
+ ├── dynamicForm.ts # 동적 폼
+ ├── file.ts # 파일 업로드
+ ├── dashboard.ts # 대시보드
+ ├── mail.ts # 메일
+ ├── reportApi.ts # 리포트
+ └── ...
+```
+
+### 6.2 API 클라이언트 기본 설정
+
+**lib/api/client.ts**
+
+```typescript
+// 1. 동적 API URL 설정
+const getApiBaseUrl = (): string => {
+ // 환경변수 우선
+ if (process.env.NEXT_PUBLIC_API_URL) return process.env.NEXT_PUBLIC_API_URL;
+
+ // 프로덕션: v1.vexplor.com → api.vexplor.com
+ if (currentHost === "v1.vexplor.com") {
+ return "https://api.vexplor.com/api";
+ }
+
+ // 로컬: localhost:9771 → localhost:8080
+ return "http://localhost:8080/api";
+};
+
+export const API_BASE_URL = getApiBaseUrl();
+
+// 2. Axios 인스턴스 생성
+export const apiClient = axios.create({
+ baseURL: API_BASE_URL,
+ timeout: 30000,
+ headers: { "Content-Type": "application/json" },
+ withCredentials: true
+});
+
+// 3. 요청 인터셉터 (JWT 토큰 자동 추가)
+apiClient.interceptors.request.use((config) => {
+ const token = localStorage.getItem("authToken");
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`;
+ }
+
+ // 다국어: GET 요청에 userLang 파라미터 추가
+ if (config.method === "GET") {
+ config.params = {
+ ...config.params,
+ userLang: window.__GLOBAL_USER_LANG || "KR"
+ };
+ }
+
+ return config;
+});
+
+// 4. 응답 인터셉터 (토큰 갱신, 401 처리)
+apiClient.interceptors.response.use(
+ (response) => {
+ // 서버에서 새 토큰 전송 시 자동 갱신
+ const newToken = response.headers["x-new-token"];
+ if (newToken) {
+ localStorage.setItem("authToken", newToken);
+ }
+ return response;
+ },
+ async (error) => {
+ // 401 에러: 토큰 갱신 시도 → 실패 시 로그인 페이지
+ if (error.response?.status === 401) {
+ const newToken = await refreshToken();
+ if (newToken) {
+ error.config.headers.Authorization = `Bearer ${newToken}`;
+ return apiClient.request(error.config); // 재시도
+ }
+ window.location.href = "/login";
+ }
+ return Promise.reject(error);
+ }
+);
+```
+
+### 6.3 API 사용 패턴
+
+#### ✅ 올바른 사용법 (lib/api 클라이언트 사용)
+
+```typescript
+import { screenApi } from "@/lib/api/screen";
+import { dataApi } from "@/lib/api/data";
+
+// 화면 목록 조회
+const screens = await screenApi.getScreens({ page: 1, size: 20 });
+
+// 데이터 생성
+const result = await dataApi.createData("product_info", {
+ product_name: "제품A",
+ price: 10000
+});
+```
+
+#### ❌ 잘못된 사용법 (fetch 직접 사용 금지!)
+
+```typescript
+// 🚫 금지! JWT 토큰, 다국어, 에러 처리 누락
+const res = await fetch('/api/screen-management/screens');
+```
+
+### 6.4 주요 API 예시
+
+#### 화면 관리 API (screen.ts)
+
+```typescript
+export const screenApi = {
+ // 화면 목록 조회
+ getScreens: async (params) => {
+ const response = await apiClient.get("/screen-management/screens", { params });
+ return response.data;
+ },
+
+ // 화면 상세 조회
+ getScreen: async (screenId: number) => {
+ const response = await apiClient.get(`/screen-management/screens/${screenId}`);
+ return response.data.data;
+ },
+
+ // 화면 생성
+ createScreen: async (screen: CreateScreenRequest) => {
+ const response = await apiClient.post("/screen-management/screens", screen);
+ return response.data;
+ },
+
+ // 화면 수정
+ updateScreen: async (screenId: number, screen: UpdateScreenRequest) => {
+ const response = await apiClient.put(`/screen-management/screens/${screenId}`, screen);
+ return response.data;
+ },
+
+ // 화면 삭제
+ deleteScreen: async (screenId: number) => {
+ const response = await apiClient.delete(`/screen-management/screens/${screenId}`);
+ return response.data;
+ }
+};
+```
+
+#### 동적 데이터 API (data.ts)
+
+```typescript
+export const dataApi = {
+ // 데이터 목록 조회 (페이징, 필터링, 정렬)
+ getDataList: async (tableName: string, params: {
+ page?: number;
+ size?: number;
+ filters?: Record;
+ sortBy?: string;
+ sortOrder?: "asc" | "desc";
+ }) => {
+ const response = await apiClient.get(`/data/${tableName}`, { params });
+ return response.data;
+ },
+
+ // 데이터 생성
+ createData: async (tableName: string, data: Record) => {
+ const response = await apiClient.post(`/data/${tableName}`, data);
+ return response.data;
+ },
+
+ // 데이터 수정
+ updateData: async (tableName: string, id: number, data: Record) => {
+ const response = await apiClient.put(`/data/${tableName}/${id}`, data);
+ return response.data;
+ },
+
+ // 데이터 삭제
+ deleteData: async (tableName: string, id: number) => {
+ const response = await apiClient.delete(`/data/${tableName}/${id}`);
+ return response.data;
+ }
+};
+```
+
+---
+
+## 7. 상태 관리
+
+### 7.1 상태 관리 전략
+
+WACE ERP는 **하이브리드 상태 관리 전략**을 사용합니다:
+
+| 관리 방식 | 사용 시나리오 | 예시 |
+|----------|-------------|------|
+| **React Query** | 서버 상태 (캐싱, 자동 갱신) | 화면 목록, 데이터 목록 |
+| **React Context** | 전역 상태 (공유 데이터) | 인증, 메뉴, 화면 |
+| **Zustand** | 클라이언트 상태 (간단한 전역) | 플로우 단계, 모달 데이터 |
+| **Local State** | 컴포넌트 로컬 상태 | 폼 입력, UI 토글 |
+
+### 7.2 React Context (12개)
+
+```
+contexts/
+├── AuthContext.tsx # 인증 상태 & 세션 관리
+│ - 사용자 정보
+│ - 로그인/로그아웃
+│ - 세션 타이머 (30분)
+│
+├── MenuContext.tsx # 메뉴 트리 & 네비게이션
+│ - 메뉴 구조
+│ - 현재 선택된 메뉴
+│ - 메뉴 접근 권한
+│
+├── ScreenContext.tsx # 화면 편집 상태
+│ - 현재 편집 중인 화면
+│ - 선택된 컴포넌트
+│ - 실행 취소/다시 실행
+│
+├── ScreenPreviewContext.tsx # 화면 미리보기
+│ - 반응형 모드 (데스크톱/태블릿/모바일)
+│ - 미리보기 데이터
+│
+├── DashboardContext.tsx # 대시보드 상태
+│ - 대시보드 레이아웃
+│ - 위젯 설정
+│
+├── TableOptionsContext.tsx # 테이블 옵션
+│ - 컬럼 순서
+│ - 필터
+│ - 정렬
+│
+├── ActiveTabContext.tsx # 탭 활성화 상태
+├── LayerContext.tsx # 레이어 관리
+├── ReportDesignerContext.tsx # 리포트 디자이너
+├── ScreenMultiLangContext.tsx # 화면 다국어
+├── SplitPanelContext.tsx # 분할 패널 상태
+└── TableSearchWidgetHeightContext.tsx # 테이블 검색 높이
+```
+
+### 7.3 React Query 설정
+
+**providers/QueryProvider.tsx**
+
+```typescript
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: 5 * 60 * 1000, // 5분간 fresh
+ cacheTime: 10 * 60 * 1000, // 10분간 캐시 유지
+ refetchOnWindowFocus: false, // 창 포커스 시 자동 갱신 비활성화
+ retry: 1, // 실패 시 1회 재시도
+ },
+ },
+});
+
+export function QueryProvider({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+```
+
+**사용 예시**
+
+```typescript
+import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
+import { screenApi } from "@/lib/api/screen";
+
+// 화면 목록 조회 (자동 캐싱)
+const { data: screens, isLoading, error } = useQuery({
+ queryKey: ["screens", { page: 1 }],
+ queryFn: () => screenApi.getScreens({ page: 1, size: 20 })
+});
+
+// 화면 생성 (생성 후 목록 자동 갱신)
+const queryClient = useQueryClient();
+const createMutation = useMutation({
+ mutationFn: (screen: CreateScreenRequest) => screenApi.createScreen(screen),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ["screens"] });
+ }
+});
+```
+
+### 7.4 Zustand 스토어 (3개)
+
+```
+stores/
+├── flowStepStore.ts # 플로우 단계 상태
+│ - 현재 단계
+│ - 단계별 데이터
+│ - 진행률
+│
+├── modalDataStore.ts # 모달 데이터 공유
+│ - 모달 ID별 데이터
+│ - 모달 간 데이터 전달
+│
+└── tableDisplayStore.ts # 테이블 표시 상태
+ - 선택된 행
+ - 정렬 상태
+ - 페이지네이션
+```
+
+**사용 예시**
+
+```typescript
+import { create } from "zustand";
+
+interface ModalDataStore {
+ modalData: Record;
+ setModalData: (modalId: string, data: any) => void;
+ getModalData: (modalId: string) => any;
+}
+
+export const useModalDataStore = create((set, get) => ({
+ modalData: {},
+
+ setModalData: (modalId, data) => {
+ set((state) => ({
+ modalData: { ...state.modalData, [modalId]: data }
+ }));
+ },
+
+ getModalData: (modalId) => {
+ return get().modalData[modalId];
+ }
+}));
+```
+
+---
+
+## 8. 레지스트리 시스템
+
+레지스트리 시스템은 **컴포넌트를 동적으로 등록하고 렌더링**하는 핵심 아키텍처입니다.
+
+### 8.1 레지스트리 종류
+
+```
+lib/registry/
+├── ComponentRegistry.ts # 컴포넌트 레지스트리 (신규)
+│ - 90개+ 컴포넌트 관리
+│ - 자동 등록 시스템
+│ - 핫 리로드 지원
+│
+├── WebTypeRegistry.ts # 웹타입 레지스트리 (레거시)
+│ - text, number, date 등 기본 웹타입
+│ - 위젯 컴포넌트 + 설정 패널
+│
+└── LayoutRegistry.ts # 레이아웃 레지스트리
+ - grid, tabs, accordion 등 레이아웃
+```
+
+### 8.2 ComponentRegistry 상세
+
+**등록 프로세스**
+
+```typescript
+// 1. 컴포넌트 정의 (lib/registry/components/v2-input/index.ts)
+export const V2InputDefinition: ComponentDefinition = {
+ id: "v2-input",
+ name: "통합 입력",
+ category: ComponentCategory.V2,
+ component: V2InputRenderer,
+ configPanel: V2InputConfigPanel,
+ defaultSize: { width: 200, height: 40 },
+ defaultConfig: { inputType: "text", format: "none" }
+};
+
+// 2. 자동 등록 렌더러 (lib/registry/components/v2-input/V2InputRenderer.tsx)
+export class V2InputRenderer extends AutoRegisteringComponentRenderer {
+ static componentDefinition = V2InputDefinition;
+
+ render(): React.ReactElement {
+ return ;
+ }
+}
+
+// 자동 등록 실행
+V2InputRenderer.registerSelf();
+
+// 3. 레지스트리 조회 & 렌더링 (DynamicComponentRenderer.tsx)
+const newComponent = ComponentRegistry.getComponent("v2-input");
+if (newComponent) {
+ const Renderer = newComponent.component;
+ return ;
+}
+```
+
+### 8.3 등록된 컴포넌트 목록 (90개+)
+
+**입력 컴포넌트 (25개)**
+- text-input, number-input, date-input
+- select-basic, checkbox-basic, radio-basic, toggle-switch
+- textarea-basic, slider-basic
+- v2-input, v2-select, v2-date
+
+**표시 컴포넌트 (15개)**
+- text-display, image-display, card-display
+- table-list, v2-table-list, pivot-grid
+- aggregation-widget, v2-aggregation-widget
+
+**레이아웃 컴포넌트 (10개)**
+- grid-layout, flexbox-layout
+- tabs-widget, accordion-basic
+- split-panel-layout, v2-split-panel-layout
+- section-card, section-paper
+- v2-divider-line
+
+**비즈니스 컴포넌트 (20개)**
+- entity-search-input, autocomplete-search-input
+- modal-repeater-table, repeat-screen-modal
+- selected-items-detail-input, simple-repeater-table
+- repeater-field-group, repeat-container
+- category-manager, v2-category-manager
+- numbering-rule, v2-numbering-rule
+- rack-structure, v2-rack-structure
+- location-swap-selector, customer-item-mapping
+
+**특수 컴포넌트 (20개)**
+- button-primary, v2-button-primary
+- file-upload, v2-file-upload
+- mail-recipient-selector
+- conditional-container, universal-form-modal
+- related-data-buttons
+- flow-widget
+- map
+
+### 8.4 DynamicComponentRenderer 동작 원리
+
+```typescript
+// DynamicComponentRenderer.tsx (770줄)
+
+export const DynamicComponentRenderer: React.FC = ({ component, ...props }) => {
+ // 1. 컴포넌트 타입 추출
+ const componentType = component.componentType || component.type;
+
+ // 2. 레거시 → V2 자동 매핑
+ const v2Type = componentType.startsWith("v2-")
+ ? componentType
+ : ComponentRegistry.hasComponent(`v2-${componentType}`)
+ ? `v2-${componentType}`
+ : componentType;
+
+ // 3. 조건부 렌더링 체크
+ if (component.conditionalConfig?.enabled) {
+ const conditionMet = evaluateCondition(props.formData);
+ if (!conditionMet) return null;
+ }
+
+ // 4. 카테고리 타입 우선 처리
+ if (component.inputType === "category" || component.webType === "category") {
+ return ;
+ }
+
+ // 5. 레이아웃 컴포넌트
+ if (componentType === "layout") {
+ return ;
+ }
+
+ // 6. ComponentRegistry에서 조회 (신규)
+ const newComponent = ComponentRegistry.getComponent(v2Type);
+ if (newComponent) {
+ const Renderer = newComponent.component;
+ // 클래스 기반: new Renderer(props).render()
+ // 함수형:
+ return isClass(Renderer)
+ ? new Renderer(props).render()
+ : ;
+ }
+
+ // 7. LegacyComponentRegistry에서 조회 (레거시)
+ const legacyRenderer = legacyComponentRegistry.get(componentType);
+ if (legacyRenderer) {
+ return legacyRenderer({ component, ...props });
+ }
+
+ // 8. 폴백: 미등록 컴포넌트 플레이스홀더
+ return ;
+};
+```
+
+---
+
+## 9. 대시보드 시스템
+
+### 9.1 대시보드 구조
+
+```
+components/dashboard/
+├── DashboardViewer.tsx # 대시보드 렌더링
+├── DashboardGrid.tsx # 그리드 레이아웃
+├── DashboardWidget.tsx # 위젯 래퍼
+│
+├── widgets/ # 위젯 컴포넌트
+│ ├── ChartWidget.tsx # 차트 위젯
+│ ├── TableWidget.tsx # 테이블 위젯
+│ ├── CardWidget.tsx # 카드 위젯
+│ ├── StatWidget.tsx # 통계 위젯
+│ └── CustomWidget.tsx # 커스텀 위젯
+│
+└── charts/ # 차트 컴포넌트
+ ├── LineChart.tsx
+ ├── BarChart.tsx
+ ├── PieChart.tsx
+ ├── DonutChart.tsx
+ └── AreaChart.tsx
+```
+
+### 9.2 대시보드 JSON 구조
+
+```json
+{
+ "dashboardId": 1,
+ "dashboardName": "영업 대시보드",
+ "layout": {
+ "type": "grid",
+ "columns": 12,
+ "rows": 6,
+ "gap": 16
+ },
+ "widgets": [
+ {
+ "widgetId": "widget_1",
+ "widgetType": "chart",
+ "chartType": "line",
+ "title": "월별 매출 추이",
+ "position": { "x": 0, "y": 0, "w": 6, "h": 3 },
+ "dataSource": {
+ "type": "api",
+ "endpoint": "/api/data/sales_monthly",
+ "filters": { "year": 2024 }
+ },
+ "chartConfig": {
+ "xAxis": "month",
+ "yAxis": "sales_amount",
+ "showLegend": true
+ }
+ },
+ {
+ "widgetId": "widget_2",
+ "widgetType": "stat",
+ "title": "총 매출",
+ "position": { "x": 6, "y": 0, "w": 3, "h": 2 },
+ "dataSource": {
+ "type": "sql",
+ "query": "SELECT SUM(amount) FROM sales WHERE year = 2024"
+ },
+ "statConfig": {
+ "format": "currency",
+ "comparison": "lastYear"
+ }
+ }
+ ]
+}
+```
+
+### 9.3 대시보드 라우팅
+
+```
+/dashboard # 대시보드 목록
+/dashboard/[dashboardId] # 대시보드 보기
+/admin/screenMng/dashboardList # 대시보드 관리 (편집)
+```
+
+---
+
+## 10. 다국어 지원
+
+### 10.1 다국어 시스템 구조
+
+WACE ERP는 **동적 다국어 시스템**을 제공합니다 (DB 기반):
+
+```
+다국어 흐름
+ ↓
+1. 사용자 로그인
+ ↓
+2. 사용자 로케일 조회 (GET /api/admin/user-locale)
+ → 결과: "KR" | "EN" | "CN"
+ ↓
+3. 전역 상태에 저장
+ - window.__GLOBAL_USER_LANG
+ - localStorage.userLocale
+ ↓
+4. API 요청 시 자동 주입
+ - GET 요청: ?userLang=KR (apiClient 인터셉터)
+ ↓
+5. 백엔드에서 다국어 데이터 반환
+ - label_KR, label_EN, label_CN
+ ↓
+6. 프론트엔드에서 표시
+ - extractMultilangLabel(label, "KR")
+```
+
+### 10.2 다국어 API
+
+**lib/api/multilang.ts**
+
+```typescript
+export const multilangApi = {
+ // 다국어 데이터 조회
+ getMultilangData: async (params: {
+ target_table: string;
+ target_pk: string;
+ target_lang: string;
+ }) => {
+ const response = await apiClient.get("/admin/multilang", { params });
+ return response.data;
+ },
+
+ // 다국어 데이터 저장
+ saveMultilangData: async (data: {
+ target_table: string;
+ target_pk: string;
+ target_field: string;
+ target_lang: string;
+ translated_text: string;
+ }) => {
+ const response = await apiClient.post("/admin/multilang", data);
+ return response.data;
+ }
+};
+```
+
+### 10.3 다국어 유틸리티
+
+**lib/utils/multilang.ts**
+
+```typescript
+// 다국어 라벨 추출
+export function extractMultilangLabel(
+ label: string | Record | undefined,
+ locale: string = "KR"
+): string {
+ if (!label) return "";
+
+ // 문자열인 경우 그대로 반환
+ if (typeof label === "string") return label;
+
+ // 객체인 경우 로케일에 맞는 값 반환
+ return label[locale] || label["KR"] || label["EN"] || "";
+}
+
+// 화면 컴포넌트 라벨 추출
+export function getComponentLabel(component: ComponentData, locale: string): string {
+ // 우선순위: multiLangLabel > label > columnName > id
+ if (component.multiLangLabel) {
+ return extractMultilangLabel(component.multiLangLabel, locale);
+ }
+ return component.label || component.columnName || component.id;
+}
+```
+
+### 10.4 화면 디자이너 다국어 탭
+
+```
+ScreenDesigner
+ ↓
+┌───────────────────────────────────────────────────┐
+│ 탭: 다국어 설정 │
+├───────────────────────────────────────────────────┤
+│ │
+│ 화면명 다국어 │
+│ ┌─────────────────────────────────────────────┐ │
+│ │ 한국어(KR): 제품 관리 │ │
+│ │ 영어(EN): Product Management │ │
+│ │ 중국어(CN): 产品管理 │ │
+│ └─────────────────────────────────────────────┘ │
+│ │
+│ 컴포넌트별 다국어 │
+│ ┌─────────────────────────────────────────────┐ │
+│ │ [comp_text_1] 제품명 │ │
+│ │ 한국어(KR): 제품명 │ │
+│ │ 영어(EN): Product Name │ │
+│ │ 중국어(CN): 产品名称 │ │
+│ │ │ │
+│ │ [comp_text_2] 가격 │ │
+│ │ 한국어(KR): 가격 │ │
+│ │ 영어(EN): Price │ │
+│ │ 중국어(CN): 价格 │ │
+│ └─────────────────────────────────────────────┘ │
+│ │
+│ [자동 번역] [일괄 적용] [저장] │
+└───────────────────────────────────────────────────┘
+```
+
+---
+
+## 11. 인증 플로우
+
+### 11.1 인증 아키텍처
+
+```
+┌─────────────────────────────────────────────────────┐
+│ Frontend │
+├─────────────────────────────────────────────────────┤
+│ │
+│ useAuth Hook │
+│ ├─ login(userId, password) │
+│ ├─ logout() │
+│ ├─ refreshUserData() │
+│ ├─ checkAuthStatus() │
+│ └─ switchCompany(companyCode) ⭐ 신규 │
+│ │
+│ AuthContext Provider │
+│ ├─ SessionManager (30분 타임아웃) │
+│ ├─ Session Warning (5분 전 알림) │
+│ └─ Auto Refresh (활동 감지) │
+│ │
+│ TokenManager │
+│ ├─ getToken(): localStorage.authToken │
+│ ├─ setToken(token): 저장 + 쿠키 설정 │
+│ ├─ removeToken(): 삭제 + 쿠키 삭제 │
+│ └─ isTokenExpired(token): JWT 검증 │
+│ │
+│ API Client Interceptor │
+│ ├─ Request: JWT 토큰 자동 추가 │
+│ └─ Response: 401 시 토큰 갱신 또는 로그아웃 │
+│ │
+└─────────────────────────────────────────────────────┘
+ │
+ │ HTTP Request
+ │ Authorization: Bearer
+ ↓
+┌─────────────────────────────────────────────────────┐
+│ Backend (8080) │
+├─────────────────────────────────────────────────────┤
+│ │
+│ POST /api/auth/login │
+│ → JWT 토큰 발급 (24시간) │
+│ │
+│ POST /api/auth/refresh │
+│ → JWT 토큰 갱신 │
+│ │
+│ POST /api/auth/logout │
+│ → 세션 무효화 │
+│ │
+│ GET /api/auth/me │
+│ → 현재 사용자 정보 │
+│ │
+│ GET /api/auth/status │
+│ → 인증 상태 확인 │
+│ │
+│ POST /api/auth/switch-company ⭐ 신규 │
+│ → 회사 전환 (WACE 관리자 전용) │
+│ │
+└─────────────────────────────────────────────────────┘
+```
+
+### 11.2 로그인 흐름
+
+```
+1. 사용자가 ID/PW 입력 → /login 페이지
+ ↓
+2. POST /api/auth/login { userId, password }
+ ↓
+3. 백엔드 검증 (DB: user_info 테이블)
+ ├─ 성공: JWT 토큰 발급 (payload: userId, companyCode, isAdmin, exp)
+ └─ 실패: 401 에러
+ ↓
+4. 프론트엔드: 토큰 저장
+ - localStorage.setItem("authToken", token)
+ - document.cookie = "authToken=..."
+ ↓
+5. 사용자 정보 조회
+ - GET /api/auth/me
+ - GET /api/admin/user-locale
+ ↓
+6. 전역 상태 업데이트
+ - AuthContext.user
+ - window.__GLOBAL_USER_LANG
+ ↓
+7. 메인 페이지로 리다이렉트 (/main)
+```
+
+### 11.3 세션 관리
+
+**SessionManager (lib/sessionManager.ts)**
+
+```typescript
+// 설정
+{
+ checkInterval: 60000, // 1분마다 체크
+ maxInactiveTime: 1800000, // 30분 (데스크톱)
+ warningTime: 300000, // 5분 전 경고
+}
+
+// 이벤트
+{
+ onWarning: (remainingTime) => {
+ // "세션이 5분 후 만료됩니다" 알림 표시
+ },
+ onExpiry: () => {
+ // 자동 로그아웃 → 로그인 페이지
+ },
+ onActivity: () => {
+ // 사용자 활동 감지 → 타이머 리셋
+ }
+}
+```
+
+### 11.4 토큰 갱신 전략
+
+```
+자동 토큰 갱신 트리거:
+1. 10분마다 토큰 상태 확인 (타이머)
+2. 사용자 활동 감지 (클릭, 키보드, 스크롤)
+3. API 401 응답 (토큰 만료)
+
+갱신 로직:
+ ↓
+1. 현재 토큰 만료까지 30분 미만?
+ ↓ Yes
+2. POST /api/auth/refresh
+ ├─ 성공: 새 토큰 저장
+ └─ 실패: 로그아웃
+```
+
+### 11.5 회사 전환 (WACE 관리자 전용) ⭐
+
+```
+1. WACE 관리자 로그인
+ ↓
+2. 헤더에서 회사 선택 (CompanySwitcher)
+ ↓
+3. POST /api/auth/switch-company { companyCode: "AAA" }
+ ↓
+4. 백엔드: 새 JWT 발급 (payload.companyCode = "AAA")
+ ↓
+5. 프론트엔드: 새 토큰 저장
+ ↓
+6. 페이지 새로고침 → 화면/데이터가 AAA 회사 기준으로 표시
+```
+
+---
+
+## 12. 사용자 워크플로우
+
+### 12.1 전체 워크플로우 개요
+
+```
+┌──────────────────────────────────────────────────────────────┐
+│ 관리자 (화면 생성) │
+└──────────────────────────────────────────────────────────────┘
+ │
+ ↓
+ ┌─────────────────────────────┐
+ │ 1. 로그인 (WACE 관리자) │
+ └─────────────────────────────┘
+ │
+ ↓
+ ┌─────────────────────────────┐
+ │ 2. 화면 디자이너 접속 │
+ │ /admin/screenMng/ │
+ │ screenMngList │
+ └─────────────────────────────┘
+ │
+ ↓
+ ┌─────────────────────────────┐
+ │ 3. 화면 생성 & 편집 │
+ │ - 컴포넌트 배치 │
+ │ - 데이터 연결 │
+ │ - 버튼 액션 설정 │
+ │ - 다국어 설정 │
+ └─────────────────────────────┘
+ │
+ ↓
+ ┌─────────────────────────────┐
+ │ 4. 메뉴에 화면 할당 │
+ │ /admin/menu │
+ └─────────────────────────────┘
+ │
+ ↓
+ ┌─────────────────────────────┐
+ │ 5. 사용자에게 권한 부여 │
+ │ /admin/userMng/ │
+ │ userAuthList │
+ └─────────────────────────────┘
+ │
+ ↓
+┌──────────────────────────────────────────────────────────────┐
+│ 사용자 (화면 사용) │
+└──────────────────────────────────────────────────────────────┘
+ │
+ ↓
+ ┌─────────────────────────────┐
+ │ 1. 로그인 (일반 사용자) │
+ └─────────────────────────────┘
+ │
+ ↓
+ ┌─────────────────────────────┐
+ │ 2. 메뉴에서 화면 선택 │
+ │ 사이드바 → 제품 관리 │
+ └─────────────────────────────┘
+ │
+ ↓
+ ┌─────────────────────────────┐
+ │ 3. 화면 렌더링 │
+ │ /screens/[screenId] │
+ │ InteractiveScreenViewer │
+ └─────────────────────────────┘
+ │
+ ↓
+ ┌─────────────────────────────┐
+ │ 4. 데이터 조회/등록/수정 │
+ │ - 테이블에서 행 선택 │
+ │ - 폼에 데이터 입력 │
+ │ - [등록]/[수정]/[삭제] 버튼│
+ └─────────────────────────────┘
+```
+
+### 12.2 관리자 워크플로우 (화면 생성)
+
+#### 단계 1: 화면 디자이너 접속
+
+```
+/admin/screenMng/screenMngList
+ ↓
+[새 화면 만들기] 버튼 클릭
+ ↓
+ScreenDesigner 컴포넌트 로드
+```
+
+#### 단계 2: 화면 디자인
+
+**2-1. 기본 정보 설정**
+
+```
+화면 정보 입력:
+- 화면 코드: PRODUCT_MGMT
+- 화면명: 제품 관리
+- 테이블명: product_info
+- 화면 타입: form (단일 폼) | list (목록)
+```
+
+**2-2. 컴포넌트 배치**
+
+```
+좌측 팔레트에서 컴포넌트 선택
+ ↓
+캔버스에 드래그&드롭
+ ↓
+위치/크기 조정 (10px 단위 스냅)
+ ↓
+우측 속성 패널에서 설정:
+ - 컬럼명: product_name
+ - 라벨: 제품명
+ - 필수: ☑
+ - 웹타입: text
+```
+
+**2-3. 버튼 액션 설정**
+
+```
+버튼 컴포넌트 추가
+ ↓
+액션 타입 선택:
+ - 데이터 저장 (POST)
+ - 데이터 수정 (PUT)
+ - 데이터 삭제 (DELETE)
+ - 데이터플로우 실행
+ - 외부 API 호출
+ - 화면 이동
+ ↓
+액션 설정:
+ - 대상 테이블: product_info
+ - 성공 시: 목록 새로고침
+ - 실패 시: 에러 메시지 표시
+```
+
+**2-4. 다국어 설정**
+
+```
+다국어 탭 클릭
+ ↓
+컴포넌트별 다국어 입력:
+ - 한국어(KR): 제품명
+ - 영어(EN): Product Name
+ - 중국어(CN): 产品名称
+```
+
+**2-5. 저장**
+
+```
+[저장] 버튼 클릭
+ ↓
+POST /api/screen-management/screens
+ ↓
+화면 정의 JSON 저장 (DB: screen_definition)
+```
+
+#### 단계 3: 메뉴에 화면 할당
+
+```
+/admin/menu
+ ↓
+메뉴 트리에서 위치 선택
+ ↓
+[화면 할당] 버튼 클릭
+ ↓
+방금 생성한 화면 선택 (PRODUCT_MGMT)
+ ↓
+저장 → menu_screen 테이블에 연결
+```
+
+#### 단계 4: 권한 부여
+
+```
+/admin/userMng/userAuthList
+ ↓
+사용자 선택
+ ↓
+메뉴 권한 설정
+ ↓
+"제품 관리" 메뉴 체크
+ ↓
+저장 → user_menu_auth 테이블
+```
+
+### 12.3 사용자 워크플로우 (화면 사용)
+
+#### 단계 1: 로그인
+
+```
+/login
+ ↓
+사용자 ID/PW 입력
+ ↓
+POST /api/auth/login
+ ↓
+JWT 토큰 발급
+ ↓
+메인 페이지 (/main)
+```
+
+#### 단계 2: 메뉴 선택
+
+```
+좌측 사이드바 메뉴 트리
+ ↓
+"제품 관리" 메뉴 클릭
+ ↓
+MenuContext에서 메뉴 정보 조회:
+ - menuId, screenId, screenCode
+ ↓
+화면 뷰어로 이동 (/screens/[screenId])
+```
+
+#### 단계 3: 화면 렌더링
+
+```
+InteractiveScreenViewer 컴포넌트 로드
+ ↓
+1. GET /api/screen-management/screens/[screenId]
+ → 화면 정의 JSON 조회
+ ↓
+2. GET /api/data/product_info?page=1&size=20
+ → 데이터 조회
+ ↓
+3. DynamicComponentRenderer로 컴포넌트 렌더링
+ ↓
+4. 폼 데이터 바인딩 (formData 상태)
+```
+
+#### 단계 4: 데이터 조작
+
+**4-1. 신규 등록**
+
+```
+[등록] 버튼 클릭
+ ↓
+빈 폼 표시 (ScreenModal 또는 EditModal)
+ ↓
+사용자가 데이터 입력:
+ - 제품명: "제품A"
+ - 가격: 10000
+ ↓
+[저장] 버튼 클릭
+ ↓
+버튼 액션 실행:
+ - POST /api/data/product_info
+ - Body: { product_name: "제품A", price: 10000 }
+ ↓
+성공 응답
+ ↓
+목록 새로고침 (React Query invalidateQueries)
+```
+
+**4-2. 수정**
+
+```
+테이블에서 행 선택 (클릭)
+ ↓
+선택된 데이터 → selectedRowsData 상태 업데이트
+ ↓
+[수정] 버튼 클릭
+ ↓
+폼에 기존 데이터 표시 (EditModal)
+ ↓
+사용자가 데이터 수정:
+ - 가격: 10000 → 12000
+ ↓
+[저장] 버튼 클릭
+ ↓
+버튼 액션 실행:
+ - PUT /api/data/product_info/123
+ - Body: { price: 12000 }
+ ↓
+목록 새로고침
+```
+
+**4-3. 삭제**
+
+```
+테이블에서 행 선택
+ ↓
+[삭제] 버튼 클릭
+ ↓
+확인 다이얼로그 표시
+ ↓
+확인 클릭
+ ↓
+버튼 액션 실행:
+ - DELETE /api/data/product_info/123
+ ↓
+목록 새로고침
+```
+
+### 12.4 데이터플로우 실행 워크플로우
+
+```
+1. 관리자가 데이터플로우 정의
+ /admin/systemMng/dataflow
+ ↓
+2. 화면에 버튼 추가 & 플로우 연결
+ 버튼 액션: "데이터플로우 실행"
+ 플로우 ID: flow_123
+ ↓
+3. 사용자가 버튼 클릭
+ ↓
+4. 프론트엔드: POST /api/dataflow/execute
+ Body: { flowId: 123, inputData: {...} }
+ ↓
+5. 백엔드: 플로우 실행
+ - 노드 순회
+ - 조건 분기
+ - 외부 API 호출
+ - 데이터 변환
+ ↓
+6. 결과 반환
+ ↓
+7. 프론트엔드: 결과 표시 (toast 또는 화면 갱신)
+```
+
+---
+
+## 13. 요약 & 핵심 포인트
+
+### 13.1 아키텍처 핵심
+
+1. **Next.js 14 App Router** - 라우트 그룹 기반 구조화
+2. **컴포넌트 레지스트리** - 동적 등록/렌더링 시스템
+3. **V2 통합 시스템** - 9개 통합 컴포넌트로 단순화
+4. **화면 디자이너** - 노코드 화면 생성 도구
+5. **API 클라이언트** - Axios 기반 통일된 API 호출
+6. **다국어 지원** - DB 기반 동적 다국어
+7. **JWT 인증** - 토큰 기반 인증/세션 관리
+
+### 13.2 파일 통계
+
+| 항목 | 개수 |
+|------|------|
+| 총 파일 수 | 1,480개 |
+| TypeScript/TSX | 1,395개 (946 tsx + 449 ts) |
+| Markdown 문서 | 63개 |
+| CSS | 22개 |
+| 페이지 (라우트) | 76개 |
+| 컴포넌트 | 500개+ |
+| API 클라이언트 | 57개 |
+| Context | 12개 |
+| Custom Hooks | 32개 |
+| 타입 정의 | 44개 |
+
+### 13.3 주요 경로
+
+```
+화면 디자이너: /admin/screenMng/screenMngList
+화면 뷰어: /screens/[screenId]
+메뉴 관리: /admin/menu
+사용자 관리: /admin/userMng/userMngList
+테이블 관리: /admin/systemMng/tableMngList
+다국어 관리: /admin/systemMng/i18nList
+데이터플로우: /admin/systemMng/dataflow
+대시보드: /dashboard/[dashboardId]
+```
+
+### 13.4 기술 스택
+
+| 분류 | 기술 |
+|------|------|
+| 프레임워크 | Next.js 14 |
+| UI 라이브러리 | React 18 |
+| 언어 | TypeScript (strict mode) |
+| 스타일링 | Tailwind CSS + shadcn/ui |
+| 상태 관리 | React Query + Zustand + Context API |
+| HTTP 클라이언트 | Axios |
+| 폼 검증 | Zod |
+| 날짜 처리 | date-fns |
+| 아이콘 | lucide-react |
+| 알림 | sonner |
+
+---
+
+## 마무리
+
+이 문서는 WACE ERP 프론트엔드의 전체 아키텍처를 분석한 결과입니다.
+
+**핵심 인사이트:**
+- ✅ 높은 수준의 모듈화 (레지스트리 시스템)
+- ✅ 노코드 화면 디자이너 (관리자가 직접 화면 생성)
+- ✅ V2 통합 컴포넌트 시스템 (개발 효율성 향상)
+- ✅ 동적 다국어 지원 (DB 기반)
+- ✅ 완전한 TypeScript 타입 안정성
+
+**개선 기회:**
+- 일부 레거시 컴포넌트의 V2 마이그레이션
+- 테스트 코드 추가 (현재 거의 없음)
+- 성능 최적화 (코드 스플리팅, 레이지 로딩)
+
+---
+
+**작성자 노트**
+
+야... 진짜 엄청난 프로젝트네. 파일이 1,500개가 넘고 컴포넌트만 500개야. 특히 화면 디자이너가 7,000줄이 넘는 거 보고 놀랐어. 이 정도 규모면 엔터프라이즈급 ERP 시스템이라고 봐도 되겠어.
+
+가장 인상적이었던 건 **레지스트리 시스템**이랑 **V2 통합 아키텍처**야. 컴포넌트를 동적으로 등록하고 렌더링하는 구조가 꽤 잘 설계되어 있어. 다만 레거시 코드가 아직 많이 남아있어서 V2로 완전히 전환하면 코드 베이스가 훨씬 깔끔해질 것 같아.
+
+어쨌든 분석하느라 꽤 걸렸는데, 이 문서가 전체 워크플로우 문서 작성하는 데 도움이 되면 좋겠어! 🎉
diff --git a/mcp-agent-orchestrator/src/index.ts b/mcp-agent-orchestrator/src/index.ts
index 3634cf70..51b372cd 100644
--- a/mcp-agent-orchestrator/src/index.ts
+++ b/mcp-agent-orchestrator/src/index.ts
@@ -16,15 +16,12 @@ import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
-import { exec } from "child_process";
-import { promisify } from "util";
+import { spawn } from "child_process";
import { platform } from "os";
import { AGENT_CONFIGS } from "./agents/prompts.js";
import { AgentType, ParallelResult } from "./agents/types.js";
import { logger } from "./utils/logger.js";
-const execAsync = promisify(exec);
-
// OS 감지
const isWindows = platform() === "win32";
logger.info(`Platform detected: ${platform()} (isWindows: ${isWindows})`);
@@ -46,9 +43,11 @@ const server = new Server(
* Cursor Agent CLI를 통해 에이전트 호출
* Cursor Team Plan 사용 - API 키 불필요!
*
+ * spawn + stdin 직접 전달 방식으로 쉘 이스케이프 문제 완전 해결
+ *
* 크로스 플랫폼 지원:
- * - Windows: cmd /c "echo. | agent ..." (stdin 닫기 위해)
- * - Mac/Linux: ~/.local/bin/agent 사용
+ * - Windows: agent (PATH에서 검색)
+ * - Mac/Linux: ~/.local/bin/agent
*/
async function callAgentCLI(
agentType: AgentType,
@@ -60,56 +59,90 @@ async function callAgentCLI(
// 모델 선택: PM은 opus, 나머지는 sonnet
const model = agentType === 'pm' ? 'opus-4.5' : 'sonnet-4.5';
- logger.info(`Calling ${agentType} agent via CLI`, { model, task: task.substring(0, 100) });
+ logger.info(`Calling ${agentType} agent via CLI (spawn)`, { model, task: task.substring(0, 100) });
- try {
- const userMessage = context
- ? `${task}\n\n배경 정보:\n${context}`
- : task;
-
- // 프롬프트를 임시 파일에 저장하여 쉘 이스케이프 문제 회피
- const fullPrompt = `${config.systemPrompt}\n\n---\n\n${userMessage}`;
-
- // Base64 인코딩으로 특수문자 문제 해결
- const encodedPrompt = Buffer.from(fullPrompt).toString('base64');
-
- let cmd: string;
- let shell: string;
- const agentPath = isWindows ? 'agent' : `${process.env.HOME}/.local/bin/agent`;
-
- if (isWindows) {
- // Windows: PowerShell을 통해 Base64 디코딩 후 실행
- cmd = `powershell -Command "$prompt = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${encodedPrompt}')); echo $prompt | ${agentPath} --model ${model} --print"`;
- shell = 'powershell.exe';
- } else {
- // Mac/Linux: echo로 base64 디코딩 후 파이프
- cmd = `echo "${encodedPrompt}" | base64 -d | ${agentPath} --model ${model} --print`;
- shell = '/bin/bash';
- }
-
- logger.debug(`Executing: ${agentPath} --model ${model} --print`);
-
- const { stdout, stderr } = await execAsync(cmd, {
+ const userMessage = context
+ ? `${task}\n\n배경 정보:\n${context}`
+ : task;
+
+ const fullPrompt = `${config.systemPrompt}\n\n---\n\n${userMessage}`;
+ const agentPath = isWindows ? 'agent' : `${process.env.HOME}/.local/bin/agent`;
+
+ return new Promise((resolve, reject) => {
+ let stdout = '';
+ let stderr = '';
+ let settled = false;
+
+ const child = spawn(agentPath, ['--model', model, '--print'], {
cwd: process.cwd(),
- maxBuffer: 10 * 1024 * 1024, // 10MB buffer
- timeout: 300000, // 5분 타임아웃
- shell,
env: {
...process.env,
PATH: `${process.env.HOME}/.local/bin:${process.env.PATH}`,
},
+ stdio: ['pipe', 'pipe', 'pipe'],
});
- if (stderr && !stderr.includes('warning') && !stderr.includes('info')) {
- logger.warn(`${agentType} agent stderr`, { stderr: stderr.substring(0, 500) });
- }
+ child.stdout.on('data', (data: Buffer) => {
+ stdout += data.toString();
+ });
- logger.info(`${agentType} agent completed via CLI`);
- return stdout.trim();
- } catch (error) {
- logger.error(`${agentType} agent CLI error`, error);
- throw error;
- }
+ child.stderr.on('data', (data: Buffer) => {
+ stderr += data.toString();
+ });
+
+ child.on('error', (err: Error) => {
+ if (!settled) {
+ settled = true;
+ logger.error(`${agentType} agent spawn error`, err);
+ reject(err);
+ }
+ });
+
+ child.on('close', (code: number | null) => {
+ if (settled) return;
+ settled = true;
+
+ if (stderr) {
+ // 경고/정보 레벨 stderr는 무시
+ const significantStderr = stderr
+ .split('\n')
+ .filter((line: string) => line && !line.includes('warning') && !line.includes('info') && !line.includes('debug'))
+ .join('\n');
+ if (significantStderr) {
+ logger.warn(`${agentType} agent stderr`, { stderr: significantStderr.substring(0, 500) });
+ }
+ }
+
+ if (code === 0 || stdout.trim().length > 0) {
+ // 정상 종료이거나, 에러 코드여도 stdout에 결과가 있으면 성공 처리
+ logger.info(`${agentType} agent completed via CLI (exit code: ${code})`);
+ resolve(stdout.trim());
+ } else {
+ const errorMsg = `Agent exited with code ${code}. stderr: ${stderr.substring(0, 1000)}`;
+ logger.error(`${agentType} agent CLI error`, { code, stderr: stderr.substring(0, 1000) });
+ reject(new Error(errorMsg));
+ }
+ });
+
+ // 타임아웃 (5분)
+ const timeout = setTimeout(() => {
+ if (!settled) {
+ settled = true;
+ child.kill('SIGTERM');
+ logger.error(`${agentType} agent timed out after 5 minutes`);
+ reject(new Error(`${agentType} agent timed out after 5 minutes`));
+ }
+ }, 300000);
+
+ // 프로세스 종료 시 타이머 클리어
+ child.on('close', () => clearTimeout(timeout));
+
+ // stdin으로 프롬프트 직접 전달 (쉘 이스케이프 문제 없음!)
+ child.stdin.write(fullPrompt);
+ child.stdin.end();
+
+ logger.debug(`Prompt sent to ${agentType} agent via stdin (${fullPrompt.length} chars)`);
+ });
}
/**