This commit is contained in:
2026-07-05 21:27:23 +09:00
parent 23d907265a
commit 3abc2edba6
83 changed files with 10351 additions and 1217 deletions
+132 -28
View File
@@ -1,44 +1,148 @@
# 에이전트 행동 지침 및 라우팅 가이드 (agent.md)
# 프로젝트 행동지침 및 기술 스택 명세 (agent.md)
## 1. 개발 환경 및 기술 스택 기준 (Technical Stack Baseline)
에이전트는 코드를 작성, 수정 또는 검증할 때 환경 기준과 호환되도록 구현
## 프로젝트 & DB 정보
### A. 백엔드 및 연산 엔진 (Python)
### 📌 프로그램명
**Aislo (아이슬로)**
- **의미**: AI (인공지능) + Slotti (핀란드어: 임도/산림 도로)
- **콘셉트**: 산림의 미래를 열어가는 인공지능 경로 설계 솔루션
---
## 1. 기술 환경 및 기본 스택 (Technical Stack Baseline)
프로젝트의 코드를 작성, 테스트 또는 검증할 환경 명확화 및 호환 기준 제시.
### A. 백엔드 프레임워크 (Python)
* **Runtime:** Python v3.12 or v3.13.7
* **Framework:** FastAPI / Pydantic
* **Geometry/GIS Engine:** Trimesh, Whitebox, Geopandas, Shapely, Rasterio, Laspy
### B. 데이터베이스 (PostgreSQL)
* **DBMS:** PostgreSQL v17
* **Spatial Extension:** PostGIS v3.4 (공간 쿼리 및 연산 활용)
* **DB Driver:** 비동기 연동을 위한 `asyncpg` 기준 구동
### B. 데이터베이스 (MariaDB)
* **DBMS:** MariaDB v10.6+
* **Character Set:** utf8mb4 (한글 완벽 지원)
* **Collation:** utf8mb4_unicode_ci
* **DB Driver (비동기):** `aiomysql` (순수 Python, Windows 호환. asyncmy는 Cython 빌드 필요로 미채택)
* **쿼리 방식:** Raw SQL (ORM 사용 금지)
* **공간 데이터:** JSON 기반 저장 (MariaDB는 PostGIS 미지원)
### C. 프론트엔드 및 인터페이스 (TypeScript & WebCAD)
### C. 프론트엔드 & WebCAD (TypeScript & WebGL)
* **Language/Runtime:** TypeScript / Node.js
* **인터페이스:** HTML5 Canvas 및 WebGL 기반 WebCAD 시스템 연동 표준 준수
* **Rendering:** HTML5 Canvas 및 WebGL 기반 WebCAD 시스템 구현
### D. 데이터 및 파일 인코딩 표준
* **인코딩:** 텍스트 파일은 UTF-8 표준을 강제 적용
### D. 인코딩 및 다국어 표준
* **인코딩:** 텍스트 파일은 UTF-8 표준으로 통일
---
## 2. 컨텍스트 라우팅 규칙 (Context Routing)
명령 수신 시 답변 작성 및 코드 수정 전 다음 단계에 따라 정의서(MD) 필수 로드.
## 2. 문맥 라우팅 및 문서 구조 (Context Routing)
코드 작성 및 기술 검증 시 문서 우선순위에 따른 계층적 레퍼런스 필수 읽기.
* **1단계 (필수):** `.agent/structure.md` 즉시 로드 및 현재 디렉토리 구조 분석.
* **2단계 (조건 분기):** 프롬프트 성격에 따라 필요한 파일 선별 로드.
* **조건 A (UI, 제어, 컴포넌트, WebCAD):** `.agent/frontend.md` 필수 추가 로드.
* **조건 B (연산 수식, 설계 기준, DB, PostGIS):** `.agent/backend.md` 필수 추가 로드.
* **1단계 (필수):** `.agent/structure.md` 읽기 후 신규 폴더 생성 기준 분석.
* **2단계 (선택 읽기):** 프로젝트 구현에 필요한 세부 기술 명세 읽기.
* **그룹 A (UI, 스타일, 컴포넌트, WebCAD):** `.agent/frontend.md` 필수 추가 읽기.
* **그룹 B (알고리즘, 저장, DB, MariaDB 값 제어):** `.agent/backend.md` 필수 추가 읽기.
* **그룹 C (DB 구조 변경):** DB 구조 변경 시 `.agent/db_schema.md` 선택적 읽기.
---
## 3. 코드 작성 제약 조건 (Constraints)
* **구조 준수:** `.agent/structure.md` 트리 구조 강제 준수.
* **700줄 제한:** 단일 파일 코드 성/수정 700줄 이상 시 리팩토링 필수 제안. 유저 승인 후 분할 `structure.md` 동시 업데이트.
* **영역 격리:** 지정된 작업 영역 외 디렉토리 파일 접근·수정 시 유저 사전 승인 필수.
* **정합성 검증:** 이전 맥락 단절을 고려하여 현재 소스 코드와 분할 정의서(`frontend.md`, `backend.md`) 간 정합성 최우선 검토.
* **정보 요구 필수:** 연관 파일의 실제 내부 코드가 프롬프트에 제공되지 않은, 임의 추측 코딩을 절대 금지하며 유저에게 파일 내용 제시를 먼저 요구할 것.
* **스타일 통합 (자율 포매팅):** 에이전트는 코드 작성 완료한 후, 반드시 프로젝트 터미널에서 다음 명령어를 직접 실행하여 서식을 정렬한 뒤 최종 저장해야 한다. (유저에게 서식 교정 작업을 전가하지 말 것)
* *Python 파일 수정 시:* `ruff format [파일명]``ruff check --fix [파일명]` 실행
* *TypeScript/CSS 파일 수정 시:* `npx prettier --write [파일명]` 실행
* **의존성 준수:** 외부 라이브러리 활용 시 프로젝트에 명시된 호환 버전을 반드시 준수하며, 존재하지 않거나 단종된 함수를 유추하여 작성하는 것을 금지함.
## 3. 코드 작성 기본 제약 (Constraints)
* **구조 준수:** `.agent/structure.md` 트리 구조 엄격 준수.
* **700줄 제한:** 단일 파일 코드 성/수정 700줄 이상이 될 경우 사전 공지. 기능별 파일 분할 `structure.md` 갱신 필수.
* **안전 보관:** 백엔드 작업 시 보안 저장소 경로 명시 및 임의 삭제 방지 필수.
* **경로 활용:** 단계별 페이지 기반 폴더 구조(B03~B09)와 DB 경로 열의 기준은 `.agent/db_schema_simple.md`의 「파일시스템 경로와 DB 링크」를 따른다.
* **일관성 검증:** 설계 단계에서부터 DB 설계, 파일, API 라우팅이 모두 동일한 워크플로우 기준으로 통일되어야 한다. (불일치 발생 시 설계 단계에서 재논의 필수)
* **코드 포맷팅 (자동화 도구):** 프로젝트의 코드 작성 완료되는 시, 반드시 프로젝트 루트에서 각 언어별 포맷터를 재실행하여 스타일을 통일해야 한다.
* *Python 포맷팅 명령어:* `ruff format [파일명]``ruff check --fix [파일명]` 실행
* *TypeScript/CSS 포맷팅 명령어:* `npx prettier --write [파일명]` 실행
* **외부 라이브러리 활용:** 외부 라이브러리 활용 시 프로젝트에 맞춰 호환 라이브러리 선정하고, 불필요한 함수는 작성하지 않도록 최소화.
---
## 4. 데이터베이스 상세 명세 (Database Specification)
### 4.1 스키마 기본 정보
- **DB 명:** `aislo_db`
- **DBMS:** MariaDB v10.6+
- **인코딩:** utf8mb4_unicode_ci
- **테이블 수:** 18개
- **드라이버:** aiomysql (비동기)
### 4.2 파일 경로 추적 (Path Tracking)
**모든 중간 산출물의 파일 경로를 DB에 기록:**
- `input_files.raw_file_path` — 원본 입력 파일
- `processed_point_cloud.converted_file_path` — 변환된 포인트클라우드
- `surface_models.model_file_path` — 지표면 모델
- `routes.route_data_path` — 경로 데이터
- `longitudinal_sections.longitudinal_file_path` — 종단면
- `cross_sections.cross_section_file_path` — 횡단면
- `structures.structure_data_path` — 구조물 배치
- `quantity_items.quantity_data_path` — 수량 항목
- `outputs.outputs_directory_path` — 산출물 폴더
- `output_files.output_file_path` — 개별 산출 파일
### 4.3 저장소 구조 (Workflow-based Folder Structure)
```
storage/{company_slug}/{user_slug}/{project_id}/
├── B03_FileInput/input/ (WF0: 원본 입력)
├── B04_wf1_Surface/processed/ (WF1: 변환된 포인트클라우드 & 모델)
├── B05_wf2_Route/route/ (WF2: 경로 설계)
├── B06_wf3_ProfileCross/ (WF3: 종단면 & 횡단면)
├── B07_wf4_DesignDetail/structures/ (WF4: 구조물 배치)
├── B08_wf5_Quantity/quantities/ (WF5: 수량 산출)
└── B09_wf6_Estimation/v1,v2,.../ (WF6: 최종 산출물)
```
### 4.4 공간 데이터 처리 (MariaDB 특성)
- **지하형 기하 데이터:** GEOMETRY 타입 미지원 → JSON으로 저장
- **좌표 저장 예:** `{"type": "Point", "coordinates": [127.5, 37.5]}`
- **경로 저장 예:** `{"type": "LineString", "coordinates": [[127.5, 37.5], [127.6, 37.6]]}`
- **애플리케이션 처리:** Python의 Shapely, Geopandas에서 JSON 파싱 후 기하 연산
---
## 5. 워크플로우 6단계 (6-Stage Workflow)
```
WF0: B03_FileInput (파일 입력)
↓ (LAS, TIF, TFW, PRJ, DXF 업로드)
WF1: B04_wf1_Surface (지표면 분석)
↓ (DEM, TIN, 포인트클라우드 변환)
WF2: B05_wf2_Route (경로 설계)
↓ (최적 경로 계산)
WF3: B06_wf3_ProfileCross (종횡단 생성)
↓ (종단면, 횡단면 생성)
WF4: B07_wf4_DesignDetail (상세 설계)
↓ (구조물 배치)
WF5: B08_wf5_Quantity (수량 산출)
↓ (수량 항목 계산)
WF6: B09_wf6_Estimation (견적·문서)
↓ (Excel, PDF, DXF 생성)
```
---
## 6. 주요 문서 참고 순서
1. **구조 설계:** `.agent/structure.md` 읽기
2. **프론트 구현:** `.agent/frontend.md` 읽기
3. **백엔드 구현:** `.agent/backend.md` 읽기
4. **마이그레이션:** `.agent/migration_plan.md` 읽기
5. **DB 스키마:** `.agent/db_schema_simple.md` 읽기
6. **프로젝트 정보:** `.agent/project_info.md` 읽기
---
## 7. 요약
| 항목 | 값 |
|------|-----|
| **프로그램명** | Aislo (아이슬로) |
| **DB 명** | aislo_db |
| **DBMS** | MariaDB v10.6+ |
| **인코딩** | utf8mb4_unicode_ci |
| **Backend** | Python 3.12+ / FastAPI |
| **Frontend** | TypeScript / Node.js |
| **드라이버** | aiomysql |
| **워크플로우** | 6단계 (WF0~WF6) |
| **테이블 수** | 18개 |
| **폴더 구조** | 워크플로우 기반 (B03~B09) |
+22 -10
View File
@@ -8,9 +8,12 @@
---
## 2. DB 및 PostGIS (Database)
* **비동기 통신:** `async/await``asyncpg` 드라이버 사용 강제.
* **공간 연산:** ORM 사용 금지. `ST_Volume`, `ST_Distance` 등 PostGIS 기반 **Raw SQL** 작성 원칙.
## 2. DB 및 MariaDB (Database)
* **비동기 통신:** `async/await``aiomysql` 드라이버 사용 강제. (asyncmy는 Cython 빌드 필요로 Windows 환경 미채택)
* **쿼리 방식:** ORM 사용 금지. **Raw SQL** 작성 원칙. 파라미터 바인딩은 `%s` 플레이스홀더 사용.
* **커넥션/트랜잭션:** 풀에서 `pool.acquire()`로 커넥션 확보 후 `connection.cursor()`로 커서 생성. 다건 쓰기는 `connection.begin()``commit()` / 예외 시 `rollback()`.
* **자동 증가 ID:** `INSERT``cursor.lastrowid`로 조회 (PostgreSQL의 `RETURNING id` 미지원).
* **공간 데이터:** GEOMETRY 타입 미지원. 좌표, 다각형, 경로 등은 JSON 형식으로 저장 후 애플리케이션에서 처리.
---
@@ -18,6 +21,14 @@
* **하드코딩 금지:** 제어 변수/파라미터 하드코딩 금지. `config/config_system.py`에서 `import` 필수.
* **물리 파일 격리:** 포인트 클라우드, 분석 중간 파일, 메쉬, 임시 커서는 DB 저장 금지.
* **저장 경로:** `storage/[고객사명]/[사용자명]/[프로젝트ID]/`에 물리 파일 저장 후 DB에는 경로만 기록.
* **단계별 루트 강제:** 프로젝트 저장소 내부는 실제 워크플로우 페이지명과 동일한 `B03_FileInput/` ~ `B09_wf6_Estimation/` 폴더로 분리한다. `raw/`, `processed/`, `computed/`를 프로젝트 루트의 공용 폴더로 만들지 않는다.
* **경로 정의 우선순위:** 단계별 세부 폴더와 DB 경로 열의 기준은 `.agent/db_schema_simple.md`의 「파일시스템 경로와 DB 링크」를 따른다.
* **경로 생성 책임:** 페이지별 백엔드는 자기 단계 폴더만 생성·수정한다. 다른 단계의 산출물을 직접 삭제하거나 덮어쓰지 않고 stale 상태를 통해 재계산 필요성을 전파한다.
* **DB 저장 형식:** DB에는 프로젝트 루트 기준 상대 경로를 기록하고, 실제 파일 접근 시 설정의 저장소 루트와 안전하게 결합한다. 사용자 입력 경로를 직접 결합하거나 절대 경로를 DB에 저장하지 않는다.
* **MariaDB 특성:**
- 공간 기하 데이터(좌표, 폴리곤, 경로 등)는 GEOMETRY 타입 미지원 → JSON 또는 TEXT로 저장
- 예: `{"type": "Point", "coordinates": [127.5, 37.5]}` (GeoJSON 형식)
- 애플리케이션(Python)에서 JSON 파싱 후 기하 연산 처리
---
@@ -33,11 +44,12 @@
### 5.1 다중 브라우저 동시 작업 원칙
* **브라우저 독립성:** 같은 계정/프로젝트를 여러 브라우저에서 동시 접근 가능. 각 브라우저는 서버 데이터 기반 독립 작동.
* **클라이언트 상태 격리:** `localStorage`는 브라우저별 격리(도메인 단위 공유). 공유 데이터는 항상 서버 영구저장소 우선.
* **영구저장소 설계:** 단계별 계산 결과 `storage/[고객사]/[사용자]/[프로젝트ID]/result_[단계].json` 형태로 물리 저장.
* `result_scan.json` — 포인트 클라우드 필터링 결과
* `result_surface.json` — 지표면 모델 선택
* `result_route.json` — 경로 설계 결과
* `result_section.json` — 종횡단 생성 결과
* **영구저장소 설계:** 계산 결과 `.agent/db_schema_simple.md`에 정의된 페이지별 단계 폴더에 저장한다.
* `B03_FileInput/` — 원본 입력 및 파일 메타데이터
* `B04_wf1_Surface/` — 변환 포인트클라우드, 지표면 모델 및 분석 결과
* `B05_wf2_Route/` — 경로, 경로점 및 설계 파라미터
* `B06_wf3_ProfileCross/` — 종단·횡단 결과 및 인덱스
* **워크플로우 상태:** 여러 단계가 공유하는 `workflow.json`의 실제 위치는 저장소 경로 유틸에서 단일하게 정의하며, 원자적 쓰기를 적용한다. 단계별 결과 파일의 위치를 프로젝트 루트의 `result_*.json` 이름으로 추정하지 않는다.
### 5.2 Stale 상태 감지 및 전파
* **workflow.json 구조:**
@@ -56,7 +68,7 @@
```python
def _patch_workflow_stale(project_id: str, stale_from: str | None) -> None:
"""workflow.json의 stale_from 필드만 원자적 업데이트"""
wf_path = storage_path / project_id / "workflow.json"
wf_path = get_project_workflow_path(project_id)
# 기존 상태 유지하며 stale_from만 변경
```
@@ -88,4 +100,4 @@
"changed_by": "other_browser"
}
```
* **마이그레이션 시기:** 사용자 수 증가 또는 실시간성 요구 시 적용
* **마이그레이션 시기:** 사용자 수 증가 또는 실시간성 요구 시 적용
-682
View File
@@ -1,682 +0,0 @@
# 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 및 마이그레이션 코드로 구체화하겠습니다.**
@@ -9,8 +9,10 @@ users
├── email (TEXT, UNIQUE) — 로그인 이메일
├── password_hash (TEXT) — 비밀번호 암호화 저장
├── name (TEXT) — 사용자 이름
├── position (TEXT) — 직급 (예: 과장, 대리, 사원)
├── department (TEXT) — 부서명 (예: 설계팀, 영업팀)
├── phone (TEXT) — 연락처 (예: 010-1234-5678)
├── company_id (INT, FK → companies.id) — 소속 회사
├── role (TEXT) — 역할: admin, user, guest
├── created_at (TIMESTAMP) — 가입일
├── updated_at (TIMESTAMP) — 수정일
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
@@ -21,9 +23,14 @@ users
companies
├── id (INT, PK) — 회사 고유 번호
├── name (TEXT, UNIQUE) — 회사명
├── business_registration_number (TEXT, UNIQUE) — 사업자등록번호
├── business_address (TEXT) — 사업장 주소
├── business_owner (TEXT) — 사업주 이름
├── business_status (TEXT) — 기업 상태 (예: 활동중, 폐업, 휴업)
├── created_by (INT, FK → users.id) — 회사 생성자
├── created_at (TIMESTAMP) — 생성일
── updated_at (TIMESTAMP) — 수정일
── updated_at (TIMESTAMP) — 수정일
└── deleted_at (TIMESTAMP, NULL) — 삭제일 (soft delete)
```
---
@@ -67,14 +74,14 @@ project_versions
## 3. 파일 & 입력 데이터 그룹
### 3-1. input_files 테이블
### 3-1. input_files 테이블 (원본 입력 파일 — 영구저장소)
```
input_files
├── id (INT, PK) — 파일 고유 번호
├── project_id (UUID, FK → projects.id) — 속한 프로젝트
├── file_type (TEXT) — 파일 종류: las, tif, tfw, prj, dxf, dwg, other
├── original_filename (TEXT) — 원래 파일명 (예: "cloud_merged.las")
├── stored_path (TEXT) — 저장 경로 (예: "storage/.../raw/las/cloud_merged.las")
├── raw_file_path (TEXT) — 원본 저장 경로 (예: "storage/.../raw/las/cloud_merged.las")
├── file_size_mb (FLOAT) — 파일 크기 (MB)
├── upload_by (INT, FK → users.id) — 업로드한 사용자
├── upload_at (TIMESTAMP) — 업로드 날짜
@@ -86,11 +93,16 @@ input_files
└── status (TEXT) — 상태: UPLOADED, PROCESSED, ARCHIVED
```
### 3-2. point_cloud_metadata 테이블
### 3-2. processed_point_cloud 테이블 (변환된 포인트클라우드 데이터 — 영구저장소)
```
point_cloud_metadata
├── id (INT, PK)
├── input_file_id (INT, FK → input_files.id) — LAS 파일 참조
processed_point_cloud
├── id (INT, PK) — 변환 데이터 고유 번호
├── input_file_id (INT, FK → input_files.id) — 원본 LAS 파일 참조
├── project_id (UUID, FK → projects.id)
├── process_type (TEXT) — 변환 방식: filtered, sampled, classified 등
├── processed_file_path (TEXT) — 변환된 파일 경로 (예: "storage/.../processed/las/ground_filtered.las")
├── converted_format (TEXT) — 변환 포맷: las, ply, laz 등 (컴퓨터가 빠르게 읽기 위해)
├── converted_file_path (TEXT) — 변환 포맷 저장 경로 (예: "storage/.../processed/ply/cloud_sampled_10m.ply")
├── point_count (INT) — 포인트 개수
├── min_z (FLOAT) — 최저 높이
├── max_z (FLOAT) — 최고 높이
@@ -100,59 +112,70 @@ point_cloud_metadata
├── y_min (FLOAT) — 최소 위도
├── y_max (FLOAT) — 최대 위도
├── density_per_sqm (FLOAT) — 단위 면적당 포인트 밀도
── classification_summary (JSONB) — 분류 요약
├── ground (지표면 포인트)
├── vegetation (식생)
├── building (건물)
└── ...
── classification_summary (JSONB) — 분류 요약
├── ground (지표면 포인트)
├── vegetation (식생)
├── building (건물)
└── ...
├── processing_params (JSONB) — 변환 파라미터
├── processed_at (TIMESTAMP) — 변환 완료 날짜
└── status (TEXT) — 상태: PROCESSING, COMPLETE, FAILED
```
---
## 4. 지표면 & 분석 결과 그룹
### 4-1. surface_models 테이블 (WF1 출력)
### 4-1. surface_models 테이블 (WF1 출력 — 지표면 분석 결과)
```
surface_models
├── id (INT, PK) — 지표면 모델 고유 번호
├── project_id (UUID, FK → projects.id)
├── model_type (TEXT) — 모델 종류: dem_grid, tin, mesh_triangulated, contour_lines
├── source_file_id (INT, FK → input_files.id) — 원본 LAS 파일
├── processed_cloud_id (INT, FK → processed_point_cloud.id) — 변환된 포인트클라우드 참조
├── status (TEXT) — 상태: PROCESSING, COMPLETE, FAILED
├── crs_epsg (INT) — 좌표계
├── resolution_m (FLOAT) — 해상도 (래스터인 경우, NULL 가능)
├── stored_path (TEXT) — 저장 경로 (예: "storage/.../processed/surface/dem.tif")
├── model_file_path (TEXT) — 모델 저장 경로 (예: "storage/.../processed/surface/dem.tif")
├── bounds (GEOMETRY) — 모델 범위 (다각형)
├── metadata (JSONB) — 생성 파라미터
├── generation_params (JSONB) — 생성 파라미터
│ ├── filter_type (필터링 방식)
│ ├── creation_time (생성 시각)
── ...
│ ├── algorithm (사용 알고리즘)
── params (알고리즘 파라미터)
│ └── creation_time (생성 시각)
├── created_at (TIMESTAMP)
└── completed_at (TIMESTAMP)
```
### 4-2. terrain_layers 테이블
### 4-2. terrain_layers 테이블 (WF1 산출물 — 각 지형 레이어)
```
terrain_layers
├── id (INT, PK)
├── id (INT, PK) — 레이어 고유 번호
├── surface_model_id (INT, FK → surface_models.id)
├── layer_name (TEXT) — 레이어명 (예: "지표", "제1층", "제2층" 등)
├── geometry_type (TEXT) — 기하 종류: POINTCLOUD, GRID, MESH, CONTOUR
├── stored_path (TEXT) — 저장 경로 (GeoJSON, GeoTIFF, LAS 등)
── statistics (JSONB) — 통계
├── min_z, max_z, mean_slope
└── ...
├── layer_file_path (TEXT) — 레이어 파일 저장 경로 (예: "storage/.../processed/surface/layer_1.geojson")
── file_format (TEXT) — 파일 형식: geojson, geotiff, ply, obj 등
├── file_size_mb (FLOAT) — 파일 크기
├── bounds (GEOMETRY) — 레이어 범위
├── statistics (JSONB) — 통계
│ ├── min_z, max_z, mean_slope
│ ├── point_count (포인트 개수)
│ └── ...
└── created_at (TIMESTAMP)
```
---
## 5. 경로 & 설계 그룹
### 5-1. routes 테이블 (WF2 출력)
### 5-1. routes 테이블 (WF2 출력 — 경로 설계 결과)
```
routes
├── id (INT, PK) — 경로 고유 번호
├── project_id (UUID, FK → projects.id)
├── surface_model_id (INT, FK → surface_models.id) — 기반 지표면 모델
├── status (TEXT) — 상태: DRAFT, CONFIRMED, ARCHIVED
├── start_point (GEOMETRY(Point)) — 시작점 좌표 (3D)
├── end_point (GEOMETRY(Point)) — 종료점 좌표 (3D)
@@ -170,6 +193,7 @@ routes
│ ├── cost_weights (비용함수 가중치)
│ └── ...
├── geometry (GEOMETRY(LineString, Z)) — 3D 경로 좌표 열
├── route_data_path (TEXT) — 경로 데이터 저장 경로 (예: "storage/.../computed/route/route_main.geojson")
├── computed_at (TIMESTAMP) — 계산 완료 날짜
└── created_at (TIMESTAMP)
```
@@ -204,7 +228,7 @@ route_statistics
## 6. 종단면 & 횡단면 그룹
### 6-1. longitudinal_sections 테이블 (WF3 출력)
### 6-1. longitudinal_sections 테이블 (WF3 출력 — 종단면)
```
longitudinal_sections
├── id (INT, PK) — 종단면 고유 번호
@@ -217,10 +241,11 @@ longitudinal_sections
│ ├── elevations ([100.5, 102.3, 101.8, ...]) — 지표 표고 배열
│ ├── grades ([2.5, 1.8, 1.0, ...]) — 경사도 배열 (%)
│ └── design_elevations ([100.0, 102.0, 101.5, ...]) — 설계 표고
── stored_path (TEXT) — 저장 경로 (예: "storage/.../sections/longitudinal.json")
── longitudinal_file_path (TEXT) — 저장 경로 (예: "storage/.../computed/sections/longitudinal.json")
└── status (TEXT) — 상태: DRAFT, CONFIRMED
```
### 6-2. cross_sections 테이블 (WF3 출력)
### 6-2. cross_sections 테이블 (WF3 출력 — 횡단면)
```
cross_sections
├── id (INT, PK) — 횡단면 고유 번호
@@ -238,14 +263,15 @@ cross_sections
│ ├── fill_volume_m3 (FLOAT) — 성토량
│ ├── structures ([]) — 이 단면 내 구조물 ID 배열
│ └── notes (TEXT) — 메모
── stored_path (TEXT) — 저장 경로 (예: "storage/.../sections/cross_0020m.json")
── cross_section_file_path (TEXT) — 저장 경로 (예: "storage/.../computed/sections/cross_0020m.json")
└── status (TEXT) — 상태: DRAFT, CONFIRMED
```
---
## 7. 설계 & 구조물 그룹
### 7-1. structures 테이블 (WF4 출력)
### 7-1. structures 테이블 (WF4 출력 — 구조물)
```
structures
├── id (INT, PK) — 구조물 고유 번호
@@ -262,11 +288,13 @@ structures
├── unit_price (FLOAT) — 단가
├── geometry (GEOMETRY(Polygon)) — 3D 배치 도형
├── design_notes (JSONB) — 설계 노트
├── structure_data_path (TEXT) — 구조물 데이터 저장 경로 (예: "storage/.../computed/structures/struct_0020m_001.json")
├── last_modified_by (INT, FK → users.id) — 마지막 수정자
── created_at (TIMESTAMP)
── created_at (TIMESTAMP)
└── updated_at (TIMESTAMP)
```
### 7-2. quantity_items 테이블 (WF5 출력)
### 7-2. quantity_items 테이블 (WF5 출력 — 수량 산출)
```
quantity_items
├── id (INT, PK) — 수량 항목 고유 번호
@@ -280,8 +308,10 @@ quantity_items
├── total_price (FLOAT) — 소계 (quantity_actual × unit_price)
├── standard_reference (TEXT) — 기준 참고 자료
├── computed_at (TIMESTAMP) — 계산 날짜
├── quantity_data_path (TEXT) — 수량 데이터 저장 경로 (예: "storage/.../computed/quantities/items.json")
└── data (JSONB) — 계산 과정 메모
├── formula (계산식)
├── source (데이터 출처)
└── ...
```
@@ -289,7 +319,7 @@ quantity_items
## 8. 산출물 & 문서 그룹
### 8-1. outputs 테이블 (WF6 출력)
### 8-1. outputs 테이블 (WF6 출력 — 최종 산출물 세트)
```
outputs
├── id (INT, PK) — 산출물 세트 고유 번호
@@ -298,7 +328,8 @@ outputs
├── status (TEXT) — 상태: GENERATING, COMPLETE, FAILED
├── generated_by (INT, FK → users.id) — 생성자
├── generated_at (TIMESTAMP) — 생성 날짜
├── version (INT) — 버전 (프로젝트 확정 자동 증가)
├── version (INT) — 버전 (프로젝트 확정 단계에서 자동 증가)
├── outputs_directory_path (TEXT) — 산출물 저장 폴더 (예: "storage/.../outputs/v1/")
└── metadata (JSONB) — 메타데이터
├── template_used (사용한 양식)
├── company_name (회사명)
@@ -307,14 +338,14 @@ outputs
└── generation_time_sec (생성 소요 시간)
```
### 8-2. output_files 테이블
### 8-2. output_files 테이블 (최종 산출 파일들)
```
output_files
├── id (INT, PK) — 파일 고유 번호
├── output_id (INT, FK → outputs.id)
├── file_type (TEXT) — 파일 형식: xlsx, pdf, dxf, dwg, json, zip
├── original_filename (TEXT) — 파일명 (예: "견적서_v1.xlsx")
├── stored_path (TEXT) — 저장 경로 (예: "storage/.../outputs/estimation/견적서_v1.xlsx")
├── output_file_path (TEXT) — 저장 경로 (예: "storage/.../outputs/v1/견적서.xlsx")
├── file_size_mb (FLOAT) — 파일 크기
├── created_at (TIMESTAMP) — 생성 날짜
└── download_count (INT) — 다운로드 횟수
@@ -385,35 +416,100 @@ users (사용자)
---
## 11. 파일시스템 경로와 DB 링크
## 11. 파일시스템 경로와 DB 링크 (워크플로우 기반 구조)
### 파일시스템 구조 (6단계 워크플로우에 맞춤)
```
storage/
├── {company_slug}/
│ └── {user_slug}/
│ └── {project_id}/ ← projects.storage_path
├── raw/ ← input_files.stored_path
│ ├── las/
│ │ ├── tif/
│ │ ├── tfw/
│ │ ├── prj/
│ │ ── dxf/
├── processed/ ← surface_models.stored_path, terrain_layers.stored_path
│ │ ├── surface/
│ │ ├── points/
│ │ └── tiles/
├── sections/ ← longitudinal_sections.stored_path, cross_sections.stored_path
│ ├── longitudinal.json
│ │ ├── cross_0000m.json
│ │ ├── cross_0020m.json
│ │ └── ...
└── outputs/ ← output_files.stored_path
├── estimation/
├── drawings/
├── reports/
├── B03_FileInput/ ← WF0: 파일 입력
│ │ ├── input/ (원본 입력 파일들)
│ │ ├── las/
│ │ │ │ └── cloud_merged.las ← input_files.raw_file_path
│ │ │ ├── tif/
│ │ ├── tfw/
│ │ │ ├── prj/
│ │ │ └── dxf/
│ │ └── metadata.json (파일 메타데이터)
├── 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
│ │ │ └── ...
│ │ └── analysis_report.json
│ │
│ ├── B05_wf2_Route/ ← WF2: 경로 설계
│ │ ├── route/
│ │ │ ├── route_main.geojson ← routes.route_data_path
│ │ │ ├── route_points.json
│ │ │ └── route_statistics.json ← route_statistics 데이터
│ │ └── design_params.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
│ │ │ ├── cross_0040m.json
│ │ │ └── ...
│ │ └── sections_index.json
│ │
│ ├── B07_wf4_DesignDetail/ ← WF4: 상세 설계
│ │ ├── structures/
│ │ │ ├── struct_0020m_001.json ← structures.structure_data_path
│ │ │ ├── struct_0020m_002.json
│ │ │ ├── struct_0040m_001.json
│ │ │ └── ...
│ │ └── design_review.json
│ │
│ ├── B08_wf5_Quantity/ ← WF5: 수량 산출
│ │ ├── quantities/
│ │ │ └── items.json ← quantity_items.quantity_data_path
│ │ ├── breakdown_by_category.json (항목별 집계)
│ │ └── cost_summary.json (비용 요약)
│ │
│ └── B09_wf6_Estimation/ ← WF6: 견적·문서
│ ├── v1/ ← outputs.outputs_directory_path
│ │ ├── 견적서.xlsx ← output_files.output_file_path
│ │ ├── 설계도.dxf
│ │ ├── 보고서.pdf
│ │ ├── 종단면도.pdf
│ │ └── 횡단면도.pdf
│ ├── v2/
│ │ ├── 견적서.xlsx
│ │ └── ...
│ └── bundles/
│ ├── v1_complete.zip (버전 1 전체 패키지)
│ └── v2_complete.zip (버전 2 전체 패키지)
```
**핵심 개념:**
- **B03_FileInput/**: WF0 파일 입력 단계 (사용자 업로드 원본)
- **B04_wf1_Surface/**: WF1 지표면 분석 결과 (DEM, TIN, 메시, 레이어)
- **B05_wf2_Route/**: WF2 경로 설계 결과 (경로 GeoJSON)
- **B06_wf3_ProfileCross/**: WF3 종횡단 생성 결과 (종단면, 횡단면 JSON)
- **B07_wf4_DesignDetail/**: WF4 상세 설계 결과 (구조물 배치)
- **B08_wf5_Quantity/**: WF5 수량 산출 결과 (수량 항목)
- **B09_wf6_Estimation/**: WF6 최종 산출물 (Excel, PDF, DXF)
**장점:**
✅ 워크플로우 단계와 폴더가 1:1 대응 → 직관적
✅ 각 단계의 입력/출력이 명확히 분리
✅ 각 단계 재실행 시 폴더만 초기화하면 됨
✅ 버전 관리 (v1, v2, ...) 단순화
---
## 12. 핵심 쿼리 예제
@@ -480,20 +576,86 @@ ORDER BY timestamp DESC;
## 요약
**총 17개 테이블:**
1. users, companies
2. projects, project_versions
3. input_files, point_cloud_metadata
4. surface_models, terrain_layers
5. routes, route_points, route_statistics
6. longitudinal_sections, cross_sections
7. structures, quantity_items
8. outputs, output_files
9. audit_logs, change_logs
### 주요 변경사항
1. **users 테이블**: position(직급), department(부서), phone(연락처) 추가
2. **companies 테이블**: business_registration_number, business_address, business_owner, business_status 추가
3. **경로 관리 추가**: 모든 데이터의 저장소 경로를 DB에 기록
4. **processed_point_cloud 테이블 신규**: LAS → 빠른 포맷 변환 추적
5. **파일 경로 세분화**:
- `raw_file_path`: 원본 파일 위치
- `processed_file_path / converted_file_path`: 변환된 파일 위치
- `*_file_path`: 각 워크플로우 단계별 산출물 위치
**다음 단계:**
- [ ] PostgreSQL에 위 테이블 생성
### 총 18개 테이블:
1. **사용자/회사**: users, companies (2개)
2. **프로젝트**: projects, project_versions (2개)
3. **입력 데이터**: input_files, processed_point_cloud (2개)
4. **지표면 분석**: surface_models, terrain_layers (2개)
5. **경로 설계**: routes, route_points, route_statistics (3개)
6. **단면 설계**: longitudinal_sections, cross_sections (2개)
7. **구조물/수량**: structures, quantity_items (2개)
8. **산출물**: outputs, output_files (2개)
9. **감시/이력**: audit_logs, change_logs (2개)
### DB vs 파일시스템 역할 분리
| 저장 위치 | 내용 | 예시 |
|----------|------|------|
| **DB** | 메타데이터 + 경로정보 | 파일명, 크기, 좌표계, 상태, 저장경로 |
| **파일시스템** | 실제 파일 데이터 | LAS, DXF, Excel, PDF, JSON, GeoJSON 파일들 |
**워크플로우 6단계에 따른 파일 흐름:**
```
B03_FileInput/ (WF0: 파일 입력)
└─ input/*.las, *.tif, *.prj, *.tfw
B04_wf1_Surface/ (WF1: 지표면 분석)
├─ processed/*.ply (변환된 포인트클라우드)
└─ models/*.tif, *.geojson (DEM, TIN, 레이어)
B05_wf2_Route/ (WF2: 경로 설계)
└─ route/*.geojson (최적 경로)
B06_wf3_ProfileCross/ (WF3: 종횡단 생성)
├─ longitudinal/*.json (종단면)
└─ cross_sections/*.json (횡단면 배열)
B07_wf4_DesignDetail/ (WF4: 상세 설계)
└─ structures/*.json (구조물 배치)
B08_wf5_Quantity/ (WF5: 수량 산출)
└─ quantities/*.json (수량 항목)
B09_wf6_Estimation/ (WF6: 견적·문서)
├─ v1/*.xlsx, *.pdf, *.dxf (최종 산출물 v1)
├─ v2/*.xlsx, *.pdf, *.dxf (최종 산출물 v2)
└─ bundles/*.zip (완전 패키지)
```
**각 단계별 재실행:**
- 예: B05 경로 설계 재실행 → `B05_wf2_Route/route/` 폴더만 초기화
- 예: B09 문서 재생성 → `B09_wf6_Estimation/v3/` 폴더 생성 (v1, v2는 유지)
### 워크플로우 기반 폴더 구조의 이점
| 항목 | 이전 (raw/processed/computed) | 현재 (B04/B05/B06/.../B09) |
|------|-----|------|
| **직관성** | 계층적이나 추상적 | ✅ 워크플로우 단계와 정확히 일치 |
| **단계 재실행** | 어느 폴더를 초기화할지 모호 | ✅ 해당 B0X 폴더만 초기화 |
| **버전 관리** | outputs/v1, v2, ... 만 가능 | ✅ 모든 단계에서 가능 |
| **파일 찾기** | raw/ 또는 processed/ 에서 검색 | ✅ B0X_Naming으로 명확함 |
| **API 라우터** | 경로 결정이 복잡 | ✅ `f"storage/{company}/{user}/{project_id}/B{step}_*/..."` |
### 다음 단계:
- [X] MariaDB에 위 테이블 생성 (18개 테이블)
- [ ] 관계(FK) 설정
- [ ] 인덱스 추가
- [ ] Alembic 마이그레이션 코드 작성
- [ ] Python ORM(SQLAlchemy) 모델 정의
- [ ] 인덱스 추가 (경로 조회, 프로젝트별 필터 등)
- [ ] Alembic 마이그레이션 코드 작성<<<<<< 확인 필요(mariaDB사용중)
- [ ] Python ORM(SQLAlchemy) 모델 정의<<<<<< 확인 필요(mariaDB사용중)
- [ ] 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` 저장
+230 -344
View File
@@ -1,388 +1,274 @@
# 프로젝트 리팩토링 및 마이그레이션 계획서
# B03~B06 기능 마이그레이션 계획서
**작성일:** 2026-07-05
**상태:** 계획 수립 완료
**목표:** 기존 POC(Proof of Concept) 코드를 정식 프로덕션 구조로 전환
**갱신일:** 2026-07-05
**상태:** 백엔드 마이그레이션 완료 — Stage 0~4 (B03~B06) 백엔드 이전 완료. 남은 항목은 프론트엔드(Vanilla TS) 재작성과 실제 DB 연동 검증.
**범위:** `B03_FileInput` ~ `B06_wf3_ProfileCross`
**DB:** MariaDB v10.6+ (aiomysql 드라이버, Raw SQL)
**진척 요약:**
- Stage 0 (공통 기반): ✅ 완료
- Stage 1 B03 (파일 입력): ✅ 백엔드 완료
- Stage 2 B04 (지표면 분석): ✅ 백엔드 완료 (필터 4종 + 모델 5종 + 스무딩 + 등고선 + 파이프라인)
- Stage 3 B05 (경로 설계): ✅ 백엔드 완료 (Dijkstra + ridge-valley + skeleton)
- Stage 4 B06 (종횡단 생성): ✅ 백엔드 완료 (sampler + 종횡단 + Repository/Router)
- 공통: 전 페이지 DB 접근을 aiomysql Raw SQL로 통일, 순수 계산부는 실데이터로 검증
- 남은 작업: B04~B06 프론트엔드(Vanilla TS/WebCAD), 실제 aislo_db 연동·구형 수치 비교
---
## 1. 현황 분석 (Baseline Assessment)
## 1. 확정 기준
### 1.1 기존 상태
- **위치:** `0_old/` 폴더에 백업 완료
- **제외 사항:** `venv/`, `node_modules/` 미포함 (별도 재설치 필요)
- **특성:** 프로토타입 개발용 단순 구조
### 1.2 신 구조 목표
- **거대 모듈 금지:** 기능별/페이지별 분할 자동화
- **수직 통합 관리:** 프론트엔드(TypeScript) + 백엔드(Python) 페어링
- **명확한 책임 분리:** 시스템 폴더 vs. 페이지 폴더 격리
- 기능 동작의 기준 원본은 `0_old/main.py``0_old/utils/`이다.
- `0_old/backend/app/`은 이전 프로토타입 참고 자료이며 최종 동작 기준으로 사용하지 않는다.
- 신규 배치 구조는 `.agent/structure.md`를 따른다. 구형 파일 구조는 승계하지 않는다.
- 백엔드는 `.agent/backend.md`, 프론트엔드는 `.agent/frontend.md`를 따른다.
- 저장 구조 및 DB 경로 열은 `.agent/db_schema_simple.md`의 단계별 파일시스템 구조를 따른다.
- DB 스키마 작성·수정은 별도 담당 작업이다. 이 마이그레이션은 확정된 DB 계약만 `aiomysql`와 Raw SQL로 사용한다.
- 루트 `main.py`에는 페이지 라우터 등록만 추가한다.
- 구형 React/TSX UI는 복사하지 않고 현재 Vanilla TypeScript 구조와 공통 UI 템플릿에 맞게 재작성한다.
---
## 2. 신 디렉토리 구조 (New Structure)
## 2. 작업 원칙
```
프로젝트_루트/
├── .agent/ # 에이전트 명세서
│ ├── structure.md # 폴더/파일 명명규칙
│ ├── agent.md # 기술스택 및 제약조건
│ ├── backend.md # 백엔드 명세서
│ ├── frontend.md # 프론트엔드 명세서
│ └── migration_plan.md # 본 파일 (마이그레이션 가이드)
├── common_util/ # 공통 유틸리티 모듈
│ ├── common_util_geometry.py # 기하학 연산 (Trimesh, Shapely)
│ ├── common_util_gis.py # GIS 유틸 (PostGIS, Geopandas)
│ ├── common_util_file.py # 파일 I/O 및 경로 관리
│ ├── common_util_validation.py # 데이터 검증 (Pydantic)
│ ├── common_util_math.py # 수학/통계 함수
│ └── common_util_string.ts # 문자열 처리 (TypeScript)
├── db_management/ # DB 스키마 및 마이그레이션
│ ├── schema.sql # PostgreSQL + PostGIS 초기 스키마
│ ├── migration_001_init.py # 마이그레이션 스크립트 v1
│ └── README.md # DB 운영 가이드
├── resources/ # UI 리소스 (로고, 이미지, 폰트)
│ ├── images/
│ │ ├── logo.svg
│ │ ├── favicon.ico
│ │ └── banners/
│ └── fonts/
│ └── noto-sans-kr.woff2
├── storage/ # 영구 저장소 (물리 파일)
│ └── README.md # 저장소 구조 설명
├── ui_template/ # 공통 UI 템플릿 및 테마
│ ├── ui_template_theme.css # 글로벌 색상/폰트 변수
│ ├── ui_template_locale.ts # 다국어 관리 (i18n)
│ ├── ui_template_elements.ts # 공통 컴포넌트 정의
│ └── ui_template_styles.css # 기본 컴포넌트 스타일
├── templates/ # 보고서/도면 템플릿
│ ├── template_report_standard.xlsx # 표준 보고서
│ ├── template_drawing_base.dwg # CAD 기본 도면
│ └── template_quantity_sheet.xlsx # 수량 산출서
├── config/ # 환경 설정
│ ├── config_system.py # 백엔드 파라미터
│ ├── config_db.py # DB 연결 설정
│ ├── config_frontend.ts # 프론트엔드 상수
│ └── .env.example # 환경 변수 템플릿
├── A01_Home/ # 로그인 전 01: 홈
│ ├── A01_Home_UI_Page.ts # 페이지 UI 컴포넌트
│ ├── A01_Home_UI_Style.css # 페이지 스타일
│ ├── A01_Home_Api_Fetch.py # API 라우터
│ └── A01_Home_Schema.py # Pydantic 스키마
├── A02_ProgDetail/ ... A08_Support/ # 로그인 전 페이지들 (동일 구조)
├── B01_AccountDetail/ # 로그인 후 01: 계정
├── B02_ProjRegister/ # 로그인 후 02: 프로젝트 등록
├── B03_FileInput/ # 로그인 후 03: 파일 입력
├── B04_wf1_Surface/ # Workflow 1: 지표면 분석
│ ├── B04_wf1_Surface_UI_View.ts # UI 뷰어
│ ├── B04_wf1_Surface_UI_Form.ts # 입력 폼
│ ├── B04_wf1_Surface_UI_Style.css
│ ├── B04_wf1_Surface_Api_Router.py # API 라우터
│ ├── B04_wf1_Surface_Engine.py # 연산 엔진 (Trimesh, PostGIS)
│ └── B04_wf1_Surface_Schema.py # 데이터 스키마
├── B05_wf2_Route/ ... B09_wf6_Estimation/ # 워크플로우 2-6 (동일 구조)
├── B10_Payment/ & B11_Status/ # 로그인 후 최종 페이지
├── main.py # FastAPI 애플리케이션 진입점
├── pyproject.toml # Python 의존성 (Poetry)
├── package.json & package-lock.json # Node.js 의존성
├── CLAUDE.md # 프로젝트 기본 정보
├── README.md # 프로젝트 개요
└── .gitignore # Git 제외 목록
### 2.1 함수 하나씩 이전
각 작업 단위는 다음 순서를 지킨다.
1. 대상 구형 함수와 직접 의존성 확인
2. 신규 페이지 폴더에 책임에 맞는 파일 하나 생성
3. 함수 하나만 이전·수정
4. 해당 함수 단위 테스트 또는 최소 실행 검증
5. 포맷터·린터 실행
6. 검증 완료 후 다음 함수 진행
한 번에 구형 모듈 전체를 복사하지 않는다. 구형 전역 경로, 동기식 파일 처리, 하드코딩 설정은 그대로 가져오지 않는다.
### 2.2 파일 분리
- `*_Router.py`: FastAPI 엔드포인트와 응답 변환
- `*_Schema.py`: Pydantic 요청·응답 검증
- `*_Engine_*.py`: 페이지 고유 연산
- `*_Repository.py`: `asyncpg` Raw SQL과 DB 경로 메타데이터 처리
- `common_util/`: 둘 이상의 페이지가 실제로 공유하는 경로·원자적 파일 쓰기·기하 유틸만 배치
- 단일 파일이 700줄에 도달하기 전에 기능별 파일 분할을 제안하고 승인 후 `structure.md`를 갱신한다.
### 2.3 저장소 경계
```text
storage/{company_slug}/{user_slug}/{project_id}/
├── B03_FileInput/
├── B04_wf1_Surface/
├── B05_wf2_Route/
└── B06_wf3_ProfileCross/
```
---
## 3. 마이그레이션 단계별 계획 (Phased Migration)
### Phase 1: 기초 인프라 (Infrastructure Setup) - 1-2주
**담당:** 전체
**목표:** 설정 및 공통 유틸 정리
#### 1.1 설정 파일 정리
- [ ] `config_system.py` 작성
- FastAPI 포트, 로깅 레벨, 알고리즘 파라미터
- 예: `MESH_GRID_SIZE`, `UPLOAD_MAX_MB`, `LOG_LEVEL`
- [ ] `config_db.py` 작성
- PostgreSQL 연결 문자열 (asyncpg)
- PostGIS 테이블 정의 위치
- [ ] `config_frontend.ts` 작성
- API 기본 URL, 파일 업로드 제한 크기
- WebCAD 렌더링 옵션
#### 1.2 공통 유틸 마이그레이션
- [ ] **0_old/** 에서 기존 유틸 함수 추출
- [ ] `common_util/` 내 분류:
- `common_util_geometry.py` ← Trimesh, Shapely 관련
- `common_util_gis.py` ← PostGIS 쿼리, Geopandas 변환
- `common_util_file.py` ← LAS, DEM, DWG 읽기/쓰기
- `common_util_validation.py` ← Pydantic 모델, 데이터 검증
#### 1.3 UI 템플릿 기초 설정
- [ ] `ui_template_theme.css` 작성
- CSS 변수 정의 (라이트/다크 모드)
- 예: `--color-bg`, `--color-text`, `--color-primary`
- [ ] `ui_template_locale.ts` 작성
- 한글 / 영문 키-값 쌍
- 예: `A01_Home_Title: ["반갑습니다", "Welcome"]`
- [ ] `ui_template_elements.ts` 작성
- 공통 버튼, 입력창, 카드 컴포넌트 정의
#### 1.4 DB 스키마 및 마이그레이션 준비
- [ ] `db_management/schema.sql` 작성
- 사용자, 프로젝트, 파일 정보 테이블
- PostGIS 기하학 타입 (geometry, raster)
- [ ] `db_management/migration_001_init.py` 작성
- asyncpg 기반 초기화 스크립트
#### 1.5 FastAPI 메인 애플리케이션 구조
- [ ] `main.py` 작성 (라우터 등록만)
```python
from fastapi import FastAPI
# A01_Home, B04_wf1_Surface 등에서 라우터 import
# app.include_router(a01_router, prefix="/api/a01")
```
각 페이지는 자기 폴더만 직접 생성·수정한다. 상위 단계 변경 시 하위 결과를 임의 삭제하지 않고 `stale_from`을 갱신한다.
---
### Phase 2: 로그인 전 페이지 (Pre-Login Pages) - 2-3주
**담당:** 프론트엔드 + 백엔드 (페이지별 페어)
**목표:** A01~A08 페이지 완성
## 3. 구형 기능 분석 결과
#### 2.1 페이지별 구조 (A01_Home 예시)
각 페이지는 다음 파일 구성:
### 3.1 B03_FileInput
**프론트엔드 (TypeScript)**
- `A01_Home_UI_Page.ts` ← 페이지 진입점 + 컴포넌트 조립
- `A01_Home_UI_Style.css` ← 페이지별 커스텀 스타일
구형 원본:
**백엔드 (Python)**
- `A01_Home_Api_Fetch.py` ← FastAPI 라우터 (`/api/a01/...`)
- `A01_Home_Schema.py` ← Pydantic 요청/응답 모델
- `0_old/main.py`: `check_sample`, `upload_files`
- 보조 참고: `0_old/backend/app/analyzer.py`의 파일 분석 함수
#### 2.2 A01_Home (홈) - Week 1
- [ ] `A01_Home_UI_Page.ts` 작성
- 로고, 최신 소식 패널, 사이트맵 링크
- [ ] `A01_Home_Api_Fetch.py` 작성
- `GET /api/a01/news` ← 최신 소식 조회
- [ ] 기본 스타일 적용
책임:
#### 2.3 A02_ProgDetail ~ A08_Support - Week 2-3
동일 구조로 순차 진행:
- [ ] A02_ProgDetail (프로그램 상세)
- [ ] A03_CompDetail (회사 상세)
- [ ] A04_NewsHistory (뉴스/이력)
- [ ] A05_EduDetail (교육)
- [ ] A06_Login (로그인) ← **핵심: JWT 토큰 발급**
- [ ] A07_Register (회원가입) ← **핵심: 사용자 생성, 이메일 검증**
- [ ] A08_Support (기술 지원)
- LAS/TIF/TFW/PRJ/DXF 업로드 검증
- `B03_FileInput/input/{file_type}/` 저장
- 파일 메타데이터 추출 및 `input_files` 기록
- 업로드 완료 상태 반환
B04로 넘길 기능:
- `process_pipeline`
- `run_ground_analysis`
- 포인트 필터링 및 지표면 모델 생성
### 3.2 B04_wf1_Surface
구형 원본:
- `0_old/main.py`: `process_pipeline`, `ensure_terrain_models`, 지표면·포인트 API
- `0_old/utils/structurizer.py`
- `filter_grid_min_z.py`, `filter_csf.py`, `filter_pmf.py`, `filter_ransac.py`
- `terrain_model_converter.py`, `surface_smoother.py`, `contour_extractor.py`
책임:
- LAS 구조화와 지면 필터 생성
- 변환 포인트클라우드 저장
- TIN/DTM/NURBS/implicit/meshfree 모델 생성
- 스무딩, 미리보기, 등고선 생성
- 지표면 모델 선택 확정 및 하위 단계 stale 전파
### 3.3 B05_wf2_Route
구형 원본:
- `0_old/main.py`: 경로점 CRUD, solve/result/confirm, stale 서명 처리
- `0_old/utils/route_solver.py`
- `0_old/utils/route_solver_ridgevalley.py`
- `0_old/utils/terrain_skeleton.py`
책임:
- BP/EP/CP/AP/FP 입력 검증과 저장
- Dijkstra 및 ridge-valley 경로 계산
- 경사·곡선반경·회피/금지 구역 검증
- 경로 결과 조회·확정 및 B06 stale 전파
### 3.4 B06_wf3_ProfileCross
구형 원본:
- `0_old/main.py`: sections generate/get/confirm 및 입력 서명 처리
- `0_old/utils/surface_elevation_sampler.py`
- `0_old/utils/section_generator.py`
책임:
- 확정 경로와 확정 지표면 모델 검증
- 종단 표고 프로필 생성
- 지정 측점 간격의 횡단면 생성
- 결과 조회·확정 및 재현성 서명 관리
---
### Phase 3: 로그인 후 기초 페이지 (Post-Login Basic Pages) - 1-2주
**담당:** 프론트엔드 + 백엔드
**목표:** B01~B03 페이지 완성
## 4. 함수 단위 실행 순서
#### 3.1 B01_AccountDetail (계정 상세)
- [ ] 사용자 정보 조회/수정
- [ ] 구독 상태, 다운로드 이력
### Stage 0 — 공통 기반
#### 3.2 B02_ProjRegister (프로젝트 등록)
- [ ] 프로젝트 생성 폼
- [ ] 고객사명 / 사용자명 / 프로젝트ID 관리
- [ ] **핵심:** `storage/[고객사]/[사용자]/[프로젝트ID]/` 자동 생성
- [x] `config_system.py`의 저장 경로에서 불필요한 `projects` 경로 제거
- [x] 프로젝트 루트와 B03~B06 단계별 경로를 안전하게 만드는 공통 경로 함수 추가
- [x] JSON 원자적 쓰기 함수 이전
- [x] `workflow.json` 읽기 및 stale 필드 원자적 갱신 함수 이전
- [x] 공통 함수별 테스트 작성
#### 3.3 B03_FileInput (파일 입력)
- [ ] 파일 업로드 UI (Drag & Drop)
- [ ] LAS, DEM, DWG 파일 검증
- [ ] `storage/` 경로에 물리 저장
### Stage 1 — B03_FileInput
- [x] `B03_FileInput_Schema.py`: 파일 종류·크기·확장자 검증 모델
- [x] `B03_FileInput_Engine.py`: 안전한 파일명과 목적 경로 결정 함수
- [x] `B03_FileInput_Engine.py`: 업로드 스트림 저장 함수
- [x] `B03_FileInput_Engine_Analyze.py`: LAS 메타데이터 분석 함수
- [x] `B03_FileInput_Engine_Analyze.py`: PRJ/TIF/TFW 메타데이터 분석 함수
- [x] `B03_FileInput_Repository.py`: `input_files` 저장 함수
- [x] `B03_FileInput_Router.py`: 업로드 엔드포인트
- [x] B03 프론트 드롭존·목록·검증·로딩 UI를 공통 컴포넌트 기반으로 작성
- [x] 업로드 통합 테스트
### Stage 2 — B04_wf1_Surface
- [x] LAS 구조화 함수 이전
- [x] grid-min-z 필터 함수 이전 및 검증
- [x] CSF 필터 함수 이전 및 검증
- [x] PMF 필터 함수 이전 및 검증
- [x] RANSAC 필터 함수 이전 및 검증
- [x] 지표면 모델 공통 컨텍스트 생성 함수 이전 (ModelContext + 메시 유틸)
- [x] TIN 생성 함수 이전
- [x] DTM 생성 함수 이전
- [x] NURBS 생성 함수 이전
- [x] implicit 생성 함수 이전
- [x] meshfree 생성 함수 이전
- [x] DTM/TIN 스무딩 함수 이전
- [x] 등고선 생성 함수 이전
- [x] 미리보기·메타데이터·모델 확정 라우트 추가 (analyze/models 라우트 + 파이프라인 manifest)
- [x] `processed_point_cloud`, `surface_models`, `terrain_layers` Raw SQL 저장 함수 추가 (aiomysql)
- [ ] B04 폼·WebCAD 뷰어를 Vanilla TypeScript로 재작성
- [ ] 샘플 LAS 결과를 구형 출력과 비교 검증
### Stage 3 — B05_wf2_Route
- [x] 경로점 Pydantic 모델 및 유효성 검사 이전 (Schema)
- [x] 경로점 저장·조회·초기화 함수 이전 (Repository)
- [x] 비용면 생성 및 캐시 함수 이전 (Solver)
- [x] Dijkstra 단일 구간 탐색 함수 이전 (Geometry)
- [x] 최적 경로 조립 함수 이전 (Solver solve_optimal_route)
- [x] 지형 skeleton 생성 함수 이전 (Skeleton, D8 흐름누적)
- [x] ridge-valley 탐색 함수 이전 (RidgeValley, 정속경사+fillet)
- [ ] 입력 서명·stale 판정 함수 이전
- [x] 경로 확정 함수 이전 (Repository confirm_route + confirm 라우트)
- [x] `routes`, `route_points`, `route_statistics` Raw SQL 저장 함수 추가 (aiomysql)
- [ ] B05 경로 편집·제약조건·결과 뷰어를 Vanilla TypeScript로 재작성
- [x] 기존 route solver 테스트를 신규 구조로 이관·통과
### Stage 4 — B06_wf3_ProfileCross
- [x] 지표면 sampler 인터페이스 이전 (Engine_Sampler: SurfaceElevationSampler Protocol)
- [x] DTM grid sampler 함수 이전 (DtmGridSampler, 4-꼭짓점 valid 검증)
- [x] 보간 sampler 함수 이전 (InterpolatedSurfaceSampler + build_surface_sampler 5종)
- [x] 측점 배열 생성 함수 이전 (Engine_Section: _chainages)
- [x] 경로 보간 및 접선 계산 함수 이전 (_interpolate_xy, _tangent_at)
- [x] 종단·횡단 생성 함수 이전 (generate_sections)
- [ ] 입력 서명·중복 실행 방지 함수 이전 (delete_sections_for_route로 멱등 재실행만 구현)
- [x] 결과 조회·확정 라우트 추가 (sections/generate·get·confirm 라우트)
- [x] `longitudinal_sections`, `cross_sections` Raw SQL 저장 함수 추가 (aiomysql Repository)
- [ ] B06 종단도·횡단 카드 UI를 Vanilla TypeScript로 재작성
- [ ] 구형 section 결과와 수치 비교 검증
**B06 백엔드 구현 파일:**
- `B06_wf3_ProfileCross_Engine_Sampler.py` — 표고 sampler (Protocol + DTM/보간 5종)
- `B06_wf3_ProfileCross_Engine_Section.py` — 종횡단 생성 (측점/접선/횡단 샘플)
- `B06_wf3_ProfileCross_Engine.py` — 오케스트레이터 (경로 GeoJSON→종횡단→파일 저장)
- `B06_wf3_ProfileCross_Repository.py` — aiomysql Raw SQL (2 테이블)
- `B06_wf3_ProfileCross_Schema.py` / `_Router.py` — Pydantic + FastAPI 라우트
**검증 상태:** B04(DTM)→B05(경로 70.71m)→B06(종단면+횡단면 5건) end-to-end 파일 생성 확인.
DB 저장 함수는 FakeCursor 기반 문법 검증만 수행 (실제 aislo_db 연동은 나스 서버 준비 후).
---
### Phase 4: Workflow 엔진 (1-4) - 4-6주
**담당:** 백엔드 주 (프론트엔드 보조)
**목표:** B04~B07 워크플로우 엔진 완성
## 5. API 및 상태 원칙
#### 4.1 B04_wf1_Surface (지표면 분석) - Week 1-2
**구조:**
```
B04_wf1_Surface/
├── B04_wf1_Surface_UI_View.ts # WebCAD 뷰어
├── B04_wf1_Surface_UI_Form.ts # 입력 폼 (좌측 패널)
├── B04_wf1_Surface_UI_Style.css
├── B04_wf1_Surface_Api_Router.py # GET/POST 엔드포인트
├── B04_wf1_Surface_Engine.py # Trimesh 메쉬 생성 로직
└── B04_wf1_Surface_Schema.py # Pydantic 모델
```
**구현:**
- [ ] LAS 포인트클라우드 → Trimesh 메쉬 변환
- [ ] 메쉬 좌표 직렬화 (JSON)
- [ ] WebGL 캔버스에서 렌더링
- [ ] 메쉬 정보 DB 저장 (경로만)
#### 4.2 B05_wf2_Route (경로 설계) - Week 2-3
- [ ] 지도 UI (2D 평면도)
- [ ] 드래그로 경로 그리기
- [ ] Geopandas + PostGIS 거리 계산
- [ ] 경로 점 배열 저장
#### 4.3 B06_wf3_ProfileCross (종횡단) - Week 3-4
- [ ] 경로 기반 고도 프로필 생성
- [ ] 종단도 + 횡단도 계산 (PostGIS)
- [ ] 기울기, 높이차 자동 산출
#### 4.4 B07_wf4_DesignDetail (상세 설계) - Week 4-5
- [ ] 도로 포장 두께, 기울기 입력
- [ ] Whitebox 토양 분석
- [ ] 3D 설계 도면 생성
- API 경로는 `/api/projects/{project_id}/...` 형태를 유지하되 페이지별 Router에서 선언한다.
- 모든 JSON 요청은 Pydantic 검증 후 Engine으로 전달한다.
- 긴 지형 연산은 이벤트 루프를 막지 않도록 실행 방식을 별도 검토한다.
- 오류 응답은 `{"status": "error", "message": "원인"}` 형식을 유지한다.
- 프론트 API 호출은 로딩 Overlay를 항상 시작·해제한다.
- UI 문자열은 `ui_template_locale.ts`에 먼저 등록한 뒤 참조한다.
- 상위 단계 재계산 시 하위 결과는 stale 처리하며 다른 페이지 폴더를 직접 삭제하지 않는다.
---
### Phase 5: 워크플로우 후반 (5-6) + 최종 페이지 - 2-3주
**담당:** 백엔드 주 + 프론트엔드
**목표:** B08~B11 완성
## 6. 검증 체크리스트
#### 5.1 B08_wf5_Quantity (수량 산출)
- [ ] 포장재, 흙 깍기/쌓기 수량 계산
- [ ] Excel 템플릿 자동 채우기
각 함수 이전 후:
#### 5.2 B09_wf6_Estimation (견적/문서)
- [ ] 단가 × 수량 = 금액 계산
- [ ] PDF 보고서 생성 (Jinja2 템플릿)
- [ ] DWG 도면 생성 (pyDWG)
- [ ] 구형 함수의 입력·출력 계약 비교
- [ ] 하드코딩 설정 제거 및 `config_system.py` 연결
- [ ] 신규 단계별 저장 경로 사용 확인
- [ ] 경로 이탈 및 파일명 공격 방지 확인
- [ ] 예외 처리와 표준 오류 응답 확인
- [ ] Python: `ruff format`, `ruff check --fix`
- [ ] TypeScript/CSS: `npx prettier --write`
- [ ] 단위 테스트 실행
- [ ] 관련 통합 테스트 실행
- [ ] 단일 파일 700줄 미만 확인
#### 5.3 B10_Payment (결재)
- [ ] 견적서 검토 및 승인
- [ ] 결재자 서명 프로세스
단계 완료 후:
#### 5.4 B11_Status (상태/다운로드)
- [ ] 프로젝트 상태 조회
- [ ] 최종 산출물 다운로드
- [ ] B03 업로드 파일과 DB 메타데이터 일치
- [ ] B04 지표면 모델의 범위·점 수·표고 통계 비교
- [ ] B05 경로 길이·경사·곡선반경·제약 위반 비교
- [ ] B06 측점 수·종단 표고·횡단 좌표 비교
- [ ] 다중 브라우저 polling 및 stale 전파 확인
---
## 4. 기술 구현 가이드 (Technical Guidelines)
## 7. 작업 경계와 보류 항목
### 4.1 백엔드 파이썬 스타일
```python
# config 반드시 import
from config.config_system import MESH_GRID_SIZE
from config.config_db import DB_URL
# 공통 유틸 사용
from common_util.common_util_geometry import create_mesh_from_points
from common_util.common_util_validation import validate_project_id
# Raw SQL (ORM 금지)
query = "SELECT ST_Volume(geom) FROM surfaces WHERE project_id = $1"
result = await db_pool.fetchval(query, project_id)
# 에러 처리
try:
mesh = create_mesh(points)
except ValueError as e:
return {"status": "error", "message": str(e)}
```
### 4.2 프론트엔드 TypeScript 스타일
```typescript
// CSS 변수 사용 (색상 하드코딩 금지)
const styles = `
.panel {
background-color: var(--color-bg);
color: var(--color-text);
}
`;
// 다국어 (i18n 필수)
const title = ui_locales.A01_Home_Title[currentLanguageIndex];
// 이벤트 핸들러 명명법
function onB04_Surface_Calculate_Click() { ... }
// 로딩 스피너 (API 호출 시 필수)
showLoadingOverlay();
const result = await fetchAPI("/api/b04/calculate", data);
hideLoadingOverlay();
```
### 4.3 파일 정보 저장 규칙
- **DB에 저장:** 메타데이터만
- 파일명, 원본 경로, 업로드 시간, 파일 크기
- **물리 저장:** `storage/[고객사]/[사용자]/[프로젝트ID]/`
- LAS, DEM, 중간 계산 파일, 최종 메쉬
- DB 테이블·FK·인덱스 정의 변경은 본 작업에서 수행하지 않는다.
- `.agent/db_schema_simple.md`는 DB 담당자의 작업 영역이므로 직접 수정하지 않는다.
- DB 스키마가 확정되지 않은 Repository 함수는 인터페이스만 계획하고 임의 열을 만들지 않는다.
- B07 이후 기능은 이번 마이그레이션 범위에서 제외한다.
- 구형 샘플 데이터는 검증 입력으로만 사용하고 신규 영구저장소로 자동 복사하지 않는다.
---
## 5. 의존성 (Dependencies)
## 8. 다음 실행 단위
### Python 라이브러리
```
fastapi==0.104.1
pydantic==2.5.0
asyncpg==0.29.0
trimesh==3.23.0
geopandas==0.14.0
shapely==2.0.1
rasterio==1.3.8
laspy==2.4.1
whitebox==2.3.0
python-multipart==0.0.6
jinja2==3.1.2
pydwg==0.2.0
```
### Node.js 라이브러리
```
typescript@5.x
react@18.x (또는 vanilla TS)
webpack@5.x
tailwindcss@3.x (또는 커스텀 CSS)
```
---
## 6. 체크리스트 (Checklist)
### 시작 전 (Pre-Migration)
- [ ] `0_old/` 백업 확인
- [ ] git 커밋 (`"Refactor: Start migration to new structure"`)
- [ ] 모든 폴더 생성 완료 (Phase 1에서 수행)
### 각 Phase별 마무리
- [ ] 코드 포맷팅 (`ruff format`, `prettier --write`)
- [ ] 린터 체크 (`ruff check`)
- [ ] 단위 테스트 작성
- [ ] git 커밋 (Phase별로 구분)
### 최종 (Post-Migration)
- [ ] 전체 시스템 통합 테스트
- [ ] 프로덕션 배포 (Docker)
- [ ] 사용자 승인
---
## 7. 참고 자료
- **폴더 구조:** `.agent/structure.md`
- **백엔드 명세:** `.agent/backend.md`
- **프론트엔드 명세:** `.agent/frontend.md`
- **기술 스택:** `.agent/agent.md`
---
**다음 단계:** Phase 1 시작 → `config/` 및 `common_util/` 작성
첫 구현은 Stage 0의 저장 경로 함수 하나로 시작한다. 함수와 테스트를 검증한 뒤 다음 공통 함수로 이동한다.
+13 -2
View File
@@ -26,6 +26,9 @@ my-project/
├── common_util/ # 공통 유틸리티 (공통 기능 코드)
│ ├── common_util_validate.ts # 프론트 1차 유효성 검사 (이메일/빈값/최소길이)
│ ├── common_util_string.ts
│ ├── common_util_storage.py # 프로젝트 및 워크플로우 단계별 저장 경로
│ ├── common_util_json.py # JSON 원자적 저장
│ ├── common_util_workflow.py # 공유 workflow.json 상태 처리
│ └── common_util_format.py
├── db_management/ # DB 관리 폴더 (PostgreSQL 마이그레이션 및 스키마 관리)
│ ├── schema.sql
@@ -86,9 +89,17 @@ my-project/
│ └── B02_ProjRegister_UI_Style.css
├── B03_FileInput/ # 로그인 후 03: 파일 입력 (헤더+준비중 안내)
│ ├── B03_FileInput_UI_Page.ts
── B03_FileInput_UI_Style.css
── B03_FileInput_UI_Style.css
│ ├── B03_FileInput_Api_Fetch.ts # 다중 파일 업로드 API 클라이언트
│ ├── B03_FileInput_Schema.py # 업로드 파일 Pydantic 검증
│ ├── B03_FileInput_Engine.py # 업로드 저장 경로·스트림 처리
│ ├── B03_FileInput_Engine_Analyze.py # 원본 파일 메타데이터 분석
│ ├── B03_FileInput_Repository.py # input_files asyncpg Raw SQL
│ └── B03_FileInput_Router.py # 다중 파일 업로드 API
├── B04_wf1_Surface/ # 로그인 후 04: 1차 workflow (지표면 모델 분석) — 워크플로우 셸
── B04_wf1_Surface_UI_Page.ts
── B04_wf1_Surface_UI_Page.ts
│ ├── B04_wf1_Surface_Engine_Structurize.py # LAS/LAZ 구조화
│ └── B04_wf1_Surface_Engine_Filter_Grid.py # grid minimum-Z 필터
├── B05_wf2_Route/ # 로그인 후 05: 2차 workflow (경로설계) — 워크플로우 셸
│ └── B05_wf2_Route_UI_Page.ts
├── B06_wf3_ProfileCross/ # 로그인 후 06: 3차 workflow (종횡단 생성) — 워크플로우 셸