산업 데이터의 특수성
일반 웹 데이터와 달리 산업 현장 데이터는 초당 수천 건의 센서 값, 밀리초 타임스탬프 정밀도, 수십 년 된 레거시 장비의 독점 프로토콜을 가집니다. 1편의 AI-OT 게이트웨이 위에서 이 데이터를 AI가 소비할 수 있는 형태로 만드는 파이프라인을 다룹니다.
MQTT → 표준 이벤트 스트림
Python
import paho.mqtt.client as mqtt
import json
from datetime import datetime, timezone
def on_message(client, userdata, msg):
raw = json.loads(msg.payload)
event = {
"specversion": "1.0",
"type": "sensor.measurement",
"source": f"factory/line-A/{msg.topic}",
"time": datetime.now(timezone.utc).isoformat(),
"data": {
"value": raw["v"],
"unit": raw.get("u", "?"),
"quality": raw.get("q", 192),
}
}
kafka_producer.send("sensor-events", json.dumps(event).encode())
client = mqtt.Client()
client.on_message = on_message
client.connect("broker.factory.local", 1883)
client.subscribe("sensors/#", qos=1)엣지 버퍼링: 네트워크 단절 대응
산업 현장의 네트워크는 불안정합니다. WAN 연결이 끊겨도 데이터가 유실되면 안 됩니다.
Python
import sqlite3
class EdgeBuffer:
def __init__(self, db_path: str):
self.conn = sqlite3.connect(db_path)
self.conn.execute("PRAGMA journal_mode=WAL")
self.conn.execute(
"CREATE TABLE IF NOT EXISTS events ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"payload TEXT NOT NULL,"
"sent INTEGER DEFAULT 0)"
)
def write(self, payload: str):
self.conn.execute("INSERT INTO events(payload) VALUES(?)", (payload,))
self.conn.commit()
def flush(self, producer, batch: int = 1000):
rows = self.conn.execute(
"SELECT id, payload FROM events WHERE sent=0 LIMIT ?", (batch,)
).fetchall()
for _, p in rows:
producer.send("sensor-events", p.encode())
producer.flush()
ids = [str(r[0]) for r in rows]
self.conn.execute(
f"UPDATE events SET sent=1 WHERE id IN ({','.join(ids)})"
)
self.conn.commit()TimescaleDB 설계
SQL
CREATE TABLE sensor_readings (
time TIMESTAMPTZ NOT NULL,
device_id TEXT NOT NULL,
metric TEXT NOT NULL,
value DOUBLE PRECISION
);
SELECT create_hypertable('sensor_readings', 'time',
chunk_time_interval => INTERVAL '1 hour');
-- 자동 압축 (7일 후)
SELECT add_compression_policy('sensor_readings', INTERVAL '7 days');
-- 1분 집계 연속 뷰
CREATE MATERIALIZED VIEW sensor_1min
WITH (timescaledb.continuous) AS
SELECT time_bucket('1 minute', time) AS bucket,
device_id, metric,
AVG(value) avg_val, MAX(value) max_val
FROM sensor_readings
GROUP BY bucket, device_id, metric;Kafka vs AWS IoT Greengrass
| 항목 | Apache Kafka | AWS IoT Greengrass |
|---|---|---|
| 설치 복잡도 | 높음 | 낮음 (관리형) |
| 오프라인 버퍼링 | 직접 구현 | 내장 |
| 엣지 ML 추론 | 별도 구성 | Lambda 통합 |
| 비용 | 인프라 비용 | 사용량 과금 |
전체 파이프라인
CODE
OPC-UA / MQTT / Modbus
↓
엣지 게이트웨이 (EdgeBuffer + 프로토콜 변환)
↓ Kafka
스트림 처리 (Kafka Streams)
↓
TimescaleDB (단기 고해상도) + S3 (장기 콜드)
↓
AI 모델 (이상 탐지 / 예측 유지보수 / 품질 검사)3편에서는 이 파이프라인 위에서 실시간으로 동작하는 예측 유지보수 모델 배포와 드리프트 감지를 다룹니다.
파이프라인 스케치
TEXT
센서 → 게이트웨이(프로토콜 변환) → 스트림(Kafka 등)
→ 온라인 점수화(엣지) → 알람
→ 배치 재학습(클라우드)| 계층 | 역할 |
|---|---|
| 엣지 | 저지연 1차 탐지 |
| 클라우드 | 학습·장기 보관·대시보드 |
운영 함정
- 개념 드리프트: 설비 교체 후 재캘리브레이션
- 알람 피로: 등급·억제 규칙 필수
- 라벨 부족: 약한 지도/이상치 탐지부터
센서 데이터 누락·중복 진단과 해결
이상 감지 모델의 오탐 상당수는 모델이 아니라 입력 데이터의 누락·중복·시각 오류에서 옵니다.
원인
- MQTT QoS 0으로 발행해 네트워크 순간 단절 시 메시지가 유실됨
- QoS 1은 "최소 1회" 전달이라 재전송 시 같은 메시지가 중복 저장됨
- 게이트웨이 시계가 틀어져(NTP 미동기화) 타임스탬프 순서가 뒤섞임
재현·검증 명령
Bash
# 토픽 흐름과 QoS 확인
mosquitto_sub -h broker.local -t 'plant/+/sensor/#' -q 1 -v
# 게이트웨이 시계 동기화 상태
chronyc trackingSQL
-- TimescaleDB: 1분 버킷 기준 데이터 공백 찾기
SELECT time_bucket_gapfill('1 minute', ts) AS b, device_id, count(*) AS n
FROM sensor_readings
WHERE ts > now() - interval '6 hours' AND ts < now()
GROUP BY b, device_id
HAVING count(*) = 0;
-- 중복 확인
SELECT device_id, ts, count(*) FROM sensor_readings
GROUP BY device_id, ts HAVING count(*) > 1 LIMIT 20;해결
- 손실이 치명적인 신호는 QoS 1 이상으로 발행하고, 저장 측을 멱등하게 만듭니다:
(device_id, ts)유니크 제약 +INSERT ... ON CONFLICT DO NOTHING. - 게이트웨이에서 측정 시각을 찍고(수신 시각이 아님), chrony로 동기화 상태를 모니터링합니다.
- 공백 구간은 모델 입력에서 보간하지 말고 "결측" 플래그로 넘겨, 결측을 이상으로 오판하지 않게 합니다.
재발 방지
- 장비별 수신률(기대 건수 대비 실제 건수)을 대시보드 지표로 두고, 임계치 아래로 떨어지면 데이터 품질 경보를 먼저 울립니다.
참고 자료
검증 환경·편집자 주
- 2026-09. OT망 분리 정책을 먼저 확인하세요.
자주 묻는 질문 (FAQ)
Q. 딥러닝이 필수인가요?
아니요. 통계+규칙으로도 PoC 가능한 케이스가 많습니다.
✦ ✦ ✦
출처·검증 안내
글마다 출처와 확인 범위가 다릅니다. 아래에 확인일·확인 범위·출처가 기록된 글만 그 범위에서 확인된 것입니다. 기록이 없는 글은 적용 전 사용 중인 버전의 공식 문서로 직접 확인하고, 오류는 이메일로 알려주세요. 편집 정책 · 이메일로 제보
이 글에는 사람이 확인한 날짜·범위 기록이 아직 없습니다.
이 글이 도움이 되었나요?
댓글
첫 번째 댓글을 남겨보세요.