# core-api 아키텍처 문서 ## 1. 개요 IoT 임베디드 디바이스 통신, 사용자/관리자 관리, 모니터링, 데이터 분석을 지원하는 FastAPI 기반 통합 백엔드 서버이다. 향후 MSA 전환을 고려한 논리적 계층 분리를 핵심 설계 원칙으로 한다. **주요 특징:** - Flutter 웹/앱 프론트엔드 연동 - MariaDB + MongoDB 듀얼 DB - MQTT + Socket.IO 실시간 통신 - Celery 비동기 태스크 처리 - RBAC 기반 접근 제어 --- ## 2. 계층 아키텍처 ``` ┌─────────────────────────────────────────────────────────────┐ │ ASGI Entry Point │ │ Socket.IO ASGIApp (최외곽 래퍼) │ │ app/asgi.py │ ├─────────────────────────────────────────────────────────────┤ │ Middleware Stack │ │ RequestIDMiddleware → RequestLoggingMiddleware → CORS │ ├─────────────────────────────────────────────────────────────┤ │ API Layer │ │ app/api/v1/endpoints/*.py │ │ auth │ users │ devices │ monitoring │ analytics │ system│ ├─────────────────────────────────────────────────────────────┤ │ Service Layer │ │ app/services/*.py │ │ AuthService │ UserService │ DeviceService │ ... │ ├───────────────────────┬─────────────────────────────────────┤ │ Repository Layer │ Communication Layer │ │ app/repositories/*.py │ app/communication/ │ │ BaseRepository[T] │ mqtt/ │ socketio/ │ external/ │ ├───────────────────────┴─────────────────────────────────────┤ │ DB Layer │ │ MariaDB │ MongoDB │ Redis │ │ app/db/mariadb.py │ mongodb.py │ redis.py │ └─────────────────────────────────────────────────────────────┘ │ │ ┌───────┴───────┐ ┌────────┴────────┐ │ Celery Tasks │ │ Data Processing │ │ app/tasks/*.py │ │ app/processing/ │ └────────────────┘ └─────────────────┘ ``` ### 의존성 규칙 ``` API Layer → Service Layer → Repository Layer → DB Layer │ Communication Layer (MQTT, Socket.IO, External API) │ Task Layer (Celery) ``` - 각 계층은 **바로 아래 계층만** 의존한다. - 도메인 간 직접 Repository 호출을 금지하고, 반드시 Service 인터페이스를 통해 통신한다. - 이 규칙을 지키면 향후 MSA 전환 시 Service 인터페이스를 gRPC/HTTP 호출로 교체하는 것만으로 분리가 가능하다. --- ## 3. 디렉토리 구조 ``` python-api/ ├── pyproject.toml # 의존성 + 빌드 설정 ├── .env.example # 환경변수 템플릿 ├── alembic.ini # Alembic 마이그레이션 설정 ├── Dockerfile # 앱 이미지 ├── Dockerfile.worker # Celery 워커 이미지 ├── docker-compose.yml # 개발 환경 ├── docker-compose.prod.yml # 프로덕션 오버라이드 │ ├── alembic/ # DB 마이그레이션 │ ├── env.py │ ├── script.py.mako │ └── versions/ │ ├── scripts/ # 운영 스크립트 │ ├── init_db.py # DB 시드 데이터 │ ├── create_superuser.py # 관리자 계정 생성 │ ├── run_dev.sh │ ├── run_worker.sh │ └── run_beat.sh │ ├── docker/ │ └── mosquitto/mosquitto.conf │ ├── tests/ # 테스트 │ ├── conftest.py │ ├── unit/ │ ├── integration/ │ └── e2e/ │ ├── docs/ # 문서 │ └── app/ # 애플리케이션 루트 ├── main.py # FastAPI 앱 팩토리 + 라이프사이클 ├── asgi.py # ASGI 엔트리포인트 ├── core/ # 횡단 관심사 ├── middleware/ # HTTP 미들웨어 ├── db/ # DB 연결/세션 ├── models/ # DB 모델 (mariadb/ + mongodb/) ├── schemas/ # Pydantic DTO ├── repositories/ # 데이터 접근 계층 ├── services/ # 비즈니스 로직 ├── api/ # API 라우터 ├── admin/ # SQLAdmin 관리자 패널 ├── communication/ # MQTT, Socket.IO, 외부 API ├── tasks/ # Celery 비동기 태스크 ├── processing/ # 데이터 분석 파이프라인 └── utils/ # 범용 유틸리티 ``` --- ## 4. 기술 스택 | 카테고리 | 기술 | 용도 | |---------|------|------| | **Web Framework** | FastAPI + Uvicorn | ASGI 비동기 웹 서버 | | **MariaDB ORM** | SQLModel + SQLAlchemy(async) + aiomysql | 관계형 DB 비동기 ORM | | **Migration** | Alembic + pymysql | DB 스키마 마이그레이션 | | **MongoDB ODM** | Beanie + Motor | 문서 DB 비동기 ODM | | **Cache/Queue** | Redis (hiredis) | 캐싱 + Celery 브로커/백엔드 | | **Auth** | python-jose + passlib[bcrypt] | JWT 토큰 + 비밀번호 해싱 | | **MQTT** | fastapi-mqtt | IoT 디바이스 양방향 통신 | | **WebSocket** | python-socketio | 실시간 프론트엔드 푸시 | | **Background** | Celery + Flower | 비동기 태스크 + 모니터링 | | **Admin** | SQLAdmin | 관리자 대시보드 | | **Data** | Polars + Pandas + NumPy | 데이터 분석/집계/통계 | | **Logging** | structlog | 구조화 로깅 (JSON/Console) | | **Test** | pytest + pytest-asyncio + factory-boy | 테스트 프레임워크 | | **Lint** | Ruff + mypy + pre-commit | 코드 품질 | --- ## 5. 데이터 모델 ### 5.1 MariaDB (관계형 — SQLModel) ``` ┌──────────────┐ 1:1 ┌──────────────┐ │ User │────────────▶│ UserProfile │ │──────────────│ │──────────────│ │ id (PK) │ │ user_id (FK) │ │ email │ │ full_name │ │ hashed_pwd │ │ phone │ │ role │ │ organization │ │ is_active │ │ avatar_url │ │ is_verified │ └───────────────┘ │ last_login_at│ └──────┬───────┘ │ 1:N ▼ ┌──────────────┐ ┌───────────────┐ │ RefreshToken │ │ OAuthAccount │ │──────────────│ │───────────────│ │ user_id (FK) │ │ user_id (FK) │ │ token │ │ provider │ │ expires_at │ │ provider_uid │ │ is_revoked │ │ access_token │ └──────────────┘ └───────────────┘ ┌──────────────┐ N:1 ┌──────────────┐ │ Device │────────────▶│ DeviceGroup │ │──────────────│ │──────────────│ │ device_uid │ │ name │ │ name │ │ description │ │ device_type │ └──────────────┘ │ status │ │ firmware_ver │ │ ip_address │ │ owner_id(FK) │ │ last_seen_at │ └──────────────┘ ┌──────────────┐ ┌──────────────┐ │ AlertRule │ │ Alert │ │──────────────│ │──────────────│ │ metric │◀────────────│ rule_id (FK) │ │ condition │ │ device_id(FK)│ │ threshold │ │ severity │ │ severity │ │ message │ │ is_enabled │ │ is_ack │ └──────────────┘ └──────────────┘ ┌──────────────┐ ┌──────────────┐ │ SystemConfig │ │ AuditLog │ │──────────────│ │──────────────│ │ key (unique) │ │ user_id (FK) │ │ value │ │ action │ │ is_secret │ │ resource_type│ └──────────────┘ │ details │ └──────────────┘ ``` **공통 Mixin:** - `TimestampMixin` — `created_at`, `updated_at` (서버 기본값 + 자동 갱신) - `SoftDeleteMixin` — `is_deleted`, `deleted_at` (논리 삭제) ### 5.2 MongoDB (문서형 — Beanie) | Collection | 주요 필드 | 인덱스 | 비고 | |-----------|----------|--------|------| | `device_logs` | device_id, event_type, payload, timestamp | device_id, event_type, timestamp(desc) | TTL: 90일 | | `telemetry_data` | device_id, metrics(dict), timestamp | device_id, timestamp(desc), 복합(device_id+timestamp) | 시계열 | | `analytics_results` | analysis_type, parameters, result, device_id, period | analysis_type, device_id, created_at(desc) | 분석 결과 | | `notifications` | user_id, title, message, type, is_read | user_id, 복합(user_id+is_read), created_at(desc) | 사용자 알림 | --- ## 6. API 엔드포인트 ### 6.1 인증 (`/api/v1/auth`) | Method | Path | 설명 | 인증 | |--------|------|------|------| | POST | `/register` | 회원가입 → 토큰 반환 | - | | POST | `/login` | 로그인 → 토큰 반환 | - | | POST | `/refresh` | 리프레시 토큰으로 갱신 | - | | POST | `/logout` | 모든 리프레시 토큰 폐기 | Bearer | ### 6.2 사용자 (`/api/v1/users`) | Method | Path | 설명 | 권한 | |--------|------|------|------| | GET | `/me` | 내 정보 조회 | 인증됨 | | PATCH | `/me` | 내 프로필 수정 | 인증됨 | | GET | `/` | 사용자 목록 (페이징) | SUPERADMIN, ADMIN | | GET | `/{user_id}` | 사용자 상세 | SUPERADMIN, ADMIN | | POST | `/` | 사용자 생성 | SUPERADMIN, ADMIN | | PATCH | `/{user_id}` | 사용자 수정 | SUPERADMIN, ADMIN | | DELETE | `/{user_id}` | 사용자 삭제 (소프트) | SUPERADMIN, ADMIN | ### 6.3 디바이스 (`/api/v1/devices`) | Method | Path | 설명 | 권한 | |--------|------|------|------| | GET | `/` | 디바이스 목록 (페이징) | 인증됨 | | GET | `/{device_id}` | 디바이스 상세 | 인증됨 | | POST | `/` | 디바이스 등록 | SUPERADMIN, ADMIN, MANAGER | | PATCH | `/{device_id}` | 디바이스 수정 | SUPERADMIN, ADMIN, MANAGER | | DELETE | `/{device_id}` | 디바이스 삭제 (소프트) | SUPERADMIN, ADMIN | ### 6.4 모니터링 (`/api/v1/monitoring`) | Method | Path | 설명 | 권한 | |--------|------|------|------| | GET | `/health` | 시스템 상태 상세 | MANAGEMENT | | GET | `/alerts` | 미확인 알림 목록 | MANAGEMENT | | POST | `/alerts/{id}/acknowledge` | 알림 확인 처리 | 인증됨 | | GET | `/alert-rules` | 알림 규칙 목록 | MANAGEMENT | | POST | `/alert-rules` | 알림 규칙 생성 | 인증됨 | ### 6.5 분석 (`/api/v1/analytics`) | Method | Path | 설명 | 권한 | |--------|------|------|------| | GET | `/telemetry/{device_id}` | 텔레메트리 집계 | MANAGEMENT | | POST | `/reports/{device_id}` | 종합 리포트 생성 | MANAGEMENT | | GET | `/status/{device_id}` | 디바이스 상태 분석 | MANAGEMENT | | GET | `/trends/{device_id}` | 추세 분석 | MANAGEMENT | | GET | `/results` | 분석 결과 조회 | MANAGEMENT | ### 6.6 시스템 (`/api/v1/system`) | Method | Path | 설명 | 권한 | |--------|------|------|------| | GET | `/health` | 헬스체크 | - | | GET | `/info` | 시스템 정보 | SUPERADMIN | --- ## 7. 인증/인가 체계 ### 7.1 JWT 토큰 흐름 ``` 클라이언트 서버 │ │ │─── POST /auth/login ──────────▶│ │ {email, password} │ │ │── 비밀번호 검증 │ │── Access Token 생성 (30분) │ │── Refresh Token 생성 (7일) │ │── Refresh Token DB 저장 │◀── {access_token, │ │ refresh_token} ────────────│ │ │ │─── GET /api/v1/users/me ──────▶│ │ Authorization: Bearer {AT} │── decode_token() │ │── get_current_user_payload() │◀── {user data} ───────────────│ │ │ │─── POST /auth/refresh ────────▶│ │ {refresh_token} │── 기존 RT 폐기 │ │── 새 AT + RT 발급 │◀── {new tokens} ──────────────│ ``` ### 7.2 역할 기반 접근 제어 (RBAC) ``` SUPERADMIN (4) ─── 전체 시스템 관리, 시스템 설정 │ ADMIN (3) ─── 사용자/디바이스 관리, 삭제 권한 │ MANAGER (2) ─── 디바이스 등록/수정, 모니터링, 분석 │ USER (1) ─── 자기 프로필, 디바이스 조회 │ DEVICE (0) ─── 디바이스 전용 (MQTT 인증) ``` - `require_role(*roles)` — 허용된 역할만 접근 가능한 Dependency - `can_manage_user(actor_role, target_role)` — 상위 역할만 하위 역할 관리 가능 --- ## 8. 실시간 통신 ### 8.1 MQTT (IoT 디바이스 ↔ 서버) ``` ┌──────────┐ ┌──────────────┐ ┌──────────┐ │ Device │──MQTT───▶│ Mosquitto │──────────▶│ Server │ │ │◀──MQTT───│ (Broker) │◀──────────│ │ └──────────┘ └──────────────┘ └──────────┘ ``` **토픽 구조:** | 방향 | 토픽 패턴 | 용도 | |------|----------|------| | Device → Server | `devices/{uid}/telemetry` | 센서 데이터 전송 | | Device → Server | `devices/{uid}/status` | 상태 변경 알림 | | Device → Server | `devices/{uid}/log` | 이벤트 로그 | | Device → Server | `devices/{uid}/response` | 명령 응답 | | Server → Device | `devices/{uid}/command` | 원격 명령 | | Server → Device | `devices/{uid}/config` | 설정 변경 | | Server → Device | `devices/{uid}/ota` | 펌웨어 업데이트 | **데이터 파이프라인:** ``` MQTT 수신 → JSON 파싱 → MongoDB 저장 → Socket.IO 브로드캐스트 ``` ### 8.2 Socket.IO (서버 → 프론트엔드) | 네임스페이스 | 이벤트 | 설명 | |------------|--------|------| | `/monitoring` | `telemetry` | 실시간 텔레메트리 데이터 | | `/monitoring` | `device_status` | 디바이스 상태 변경 | | `/device` | `device_response` | 디바이스 명령 응답 | | `/device` | `send_command` | 클라이언트→디바이스 명령 전달 | | `/notification` | `notification` | 사용자 알림 푸시 | **Room 기반 구독:** - `device:{device_uid}` — 특정 디바이스 모니터링 - `user:{user_id}` — 특정 사용자 알림 --- ## 9. 비동기 태스크 (Celery) ### 9.1 큐 구성 ``` ┌─────────────┐ │ Redis │ ← Broker (redis://...6379/1) │ (Broker) │ ← Result Backend (redis://...6379/2) └──────┬──────┘ │ ┌────┴────────────────────────────────┐ │ Celery Workers │ ├─────────┬───────────┬───────────────┤ │ default │ analytics │ notifications │ devices │ │ queue │ queue │ queue │ queue │ ├─────────┼───────────┼───────────────┤ │ 토큰정리 │ 일간분석 │ 푸시알림 │ 헬스체크 │ │ 이메일 │ 디바이스분석 │ 대량알림 │ OTA배치 │ └─────────┴───────────┴───────────────┘ ``` ### 9.2 Beat 스케줄 (정기 실행) | 태스크 | 스케줄 | 큐 | 설명 | |--------|--------|-----|------| | `cleanup_expired_tokens` | 매일 03:00 | default | 만료/폐기된 리프레시 토큰 삭제 | | `check_device_health` | 5분마다 | devices | 10분 이상 미응답 디바이스를 OFFLINE 처리 | | `run_daily_analytics` | 매일 01:00 | analytics | 전일 텔레메트리 데이터 집계 및 저장 | --- ## 10. 미들웨어 파이프라인 요청은 아래 순서로 미들웨어를 통과한다: ``` 요청 ──▶ RequestIDMiddleware ──▶ RequestLoggingMiddleware ──▶ CORS ──▶ FastAPI Router │ │ │ X-Request-ID 생성/전파 │ method, path, status, elapsed_ms 로깅 │ structlog 컨텍스트 바인딩 │ ▼ ▼ 응답 ◀── X-Request-ID 헤더 추가 ◀── 로그 기록 ◀────────────────────── 핸들러 응답 ``` **RateLimitMiddleware** (선택 사용): - Redis 기반 IP별 속도 제한 - 기본값: 100 요청/60초 - `/docs`, `/redoc` 경로 제외 --- ## 11. 데이터 분석 파이프라인 ``` ┌─────────────────┐ ┌──────────────────┐ ┌────────────────┐ │ TelemetryData │────▶│ Polars/Pandas │────▶│ AnalyticsResult│ │ (MongoDB) │ │ Processing │ │ (MongoDB) │ └─────────────────┘ └──────────────────┘ └────────────────┘ │ ┌─────────┴──────────┐ │ │ ┌──────┴──────┐ ┌──────┴──────┐ │ Aggregation │ │ Analysis │ │ (Polars) │ │ (NumPy) │ │ │ │ │ │ - resample │ │ - trend │ │ - group_by │ │ - anomaly │ │ - mean/sum │ │ - percentile│ └─────────────┘ └─────────────┘ ``` | 모듈 | 기능 | |------|------| | `telemetry_pipeline` | 시간 간격별 텔레메트리 집계 (Polars group_by_dynamic) | | `report_pipeline` | 디바이스 종합 리포트 생성 (상태 + 추세) | | `device_analyzer` | 디바이스 상태 변경 분석, 가동률 계산 | | `trend_analyzer` | 선형 회귀 기반 추세 분석 (NumPy polyfit) | | `statistics` | 이동평균, Z-score 이상치 탐지, 백분위 통계 | --- ## 12. MSA 전환 전략 현재 모놀리식 구조에서 각 도메인을 독립 서비스로 분리할 수 있는 논리적 경계가 이미 설정되어 있다. | 도메인 | 현재 패키지 | 향후 MSA 서비스 | 주 DB | |--------|------------|----------------|-------| | auth | auth_service + auth_repo | Auth Service | MariaDB + Redis | | users | user_service + user_repo | User Service | MariaDB | | devices | device_service + device_repo | Device Service | MariaDB + MongoDB | | monitoring | monitoring_service + monitoring_repo | Monitoring Service | MongoDB + Redis | | analytics | analytics_service + analytics_repo | Analytics Service | MongoDB | | notification | notification_service + socketio | Notification Service | Redis + MongoDB | **전환 시 변경점:** 1. Service 간 호출을 HTTP/gRPC 클라이언트로 교체 2. 각 서비스별 독립 DB 인스턴스 할당 3. 이벤트 버스(Kafka/RabbitMQ)로 서비스 간 비동기 통신 4. API Gateway 도입 (인증/라우팅 통합) --- ## 13. 배포 구성 ### 13.1 Docker 서비스 ``` ┌─────────────┐ │ Mosquitto │:1883 (MQTT) └──────┬──────┘ │ ┌──────────┐ ┌──────┴──────┐ ┌──────────┐ │ Redis │:6379 ◀─│ App │:8000 ──▶│ MariaDB │ (호스트) └────┬─────┘ │ (FastAPI) │ └──────────┘ │ └──────┬──────┘ ┌──────────┐ │ │ ────▶│ MongoDB │ (호스트) │ ┌──────┴──────┐ └──────────┘ ├──────────────│ Worker │ │ │ (Celery) │ │ └─────────────┘ │ ┌─────────────┐ ├──────────────│ Beat │ │ │ (Scheduler) │ │ └─────────────┘ │ ┌─────────────┐ └──────────────│ Flower │:5555 (모니터링) └─────────────┘ ``` - MariaDB/MongoDB는 **호스트에 직접 설치** (Docker 외부) - App, Worker, Beat, Flower, Redis, Mosquitto는 **Docker 컨테이너**로 관리 ### 13.2 환경별 차이 | 항목 | 개발 (docker-compose.yml) | 프로덕션 (docker-compose.prod.yml) | |------|--------------------------|-----------------------------------| | App 워커 수 | 1 (reload 모드) | 4 | | Celery 동시성 | 2 | 4 | | Worker 레플리카 | 1 | 2 | | 로그 레벨 | info | warning | | Flower 인증 | 없음 | basic_auth | | 볼륨 마운트 | 소스 코드 (.:/app) | 이미지 내장 | --- ## 14. 실행 방법 ### 개발 환경 ```bash # 1. 환경변수 설정 cp .env.example .env # .env 파일에서 DB 접속 정보 등 수정 # 2. 의존성 설치 pip install -e ".[dev]" # 3. DB 마이그레이션 alembic upgrade head # 4. 시드 데이터 python -m scripts.init_db # 5. 슈퍼관리자 생성 python -m scripts.create_superuser admin@example.com password123 # 6. 인프라 (Redis + Mosquitto) docker-compose up -d redis mosquitto # 7. 앱 서버 실행 uvicorn app.asgi:app --reload # 8. Celery 워커 (별도 터미널) celery -A app.tasks.celery_app worker --loglevel=info -Q default,analytics,notifications,devices # 9. Celery 스케줄러 (별도 터미널) celery -A app.tasks.celery_app beat --loglevel=info ``` ### Docker 전체 스택 ```bash # 개발 docker-compose up -d # 프로덕션 docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d ``` ### 검증 ```bash # 헬스체크 curl http://localhost:8000/api/v1/system/health # Swagger UI open http://localhost:8000/docs # Flower (Celery 모니터링) open http://localhost:5555 # SQLAdmin (관리자) open http://localhost:8000/admin ``` --- ## 15. 테스트 ```bash # 전체 테스트 pytest tests/ -v # 커버리지 pytest tests/ -v --cov=app --cov-report=html # 단위 테스트만 pytest tests/unit/ -v # 통합 테스트만 pytest tests/integration/ -v ``` **테스트 구조:** - `tests/unit/` — 순수 함수 테스트 (보안, 검증, 통계, 권한) - `tests/integration/` — API 엔드포인트 + DB 연동 테스트 - `tests/e2e/` — 전체 CRUD 플로우 테스트