Files
Aislo/.agent/backend.md
T

9.6 KiB

백엔드 제어 명세서 (backend.md)

1. 아키텍처 및 라우팅 (Architecture)

  • 수직 통합: 백엔드 로직은 페이지 폴더(A01_, B01_ 등)별로 격리. 거대 단일 모듈 금지.
  • 파일 분할: 파일 수 제한 없음. 코드 집중 방지를 위해 기능별 분할 자율화.
  • 파일명 규칙: [폴더명]_Router.py, [폴더명]_Engine.py, [폴더명]_Schema.py 형태 준수.
  • 루트 main.py: 라우터 등록(include_router)만 수행. 비즈니스 로직 작성 금지.

2. DB 및 MariaDB (Database)

  • 비동기 통신: async/awaitaiomysql 드라이버 사용 강제. (asyncmy는 Cython 빌드 필요로 Windows 환경 미채택)
  • 쿼리 방식: ORM 사용 금지. Raw SQL 작성 원칙. 파라미터 바인딩은 %s 플레이스홀더 사용.
  • 커넥션/트랜잭션: 풀에서 pool.acquire()로 커넥션 확보 후 connection.cursor()로 커서 생성. 다건 쓰기는 connection.begin()commit() / 예외 시 rollback().
  • 자동 증가 ID: INSERTcursor.lastrowid로 조회 (PostgreSQL의 RETURNING id 미지원).
  • 공간 데이터: GEOMETRY 타입 미지원. 좌표, 다각형, 경로 등은 JSON 형식으로 저장 후 애플리케이션에서 처리.

3. 설정 및 저장소 (Config & Storage)

  • 하드코딩 금지: 제어 변수/파라미터 하드코딩 금지. 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 파싱 후 기하 연산 처리

4. 검증 및 예외 처리 (Error Handling)

  • 데이터 검증: 모든 JSON 요청은 라우터 진입 전 Pydantic 모델로 타입/범위 필수 검증.
  • 예외 포획: 모든 지형/메쉬 연산은 try-except 필수 처리.
  • 반환 포맷: 에러 발생 시 {"status": "error", "message": "원인"} 표준 포맷 반환.

5. 워크플로우 상태 동기화 (Workflow State Sync)

5.1 다중 브라우저 동시 작업 원칙

  • 브라우저 독립성: 같은 계정/프로젝트를 여러 브라우저에서 동시 접근 가능. 각 브라우저는 서버 데이터 기반 독립 작동.
  • 클라이언트 상태 격리: localStorage는 브라우저별 격리(도메인 단위 공유). 공유 데이터는 항상 서버 영구저장소 우선.
  • 영구저장소 설계: 계산 결과는 .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 구조:
    {
      "current_stage": "route",
      "completed": ["scan", "surface"],
      "stale_from": "route",
      "stage1_confirmed": {...}
    }
    
  • Stale 발생 조건:
    • 상위 단계(예: surface) 재계산 → 하위 단계(route, section) 자동으로 stale_from 설정
    • 다른 브라우저에서 변경 감지 → 기존 결과 무효화
  • 상태 업데이트 함수:
    def _patch_workflow_stale(project_id: str, stale_from: str | None) -> None:
        """workflow.json의 stale_from 필드만 원자적 업데이트"""
        wf_path = get_project_workflow_path(project_id)
        # 기존 상태 유지하며 stale_from만 변경
    

5.3 클라이언트 폴링 패턴 (Polling)

  • 주기: GET /api/projects/{project_id}/workflow — 3초마다 호출
  • 응답: workflow.json 전체 반환 (현재 단계, 완료 이력, stale 상태 포함)
  • 클라이언트 동작:
    1. stale 감지 → 사용자에게 선택지 제시
    2. "계속하기" → 기존 결과 유지 (위험 경고)
    3. "재계산하기" → 상위 단계 재실행 (모든 하위 결과 초기화)
  • 구현 예시:
    setInterval(async () => {
      const workflow = await fetch(`/api/projects/${projectId}/workflow`);
      if (workflow.stale_from && workflow.stale_from !== currentStage) {
        handleStaleness(workflow.stale_from);  // 사용자에게 알림
      }
    }, 3000);
    

