시작하기
플랫폼 아키텍처
에너지 관리 시스템은 하나의 거대한 앱보다 역할이 다른 몇 개의 서비스로 나누는 편이 운영하기 좋습니다. 수집은 24시간 멈추면 안 되고, 화면은 자주 배포하고, 보고서는 정해진 시각에 무겁게 돕니다. 성격이 다르니 배포 단위도 다르게 가져갑니다.
1. 전체 구성
[현장 계기] ──Modbus/MQTT/TCP──> [수집 서비스 (DAQ)]
│ insert
▼
[웹 (SPA)] ──HTTP──> [API 서버] ───> [DB (MariaDB)] <─── [보고서 서비스]
▲ │
│ 집계 배치 ▼
[집계/절감 배치] [파일 저장소 (S3/MinIO)]
| 구성 요소 | 역할 | 기술 예시 |
|---|---|---|
| 수집 (DAQ) | 계기 폴링/구독, 원시 저장, 실시간 알람 | Python, pymodbus, paho-mqtt, APScheduler |
| 집계 배치 | 시간/일/월 집계, 절감 계산 | Python 배치, SQL |
| API | 마스터 CRUD, 조회, 권한 | Node.js Express, Sequelize |
| 웹 | 대시보드, 관리 화면 | React, TypeScript, Chart.js |
| 보고서 | 스케줄, HTML/PDF 생성 | FastAPI, Jinja2, WeasyPrint |
| DB | 마스터, 원시, 집계, 메타 | MariaDB / MySQL |
| 저장소 | 보고서 파일, 이미지 | S3, MinIO, 로컬 |
2. 데이터 계층 분리
데이터 성격별로 테이블을 나누는 것이 성능과 운영의 핵심입니다.
| 계층 | 테이블 예시 | 특징 |
|---|---|---|
| 마스터 | company, plant, meter, collector | 적고, 자주 안 바뀜, soft delete |
| 원시 | power_measurement, flow_measurement | 매우 많음, insert 위주 |
| 집계 | summary, baseline_saving | 조회 위주, 재계산 가능 |
| 운영 | alarm_event, report | 이벤트, 이력 |
| 설정 | common_code, tariff, report_schedule | 관리자 변경 |
- 화면은 원시 테이블을 직접 조회하지 않습니다. 최근 1시간 실시간 차트 정도만 예외
- 집계 테이블은 언제든 원시에서 다시 만들 수 있어야 합니다
3. 원시 데이터 규모
1분 주기 계기 500대 기준:
500 × 60 × 24 = 720,000 건/일
720,000 × 365 ≈ 2.6억 건/년
- 월 단위 파티션 (
PARTITION BY RANGE (TO_DAYS(measured_at))) (meter_id, measured_at)복합 인덱스 + unique- 보존 정책: 원시 1~3년, 집계 영구
- 규모가 더 커지면 TimescaleDB, InfluxDB 같은 시계열 DB를 원시 계층에만 도입
4. 공유 DB vs 서비스별 DB
여러 서비스가 같은 DB를 공유하는 구조는 초기에 빠르지만 경계가 흐려집니다.
| 방식 | 장점 | 단점 |
|---|---|---|
| 공유 DB | 개발 빠름, 조인 쉬움 | 스키마 변경 시 전 서비스 영향 |
| 서비스별 DB | 독립 배포 | 데이터 동기화 필요 |
현실적인 절충:
- 테이블 쓰기 소유권을 서비스별로 정합니다 (원시/집계는 DAQ, 마스터는 API, 보고서 메타는 보고서 서비스)
- 다른 서비스는 읽기만 합니다
- 스키마 변경은 마이그레이션 도구 하나로 관리합니다
- ORM 모델이 언어별로 중복되므로 컬럼 변경 시 체크리스트로 동기화합니다
5. 실시간 데이터
대시보드 "현재 전력"을 DB 폴링으로 구현하면 부하가 큽니다.
DAQ → Redis (meter:{id}:latest) → API → WebSocket/SSE → 웹
- 최신값은 Redis에 TTL과 함께 저장
- 화면은 SSE나 WebSocket으로 구독, 또는 5~10초 폴링
- TTL이 지나면 "통신 없음"으로 표시
6. 권한과 멀티테넌시
- 모든 데이터에
company_id를 두고 API에서 강제 필터링 - 역할(role) - 권한(permission) - 메뉴(menu) 구조로 화면 접근 제어
- 사업장 단위 권한이 필요하면 member-plant 매핑을 추가
- 보고서 서비스도 같은 토큰을 검증해야 합니다. 내부 서비스라고 인증을 빼면 파일 URL이 그대로 노출됩니다
7. 시간과 타임존
에너지 시스템 버그의 상당수는 시간입니다.
- DB, 서버, 컨테이너, 게이트웨이의 타임존을 하나로 통일 (KST 또는 UTC + 변환 규칙)
- 언어별 기본 동작이 다릅니다 (Node Date, Python naive datetime, MariaDB DATETIME)
- 집계 경계, 보고서 기간, 스케줄 실행 시각이 모두 같은 타임존 기준인지 확인
8. 배포와 운영
| 대상 | 배포 빈도 | 주의 |
|---|---|---|
| 웹 | 높음 | 무중단 정적 배포 |
| API | 중간 | 롤링 배포 |
| DAQ | 낮음 | 배포 중 수집 공백 → 버퍼/재수집 |
| 보고서 | 낮음 | 스케줄 실행 시각 피해서 배포 |
- 현장 게이트웨이는 원격 업데이트가 어려우므로 서버 쪽에서 호환성을 유지
- 수집 서비스 헬스체크: 프로세스 생존이 아니라 마지막 수신 시각 기준
9. 설계 원칙 요약
- 원시는 불변, 집계는 재계산 가능
- 테이블 쓰기 소유권을 서비스별로 고정
- 설정(메모리맵, 요금, 스케줄, 계수)은 코드가 아닌 데이터
- 타임존은 한 곳에서 정의
- 수집 장애는 감지 가능해야 한다