← 전체 글
MQTT/약 9분 읽기/ 조회

SCADA Edge MQTT 페이로드 Schema Versioning

SCADA edge MQTT 페이로드를 변경할 때 HMI, 히스토리언, MES connector, 분석 시스템이 조용히 깨지지 않도록 schema version을 관리하는 실무 노트.

MQTTSCADAEdge히스토리언MES

페이로드 변경은 인터페이스 변경이다

MQTT는 publish가 쉽다. 그래서 페이로드가 하나의 인터페이스 계약이라는 사실을 놓치기 쉽다. Gateway firmware를 올리면서 sensor를 추가하거나, field 이름을 바꾸거나, timestamp 의미를 바꾸거나, quality code를 다르게 보내면 HMI나 historian connector가 바로 영향을 받는다. PLC address를 바꾼 것과 비슷한 수준의 변경이다.

Topic 이름만 인터페이스가 아니다. Consumer는 이런 것에도 의존한다.

  • Field 이름과 중첩 구조.
  • Data type과 공학 단위.
  • Timestamp 생성 위치와 format.
  • Quality와 stale-data 의미.
  • Null, missing, default value 처리.
  • Message가 full snapshot인지, delta인지, event인지.
  • Retain, QoS, replay 동작.

Schema versioning은 소프트웨어팀만 하는 문서 작업이 아니다. Edge 장비가 바뀌어도 운영 데이터가 조용히 틀어지지 않게 하는 장치다.

Consumer가 볼 수 있는 곳에 version을 둔다

Version이 PDF나 project folder에만 있으면 장애 때 도움이 적다. Payload 안이나 정해진 metadata topic에 schema version을 넣는다.

간단한 telemetry payload는 이렇게 둘 수 있다.

{
  "schemaVersion": "2.1",
  "source": "line-3/oven-2/gateway-a",
  "timestamp": "2026-06-25T08:14:22.341Z",
  "quality": "good",
  "metrics": {
    "zone1TemperatureC": 184.6,
    "conveyorSpeedMpm": 12.4
  }
}

Sparkplug B를 쓰는 경우에는 payload model과 birth certificate 동작을 일관되게 써야 한다. 원칙은 같다. Consumer가 그 시점의 metric 이름, alias, unit, data type을 알아야 한다.

Additive change와 breaking change를 나눈다

모든 변경이 전체 시스템 정지를 요구하지는 않는다. 현장에서 어떤 변경을 additive로 볼지, 어떤 변경을 breaking으로 볼지 먼저 정한다.

변경보통 안전한가현장 risk
Optional metric 추가대체로 안전기존 consumer가 모르는 field를 무시해야 함
Required field 추가Breaking기존 publisher가 값을 못 보낼 수 있음
Metric 이름 변경BreakingHistorian mapping과 dashboard가 stale됨
단위 bar를 kPa로 변경Breaking값이 그럴듯하게 틀림
Timestamp를 gateway time에서 PLC time으로 변경BreakingEvent 순서와 report 기준이 바뀜
Quality code 추가경우에 따라 breakingConsumer가 unknown을 good처럼 처리할 수 있음
Snapshot publish를 delta publish로 변경Breaking빠진 field를 zero나 null로 오해할 수 있음

현장 시스템은 보수적으로 보는 편이 낫다. 분명하게 validation fail이 나는 값보다 그럴듯하게 틀린 값이 더 위험하다.

Timestamp와 quality 의미를 고정한다

MQTT telemetry 문제는 publish 순간보다 historian report에서 늦게 드러나는 경우가 많다. Timestamp와 quality는 특히 조심해서 관리한다.

Schema version마다 다음 규칙을 적어 둔다.

  • timestamp는 PLC, edge gateway, broker ingestion layer, consuming application 중 어디에서 만드는가.
  • UTC인가, offset이 있는 local time인가, offset 없는 local time인가.
  • Timestamp가 sample time인지, publish time인지, broker receive time인지.
  • Quality 값은 무엇인가: good, bad, uncertain, stale, substituted, manual.
  • Stale metric은 마지막 snapshot에 남는가, 빠지는가, bad quality로 표시되는가.

이 규칙은 조용히 바꾸면 안 된다. 같은 topic을 historian backfill과 live HMI가 같이 구독해도 timestamp를 쓰는 방식은 다를 수 있다.

