초기 커밋

This commit is contained in:
2026-03-01 07:44:19 +09:00
commit 09359f30be
146 changed files with 6120 additions and 0 deletions

564
docs/API_REFERENCE.md Normal file
View File

@@ -0,0 +1,564 @@
# API 레퍼런스
> Base URL: `http://localhost:8000/api/v1`
> 인증: `Authorization: Bearer <access_token>`
---
## 인증 (Auth)
### POST `/auth/register`
회원가입 후 토큰을 발급한다.
**Request Body:**
```json
{
"email": "user@example.com",
"password": "securepassword",
"full_name": "홍길동"
}
```
**Response (201):**
```json
{
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"token_type": "bearer"
}
```
**에러:**
- `409` — 이미 등록된 이메일
---
### POST `/auth/login`
이메일/비밀번호로 로그인한다.
**Request Body:**
```json
{
"email": "user@example.com",
"password": "securepassword"
}
```
**Response (200):**
```json
{
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"token_type": "bearer"
}
```
**에러:**
- `401` — 잘못된 이메일/비밀번호 또는 비활성 계정
---
### POST `/auth/refresh`
리프레시 토큰으로 새 토큰 쌍을 발급한다. 기존 리프레시 토큰은 폐기된다.
**Request Body:**
```json
{
"refresh_token": "eyJ..."
}
```
**Response (200):**
```json
{
"access_token": "eyJ...(new)",
"refresh_token": "eyJ...(new)",
"token_type": "bearer"
}
```
---
### POST `/auth/logout`
현재 사용자의 모든 리프레시 토큰을 폐기한다.
**Headers:** `Authorization: Bearer <access_token>`
**Response:** `204 No Content`
---
## 사용자 (Users)
### GET `/users/me`
현재 로그인한 사용자의 정보를 조회한다.
**Response (200):**
```json
{
"id": 1,
"email": "user@example.com",
"role": "user",
"is_active": true,
"is_verified": false,
"full_name": "홍길동",
"phone": "010-1234-5678",
"organization": "ACME Corp",
"avatar_url": "",
"created_at": "2025-01-01T00:00:00"
}
```
---
### PATCH `/users/me`
자신의 프로필 정보를 수정한다. `role``is_active`는 변경할 수 없다.
**Request Body (partial):**
```json
{
"full_name": "김철수",
"phone": "010-9999-8888"
}
```
---
### GET `/users`
사용자 목록을 페이징 조회한다.
**권한:** SUPERADMIN, ADMIN
**Query Parameters:**
| 파라미터 | 타입 | 기본값 | 설명 |
|---------|------|--------|------|
| page | int | 1 | 페이지 번호 |
| size | int | 20 | 페이지 크기 (max 100) |
**Response (200):**
```json
{
"items": [{ ... }],
"total": 150,
"page": 1,
"size": 20,
"pages": 8
}
```
---
### GET `/users/{user_id}`
특정 사용자를 조회한다.
**권한:** SUPERADMIN, ADMIN
---
### POST `/users`
사용자를 생성한다.
**권한:** SUPERADMIN, ADMIN
**Request Body:**
```json
{
"email": "new@example.com",
"password": "password123",
"role": "manager",
"full_name": "박매니저",
"phone": "010-1111-2222",
"organization": "Dev Team"
}
```
---
### PATCH `/users/{user_id}`
사용자 정보를 수정한다. 관리자는 역할과 활성 상태도 변경할 수 있다.
**권한:** SUPERADMIN, ADMIN
---
### DELETE `/users/{user_id}`
사용자를 소프트 삭제한다.
**권한:** SUPERADMIN, ADMIN
**Response:** `204 No Content`
---
## 디바이스 (Devices)
### GET `/devices`
디바이스 목록을 페이징 조회한다.
**Query Parameters:**
| 파라미터 | 타입 | 기본값 | 설명 |
|---------|------|--------|------|
| page | int | 1 | 페이지 번호 |
| size | int | 20 | 페이지 크기 (max 100) |
**Response (200):**
```json
{
"items": [
{
"id": 1,
"device_uid": "sensor-temp-001",
"name": "1층 온도센서",
"device_type": "temperature",
"status": "online",
"firmware_version": "1.2.0",
"ip_address": "192.168.1.100",
"group_id": 1,
"owner_id": 2,
"last_seen_at": "2025-01-15T12:00:00",
"created_at": "2025-01-01T00:00:00"
}
],
"total": 50,
"page": 1,
"size": 20,
"pages": 3
}
```
---
### GET `/devices/{device_id}`
특정 디바이스 상세를 조회한다.
---
### POST `/devices`
디바이스를 등록한다.
**권한:** SUPERADMIN, ADMIN, MANAGER
**Request Body:**
```json
{
"device_uid": "sensor-temp-001",
"name": "1층 온도센서",
"device_type": "temperature",
"group_id": 1,
"owner_id": 2,
"firmware_version": "1.0.0"
}
```
**에러:**
- `409` — 이미 등록된 device_uid
---
### PATCH `/devices/{device_id}`
디바이스 정보를 수정한다.
**권한:** SUPERADMIN, ADMIN, MANAGER
**Request Body (partial):**
```json
{
"name": "수정된 센서명",
"status": "maintenance"
}
```
---
### DELETE `/devices/{device_id}`
디바이스를 소프트 삭제한다.
**권한:** SUPERADMIN, ADMIN
**Response:** `204 No Content`
---
## 모니터링 (Monitoring)
### GET `/monitoring/health`
시스템 상태를 상세 조회한다.
**권한:** SUPERADMIN, ADMIN, MANAGER
**Response (200):**
```json
{
"status": "ok",
"mariadb": "connected",
"mongodb": "connected",
"redis": "connected",
"mqtt": "connected",
"active_devices": 42,
"active_alerts": 3
}
```
---
### GET `/monitoring/alerts`
미확인 알림 목록을 조회한다.
**권한:** SUPERADMIN, ADMIN, MANAGER
**Query Parameters:**
| 파라미터 | 타입 | 기본값 | 설명 |
|---------|------|--------|------|
| skip | int | 0 | 건너뛸 수 |
| limit | int | 50 | 최대 개수 (max 200) |
**Response (200):**
```json
[
{
"id": 1,
"rule_id": 3,
"device_id": 5,
"severity": "warning",
"message": "Temperature exceeded threshold",
"is_acknowledged": false,
"acknowledged_by": null,
"acknowledged_at": null,
"created_at": "2025-01-15T10:30:00"
}
]
```
---
### POST `/monitoring/alerts/{alert_id}/acknowledge`
알림을 확인 처리한다.
---
### GET `/monitoring/alert-rules`
알림 규칙 목록을 조회한다.
---
### POST `/monitoring/alert-rules`
알림 규칙을 생성한다.
**Request Body:**
```json
{
"name": "고온 경고",
"description": "온도가 40도를 초과하면 알림",
"metric": "temperature",
"condition": "gt",
"threshold": 40.0,
"severity": "warning",
"device_group_id": 1
}
```
---
## 분석 (Analytics)
### GET `/analytics/telemetry/{device_id}`
특정 디바이스의 텔레메트리 데이터를 시간 간격별로 집계한다.
**권한:** SUPERADMIN, ADMIN, MANAGER
**Query Parameters:**
| 파라미터 | 타입 | 필수 | 설명 |
|---------|------|------|------|
| start | datetime | O | 시작 시각 (ISO 8601) |
| end | datetime | O | 종료 시각 (ISO 8601) |
| interval | string | X | 집계 간격 (기본값: `1h`) |
**Response (200):**
```json
{
"device_id": "sensor-temp-001",
"records": [
{"timestamp": "2025-01-15T10:00:00", "temperature": 23.5, "humidity": 45.2},
{"timestamp": "2025-01-15T11:00:00", "temperature": 24.1, "humidity": 44.8}
],
"count": 2
}
```
---
### POST `/analytics/reports/{device_id}`
디바이스 종합 리포트를 생성한다 (상태 분석 + 추세 분석).
**Query Parameters:** `start`, `end` (ISO 8601)
**Response (200):**
```json
{
"report_id": "65a1b2c3...",
"device_id": "sensor-temp-001",
"status": {
"total_events": 150,
"status_counts": {"online": 140, "offline": 10},
"uptime_ratio": 0.9333
},
"trends": {
"count": 720,
"mean": 23.5,
"std": 1.2,
"min": 20.1,
"max": 28.3,
"slope": 0.0023,
"trend": "stable"
},
"created_at": "2025-01-15T12:00:00"
}
```
---
### GET `/analytics/status/{device_id}`
디바이스 상태 변경 이력을 분석한다.
---
### GET `/analytics/trends/{device_id}`
텔레메트리 데이터의 추세를 분석한다 (선형 회귀).
---
### GET `/analytics/results`
저장된 분석 결과를 조회한다.
**Query Parameters:**
| 파라미터 | 타입 | 필수 | 설명 |
|---------|------|------|------|
| analysis_type | string | O | 분석 유형 |
| device_id | string | X | 디바이스 ID |
| skip | int | X | 건너뛸 수 (기본 0) |
| limit | int | X | 최대 개수 (기본 20) |
---
## 시스템 (System)
### GET `/system/health`
서비스 헬스체크. 인증 불필요.
**Response (200):**
```json
{
"status": "ok",
"service": "core-api",
"version": "0.1.0"
}
```
---
### GET `/system/info`
시스템 설정 정보를 조회한다.
**권한:** SUPERADMIN
---
## 공통 에러 응답
모든 에러는 다음 형식으로 반환된다:
```json
{
"detail": "에러 메시지"
}
```
| 상태 코드 | 설명 |
|----------|------|
| 401 | 인증 실패 (토큰 없음/만료/유효하지 않음) |
| 403 | 권한 부족 |
| 404 | 리소스를 찾을 수 없음 |
| 409 | 리소스 충돌 (중복) |
| 422 | 요청 데이터 검증 실패 |
| 429 | 요청 속도 제한 초과 |
| 500 | 서버 내부 오류 |
---
## Socket.IO 이벤트
> 연결: `io("http://localhost:8000", { path: "/socket.io/" })`
### 네임스페이스: `/monitoring`
| 이벤트 | 방향 | 데이터 | 설명 |
|--------|------|--------|------|
| `subscribe_device` | Client → Server | `{ device_uid }` | 디바이스 모니터링 구독 |
| `unsubscribe_device` | Client → Server | `{ device_uid }` | 구독 해제 |
| `telemetry` | Server → Client | `{ device_uid, data }` | 실시간 텔레메트리 |
| `device_status` | Server → Client | `{ device_uid, status }` | 상태 변경 |
### 네임스페이스: `/device`
| 이벤트 | 방향 | 데이터 | 설명 |
|--------|------|--------|------|
| `send_command` | Client → Server | `{ device_uid, command }` | 디바이스 명령 전송 |
| `device_response` | Server → Client | `{ device_uid, data }` | 명령 응답 |
### 네임스페이스: `/notification`
| 이벤트 | 방향 | 데이터 | 설명 |
|--------|------|--------|------|
| `join_user_room` | Client → Server | `{ user_id }` | 알림 수신 등록 |
| `notification` | Server → Client | `{ title, message, type }` | 알림 푸시 |
---
## MQTT 토픽
> 브로커: `mqtt://localhost:1883`
### Device → Server
| 토픽 | 페이로드 | 처리 |
|------|---------|------|
| `devices/{uid}/telemetry` | `{ temperature: 23.5, ... }` | MongoDB 저장 + Socket.IO 브로드캐스트 |
| `devices/{uid}/status` | `{ status: "online" }` | MongoDB 로그 + Socket.IO 전송 |
| `devices/{uid}/log` | `{ event_type: "...", ... }` | MongoDB 저장 |
| `devices/{uid}/response` | `{ command_id: "...", ... }` | Socket.IO 전송 |
### Server → Device
| 토픽 | 페이로드 | 용도 |
|------|---------|------|
| `devices/{uid}/command` | `{ action: "restart" }` | 원격 명령 |
| `devices/{uid}/config` | `{ interval: 30 }` | 설정 변경 |
| `devices/{uid}/ota` | `{ url: "...", action: "update" }` | 펌웨어 업데이트 |