5.4 WebSocket 확장 (미래 선택사항)

  • 실시간 알림: 폴링 대신 WebSocket으로 업그레이드 가능 (응답성 향상)
  • 전송 메시지:
    {
      "type": "workflow_changed",
      "project_id": "...",
      "stale_from": "route",
      "changed_by": "other_browser"
    }
    
  • 마이그레이션 시기: 사용자 수 증가 또는 실시간성 요구 시 적용

6. 로그인 및 인증 시스템 (Authentication & Authorization)

6.1 이메일 기반 OTP 인증 (Email OTP)

  • OTP 생성: 6자리 난수 (000000~999999)
  • 저장 방식: DB에 bcrypt/PBKDF2 해시값 저장 (평문 절대 금지)
  • 유효 시간: 5분 (config에서 설정 가능)
  • 발송: Google SMTP (개발) → AWS SES (상용화) 전환 예정
  • 재시도 로직: 최대 3회 (지수 백오프: 2초, 4초, 8초)

6.2 비밀번호 정책 (Password Policy)

  • 최소 길이: 8자 이상
  • 권장 구성: 대문자/소문자/숫자/특수문자 (강제 아님, Google 기준)
  • 해시 알고리즘: bcrypt (라운드 12 이상) 또는 PBKDF2
  • 저장 방식: 해시값만 DB에 저장 (평문 절대 금지)

6.3 세션 관리 (Session Management) - 확정: Secure HttpOnly 세션 쿠키

  • 방식: Secure HttpOnly 세션 쿠키 (JWT 아님)
  • 쿠키 이름: session_id
  • 유효 시간: 12시간
  • Secure 플래그: true (HTTPS만 전송)
  • HttpOnly 플래그: true (JavaScript 접근 불가)
  • SameSite: Lax (CSRF 방지)
  • 저장소: MariaDB sessions 테이블
  • 활동 감지: 마지막 요청으로부터 4시간 (파일 업로드 등 장시간 작업 고려)
  • 세션 만료 판정:
    if (current_time - last_activity_at > 4시간) or (current_time - created_at > 12시간):
         세션 자동 만료
    
  • 만료 처리: 프론트엔드는 만료 5분 전 알림, 만료 후 모든 요청 거부 (401 Unauthorized)

6.4 로그인 이력 기록 (Audit Log)

  • 기록 항목:
    • user_id, email, login_timestamp, user_agent (브라우저/OS), status (SUCCESS/FAILURE)
  • 보관 기간: 1년 (자동 삭제)
  • 무차별 대입 방지: 5회 이상 실패 시 계정 15분 잠금
  • IP 주소: 법적 이슈로 수집하지 않음 (권장)

6.5 권한 체크 (Authorization)

  • 마스터 권한:
    • 팀원 목록 조회, 승인/거부, 내보내기
    • 회사 정보 수정, 구독 관리
    • 마스터 대시보드 접근
  • 팀원 권한:
    • 프로젝트 생성/수정/조회
    • 워크플로우 실행 (B03~B09)
    • 개인 프로필 수정
  • 시스템관리자 권한:
    • 모든 회사/사용자 데이터 접근 (슈퍼유저)
    • 회사/사용자 생성/수정/삭제
    • 관리자 대시보드 (모니터링, Q&A 관리)
  • 검증 방식: 라우터 진입 전 JWT/토큰 검증, 필요시 데이터베이스에서 권한 확인

6.6 3개월 주기 재인증 (Periodic Re-authentication)

  • 목적: 재직 여부 확인 (퇴사자 자동 차단)
  • 방식: 3개월마다 이메일 OTP 재인증 강제
  • 구현: users.last_email_verified_at 컬럼으로 추적
  • 예외: 새로운 기기에서 로그인 시 즉시 재인증 요구

6.7 이메일 발송 (Email Infrastructure)

  • SMTP 설정:
    • 개발: Google Gmail SMTP (umsangdon@gmail.com)
    • 상용화: Google Workspace 또는 AWS SES로 전환
  • 발송 유형:
    • OTP 인증 코드
    • 가입 확인 메일
    • 팀원 가입 승인 요청 (마스터 수신)
    • 팀원 승인/거부 결과
    • 3개월 재인증 알림
  • 구현: common_util/common_util_email.py 모듈
    • 비동기 발송 (asyncio.create_task)
    • 재시도 로직 (최대 3회)
    • 템플릿 렌더링 (common_util_email_templates.py)
  • 자세한 내용: .agent/email_infrastructure_plan.md 참고