26 KiB
26 KiB
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)— 허용된 역할만 접근 가능한 Dependencycan_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 |
전환 시 변경점:
- Service 간 호출을 HTTP/gRPC 클라이언트로 교체
- 각 서비스별 독립 DB 인스턴스 할당
- 이벤트 버스(Kafka/RabbitMQ)로 서비스 간 비동기 통신
- 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. 실행 방법
개발 환경
# 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 전체 스택
# 개발
docker-compose up -d
# 프로덕션
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
검증
# 헬스체크
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. 테스트
# 전체 테스트
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 플로우 테스트