This commit is contained in:
2026-07-05 18:10:16 +09:00
parent 0057983671
commit 23d907265a
22 changed files with 2207 additions and 315 deletions
+682
View File
@@ -0,0 +1,682 @@
# 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 및 마이그레이션 코드로 구체화하겠습니다.**