Files
Aislo/.agent/db_design_proposal.md
T
2026-07-05 18:10:16 +09:00

683 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DB 구조 설계 제안서
**작성일:** 2026-07-05
**대상:** 임도 설계 및 견적 자동화 웹앱 (프로젝트 기반 멀티테넌트)
**기술 스택:** PostgreSQL v17 + PostGIS v3.4 / asyncpg / FastAPI
---
## 1. 설계 원칙
### 1.1 멀티테넌트 아키텍처
- **프로젝트 단위 데이터 격리:** 각 사용자의 프로젝트는 별도의 논리적 네임스페이스로 관리
- **하이브리드 저장소:**
- **DB:** 사용자, 프로젝트 메타데이터, 설계 결과(경로, 단면, 수량) 저장
- **파일시스템:** 원본 입력파일(LAS/TIF/DXF), 중간 산출물(mesh/타일), 최종 산출물(DXF/Excel/PDF) 저장
- **스토리지 경로:** `storage/{회사명}/{사용자명}/{프로젝트ID}/` 하위로 자동 생성
### 1.2 데이터 계층 분리
```
[입력] LAS, TFW, PRJ, TIF → [분석] DEM/Mesh/필터 → [설계] 경로, 단면 → [산출] DXF, Excel
(storage) (DB + storage) (DB + storage) (storage)
```
### 1.3 공간 데이터 전용 설계
- PostGIS 기하학적 타입 활용 (geometry, geography)
- 좌표계 일관성: EPSG 코드 저장 후 필요 시 변환
- 벡터 데이터(경로, 경계선) ↔ 래스터 샘플링 간 추적 가능하도록 설계
---
## 2. 핵심 Entity 및 관계
### 2.1 사용자 및 인증 (users, user_sessions)
```
users
├── id (PK)
├── email (UNIQUE)
├── password_hash
├── name
├── company (FK → companies)
├── role (admin, user, guest)
├── created_at, updated_at
companies
├── id (PK)
├── name (UNIQUE)
├── created_by (FK → users)
├── members [] (users, M:N via user_company_roles)
├── created_at, updated_at
user_sessions (토큰 저장 — optional, Redis 권장)
├── id (PK)
├── user_id (FK → users)
├── token
├── expires_at
```
**설계 의도:**
- 회사 단위로 프로젝트, 사용자를 그룹화
- 회사별 권한 분리 (협업 확장성)
- 향후 팀 공유 프로젝트 기능 추가 가능
### 2.2 프로젝트 메타데이터 (projects, project_versions)
```
projects
├── id (PK, UUID)
├── user_id (FK → users) — 소유자
├── company_id (FK → companies)
├── name
├── region (지역명: 예 "울진군 금강송면")
├── road_type (간선임도, 지선임도, 산불진화임도, 계류보전)
├── project_year (사업 연도, INT)
├── estimated_length_m (추정 연장)
├── memo
├── status (NEW, ANALYZING, WF1_COMPLETE, WF2_COMPLETE, ... , CONFIRMED, DONE)
├── crs_epsg (좌표계, INT — 예: 5178)
├── bbox (GEOMETRY(Polygon)) — 전체 프로젝트 범위
├── created_at, updated_at
├── deleted_at (soft delete)
project_versions (버전 관리 — 선택)
├── id (PK)
├── project_id (FK → projects)
├── version_num
├── snapshot_at (스냅샷 시점)
├── status (저장된 상태)
└── data (JSONB — 설계 데이터 스냅샷)
```
**설계 의도:**
- 프로젝트 생명주기 추적 (NEW → WF1 → WF2 → ... → CONFIRMED)
- 좌표계 메타데이터 저장 (변환 오류 방지)
- 버전 관리는 (선택) — 회원가입 초기엔 미필수, 협업 필요 시 추가
### 2.3 입력 파일 관리 (input_files)
```
input_files
├── id (PK)
├── project_id (FK → projects)
├── file_type (las, tif, tfw, prj, dxf, dwg, other)
├── original_filename
├── stored_path (storage/{...}/raw/{file_type}/{filename})
├── file_size_mb
├── upload_by (FK → users)
├── upload_at
├── crs_epsg (파일이 가진 좌표계)
├── metadata (JSONB — 해상도, 데이터 범위, 포인트 수 등)
└── status (UPLOADED, PROCESSED, ARCHIVED)
point_cloud_metadata
├── id (PK)
├── input_file_id (FK → input_files, LAS만)
├── point_count (INT)
├── min_z, max_z, mean_z (높이)
├── x_min, x_max, y_min, y_max (공간 범위)
├── densitiy_per_sqm (밀도)
└── classification_summary (JSONB — {ground: N, vegetation: N, building: N, ...})
```
**설계 의도:**
- 입력 파일의 생명주기 추적
- 포인트클라우드 메타데이터로 전처리 여부 판단
- storage 폴더와 DB 간 일관성 확보
### 2.4 지표면 모델 및 분석 결과 (surface_models, terrain_layers)
```
surface_models (WF1 출력)
├── id (PK)
├── project_id (FK → projects)
├── model_type (dem_grid, tin, mesh_triangulated, contour_lines)
├── source_file_id (FK → input_files, LAS)
├── status (PROCESSING, COMPLETE, FAILED)
├── crs_epsg
├── resolution_m (래스터 경우, NULL if 벡터)
├── stored_path (storage/{...}/processed/surface/)
├── bounds (GEOMETRY(Polygon)) — 모델 범위
├── metadata (JSONB — 필터링 파라미터, 생성 시각 등)
├── created_at, completed_at
terrain_layers
├── id (PK)
├── surface_model_id (FK → surface_models)
├── layer_name (지표, 제1층, 제2층 등 15종)
├── geometry_type (POINTCLOUD, GRID, MESH, CONTOUR)
├── stored_path (GeoJSON / GeoTIFF / LAS 등)
└── statistics (JSONB — min_z, max_z, mean_slope, etc.)
```
**설계 의도:**
- WF1 분석 결과(DEM, TIN, mesh 등)의 출처와 메타데이터 추적
- 층(layer)별로 렌더링 최적화 가능
- 재분석 시 기존 결과와 비교 가능
### 2.5 경로 설계 (routes, route_points, route_statistics)
```
routes (WF2 출력)
├── id (PK)
├── project_id (FK → projects)
├── status (DRAFT, CONFIRMED, ARCHIVED)
├── start_point (GEOMETRY(Point)) — 지형 위 실제 좌표
├── end_point (GEOMETRY(Point))
├── start_chainage_m, end_chainage_m (측점)
├── total_length_m
├── grade_percent[] (JSONB array — 각 구간 종단 경사도)
├── constraints (JSONB — max_grade, min_radius, avoidance_zones)
├── algorithm_params (JSONB — 비용함수 가중치, 계산 시간 등)
├── computed_at
└── geometry (GEOMETRY(LineString, Z) — 3D 경로)
route_points (경로 포인트 샘플, 웹 렌더링용)
├── id (PK)
├── route_id (FK → routes)
├── chainage_m (측점)
├── geometry (GEOMETRY(Point, Z))
├── elevation_m, slope_percent
└── sequence_num
route_statistics
├── id (PK)
├── route_id (FK → routes)
├── min_slope, max_slope, mean_slope
├── cut_volume_m3, fill_volume_m3
├── tree_cutting_volume (목재 추정)
└── cost_score (알고리즘 점수)
```
**설계 의도:**
- 경로의 기하학적 정보(3D LineString) + 측점 기반 추적
- 재계산 시 이전 결과와 버전 비교 가능
- 사용자가 수정한 경로도 저장 가능 (draft ↔ confirmed)
### 2.6 종단면 및 횡단면 (longitudinal_sections, cross_sections)
```
longitudinal_sections
├── id (PK)
├── project_id (FK → projects)
├── route_id (FK → routes)
├── computed_at
├── geometry (GEOMETRY(LineString, Z)) — 경로 따라가며 샘플링한 표고
├── data (JSONB)
│ ├── chainages [0, 20, 40, ...]
│ ├── elevations [100.5, 102.3, ...]
│ ├── grades [2.5, 1.8, ...]
│ └── design_elevations [100.0, 102.0, ...] (설계 기준)
└── stored_path (storage/{...}/sections/longitudinal.json)
cross_sections
├── id (PK)
├── project_id (FK → projects)
├── route_id (FK → routes)
├── chainage_m (측점)
├── sequence_num (1st, 2nd, ...)
├── geometry (GEOMETRY(LineString, Z)) — 지형 횡단면
├── geometry_design (GEOMETRY(LineString, Z)) — 설계 횡단면
├── data (JSONB)
│ ├── left_slope, right_slope
│ ├── width_m
│ ├── cut_volume_m3, fill_volume_m3
│ ├── structures [] (낙석방지책, 돌붙임 등)
│ └── notes
└── stored_path (storage/{...}/sections/cross_{chainage}.json)
```
**설계 의도:**
- 종단면: 경로 따라 세로 방향 표고 추적
- 횡단면: 20m 간격 가로 방향 단면 + 설계 기준
- JSONB로 유연한 추가 메타데이터 저장
### 2.7 설계 및 구조물 (design_details, structures, quantity_items)
```
structures (WF4 구조물 라이브러리)
├── id (PK)
├── project_id (FK → projects)
├── cross_section_id (FK → cross_sections)
├── structure_type (낙석방지책, 돌붙임, 계간수로, 낙차공, etc.)
├── chainage_m, location (LEFT, CENTER, RIGHT)
├── length_m, width_m, height_m (기본 치수)
├── material (강재, 콘크리트, 목재, 돌 등)
├── quantity (개수)
├── unit_price (단가)
├── geometry (GEOMETRY(Polygon)) — 3D 구조물 배치
├── design_notes (JSONB)
└── last_modified_by (FK → users)
quantity_items (WF5 수량 산출)
├── id (PK)
├── project_id (FK → projects)
├── category (토공, 구조물, 포장, 배수, 녹화, 안전시설)
├── item_name (예: "절토 일반", "낙석방지책 설치" 등)
├── unit (m3, 개, m, m2)
├── quantity_design (설계 수량)
├── quantity_actual (실제 수량, 사용자 수정 가능)
├── unit_price (단가)
├── total_price (수량 × 단가)
├── standard_reference (설계 기준 참고 자료)
├── computed_at
└── data (JSONB — 계산 과정 메모)
```
**설계 의도:**
- 구조물은 횡단면별로 배치
- 수량 항목은 자동 계산 + 사용자 수정 가능
- 단가와 기준 추적으로 감시/감독 기록 남김
### 2.8 산출물 관리 (outputs, output_files)
```
outputs
├── id (PK)
├── project_id (FK → projects)
├── output_type (estimation_excel, drawing_dxf, report_pdf, all_bundle)
├── status (GENERATING, COMPLETE, FAILED)
├── generated_by (FK → users)
├── generated_at
├── version (프로젝트 확정 단계에서 자동 증가)
├── metadata (JSONB)
│ ├── template_used
│ ├── company_name, project_name
│ ├── total_cost
│ └── generation_time_sec
└── created_at
output_files
├── id (PK)
├── output_id (FK → outputs)
├── file_type (xlsx, pdf, dxf, dwg, json, zip)
├── original_filename
├── stored_path (storage/{...}/outputs/{output_type}/{filename})
├── file_size_mb
├── created_at
└── download_count (통계)
```
**설계 의도:**
- 산출물 생성 이력 추적
- 같은 프로젝트 다중 버전 관리 (재산출 가능)
- 다운로드 통계로 사용 현황 파악
### 2.9 변경 이력 및 감시 (audit_logs, change_logs)
```
audit_logs (접근 제어 감시)
├── id (PK)
├── user_id (FK → users)
├── action (CREATE, READ, UPDATE, DELETE, EXPORT)
├── entity_type (projects, routes, structures, etc.)
├── entity_id
├── timestamp
├── ip_address
└── details (JSONB)
change_logs (설계 변경 기록)
├── id (PK)
├── project_id (FK → projects)
├── changed_by (FK → users)
├── changed_at
├── entity_type (routes, cross_sections, structures)
├── entity_id
├── old_value (JSONB)
├── new_value (JSONB)
└── reason (사용자 입력 선택)
```
**설계 의도:**
- 보안 감시 (audit_logs)
- 설계 변경 추적 및 롤백 가능성 (change_logs)
---
## 3. 파일시스템 디렉토리 구조
```
storage/
├── {company_slug}/
│ ├── {user_slug}/
│ │ └── {project_uuid}/
│ │ ├── raw/ # 입력 원본
│ │ │ ├── las/
│ │ │ ├── tif/
│ │ │ ├── prj/
│ │ │ ├── tfw/
│ │ │ └── dwg_dxf/
│ │ ├── processed/ # 중간 산출물 (DB에 메타데이터만 저장)
│ │ │ ├── surface/
│ │ │ │ ├── dem_grid_2m.tif
│ │ │ │ ├── tin_mesh.obj / .gltf
│ │ │ │ └── contours.geojson
│ │ │ ├── points/
│ │ │ │ ├── ground_filtered.las
│ │ │ │ └── ground_sampled_10m.ply
│ │ │ └── tiles/
│ │ │ └── (3D Tiles / MVT 렌더링 최적화)
│ │ ├── sections/ # 단면 데이터 (JSON)
│ │ │ ├── longitudinal.json
│ │ │ ├── cross_0000m.json
│ │ │ ├── cross_0020m.json
│ │ │ └── ...
│ │ └── outputs/ # 최종 산출물
│ │ ├── estimation/
│ │ │ ├── v1_2025-07-05.xlsx
│ │ │ ├── v2_2025-07-10.xlsx
│ │ │ └── ...
│ │ ├── drawings/
│ │ │ ├── v1_base.dxf
│ │ │ ├── v1_detailed.dxf
│ │ │ └── ...
│ │ ├── reports/
│ │ │ ├── v1_report.pdf
│ │ │ └── ...
│ │ └── bundles/
│ │ ├── v1_20250705.zip # 완전 묶음
│ │ └── ...
│ └── {project_uuid2}/
│ └── ...
├── temp/ # 임시 처리 파일 (일정 기간 후 삭제)
└── cache/ # 렌더링 캐시 (3D Tiles, 이미지 타일)
```
---
## 4. 주요 쿼리 패턴 및 인덱스 전략
### 4.1 자주 사용할 조회 패턴
```sql
-- 사용자의 전체 프로젝트 목록 (상태별 필터)
SELECT * FROM projects
WHERE user_id = :user_id AND deleted_at IS NULL
ORDER BY created_at DESC;
-- 프로젝트의 최신 경로
SELECT * FROM routes
WHERE project_id = :project_id AND status IN ('CONFIRMED', 'DRAFT')
ORDER BY computed_at DESC LIMIT 1;
-- 경로의 횡단면 일괄 조회 (웹 렌더링)
SELECT chainage_m, geometry FROM cross_sections
WHERE route_id = :route_id AND status = 'CONFIRMED'
ORDER BY chainage_m;
-- 수량 항목별 비용 집계
SELECT category, SUM(total_price) as subtotal
FROM quantity_items
WHERE project_id = :project_id
GROUP BY category;
-- 공간 범위 쿼리 (지도 렌더링)
SELECT id, geometry FROM routes
WHERE project_id = :project_id
AND ST_Intersects(geometry, ST_GeomFromText(:bbox_wkt));
```
### 4.2 권장 인덱스
```sql
-- 사용자별 프로젝트 조회
CREATE INDEX idx_projects_user_deleted
ON projects(user_id, deleted_at);
-- 프로젝트별 경로 최신순
CREATE INDEX idx_routes_project_computed
ON routes(project_id, computed_at DESC);
-- 경로의 횡단면 측점 순
CREATE INDEX idx_cross_sections_route_chainage
ON cross_sections(route_id, chainage_m);
-- PostGIS 공간 인덱스
CREATE INDEX idx_routes_geometry
ON routes USING GIST(geometry);
CREATE INDEX idx_cross_sections_geometry
ON cross_sections USING GIST(geometry);
-- 수량 항목 범위 쿼리
CREATE INDEX idx_quantity_project_category
ON quantity_items(project_id, category);
-- 감시 로그 시간순
CREATE INDEX idx_audit_logs_timestamp
ON audit_logs(user_id, timestamp DESC);
```
---
## 5. API 라우터 구조 (FastAPI)
### 5.1 라우터 계층화
```
routers/
├── auth.py # 인증 (로그인, 회원가입, 토큰 갱신)
├── users.py # 사용자 프로필
├── companies.py # 회사 관리
├── projects.py # 프로젝트 CRUD + 상태 관리
├── files.py # 파일 업로드/다운로드
├── workflows/
│ ├── wf1_surface.py # WF1: 지표면 분석 (LAS → DEM/mesh)
│ ├── wf2_route.py # WF2: 경로 설계
│ ├── wf3_sections.py # WF3: 종횡단 생성
│ ├── wf4_design.py # WF4: 상세 설계 (구조물)
│ ├── wf5_quantity.py # WF5: 수량 산출
│ └── wf6_outputs.py # WF6: 산출물 생성 (Excel/PDF/DXF)
├── analysis.py # 분석 결과 조회 (읽기 전용)
└── admin.py # 관리자 기능 (감시 로그 등)
```
### 5.2 주요 엔드포인트 예시
```
POST /api/auth/register 사용자 가입
POST /api/auth/login 로그인
GET /api/users/me 현재 사용자 정보
PATCH /api/users/{user_id} 프로필 수정
POST /api/projects 프로젝트 생성
GET /api/projects 사용자 프로젝트 목록
GET /api/projects/{proj_id} 프로젝트 상세
PATCH /api/projects/{proj_id} 프로젝트 수정 (이름, 메모 등)
POST /api/projects/{proj_id}/files/upload 파일 업로드
GET /api/projects/{proj_id}/files 파일 목록
POST /api/projects/{proj_id}/wf1/analyze WF1 시작
GET /api/projects/{proj_id}/wf1/status WF1 진행률
GET /api/projects/{proj_id}/wf1/surface-models 생성된 표면
POST /api/projects/{proj_id}/wf2/calculate-route WF2 경로 계산
GET /api/projects/{proj_id}/wf2/routes 경로 목록
PATCH /api/projects/{proj_id}/wf2/routes/{route_id} 경로 수정
GET /api/projects/{proj_id}/wf3/cross-sections 횡단면 목록
GET /api/projects/{proj_id}/wf3/sections/{ch} 특정 측점 상세
POST /api/projects/{proj_id}/wf4/structures 구조물 추가
PATCH /api/projects/{proj_id}/wf4/structures/{id} 구조물 수정
GET /api/projects/{proj_id}/wf5/quantities 수량 항목
PATCH /api/projects/{proj_id}/wf5/quantities/{id} 수량 수정
POST /api/projects/{proj_id}/wf6/generate-output 산출물 생성
GET /api/projects/{proj_id}/outputs 산출물 목록
GET /api/projects/{proj_id}/outputs/{id}/download 다운로드
POST /api/projects/{proj_id}/confirm 프로젝트 확정
```
---
## 6. 비동기 작업 및 백그라운드 처리 (Celery / asyncio)
### 6.1 장시간 작업 큐
```
작업 목록:
1. WF1 분석 (LAS 필터링 → 지형 변환) — 수십 분
2. WF2 경로 계산 (최적화 알고리즘) — 수 분
3. WF6 산출물 생성 (DXF/Excel/PDF) — 수 분
구현 방식:
- Celery + Redis (프로덕션)
또는
- FastAPI BackgroundTasks + asyncio (초기 단계)
상태 추적:
- jobs 테이블에 task_id, status, progress, error_msg 저장
- WebSocket 또는 polling으로 진행률 클라이언트에 전송
```
---
## 7. 마이그레이션 및 테이블 생성 전략
### 7.1 alembic 기반 버전 관리
```
alembic/
├── versions/
│ ├── 001_initial_schema.py (users, companies, projects)
│ ├── 002_spatial_tables.py (routes, cross_sections, PostGIS)
│ ├── 003_audit_and_logs.py (audit_logs, change_logs)
│ └── ...
└── env.py
```
### 7.2 초기 마이그레이션 단계
1. 사용자 / 회사 / 프로젝트 (로그인 후 필수)
2. 입력 파일 메타데이터 (파일 업로드 후 필수)
3. 경로 / 단면 / 구조물 (워크플로우 실행 후 필수)
4. 감시 로그 (선택, 나중에 추가 가능)
---
## 8. 데이터 일관성 및 검증 규칙
### 8.1 데이터 무결성 제약
```sql
-- 외래키 제약
ALTER TABLE projects
ADD CONSTRAINT fk_projects_user FOREIGN KEY (user_id)
REFERENCES users(id) ON DELETE RESTRICT;
-- Unique 제약
ALTER TABLE users ADD CONSTRAINT uq_users_email UNIQUE(email);
ALTER TABLE companies ADD CONSTRAINT uq_companies_name UNIQUE(name);
-- Check 제약 (상태 머신)
ALTER TABLE projects ADD CONSTRAINT check_project_status
CHECK (status IN ('NEW', 'ANALYZING', 'WF1_COMPLETE', ..., 'DONE'));
```
### 8.2 좌표계 검증
```python
# Python 유효성 검사
from pyproj import CRS
def validate_crs(epsg_code: int):
try:
crs = CRS.from_epsg(epsg_code)
return True
except:
raise ValueError(f"Invalid EPSG code: {epsg_code}")
# 좌표 변환 시 예외 처리
def transform_coordinates(src_epsg, tgt_epsg, coords):
transformer = Transformer.from_crs(f"EPSG:{src_epsg}", f"EPSG:{tgt_epsg}")
return transformer.transform(coords.x, coords.y)
```
### 8.3 입력 파일 검증
```python
# 파일 타입 검증
ALLOWED_EXTENSIONS = {
'las': laspy.read,
'tif': rasterio.open,
'prj': lambda f: f.read().decode('utf-8'),
'dxf': ezdxf.readfile,
}
# 파일 크기 제한 (config에서 읽기)
MAX_FILE_SIZE_MB = 500
def validate_upload(file, file_type):
if file.size > MAX_FILE_SIZE_MB * 1024 * 1024:
raise ValueError(f"File too large: {file.size / 1024 / 1024:.1f}MB")
```
---
## 9. 초기 vs 장기 구현 로드맵
### Phase 1 (초기, 회원가입 MVP — 2-3주)
**필수 테이블:** users, companies, projects, input_files, audit_logs
**API:** 인증, 프로젝트 CRUD, 파일 업로드
**워크플로우:** 파일 입력(B03)까지만 DB 연동
### Phase 2 (WF1-WF3, 분석 — 4-6주)
**추가 테이블:** surface_models, routes, cross_sections, change_logs
**API:** WF1 분석 시작, 경로 계산, 단면 조회
**백그라운드:** Celery 작업 큐 도입
### Phase 3 (WF4-WF6, 산출물 — 6-8주)
**추가 테이블:** structures, quantity_items, outputs, output_files
**API:** 구조물 추가, 수량 산출, 산출물 생성
**기능:** Excel/PDF/DXF 자동 생성
### Phase 4 (협업 및 감시, 선택)
**추가 기능:** 팀 공유 프로젝트, 권한 세분화, 비용 청구
**감시:** audit_logs 기반 컴플라이언스 리포트
---
## 10. 보안 및 성능 고려사항
### 10.1 보안
- **인증:** JWT 토큰 (httponly cookie)
- **권한:** user_id 기반 row-level security (RLS) + 애플리케이션 검증
- **파일 접근:** 서명된 URL 또는 토큰 검증
- **감시:** audit_logs에 모든 수정 기록
- **SQL injection 방지:** ORM (SQLAlchemy) + 파라미터화된 쿼리
### 10.2 성능 최적화
- **쿼리:** 인덱스 전략 (위 4.2 참고)
- **캐싱:** Redis에 자주 조회하는 메타데이터 캐시 (설정값, 사용자 권한 등)
- **파일 저장소:** 클라우드 스토리지 옵션 (AWS S3, GCS) 고려
- **3D 렌더링:** LOD/타일링으로 대용량 지형 최적화
- **비동기:** 장시간 작업은 백그라운드 큐에서 처리
### 10.3 백업 및 복구
- **DB 백업:** 일 1회 자동 백업 (point-in-time recovery)
- **파일 백업:** 중요 산출물은 별도 저장소에 복제
- **버전 관리:** change_logs로 프로젝트 상태 롤백 가능
---
## 11. 검토 항목 및 다음 단계
### 체크리스트
- [ ] ERD(Entity-Relationship Diagram) 작성 및 리뷰
- [ ] 좌표계 변환 로직 검증 (EPSG 코드)
- [ ] 파일 저장소 정책 확정 (로컬 vs 클라우드)
- [ ] 권한 모델 세밀화 (팀 공유 필요성 판단)
- [ ] 비용 청구 로직 정의 (필요 시)
- [ ] 성능 테스트 (대용량 파일, 대량 사용자)
- [ ] 법적 데이터 보관 기간 확인
### 다음 작업
1. **ERD 다이어그램 생성** — dbdiagram.io 또는 draw.io
2. **alembic 마이그레이션 초안 작성** — Phase 1 테이블부터
3. **FastAPI 모델(Pydantic) 정의** — 입력/출력 스키마
4. **API 문서(OpenAPI)** — Swagger 자동 생성
5. **테스트 DB 구성** — 로컬 PostgreSQL + 샘플 데이터
---
## 12. 참고: 구현 언어 & 도구
| 영역 | 도구 | 용도 |
|------|------|------|
| **ORM** | SQLAlchemy 2.0 + asyncpg | async DB 쿼리 |
| **마이그레이션** | Alembic | DB 버전 관리 |
| **검증** | Pydantic v2 | 입력 데이터 검증 |
| **공간 쿼리** | GeoAlchemy2 + PostGIS | 기하학 연산 |
| **비동기** | Celery + Redis 또는 FastAPI BackgroundTasks | 장시간 작업 |
| **파일 처리** | laspy, rasterio, geopandas, ezdxf | 공간 데이터 |
| **문서 생성** | openpyxl, reportlab, ezdxf | Excel/PDF/DXF 출력 |
---
**이 제안서는 기초 설계 단계입니다. 다음 리뷰에서 변경, 추가, 삭제 사항을 정리한 후 ERD 및 마이그레이션 코드로 구체화하겠습니다.**