595
docs/ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,595 @@
# 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 플로우 테스트

338
docs/DATABASE.md Normal file
View File

@@ -0,0 +1,338 @@
# 데이터베이스 설계 문서
## 1. 개요
| DB | 용도 | 연결 방식 |
|----|------|----------|
| **MariaDB** | 사용자, 인증, 디바이스, 알림 규칙, 시스템 설정 | SQLModel + SQLAlchemy (async, aiomysql) |
| **MongoDB** | 디바이스 로그, 텔레메트리, 분석 결과, 알림 | Beanie + Motor (async) |
| **Redis** | 캐싱, 세션, 속도 제한, Celery 브로커 | redis-py (async, hiredis) |
---
## 2. MariaDB 스키마
### 2.1 users
사용자 계정 정보.
| 컬럼 | 타입 | 제약 | 설명 |
|------|------|------|------|
| id | INT | PK, AUTO_INCREMENT | |
| email | VARCHAR(255) | UNIQUE, INDEX | 로그인 ID |
| hashed_password | VARCHAR(255) | NOT NULL | bcrypt 해시 |
| role | VARCHAR(20) | DEFAULT 'user' | superadmin/admin/manager/user/device |
| is_active | BOOLEAN | DEFAULT TRUE | 계정 활성 여부 |
| is_verified | BOOLEAN | DEFAULT FALSE | 이메일 인증 여부 |
| last_login_at | DATETIME | NULLABLE | 마지막 로그인 |
| is_deleted | BOOLEAN | DEFAULT FALSE | 소프트 삭제 |
| deleted_at | DATETIME | NULLABLE | 삭제 시각 |
| created_at | DATETIME | server_default=NOW() | |
| updated_at | DATETIME | onupdate=NOW() | |
### 2.2 user_profiles
사용자 프로필 (1:1).
| 컬럼 | 타입 | 제약 | 설명 |
|------|------|------|------|
| id | INT | PK | |
| user_id | INT | FK→users.id, UNIQUE | |
| full_name | VARCHAR(100) | | 이름 |
| phone | VARCHAR(20) | | 전화번호 |
| organization | VARCHAR(100) | | 소속 |
| avatar_url | VARCHAR(500) | | 프로필 이미지 |
| created_at | DATETIME | | |
| updated_at | DATETIME | | |
### 2.3 refresh_tokens
리프레시 토큰 저장소. 토큰 순환(rotation) 방식 사용.
| 컬럼 | 타입 | 제약 | 설명 |
|------|------|------|------|
| id | INT | PK | |
| user_id | INT | FK→users.id, INDEX | |
| token | VARCHAR(500) | UNIQUE, INDEX | JWT 리프레시 토큰 |
| expires_at | DATETIME | NOT NULL | 만료 시각 |
| is_revoked | BOOLEAN | DEFAULT FALSE | 폐기 여부 |
| device_info | VARCHAR(255) | | 접속 디바이스 정보 |
| created_at | DATETIME | | |
| updated_at | DATETIME | | |
### 2.4 oauth_accounts
소셜 로그인 연동 계정.
| 컬럼 | 타입 | 제약 | 설명 |
|------|------|------|------|
| id | INT | PK | |
| user_id | INT | FK→users.id, INDEX | |
| provider | VARCHAR(50) | | google/kakao/naver |
| provider_user_id | VARCHAR(255) | | 제공자 사용자 ID |
| access_token | VARCHAR(500) | | OAuth 액세스 토큰 |
| refresh_token | VARCHAR(500) | | OAuth 리프레시 토큰 |
| expires_at | DATETIME | NULLABLE | 토큰 만료 |
| created_at | DATETIME | | |
| updated_at | DATETIME | | |
### 2.5 devices
IoT 디바이스 정보.
| 컬럼 | 타입 | 제약 | 설명 |
|------|------|------|------|
| id | INT | PK | |
| device_uid | VARCHAR(100) | UNIQUE, INDEX | 디바이스 고유 식별자 |
| name | VARCHAR(100) | | 디바이스 이름 |
| device_type | VARCHAR(50) | | 센서 유형 (temperature, humidity 등) |
| status | VARCHAR(20) | DEFAULT 'offline' | online/offline/error/maintenance |
| firmware_version | VARCHAR(50) | | 펌웨어 버전 |
| ip_address | VARCHAR(45) | | IPv4/IPv6 |
| group_id | INT | FK→device_groups.id, NULLABLE | |
| owner_id | INT | FK→users.id, NULLABLE | |
| last_seen_at | DATETIME | NULLABLE | 마지막 통신 시각 |
| metadata_json | VARCHAR(2000) | DEFAULT '{}' | 추가 메타데이터 |
| is_deleted | BOOLEAN | DEFAULT FALSE | |
| deleted_at | DATETIME | NULLABLE | |
| created_at | DATETIME | | |
| updated_at | DATETIME | | |
### 2.6 device_groups
디바이스 그룹 (논리적 분류).
| 컬럼 | 타입 | 제약 | 설명 |
|------|------|------|------|
| id | INT | PK | |
| name | VARCHAR(100) | UNIQUE | 그룹명 |
| description | VARCHAR(500) | | 설명 |
| created_at | DATETIME | | |
| updated_at | DATETIME | | |
### 2.7 alert_rules
알림 발생 조건 규칙.
| 컬럼 | 타입 | 제약 | 설명 |
|------|------|------|------|
| id | INT | PK | |
| name | VARCHAR(100) | | 규칙명 |
| description | VARCHAR(500) | | 설명 |
| metric | VARCHAR(100) | | 감시 메트릭 (temperature, humidity 등) |
| condition | VARCHAR(50) | | 조건 (gt, lt, eq, gte, lte) |
| threshold | FLOAT | | 임계값 |
| severity | VARCHAR(20) | DEFAULT 'warning' | critical/warning/info |
| is_enabled | BOOLEAN | DEFAULT TRUE | |
| device_group_id | INT | FK→device_groups.id, NULLABLE | 대상 그룹 |
| created_by | INT | FK→users.id, NULLABLE | 생성자 |
| created_at | DATETIME | | |
| updated_at | DATETIME | | |
### 2.8 alerts
발생한 알림 이력.
| 컬럼 | 타입 | 제약 | 설명 |
|------|------|------|------|
| id | INT | PK | |
| rule_id | INT | FK→alert_rules.id, NULLABLE | 원인 규칙 |
| device_id | INT | FK→devices.id, NULLABLE | 대상 디바이스 |
| severity | VARCHAR(20) | | critical/warning/info |
| message | VARCHAR(500) | | 알림 메시지 |
| is_acknowledged | BOOLEAN | DEFAULT FALSE | 확인 여부 |
| acknowledged_by | INT | FK→users.id, NULLABLE | 확인한 사용자 |
| acknowledged_at | DATETIME | NULLABLE | 확인 시각 |
| created_at | DATETIME | | |
| updated_at | DATETIME | | |
### 2.9 system_configs
시스템 설정 키-값 저장소.
| 컬럼 | 타입 | 제약 | 설명 |
|------|------|------|------|
| id | INT | PK | |
| key | VARCHAR(100) | UNIQUE, INDEX | 설정 키 |
| value | VARCHAR(2000) | | 설정 값 |
| description | VARCHAR(500) | | 설명 |
| is_secret | BOOLEAN | DEFAULT FALSE | 비밀 값 여부 |
| created_at | DATETIME | | |
| updated_at | DATETIME | | |
### 2.10 audit_logs
감사 로그 (변경 이력 추적).
| 컬럼 | 타입 | 제약 | 설명 |
|------|------|------|------|
| id | INT | PK | |
| user_id | INT | FK→users.id, NULLABLE | 행위자 |
| action | VARCHAR(100) | | 액션 (create, update, delete, login 등) |
| resource_type | VARCHAR(50) | | 대상 리소스 타입 |
| resource_id | VARCHAR(50) | | 대상 리소스 ID |
| details | VARCHAR(2000) | DEFAULT '{}' | 변경 상세 (JSON) |
| ip_address | VARCHAR(45) | | 요청 IP |
| created_at | DATETIME | | |
| updated_at | DATETIME | | |
---
## 3. MariaDB ER 다이어그램
```
users ──────────┬──── 1:1 ──── user_profiles
├──── 1:N ──── refresh_tokens
├──── 1:N ──── oauth_accounts
├──── 1:N ──── devices (owner_id)
├──── 1:N ──── alert_rules (created_by)
├──── 1:N ──── alerts (acknowledged_by)
└──── 1:N ──── audit_logs (user_id)
device_groups ──┬──── 1:N ──── devices (group_id)
└──── 1:N ──── alert_rules (device_group_id)
alert_rules ────┬──── 1:N ──── alerts (rule_id)
devices ────────┴──── 1:N ──── alerts (device_id)
```
---
## 4. MongoDB 컬렉션
### 4.1 device_logs
디바이스 이벤트 로그. TTL 인덱스로 90일 후 자동 삭제.
```json
{
"_id": "ObjectId",
"device_id": "sensor-temp-001",
"event_type": "status_change",
"payload": {
"status": "online",
"reason": "boot"
},
"ip_address": "192.168.1.100",
"timestamp": "2025-01-15T12:00:00Z"
}
```
**인덱스:**
- `device_id` (단일)
- `event_type` (단일)
- `timestamp` (내림차순)
### 4.2 telemetry_data
디바이스 센서 측정 데이터 (시계열).
```json
{
"_id": "ObjectId",
"device_id": "sensor-temp-001",
"metrics": {
"temperature": 23.5,
"humidity": 45.2,
"pressure": 1013.25
},
"timestamp": "2025-01-15T12:05:00Z"
}
```
**인덱스:**
- `device_id` (단일)
- `timestamp` (내림차순)
- `(device_id, timestamp)` (복합, 범위 쿼리 최적화)
### 4.3 analytics_results
분석 수행 결과 저장.
```json
{
"_id": "ObjectId",
"analysis_type": "daily_telemetry",
"parameters": {
"date": "2025-01-14"
},
"result": {
"count": 1440,
"avg_value": 23.8
},
"device_id": "sensor-temp-001",
"period_start": "2025-01-14T00:00:00Z",
"period_end": "2025-01-15T00:00:00Z",
"created_at": "2025-01-15T01:05:00Z"
}
```
**인덱스:**
- `analysis_type` (단일)
- `device_id` (단일)
- `created_at` (내림차순)
### 4.4 notifications
사용자별 알림 메시지.
```json
{
"_id": "ObjectId",
"user_id": 1,
"title": "디바이스 오프라인 알림",
"message": "sensor-temp-001이 오프라인 상태입니다.",
"notification_type": "warning",
"is_read": false,
"read_at": null,
"created_at": "2025-01-15T12:10:00Z"
}
```
**인덱스:**
- `user_id` (단일)
- `(user_id, is_read)` (복합, 읽지 않은 알림 조회)
- `created_at` (내림차순)
---
## 5. Redis 사용 패턴
| 용도 | 키 패턴 | TTL | 설명 |
|------|---------|-----|------|
| 속도 제한 | `rate_limit:{ip}` | 60초 | IP별 요청 카운터 |
| Celery 브로커 | redis://...6379/1 | - | 태스크 큐 |
| Celery 결과 | redis://...6379/2 | - | 태스크 결과 저장 |
---
## 6. 마이그레이션
Alembic으로 MariaDB 스키마를 관리한다.
```bash
# 마이그레이션 생성
alembic revision --autogenerate -m "description"
# 마이그레이션 적용
alembic upgrade head
# 롤백
alembic downgrade -1
# 현재 리비전 확인
alembic current
# 히스토리 확인
alembic history
```
MongoDB는 스키마리스이므로 별도 마이그레이션이 불필요하다. 인덱스는 Beanie 모델의 `Settings.indexes`로 앱 시작 시 자동 생성된다.

