Files
Aislo/.agent/backend.md
T

14 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만 전송, 개발 환경 localhost에서는 false)
  • 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)
  • DB 풀 설정: autocommit=True (pooled 커넥션의 트랜잭션 스냅샷 문제 방지 — 새 세션 INSERT 후 다른 커넥션에서 즉시 조회 가능)

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

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

6.4.1 사용자 상태 생명주기 (User Status Lifecycle)

  • PENDING_EMAIL: 회원가입 후 이메일 인증 전 (임시)
  • NO_COMPANY: 이메일 인증 완료, 회사 미연결 (로그인 가능, B01_Dashboard 접근 가능, B02~B11 워크플로우 차단)
  • PENDING: 회사 참여 신청 후 ADMIN 승인 대기 (로그인 가능, B01_Dashboard 접근 가능, B02~B11 워크플로우 차단)
  • ACTIVE: 회사 연결 완료, 모든 기능 접근 가능 (B01_Dashboard + B02~B11)
  • INACTIVE: 퇴사/계정 휴활성화
  • REJECTED: 회사 참여 신청 거부

6.5 회사 연결 필수화 (Mandatory Company Linking)

  • 접근 제어:
    • B02~B11 워크플로우 라우터에 Depends(require_company) 의존성 추가
    • session["company_id"] is None 이면 403 Forbidden 반환 ("회사 연결이 필요합니다.")
  • 라우팅 가드 (프론트):
    • B02~B11 진입 시 fetchSessionUser()company_id 확인
    • 없으면 B01_ACCOUNT로 리다이렉트
  • 회사 생성/연결:
    • POST /api/account/company/create: 신규 회사 생성 후 사용자를 MASTER로 자동 연결 (status='ACTIVE')
    • POST /api/account/company/join: 기존 회사 참여 신청 (status='PENDING', 마스터 승인 필요)

6.6 권한 체크 (Authorization)

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

6.7 로그인 기반 인증 갱신 (Login-based Auth Renewal)

  • 목적: 활성 사용자의 인증 유지, 비활성 사용자 자동 차단
  • 방식: 로그인 시 인증 유효 기간 자동 연장 (3개월)
  • 구현:
    • users.last_login → 마지막 로그인 시간 업데이트
    • users.auth_expires_at → 매 로그인 시 NOW() + 3개월로 설정
  • 인증 확인:
    • auth_expires_at > NOW() → 인증 유효 (바로 대시보드 이동)
    • auth_expires_at <= NOW() → 재인증 모달 표시
  • 장점:
    • 활동적인 사용자: 지속적으로 인증 유지
    • 휴면 사용자: 자동으로 재인증 요구
    • 일관된 정책: 고정 3개월이 아닌 활동 기반 연장

6.8 역할 기반 접근 제어 (Role-Based Access Control)

사용자 역할 (users.role)

  • SYSTEM_ADMIN: 시스템 관리자 (최고 권한)
  • ADMIN: 회사 관리자 (회사 내 관리권 한정)
  • USER: 일반 사용자

마스터 플래그 (users.is_master)

  • TRUE: 회사 생성자 또는 ADMIN 역할 (팀원 승인/거부 권한)
  • FALSE: 일반 팀원

접근 제어 규칙:

기능 SYSTEM_ADMIN ADMIN USER
전체 회사 조회
전체 사용자 조회
사용자 역할 변경
회사 생성
회사 팀원 관리 (자신 회사만)
가입 요청 승인 (자신 회사만)
시스템 로그 조회
프로젝트 생성
프로젝트 조회 (전체) (회사별) (개인)

6.9 이메일 발송 (Email Infrastructure)

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

7. 시스템 리소스 모니터링 (System Resource Monitoring)

7.1 계측 백그라운드 태스크

  • 구현: common_util/common_util_resource_monitor.pysample_resources_loop()
  • 등록: main.py lifespan에서 asyncio.create_task로 시작, 종료 시 cancel (기존 cleanup_expired_sessions 패턴과 동일)
  • 계측 주기: 2분 (config_system.RESOURCE_SAMPLE_INTERVAL_SEC = 120)
  • 계측 항목: CPU(psutil.cpu_percent), 메모리(psutil.virtual_memory().percent), 디스크(shutil.disk_usage)
  • 방어: 계측 예외는 로깅 후 무시하여 앱 안정성 유지

7.2 이중 기록 (DB + 로그 파일)

  • DB 기록: system_resources 테이블에 INSERT (활성 세션/프로젝트 수 포함) — 대시보드 그래프 조회 소스
  • 로그 파일: 루트 log/system_resources.log 단일 파일에 탭 구분 1줄 append
    • 형식: <ISO timestamp>\tcpu=<v>\tmem=<v>\tdisk=<v>\tstorage_mb=<v>
    • log/.gitignore 처리 (런타임 생성물)

7.3 1개월 롤링 보관 (Retention)

  • 보관 기간: 30일 (config_system.RESOURCE_LOG_RETENTION_DAYS = 30)
  • DB: 매 계측 시 timestamp < NOW() - 30일 행 DELETE
  • 로그 파일: 매 계측 시 30일 경과 줄 제거 후 새 줄 추가(오래된 줄 삭제 + 신규 줄 추가 방식) → 파일 크기 무한 증가 방지

7.4 리소스 API

  • 엔드포인트: GET /api/dashboard/admin/resources?days=30 (SYSTEM_ADMIN 전용, 범위 1~90일)
  • 응답:
    • current: 요청 시점 실시간 계측값 (그래프 미저장)
    • history: system_resources 테이블의 최근 N일 시계열 (그래프 렌더링용)
    • stats: 활성 사용자/프로젝트/저장소 지표