852 lines
37 KiB
Markdown
852 lines
37 KiB
Markdown
# DB 스키마 (테이블 및 열 목록)
|
||
|
||
**최종 업데이트:** 2026-07-10
|
||
**DB 명:** `aislo_db` (MariaDB v10.6+, utf8mb4_unicode_ci)
|
||
**총 테이블 수:** 34개 (33개 현재 운영 + 1개 향후 사용)
|
||
|
||
---
|
||
|
||
## 1. 사용자 & 인증 & 조직 그룹
|
||
|
||
### 1-1. users 테이블
|
||
```
|
||
users
|
||
├── id (INT, PK) — 사용자 고유 번호
|
||
├── email (VARCHAR(255), UNIQUE) — 로그인 이메일
|
||
├── password_hash (VARCHAR(255)) — 비밀번호 암호화 저장
|
||
├── name (VARCHAR(255)) — 사용자 이름
|
||
├── position (VARCHAR(100), NULL) — 직급 (예: 과장, 대리, 사원)
|
||
├── department (VARCHAR(100), NULL) — 부서명 (예: 설계팀, 영업팀)
|
||
├── phone (VARCHAR(20), NULL) — 연락처 (예: 010-1234-5678)
|
||
├── company_id (INT, FK → companies.id, NULL) — 소속 회사
|
||
├── role (ENUM) — 역할: SYSTEM_ADMIN, ADMIN, USER (기본값: USER)
|
||
├── is_master (TINYINT(1)) — 회사 마스터 여부 (기본값: 0)
|
||
├── status (ENUM) — 상태: PENDING_EMAIL, NO_COMPANY, PENDING, ACTIVE, INACTIVE, REJECTED (기본값: PENDING_EMAIL)
|
||
├── last_login (DATETIME, NULL) — 마지막 로그인 시간
|
||
├── auth_expires_at (DATETIME, NULL) — 인증 유효 만료 시간 (3개월 주기)
|
||
├── last_email_verified_at (TIMESTAMP, NULL) — 마지막 이메일 인증 시간
|
||
├── login_failures (INT) — 연속 로그인 실패 횟수 (기본값: 0, 보안)
|
||
├── last_failed_at (TIMESTAMP, NULL) — 마지막 로그인 실패 시간
|
||
├── account_locked_until (TIMESTAMP, NULL) — 계정 잠금 해제 시간
|
||
├── created_at (TIMESTAMP) — 가입일
|
||
├── updated_at (TIMESTAMP) — 수정일
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
**상태 생명주기:**
|
||
- PENDING_EMAIL: 회원가입 후 이메일 인증 전
|
||
- NO_COMPANY: 이메일 인증 완료, 회사 미연결 (로그인 가능, B02~B11 차단)
|
||
- PENDING: 회사 참여 신청 후 관리자 승인 대기
|
||
- ACTIVE: 회사 연결 완료, 모든 기능 접근 가능
|
||
- INACTIVE: 퇴사/계정 비활성화
|
||
- REJECTED: 회사 참여 신청 거부
|
||
|
||
### 1-2. companies 테이블
|
||
```
|
||
companies
|
||
├── id (INT, PK) — 회사 고유 번호
|
||
├── name (VARCHAR(255), UNIQUE) — 회사명
|
||
├── business_registration_number (VARCHAR(20), UNIQUE, NULL) — 사업자등록번호
|
||
├── business_address (VARCHAR(255), NULL) — 사업장 주소
|
||
├── business_owner (VARCHAR(100), NULL) — 사업주 이름
|
||
├── business_status (VARCHAR(50), NULL) — 기업 상태
|
||
├── company_code (VARCHAR(20), NULL) — 회사 코드
|
||
├── phone_number (VARCHAR(20), NULL) — 회사 연락처
|
||
├── representative_name (VARCHAR(100), NULL) — 대표자명
|
||
├── master_user_id (INT, NULL) — 마스터 사용자 ID
|
||
├── status (ENUM) — 상태: ACTIVE, INACTIVE, SUSPENDED (기본값: ACTIVE)
|
||
├── subscription_status (ENUM) — 구독 상태: FREE, TRIAL, PAID, EXPIRED (기본값: FREE)
|
||
├── created_by (INT, FK → users.id, NULL) — 회사 생성자
|
||
├── created_at (TIMESTAMP) — 생성일
|
||
├── updated_at (TIMESTAMP) — 수정일
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 1-3. join_requests 테이블 (가입 신청 - 향후 사용)
|
||
```
|
||
join_requests
|
||
├── id (INT, PK) — 요청 고유 번호
|
||
├── user_id (INT, FK → users.id) — 신청한 사용자
|
||
├── company_id (INT, FK → companies.id) — 신청 대상 회사
|
||
├── requested_at (TIMESTAMP) — 신청 시간
|
||
├── status (ENUM) — 상태: PENDING, APPROVED, REJECTED (기본값: PENDING)
|
||
├── reviewed_by (INT, FK → users.id, NULL) — 검토한 관리자
|
||
├── reviewed_at (TIMESTAMP, NULL) — 검토 시간
|
||
├── review_comment (TEXT, NULL) — 검토 의견/거부 사유
|
||
└── UNIQUE KEY (user_id, company_id) — 중복 신청 방지
|
||
```
|
||
|
||
**동작 (향후 구현 예정):**
|
||
- 사용자 가입 신청 → status = PENDING 생성
|
||
- 관리자 승인 → status = APPROVED, users.status = ACTIVE, users.company_id 세팅
|
||
- 관리자 거부 → status = REJECTED, review_comment에 거부 사유 기록, 이메일 발송
|
||
|
||
### 1-5. email_otps 테이블 (이메일 인증)
|
||
```
|
||
email_otps
|
||
├── id (INT, PK) — OTP 고유 번호
|
||
├── email (VARCHAR(255)) — 이메일 주소
|
||
├── otp_code (VARCHAR(10)) — OTP 코드
|
||
├── purpose (VARCHAR(50)) — 목적: REGISTRATION, PASSWORD_RESET, EMAIL_VERIFICATION
|
||
├── expires_at (TIMESTAMP) — 만료 시간
|
||
├── verified_at (TIMESTAMP, NULL) — 인증 시간
|
||
├── created_at (TIMESTAMP) — 생성일
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 1-6. sessions 테이블 (세션 관리)
|
||
```
|
||
sessions
|
||
├── id (INT, PK) — 세션 고유 번호
|
||
├── user_id (INT, FK → users.id) — 사용자 ID
|
||
├── session_token (VARCHAR(255), UNIQUE) — 세션 토큰
|
||
├── expires_at (TIMESTAMP) — 세션 만료 시간
|
||
├── created_at (TIMESTAMP) — 생성일
|
||
├── last_accessed_at (TIMESTAMP, NULL) — 마지막 접근 시간
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 1-7. trusted_devices 테이블 (장치 신뢰 관리)
|
||
```
|
||
trusted_devices
|
||
├── id (INT, PK) — 장치 고유 번호
|
||
├── user_id (INT, FK → users.id) — 사용자 ID
|
||
├── device_fingerprint (VARCHAR(255)) — 장치 지문
|
||
├── device_name (VARCHAR(100), NULL) — 장치명
|
||
├── ip_address (VARCHAR(50)) — IP 주소
|
||
├── user_agent (TEXT) — 브라우저/OS 정보
|
||
├── trusted_until (TIMESTAMP, NULL) — 신뢰 만료 시간
|
||
├── created_at (TIMESTAMP) — 생성일
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 1-8. user_consents 테이블 (사용자 동의 관리)
|
||
```
|
||
user_consents
|
||
├── id (INT, PK) — 동의 고유 번호
|
||
├── user_id (INT, FK → users.id) — 사용자 ID
|
||
├── consent_type (VARCHAR(100)) — 동의 종류: TERMS, PRIVACY, MARKETING, etc
|
||
├── consented (TINYINT(1)) — 동의 여부 (1 = 동의, 0 = 미동의)
|
||
├── consent_version (VARCHAR(20)) — 약관 버전
|
||
├── created_at (TIMESTAMP) — 생성일
|
||
├── updated_at (TIMESTAMP) — 수정일
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 1-9. login_logs 테이블 (로그인 로그)
|
||
```
|
||
login_logs
|
||
├── id (INT, PK) — 로그 고유 번호
|
||
├── user_id (INT, FK → users.id, NULL) — 사용자 ID
|
||
├── email (VARCHAR(255), NULL) — 로그인 시도 이메일
|
||
├── status (VARCHAR(50)) — 상태: SUCCESS, FAILURE, LOCKED
|
||
├── ip_address (VARCHAR(50), NULL) — IP 주소
|
||
├── user_agent (TEXT, NULL) — 브라우저/OS 정보
|
||
├── reason (TEXT, NULL) — 실패 사유
|
||
├── created_at (TIMESTAMP) — 기록 시간
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 1-10. activity_logs 테이블 (활동 로그)
|
||
```
|
||
activity_logs
|
||
├── id (INT, PK) — 로그 고유 번호
|
||
├── user_id (INT, FK → users.id, NULL) — 사용자 ID
|
||
├── action (VARCHAR(100)) — 행동: LOGIN, LOGOUT, FILE_UPLOAD, PROJECT_CREATE, etc
|
||
├── entity_type (VARCHAR(100), NULL) — 대상 타입: project, file, route, etc
|
||
├── entity_id (VARCHAR(50), NULL) — 대상 ID
|
||
├── description (TEXT, NULL) — 행동 설명
|
||
├── ip_address (VARCHAR(50), NULL) — IP 주소
|
||
├── created_at (TIMESTAMP) — 기록 시간
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 1-11. audit_logs 테이블 (감시 로그 - 보안)
|
||
```
|
||
audit_logs
|
||
├── id (INT, PK) — 로그 고유 번호
|
||
├── user_id (INT, FK → users.id) — 행동 수행자
|
||
├── action (VARCHAR(100)) — 행동: CREATE, READ, UPDATE, DELETE, EXPORT, DOWNLOAD
|
||
├── entity_type (VARCHAR(100)) — 대상 타입: projects, routes, structures, outputs
|
||
├── entity_id (VARCHAR(50)) — 대상 ID
|
||
├── details (LONGTEXT, NULL) — 상세 정보 (JSON 형식)
|
||
├── ip_address (VARCHAR(50), NULL) — IP 주소
|
||
├── timestamp (TIMESTAMP) — 기록 시간
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 1-12. system_audit_logs 테이블 (시스템 감시)
|
||
```
|
||
system_audit_logs
|
||
├── id (INT, PK) — 로그 고유 번호
|
||
├── user_id (INT, FK → users.id) — 행동 수행자
|
||
├── action (VARCHAR(50)) — 행동: LOGIN, LOGOUT, USER_CREATE, COMPANY_CREATE, ROLE_CHANGE
|
||
├── resource_type (VARCHAR(50)) — 대상 타입: user, company, project, join_request
|
||
├── resource_id (INT, NULL) — 대상 ID
|
||
├── ip_address (VARCHAR(50), NULL) — IP 주소
|
||
├── user_agent (TEXT, NULL) — 브라우저/OS 정보
|
||
├── timestamp (TIMESTAMP) — 기록 시간
|
||
└── INDEX (timestamp, user_id) — 성능 최적화
|
||
```
|
||
|
||
### 1-13. system_resources 테이블 (시스템 리소스 모니터링)
|
||
```
|
||
system_resources
|
||
├── id (INT, PK) — 기록 고유 번호
|
||
├── cpu_usage_percent (FLOAT, NULL) — CPU 사용률 (%)
|
||
├── memory_usage_percent (FLOAT, NULL) — 메모리 사용률 (%)
|
||
├── disk_usage_percent (FLOAT, NULL) — 디스크 사용률 (%)
|
||
├── active_user_count (INT, NULL) — 활성 사용자 수
|
||
├── active_project_count (INT, NULL) — 활성 프로젝트 수
|
||
├── total_storage_mb (FLOAT, NULL) — 전체 저장소 사용량 (MB)
|
||
├── timestamp (TIMESTAMP) — 기록 시간
|
||
└── INDEX (timestamp) — 시계열 조회 최적화
|
||
```
|
||
|
||
**리소스 측정 방식:**
|
||
- **현재값 실측:** 요청 시점 실시간 측정 (DB 저장 없이 응답에 포함)
|
||
- **이력 조회:** `system_resources` 테이블에서 기본 30일치 시계열 조회
|
||
- **활성 지표:** active_user_count는 유효 세션 수, active_project_count는 미삭제 프로젝트 수 실시간 집계
|
||
|
||
### 1-14. support_requests 테이블 (고객 지원)
|
||
```
|
||
support_requests
|
||
├── id (INT, PK) — 요청 고유 번호
|
||
├── user_id (INT, FK → users.id) — 신청자
|
||
├── title (VARCHAR(255)) — 제목
|
||
├── description (LONGTEXT) — 설명
|
||
├── category (VARCHAR(100), NULL) — 카테고리
|
||
├── priority (VARCHAR(50), NULL) — 우선순위: LOW, MEDIUM, HIGH, URGENT
|
||
├── status (VARCHAR(50)) — 상태: OPEN, IN_PROGRESS, RESOLVED, CLOSED
|
||
├── assigned_to (INT, FK → users.id, NULL) — 담당자
|
||
├── resolution (LONGTEXT, NULL) — 해결 내용
|
||
├── created_at (TIMESTAMP) — 생성일
|
||
├── updated_at (TIMESTAMP) — 수정일
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 프로젝트 그룹
|
||
|
||
### 2-1. projects 테이블
|
||
```
|
||
projects
|
||
├── id (CHAR(36), PK) — 프로젝트 고유 ID (UUID)
|
||
├── user_id (INT, FK → users.id) — 프로젝트 소유자
|
||
├── company_id (INT, FK → companies.id) — 소속 회사
|
||
├── name (VARCHAR(255)) — 프로젝트명 (예: "2025년 산불진화임도")
|
||
├── region (VARCHAR(100), NULL) — 지역명 (예: "울진군 금강송면")
|
||
├── road_type (VARCHAR(100), NULL) — 임도 종류: 간선임도, 지선임도, 산불진화임도, 계류보전
|
||
├── project_year (INT, NULL) — 사업 연도 (예: 2025)
|
||
├── estimated_length_m (FLOAT, NULL) — 추정 연장 (미터)
|
||
├── memo (TEXT, NULL) — 비고/메모
|
||
├── status (VARCHAR(50), NULL) — 상태: NEW, FILE_UPLOADED, WF1_ANALYZING, WF1_COMPLETE, ... (기본값: NEW)
|
||
├── crs_epsg (INT, NULL) — 좌표계 (예: 5178 = 한국 표준, 기본값: 5178)
|
||
├── storage_path (VARCHAR(500), NULL) — 파일시스템 경로 (예: "storage/company/user/project_uuid")
|
||
├── created_at (TIMESTAMP) — 생성일
|
||
├── updated_at (TIMESTAMP) — 수정일
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 2-2. project_versions 테이블 (프로젝트 버전 관리)
|
||
```
|
||
project_versions
|
||
├── id (INT, PK) — 버전 고유 번호
|
||
├── project_id (CHAR(36), FK → projects.id) — 프로젝트 ID
|
||
├── version_num (INT) — 버전 번호 (1, 2, 3, ...)
|
||
├── status (VARCHAR(50), NULL) — 저장된 상태
|
||
├── data (LONGTEXT, NULL) — 설계 데이터 스냅샷 (JSON 형식)
|
||
├── snapshot_at (TIMESTAMP) — 스냅샷 시점
|
||
└── created_at (TIMESTAMP) — 생성일
|
||
```
|
||
|
||
### 2-3. project_automations 테이블 (프로젝트 자동화 규칙)
|
||
```
|
||
project_automations
|
||
├── id (INT, PK) — 자동화 고유 번호
|
||
├── project_id (CHAR(36), FK → projects.id) — 프로젝트 ID
|
||
├── name (VARCHAR(100)) — 자동화 규칙명
|
||
├── logic_type (VARCHAR(50)) — 로직 타입: trigger_on_stage_complete, auto_notify, etc
|
||
├── config_json (LONGTEXT) — 자동화 설정 (JSON)
|
||
├── status (ENUM) — 상태: DRAFT, ACTIVE, INACTIVE (기본값: DRAFT)
|
||
├── created_by (INT, FK → users.id) — 생성자
|
||
├── updated_by (INT, FK → users.id, NULL) — 마지막 수정자
|
||
├── last_executed_at (DATETIME, NULL) — 마지막 실행 시간
|
||
├── created_at (TIMESTAMP) — 생성일
|
||
├── updated_at (TIMESTAMP) — 수정일
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 파일 & 업로드 관리 그룹
|
||
|
||
### 3-1. upload_sessions 테이블 (파일 업로드 세션)
|
||
```
|
||
upload_sessions
|
||
├── id (INT, PK) — 업로드 세션 고유 번호
|
||
├── user_id (INT, FK → users.id) — 업로드자
|
||
├── project_id (CHAR(36), FK → projects.id) — 프로젝트 ID
|
||
├── session_id (VARCHAR(255), UNIQUE) — 세션 ID
|
||
├── file_name (VARCHAR(255)) — 파일명
|
||
├── file_size_mb (FLOAT) — 파일 크기
|
||
├── chunk_count (INT) — 청크 개수
|
||
├── uploaded_chunks (INT) — 업로드된 청크 수
|
||
├── status (VARCHAR(50)) — 상태: PENDING, IN_PROGRESS, COMPLETED, FAILED (기본값: PENDING)
|
||
├── started_at (TIMESTAMP) — 시작 시간
|
||
├── completed_at (TIMESTAMP, NULL) — 완료 시간
|
||
└── expires_at (TIMESTAMP) — 세션 만료 시간
|
||
```
|
||
|
||
### 3-2. upload_chunks 테이블 (파일 청크 관리)
|
||
```
|
||
upload_chunks
|
||
├── id (INT, PK) — 청크 고유 번호
|
||
├── upload_session_id (INT, FK → upload_sessions.id) — 업로드 세션 ID
|
||
├── chunk_index (INT) — 청크 순서
|
||
├── chunk_size_mb (FLOAT) — 청크 크기
|
||
├── checksum (VARCHAR(255), NULL) — 체크섬 (무결성 검사)
|
||
├── status (VARCHAR(50)) — 상태: PENDING, UPLOADED, VERIFIED (기본값: PENDING)
|
||
├── uploaded_at (TIMESTAMP, NULL) — 업로드 시간
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 3-3. input_files 테이블 (원본 입력 파일)
|
||
```
|
||
input_files
|
||
├── id (INT, PK) — 파일 고유 번호
|
||
├── project_id (CHAR(36), FK → projects.id) — 속한 프로젝트
|
||
├── file_type (VARCHAR(50), NULL) — 파일 종류: las, tif, tfw, prj, dxf, dwg, other
|
||
├── original_filename (VARCHAR(255)) — 원래 파일명 (예: "cloud_merged.las")
|
||
├── raw_file_path (VARCHAR(500)) — 원본 저장 경로 (예: "storage/.../raw/las/cloud_merged.las")
|
||
├── file_size_mb (FLOAT, NULL) — 파일 크기 (MB)
|
||
├── upload_by (INT, FK → users.id, NULL) — 업로드한 사용자
|
||
├── upload_at (TIMESTAMP) — 업로드 날짜
|
||
├── crs_epsg (INT, NULL) — 파일 좌표계 (예: 5178)
|
||
├── metadata (LONGTEXT, NULL) — 파일 메타데이터 (JSON)
|
||
├── status (VARCHAR(50), NULL) — 상태: UPLOADED, PROCESSED, ARCHIVED (기본값: UPLOADED)
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 지표면 분석 & 처리 그룹
|
||
|
||
### 4-1. processed_point_cloud 테이블 (변환된 포인트클라우드)
|
||
```
|
||
processed_point_cloud
|
||
├── id (INT, PK) — 변환 데이터 고유 번호
|
||
├── input_file_id (INT, FK → input_files.id) — 원본 LAS 파일 참조
|
||
├── project_id (CHAR(36), FK → projects.id) — 프로젝트 ID
|
||
├── process_type (VARCHAR(50), NULL) — 변환 방식: filtered, sampled, classified
|
||
├── processed_file_path (VARCHAR(500), NULL) — 변환된 파일 경로
|
||
├── converted_format (VARCHAR(50), NULL) — 변환 포맷: las, ply, laz 등
|
||
├── converted_file_path (VARCHAR(500), NULL) — 변환 포맷 저장 경로
|
||
├── point_count (INT, NULL) — 포인트 개수
|
||
├── min_z (FLOAT, NULL) — 최저 높이
|
||
├── max_z (FLOAT, NULL) — 최고 높이
|
||
├── mean_z (FLOAT, NULL) — 평균 높이
|
||
├── x_min (FLOAT, NULL) — 최소 경도
|
||
├── x_max (FLOAT, NULL) — 최대 경도
|
||
├── y_min (FLOAT, NULL) — 최소 위도
|
||
├── y_max (FLOAT, NULL) — 최대 위도
|
||
├── density_per_sqm (FLOAT, NULL) — 단위 면적당 포인트 밀도
|
||
├── classification_summary (LONGTEXT, NULL) — 분류 요약 (JSON)
|
||
├── processing_params (LONGTEXT, NULL) — 변환 파라미터 (JSON)
|
||
├── processed_at (TIMESTAMP) — 변환 완료 날짜
|
||
├── status (VARCHAR(50), NULL) — 상태: PROCESSING, COMPLETE, FAILED (기본값: PROCESSING)
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 4-2. surface_models 테이블 (지표면 모델)
|
||
```
|
||
surface_models
|
||
├── id (INT, PK) — 지표면 모델 고유 번호
|
||
├── project_id (CHAR(36), FK → projects.id) — 프로젝트 ID
|
||
├── model_type (VARCHAR(50), NULL) — 모델 종류: dem_grid, tin, mesh_triangulated, contour_lines
|
||
├── source_file_id (INT, FK → input_files.id, NULL) — 원본 LAS 파일
|
||
├── processed_cloud_id (INT, FK → processed_point_cloud.id, NULL) — 변환된 포인트클라우드 참조
|
||
├── status (VARCHAR(50), NULL) — 상태: PROCESSING, COMPLETE, FAILED (기본값: PROCESSING)
|
||
├── crs_epsg (INT, NULL) — 좌표계
|
||
├── resolution_m (FLOAT, NULL) — 해상도 (래스터인 경우)
|
||
├── model_file_path (VARCHAR(500), NULL) — 모델 저장 경로 (예: "storage/.../surface/dem.tif")
|
||
├── generation_params (LONGTEXT, NULL) — 생성 파라미터 (JSON)
|
||
├── created_at (TIMESTAMP) — 생성일
|
||
├── completed_at (TIMESTAMP, NULL) — 완료 시간
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 4-3. terrain_layers 테이블 (지형 레이어)
|
||
```
|
||
terrain_layers
|
||
├── id (INT, PK) — 레이어 고유 번호
|
||
├── surface_model_id (INT, FK → surface_models.id) — 지표면 모델 참조
|
||
├── layer_name (VARCHAR(100), NULL) — 레이어명 (예: "지표", "제1층", "제2층")
|
||
├── geometry_type (VARCHAR(50), NULL) — 기하 종류: POINTCLOUD, GRID, MESH, CONTOUR
|
||
├── layer_file_path (VARCHAR(500), NULL) — 레이어 파일 저장 경로
|
||
├── file_format (VARCHAR(50), NULL) — 파일 형식: geojson, geotiff, ply, obj 등
|
||
├── file_size_mb (FLOAT, NULL) — 파일 크기
|
||
├── statistics (LONGTEXT, NULL) — 통계 (JSON)
|
||
├── created_at (TIMESTAMP) — 생성일
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 경로 & 설계 그룹
|
||
|
||
### 5-1. routes 테이블 (경로 설계)
|
||
```
|
||
routes
|
||
├── id (INT, PK) — 경로 고유 번호
|
||
├── project_id (CHAR(36), FK → projects.id) — 프로젝트 ID
|
||
├── surface_model_id (INT, FK → surface_models.id, NULL) — 기반 지표면 모델
|
||
├── status (VARCHAR(50), NULL) — 상태: DRAFT, CONFIRMED, ARCHIVED (기본값: DRAFT)
|
||
├── start_chainage_m (FLOAT, NULL) — 시작점 측점 (m)
|
||
├── end_chainage_m (FLOAT, NULL) — 종료점 측점 (m)
|
||
├── total_length_m (FLOAT, NULL) — 총 연장 (m)
|
||
├── grade_percent (LONGTEXT, NULL) — 각 구간 종단 경사도 (JSON array)
|
||
├── constraints (LONGTEXT, NULL) — 설계 제약조건 (JSON)
|
||
├── algorithm_params (LONGTEXT, NULL) — 알고리즘 파라미터 (JSON)
|
||
├── route_data_path (VARCHAR(500), NULL) — 경로 데이터 저장 경로 (GeoJSON)
|
||
├── computed_at (TIMESTAMP, NULL) — 계산 완료 날짜
|
||
├── created_at (TIMESTAMP) — 생성일
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
**참고:** 경로 좌표는 JSON 형식으로 `route_data_path` 파일에 저장됨 (GeoJSON LineString)
|
||
|
||
### 5-2. route_points 테이블 (경로 샘플 포인트)
|
||
```
|
||
route_points
|
||
├── id (INT, PK) — 포인트 고유 번호
|
||
├── route_id (INT, FK → routes.id) — 경로 ID
|
||
├── chainage_m (FLOAT, NULL) — 측점 (m)
|
||
├── elevation_m (FLOAT, NULL) — 높이
|
||
├── slope_percent (FLOAT, NULL) — 경사도 (%)
|
||
├── sequence_num (INT, NULL) — 순서번호
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 5-3. route_statistics 테이블 (경로 통계)
|
||
```
|
||
route_statistics
|
||
├── id (INT, PK) — 통계 고유 번호
|
||
├── route_id (INT, FK → routes.id) — 경로 ID
|
||
├── min_slope (FLOAT, NULL) — 최소 경사도 (%)
|
||
├── max_slope (FLOAT, NULL) — 최대 경사도 (%)
|
||
├── mean_slope (FLOAT, NULL) — 평균 경사도 (%)
|
||
├── cut_volume_m3 (FLOAT, NULL) — 절토량 (m³)
|
||
├── fill_volume_m3 (FLOAT, NULL) — 성토량 (m³)
|
||
├── tree_cutting_volume (FLOAT, NULL) — 목재 절감 추정량
|
||
└── cost_score (FLOAT, NULL) — 알고리즘 점수
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 종단면 & 횡단면 그룹
|
||
|
||
### 6-1. longitudinal_sections 테이블 (종단면)
|
||
```
|
||
longitudinal_sections
|
||
├── id (INT, PK) — 종단면 고유 번호
|
||
├── project_id (CHAR(36), FK → projects.id) — 프로젝트 ID
|
||
├── route_id (INT, FK → routes.id) — 경로 ID
|
||
├── status (VARCHAR(50), NULL) — 상태: DRAFT, CONFIRMED (기본값: DRAFT)
|
||
├── data (LONGTEXT, NULL) — 상세 데이터 (JSON)
|
||
│ ├── chainages (측점 배열, m)
|
||
│ ├── elevations (지표 표고 배열)
|
||
│ ├── grades (경사도 배열, %)
|
||
│ └── design_elevations (설계 표고 배열)
|
||
├── longitudinal_file_path (VARCHAR(500), NULL) — 저장 경로 (JSON)
|
||
├── computed_at (TIMESTAMP, NULL) — 계산 날짜
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 6-2. cross_sections 테이블 (횡단면)
|
||
```
|
||
cross_sections
|
||
├── id (INT, PK) — 횡단면 고유 번호
|
||
├── project_id (CHAR(36), FK → projects.id) — 프로젝트 ID
|
||
├── route_id (INT, FK → routes.id) — 경로 ID
|
||
├── chainage_m (FLOAT, NULL) — 측점 (예: 0, 20, 40, ... m)
|
||
├── sequence_num (INT, NULL) — 횡단면 순번 (1, 2, ...)
|
||
├── status (VARCHAR(50), NULL) — 상태: DRAFT, CONFIRMED (기본값: DRAFT)
|
||
├── data (LONGTEXT, NULL) — 상세 데이터 (JSON)
|
||
│ ├── left_slope (좌측 사면 경사, %)
|
||
│ ├── right_slope (우측 사면 경사, %)
|
||
│ ├── width_m (노폭)
|
||
│ ├── cut_volume_m3 (절토량)
|
||
│ ├── fill_volume_m3 (성토량)
|
||
│ ├── structures (이 단면 내 구조물 ID 배열)
|
||
│ └── notes (메모)
|
||
├── cross_section_file_path (VARCHAR(500), NULL) — 저장 경로 (JSON)
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 구조물 & 수량 그룹
|
||
|
||
### 7-1. structures 테이블 (구조물)
|
||
```
|
||
structures
|
||
├── id (INT, PK) — 구조물 고유 번호
|
||
├── project_id (CHAR(36), FK → projects.id) — 프로젝트 ID
|
||
├── cross_section_id (INT, FK → cross_sections.id, NULL) — 횡단면 ID
|
||
├── structure_type (VARCHAR(100), NULL) — 종류: 낙석방지책, 돌붙임, 계간수로, 낙차공 등
|
||
├── chainage_m (FLOAT, NULL) — 측점 (m)
|
||
├── location (VARCHAR(50), NULL) — 위치: LEFT, CENTER, RIGHT
|
||
├── length_m (FLOAT, NULL) — 길이
|
||
├── width_m (FLOAT, NULL) — 폭
|
||
├── height_m (FLOAT, NULL) — 높이
|
||
├── material (VARCHAR(100), NULL) — 재료: 강재, 콘크리트, 목재, 돌 등
|
||
├── quantity (INT, NULL) — 개수
|
||
├── unit_price (FLOAT, NULL) — 단가
|
||
├── design_notes (LONGTEXT, NULL) — 설계 노트 (JSON)
|
||
├── structure_data_path (VARCHAR(500), NULL) — 구조물 데이터 저장 경로
|
||
├── last_modified_by (INT, FK → users.id, NULL) — 마지막 수정자
|
||
├── created_at (TIMESTAMP) — 생성일
|
||
├── updated_at (TIMESTAMP) — 수정일
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 7-2. quantity_items 테이블 (수량 항목)
|
||
```
|
||
quantity_items
|
||
├── id (INT, PK) — 수량 항목 고유 번호
|
||
├── project_id (CHAR(36), FK → projects.id) — 프로젝트 ID
|
||
├── category (VARCHAR(100), NULL) — 대분류: 토공, 구조물, 포장, 배수, 녹화, 안전시설
|
||
├── item_name (VARCHAR(255), NULL) — 항목명 (예: "절토 일반", "낙석방지책 설치")
|
||
├── unit (VARCHAR(50), NULL) — 단위: m3, 개, m, m2
|
||
├── quantity_design (FLOAT, NULL) — 설계 수량
|
||
├── quantity_actual (FLOAT, NULL) — 실제 수량 (사용자 수정 가능)
|
||
├── unit_price (FLOAT, NULL) — 단가
|
||
├── total_price (FLOAT, NULL) — 소계 (quantity_actual × unit_price)
|
||
├── standard_reference (VARCHAR(255), NULL) — 기준 참고 자료
|
||
├── quantity_data_path (VARCHAR(500), NULL) — 수량 데이터 저장 경로
|
||
├── data (LONGTEXT, NULL) — 계산 과정 메모 (JSON)
|
||
│ ├── formula (계산식)
|
||
│ ├── source (데이터 출처)
|
||
│ └── ...
|
||
├── computed_at (TIMESTAMP, NULL) — 계산 날짜
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 산출물 & 문서 그룹
|
||
|
||
### 8-1. outputs 테이블 (최종 산출물 세트)
|
||
```
|
||
outputs
|
||
├── id (INT, PK) — 산출물 세트 고유 번호
|
||
├── project_id (CHAR(36), FK → projects.id) — 프로젝트 ID
|
||
├── output_type (VARCHAR(100), NULL) — 종류: estimation_excel, drawing_dxf, report_pdf, all_bundle
|
||
├── status (VARCHAR(50), NULL) — 상태: GENERATING, COMPLETE, FAILED (기본값: GENERATING)
|
||
├── generated_by (INT, FK → users.id, NULL) — 생성자
|
||
├── generated_at (TIMESTAMP) — 생성 날짜
|
||
├── version (INT, NULL) — 버전 (기본값: 1)
|
||
├── outputs_directory_path (VARCHAR(500), NULL) — 산출물 저장 폴더 (예: "storage/.../outputs/v1/")
|
||
├── metadata (LONGTEXT, NULL) — 메타데이터 (JSON)
|
||
│ ├── template_used (사용한 양식)
|
||
│ ├── company_name (회사명)
|
||
│ ├── project_name (프로젝트명)
|
||
│ ├── total_cost (총 비용)
|
||
│ └── generation_time_sec (생성 소요 시간)
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
### 8-2. output_files 테이블 (최종 산출 파일)
|
||
```
|
||
output_files
|
||
├── id (INT, PK) — 파일 고유 번호
|
||
├── output_id (INT, FK → outputs.id) — 산출물 ID
|
||
├── file_type (VARCHAR(50), NULL) — 파일 형식: xlsx, pdf, dxf, dwg, json, zip
|
||
├── original_filename (VARCHAR(255), NULL) — 파일명 (예: "견적서_v1.xlsx")
|
||
├── output_file_path (VARCHAR(500), NULL) — 저장 경로 (예: "storage/.../outputs/v1/견적서.xlsx")
|
||
├── file_size_mb (FLOAT, NULL) — 파일 크기
|
||
├── download_count (INT, NULL) — 다운로드 횟수 (기본값: 0)
|
||
├── created_at (TIMESTAMP) — 생성 날짜
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 변경 이력 & 감시 그룹
|
||
|
||
### 9-1. change_logs 테이블 (설계 변경 기록)
|
||
```
|
||
change_logs
|
||
├── id (INT, PK) — 변경 고유 번호
|
||
├── project_id (CHAR(36), FK → projects.id) — 프로젝트 ID
|
||
├── changed_by (INT, FK → users.id) — 변경자
|
||
├── changed_at (TIMESTAMP) — 변경 날짜
|
||
├── entity_type (VARCHAR(100)) — 대상 타입: routes, cross_sections, structures, quantity_items
|
||
├── entity_id (VARCHAR(50)) — 대상 ID
|
||
├── old_value (LONGTEXT, NULL) — 변경 전 값 (JSON)
|
||
├── new_value (LONGTEXT, NULL) — 변경 후 값 (JSON)
|
||
├── reason (TEXT, NULL) — 변경 사유
|
||
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
|
||
```
|
||
|
||
---
|
||
|
||
## 10. 테이블 관계도
|
||
|
||
```
|
||
users (사용자)
|
||
├── id (PK)
|
||
├── email
|
||
├── password_hash
|
||
├── role (SYSTEM_ADMIN | ADMIN | USER)
|
||
├── is_master
|
||
├── status (생명주기)
|
||
├── company_id (FK) ──→ companies.id
|
||
├── last_login
|
||
└── auth_expires_at
|
||
|
||
companies (회사)
|
||
├── id (PK)
|
||
├── name
|
||
├── status (ACTIVE | INACTIVE | SUSPENDED)
|
||
├── subscription_status (FREE | TRIAL | PAID | EXPIRED)
|
||
├── created_by (FK) ──→ users.id
|
||
└── created_at
|
||
|
||
projects (프로젝트)
|
||
├── id (UUID, PK)
|
||
├── user_id (FK) ──→ users.id
|
||
├── company_id (FK) ──→ companies.id
|
||
├── name
|
||
├── status
|
||
├── crs_epsg
|
||
├── storage_path
|
||
└── created_at
|
||
↓
|
||
├─→ input_files (입력 파일)
|
||
│ └─→ processed_point_cloud (변환된 포인트클라우드)
|
||
│ └─→ surface_models (지표면 모델)
|
||
│ └─→ terrain_layers (지형 레이어)
|
||
├─→ routes (경로)
|
||
│ ├─→ route_points (경로 포인트)
|
||
│ ├─→ route_statistics (경로 통계)
|
||
│ ├─→ longitudinal_sections (종단면)
|
||
│ └─→ cross_sections (횡단면)
|
||
│ └─→ structures (구조물)
|
||
├─→ quantity_items (수량 항목)
|
||
├─→ outputs (산출물)
|
||
│ └─→ output_files (산출물 파일)
|
||
├─→ project_versions (프로젝트 버전)
|
||
├─→ project_automations (프로젝트 자동화)
|
||
├─→ audit_logs (감시 로그)
|
||
└─→ change_logs (변경 로그)
|
||
```
|
||
|
||
---
|
||
|
||
## 11. 파일시스템 경로와 DB 링크 (워크플로우 기반 구조)
|
||
|
||
### 파일시스템 구조 (6단계 워크플로우)
|
||
|
||
```
|
||
storage/
|
||
├── {company_slug}/
|
||
│ └── {user_slug}/
|
||
│ └── {project_id}/ ← projects.storage_path
|
||
│ │
|
||
│ ├── B03_FileInput/ ← WF0: 파일 입력
|
||
│ │ └── input/ (원본 입력 파일들)
|
||
│ │ ├── las/
|
||
│ │ │ └── cloud_merged.las ← input_files.raw_file_path
|
||
│ │ ├── tif/
|
||
│ │ ├── tfw/
|
||
│ │ ├── prj/
|
||
│ │ └── dxf/
|
||
│ │
|
||
│ ├── B04_wf1_Surface/ ← WF1: 지표면 분석
|
||
│ │ ├── processed/ (변환된 포인트클라우드)
|
||
│ │ │ ├── cloud_filtered.las ← processed_point_cloud.processed_file_path
|
||
│ │ │ └── cloud_sampled.ply ← processed_point_cloud.converted_file_path
|
||
│ │ └── models/ (지표면 모델 & 레이어)
|
||
│ │ ├── dem_2m.tif ← surface_models.model_file_path
|
||
│ │ ├── tin_mesh.obj
|
||
│ │ ├── layer_ground.geojson ← terrain_layers.layer_file_path
|
||
│ │ └── layer_1.geojson
|
||
│ │
|
||
│ ├── B05_wf2_Route/ ← WF2: 경로 설계
|
||
│ │ └── route/
|
||
│ │ ├── route_main.geojson ← routes.route_data_path
|
||
│ │ ├── route_points.json
|
||
│ │ └── route_statistics.json
|
||
│ │
|
||
│ ├── B06_wf3_ProfileCross/ ← WF3: 종횡단 생성
|
||
│ │ ├── longitudinal/
|
||
│ │ │ └── longitudinal.json ← longitudinal_sections.longitudinal_file_path
|
||
│ │ └── cross_sections/
|
||
│ │ ├── cross_0000m.json ← cross_sections.cross_section_file_path
|
||
│ │ ├── cross_0020m.json
|
||
│ │ └── ...
|
||
│ │
|
||
│ ├── B07_wf4_DesignDetail/ ← WF4: 상세 설계
|
||
│ │ └── structures/
|
||
│ │ ├── struct_0020m_001.json ← structures.structure_data_path
|
||
│ │ └── ...
|
||
│ │
|
||
│ ├── B08_wf5_Quantity/ ← WF5: 수량 산출
|
||
│ │ ├── quantities/
|
||
│ │ │ └── items.json ← quantity_items.quantity_data_path
|
||
│ │ └── ...
|
||
│ │
|
||
│ └── B09_wf6_Estimation/ ← WF6: 견적·문서
|
||
│ ├── v1/ ← outputs.outputs_directory_path
|
||
│ │ ├── 견적서.xlsx ← output_files.output_file_path
|
||
│ │ ├── 설계도.dxf
|
||
│ │ └── ...
|
||
│ └── v2/
|
||
│ └── ...
|
||
```
|
||
|
||
**핵심 개념:**
|
||
- **B03~B09**: 워크플로우 6단계와 1:1 대응
|
||
- **각 단계의 입력/출력**: 명확히 분리
|
||
- **버전 관리**: B09 단계에서만 (v1, v2, ...)
|
||
|
||
---
|
||
|
||
## 12. 핵심 쿼리 예제
|
||
|
||
### 예제 1: 사용자의 모든 프로젝트 조회
|
||
```sql
|
||
SELECT p.id, p.name, p.status, p.created_at
|
||
FROM projects p
|
||
WHERE p.user_id = :user_id AND p.deleted_at IS NULL
|
||
ORDER BY p.created_at DESC;
|
||
```
|
||
|
||
### 예제 2: 프로젝트의 최신 경로 조회
|
||
```sql
|
||
SELECT r.id, r.total_length_m, r.status
|
||
FROM routes r
|
||
WHERE r.project_id = :project_id AND r.deleted_at IS NULL
|
||
ORDER BY r.computed_at DESC
|
||
LIMIT 1;
|
||
```
|
||
|
||
### 예제 3: 경로의 모든 횡단면 조회
|
||
```sql
|
||
SELECT cs.id, cs.chainage_m, cs.data
|
||
FROM cross_sections cs
|
||
WHERE cs.route_id = :route_id AND cs.deleted_at IS NULL
|
||
ORDER BY cs.chainage_m;
|
||
```
|
||
|
||
### 예제 4: 프로젝트 총 비용 계산
|
||
```sql
|
||
SELECT
|
||
SUM(quantity_actual * unit_price) as total_cost,
|
||
category
|
||
FROM quantity_items
|
||
WHERE project_id = :project_id AND deleted_at IS NULL
|
||
GROUP BY category;
|
||
```
|
||
|
||
### 예제 5: 사용자의 감시 로그 (최근 30일)
|
||
```sql
|
||
SELECT action, entity_type, timestamp
|
||
FROM audit_logs
|
||
WHERE user_id = :user_id AND timestamp > DATE_SUB(NOW(), INTERVAL 30 DAY)
|
||
ORDER BY timestamp DESC;
|
||
```
|
||
|
||
---
|
||
|
||
## 13. 데이터 타입 가이드
|
||
|
||
| 타입 | 설명 | 예제 |
|
||
|------|------|------|
|
||
| INT | 정수 | 1, 2, 100 |
|
||
| FLOAT | 실수 | 1.5, 2.25, 100.75 |
|
||
| VARCHAR(n) | 문자열 (고정 길이) | "project_name" |
|
||
| TEXT / LONGTEXT | 문자열 (가변, 대용량) | 설명문, JSON 데이터 |
|
||
| TIMESTAMP | 날짜 + 시간 (자동 업데이트) | 2025-07-05 15:30:00 |
|
||
| DATETIME | 날짜 + 시간 (수동) | 2025-07-05 15:30:00 |
|
||
| TINYINT(1) | 참/거짓 (0 또는 1) | 0, 1 |
|
||
| ENUM | 열거형 | ACTIVE, INACTIVE |
|
||
| CHAR(36) | UUID (고정 36자) | 550e8400-e29b-41d4-a716-446655440000 |
|
||
|
||
---
|
||
|
||
## 14. 총 테이블 수 및 분류
|
||
|
||
### 34개 테이블 분류
|
||
|
||
**1. 사용자/회사/조직 (14개)**
|
||
- users, companies, join_requests (향후), email_otps, sessions, trusted_devices, user_consents
|
||
- login_logs, activity_logs, audit_logs, system_audit_logs, system_resources, support_requests
|
||
|
||
**2. 프로젝트 관리 (3개)**
|
||
- projects, project_versions, project_automations
|
||
|
||
**3. 파일/업로드 (3개)**
|
||
- upload_sessions, upload_chunks, input_files
|
||
|
||
**4. 지표면 분석 (3개)**
|
||
- processed_point_cloud, surface_models, terrain_layers
|
||
|
||
**5. 경로 설계 (3개)**
|
||
- routes, route_points, route_statistics
|
||
|
||
**6. 종횡단 설계 (2개)**
|
||
- longitudinal_sections, cross_sections
|
||
|
||
**7. 구조물/수량 (2개)**
|
||
- structures, quantity_items
|
||
|
||
**8. 산출물 (2개)**
|
||
- outputs, output_files
|
||
|
||
**9. 변경/이력 (1개)**
|
||
- change_logs
|
||
|
||
---
|
||
|
||
## 15. DB vs 파일시스템 역할 분리
|
||
|
||
| 저장 위치 | 내용 | 예시 |
|
||
|----------|------|------|
|
||
| **DB (MariaDB)** | 메타데이터 + 경로정보 + 상태 | 파일명, 크기, 좌표계, 상태, 저장경로, 통계 |
|
||
| **파일시스템** | 실제 파일 데이터 | LAS, PLY, TIF, DXF, Excel, PDF, JSON 파일들 |
|
||
|
||
**설계 원칙:**
|
||
- DB에는 "어디에 무엇이 있는가"의 메타정보만 저장
|
||
- 실제 대용량 파일은 파일시스템에 저장
|
||
- 각 워크플로우 단계의 입력/출력 경로를 DB에 기록
|
||
- JSON 형식으로 좌표 및 복잡한 데이터 구조를 저장
|
||
|
||
---
|
||
|
||
## 16. 마이그레이션 상태
|
||
|
||
### ✅ 완료됨
|
||
- [x] 33개 테이블 생성 (MariaDB)
|
||
- [x] 관계(FK) 설정
|
||
- [x] Soft delete (deleted_at) 지원
|
||
- [x] 인덱스 추가 (주요 조회 경로)
|
||
|
||
### 📋 진행 중
|
||
- [ ] API 라우터에서 파일 경로 저장 로직 추가
|
||
- B03 파일 업로드 → `input_files.raw_file_path` 저장
|
||
- B04 WF1 실행 → `surface_models.model_file_path`, `terrain_layers.layer_file_path` 저장
|
||
- B05 WF2 실행 → `routes.route_data_path` 저장
|
||
- B06 WF3 실행 → `longitudinal_sections.longitudinal_file_path`, `cross_sections.cross_section_file_path` 저장
|
||
- B07 WF4 실행 → `structures.structure_data_path` 저장
|
||
- B08 WF5 실행 → `quantity_items.quantity_data_path` 저장
|
||
- B09 WF6 실행 → `outputs.outputs_directory_path`, `output_files.output_file_path` 저장
|
||
|
||
### 🔮 향후 계획
|
||
- [ ] Python Pydantic 모델 정의 (ORM 대신)
|
||
- [ ] API 엔드포인트 보안 감시 강화
|
||
- [ ] 실시간 알림 시스템 (WebSocket)
|