260
docs/DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,260 @@
# 배포 가이드
## 1. 전제 조건
### 호스트에 직접 설치 (Docker 외부)
- **MariaDB** 10.6+ — 관계형 데이터 저장
- **MongoDB** 7.0+ — 문서 데이터 저장
### Docker로 관리
- **Docker** 24.0+
- **Docker Compose** v2
---
## 2. 최초 설정
### 2.1 환경변수
```bash
cp .env.example .env
```
`.env` 파일에서 반드시 변경해야 할 항목:
```bash
# 보안 키 (반드시 변경)
SECRET_KEY=<랜덤 문자열>
JWT_SECRET_KEY=<랜덤 문자열>
# MariaDB 접속 정보
MARIADB_HOST=127.0.0.1
MARIADB_PORT=3306
MARIADB_USER=core_api_user
MARIADB_PASSWORD=<강력한 비밀번호>
MARIADB_DATABASE=core_api
# MongoDB 접속 정보
MONGODB_URL=mongodb://127.0.0.1:27017
MONGODB_DATABASE=core_api
```
### 2.2 MariaDB 준비
```sql
CREATE DATABASE core_api CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'core_api_user'@'%' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON core_api.* TO 'core_api_user'@'%';
FLUSH PRIVILEGES;
```
### 2.3 MongoDB 준비
```javascript
use core_api
db.createUser({
user: "core_api_user",
pwd: "your_password",
roles: [{ role: "readWrite", db: "core_api" }]
})
```
---
## 3. 개발 환경
### 3.1 로컬 실행
```bash
# 의존성 설치
pip install -e ".[dev]"
# DB 마이그레이션
alembic upgrade head
# 시드 데이터
python -m scripts.init_db
# 관리자 계정 생성
python -m scripts.create_superuser admin@example.com password123
# 인프라 시작 (Redis + Mosquitto)
docker-compose up -d redis mosquitto
# 앱 서버
uvicorn app.asgi:app --reload --host 0.0.0.0 --port 8000
# Celery 워커 (별도 터미널)
celery -A app.tasks.celery_app worker --loglevel=info \
-Q default,analytics,notifications,devices
# Celery 스케줄러 (별도 터미널)
celery -A app.tasks.celery_app beat --loglevel=info
```
### 3.2 Docker 전체 스택
```bash
docker-compose up -d
```
서비스 확인:
- API: http://localhost:8000/docs
- Flower: http://localhost:5555
---
## 4. 프로덕션 배포
### 4.1 Docker Compose
```bash
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
```
프로덕션 차이점:
- Uvicorn 워커 4개 (`--workers 4`)
- Celery 동시성 4, Worker 레플리카 2
- Flower 기본 인증 활성화
- 소스 코드 볼륨 마운트 없음 (이미지 내장)
- 로그 레벨 `warning`
### 4.2 환경변수 (프로덕션 추가)
```bash
APP_ENV=production
DEBUG=false
LOG_LEVEL=WARNING
# Flower 인증
FLOWER_USER=admin
FLOWER_PASSWORD=<강력한 비밀번호>
# CORS (실제 도메인)
CORS_ORIGINS=["https://your-domain.com"]
```
### 4.3 리버스 프록시 (Nginx 예시)
```nginx
upstream core_api {
server 127.0.0.1:8000;
}
server {
listen 80;
server_name api.your-domain.com;
location / {
proxy_pass http://core_api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /socket.io/ {
proxy_pass http://core_api;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
```
---
## 5. 서비스 구성
```
┌────────────────────────────────────────────────────┐
│ Docker Compose │
│ │
│ ┌─────────┐ ┌─────────┐ ┌────────┐ ┌───────┐ │
│ │ App │ │ Worker │ │ Beat │ │Flower │ │
│ │ :8000 │ │ (x2) │ │ │ │ :5555 │ │
│ └────┬────┘ └────┬────┘ └───┬────┘ └───┬───┘ │
│ │ │ │ │ │
│ ┌────┴────┐ ┌────┴──────────┴────────────┘ │
│ │ Redis │ │ │
│ │ :6379 │ │ │
│ └─────────┘ │ │
│ ┌────────────┘ │
│ │ Mosquitto │ │
│ │ :1883 │ │
│ └───────────┘ │
└────────────────────────────────────────────────────┘
│ │
┌────┴────┐ ┌───┴────┐
│ MariaDB │ │MongoDB │
│ (호스트) │ │(호스트) │
└─────────┘ └────────┘
```
---
## 6. 헬스체크
```bash
# 기본 헬스체크
curl http://localhost:8000/api/v1/system/health
# → {"status": "ok", "service": "core-api", "version": "0.1.0"}
# 상세 시스템 상태 (인증 필요)
curl -H "Authorization: Bearer <token>" \
http://localhost:8000/api/v1/monitoring/health
# Docker 서비스 상태
docker-compose ps
# Celery 워커 상태
docker-compose logs worker
# Celery 활성 태스크
docker-compose exec worker celery -A app.tasks.celery_app inspect active
```
---
## 7. 로그
### 개발 환경
- structlog 콘솔 렌더러 (사람이 읽기 좋은 형식)
- 로그 레벨: DEBUG
### 프로덕션 환경
- structlog JSON 렌더러 (ELK/Grafana 연동)
- 로그 레벨: WARNING
```bash
# 실시간 로그 확인
docker-compose logs -f app
docker-compose logs -f worker
```
---
## 8. 백업
### MariaDB
```bash
mysqldump -u root -p core_api > backup_$(date +%Y%m%d).sql
```
### MongoDB
```bash
mongodump --db core_api --out backup_$(date +%Y%m%d)
```
---
## 9. 트러블슈팅
| 증상 | 원인 | 해결 |
|------|------|------|
| `Cannot connect to MySQL` | MariaDB 미실행 또는 접속 정보 오류 | `.env` 확인, MariaDB 상태 확인 |
| `MongoDB not initialized` | MongoDB 미실행 | MongoDB 서비스 시작 |
| `Redis connection refused` | Redis 미실행 | `docker-compose up -d redis` |
| Celery 태스크 실행 안 됨 | Worker 미실행 또는 큐 불일치 | Worker 로그 확인, 큐 이름 확인 |
| Socket.IO 연결 실패 | CORS 설정 누락 | `CORS_ORIGINS`에 클라이언트 URL 추가 |
| MQTT 연결 실패 | Mosquitto 미실행 | `docker-compose up -d mosquitto` |