Rollout 기간에는 호환성을 둔다

산업 현장의 배포는 한 번에 끝나지 않는다. 한 라인은 오늘 update하고, 다른 라인은 다음 달 shutdown 때 update할 수 있다. 어떤 설비는 오래된 firmware를 계속 써야 할 수도 있다. 그래서 호환 기간을 설계해야 한다.

실무적으로는 이런 방법을 쓴다.

  • 정해진 기간 동안 old field와 new field를 같이 publish하고, consumer 이관 후 old field를 제거한다.
  • Breaking schema는 v2 같은 새 topic branch로 내고, v1을 임시로 유지한다.
  • Historian mapping은 production subscription을 바꾸기 전에 test namespace에서 검증한다.
  • Gateway가 birth 또는 startup 때 schema version과 firmware version을 publish한다.
  • Production cutover 전에 payload shape를 검사하는 작은 subscriber test tool을 돌린다.

Dual publishing을 영구로 두는 것은 피한다. 실제 support 이유가 없다면 끝나는 날짜가 있어야 한다. 아니면 schema 두 개를 계속 운영하는 시스템이 된다.

실제 consumer로 검증한다

JSON schema check는 유용하지만 충분하지 않다. 시운전에서는 새 payload가 중요한 consumer에서 제대로 처리되는지 확인해야 한다.

최소한 다음 경로는 본다.

  1. HMI 또는 SCADA client가 live value를 올바른 단위와 quality로 표시한다.
  2. Historian이 기대한 tag name과 timestamp로 저장한다.
  3. MES connector가 event를 중복 또는 누락 transaction 없이 받아들인다.
  4. Alarm 또는 notification logic이 missing value와 bad quality를 안전하게 처리한다.
  5. Analytics나 dashboard job이 모르는 optional field를 문제 없이 무시한다.
  6. Store-and-forward replay가 지난 payload를 현재 데이터처럼 보이게 만들지 않는다.

시험 message는 happy path 하나로 부족하다. 정상 message, bad-quality message, optional field가 빠진 message, retained 또는 replayed message, version mismatch case를 같이 넣어 본다.

자주 생기는 실패 형태

단위는 바뀌었는데 field 이름은 그대로다

Pressure 값이 bar에서 kPa로 바뀌었는데 field 이름은 그대로 유지된다. Trend는 계속 선을 그리기 때문에 공정이 바뀐 것처럼 보인다. Unit은 metadata, tag mapping, field name 중 적절한 곳에 남기고, 단위 변경은 breaking change로 취급한다.

Missing을 zero로 처리한다

Delta payload나 filtered publish에서는 변하지 않은 metric이 빠질 수 있다. Consumer가 missing을 zero로 바꾸면 report와 alarm이 틀어진다. Missing이 unchanged, unavailable, not configured, invalid 중 무엇을 뜻하는지 정해야 한다.

Retained message가 old schema를 숨긴다

Old gateway가 남긴 retained message가 배포 뒤 새 subscriber에게 전달될 수 있다. Subscriber는 schema version과 timestamp를 확인한 뒤 신뢰해야 한다.

Quality 표현이 제각각이다

어떤 gateway는 bad, 다른 gateway는 BAD, 또 다른 gateway는 0을 보낸다. Quality 값은 normalize하거나 mapping table을 제공한다. Consumer마다 추측하게 두면 장애 때 해석이 갈린다.

Test tool은 통과하지만 production connector가 실패한다

Command-line subscriber는 어떤 JSON이든 출력한다. Historian connector는 정확한 field name과 type을 요구할 수 있다. 가능하면 실제 connector로 시험한다.

작은 version 기록이면 충분하다

문서가 거창할 필요는 없다. Schema version마다 다음만 남겨도 효과가 크다.

  • Version number와 release date.
  • 정상, bad-quality, replay message 예시.
  • Field별 type, unit, required/optional, 의미.
  • Timestamp와 quality 규칙.
  • Known consumer와 migration 상태.
  • 이전 version 대비 breaking change.
  • Edge update를 되돌렸을 때 rollback 동작.

Schema versioning을 잘 해 두면 MQTT 운영이 지루해진다. 운전자는 현재 값을 보고, historian은 맞는 데이터를 저장하고, 엔지니어는 edge telemetry를 바꿀 때마다 전체 시스템을 추적 조사하지 않아도 된다.