왜 MQTT Last Will Testament만으로는 프로덕션 IoT에 충분하지 않은가 (그리고 우리가 대신 만든 것)
Source: Dev.to
나는 7 년 동안 클라우드 백엔드를 구축해 왔지만—집에 있는 ESP32들을 실제 하드웨어와 연결하려고 했을 때, 벽에 부딪혔다:
“내 장치는 AWS IoT Core에서 ‘connected’ 상태로 표시되는데… 4 시간째 데이터를 보내지 않고 있어. 멈춘 건가? 죽은 건가? 아니면 그냥 오프라인인가?”
결과: MQTT의 Last Will Testament (LWT)는 당신에게 거짓말을 한다.
거짓말: “Connected” ≠ 살아있음
LWT는 TCP 연결이 끊어질 때만 트리거된다. 실제 장치는 조용히 실패할 수 있다:
- Wi‑Fi가 끊겨도 TCP 소켓은 열려 있다 (NAT 타임아웃 = 5 분 이상)
- 장치가 멈추지만 재부팅되지 않는다 (워치독 실패)
- MQTT 클라이언트가 “connected” 상태를 유지한 채 센서 루프가 크래시 난다
결과? 대시보드에는 ✅ Online 이 표시되지만 장치는 어제부터 데이터를 보내지 않는다.

우리의 해결책: 애플리케이션‑레벨 하트비트 + 상태 기반 ACK
우리는 경량 Spring Boot 백엔드(hear‑beat)를 구축했으며, 텔레메트리를 하트비트 펄스로 취급한다 — 단순 데이터가 아니다.
Device → [temp=28°C, ts=1708512000] → Backend
Backend → "ACK @ 1708512000" → Device
오프라인 감지 = 하트비트 누락 윈도우
// DeviceRegistry.java
if (System.currentTimeMillis() - lastHeartbeat > OFFLINE_THRESHOLD) {
markDeviceOffline(deviceId); // TCP 끊김이 아니라 실제 침묵
}
ACK 루프를 통한 명령 안전성
// CommandService.java
sendCommand(deviceId, "REBOOT");
waitForAck(deviceId, timeout = 30_000); // “받았다”가 아니라 *실행했는지* 확인
REST 제어 평면 + MQTT 데이터 평면
- 모바일 앱은 REST와 통신 (
POST /devices/{id}/command) - 장치는 MQTT와 통신 (
iot/device/{id}/cmd) - 백엔드가 두 흐름을 연결 → 깔끔한 분리
실제 배포에서 왜 중요한가
| 시나리오 | LWT 결과 | 우리의 하트비트 결과 |
|---|---|---|
| 장치가 멈춤 (재부팅 안 함) | ✅ Connected | ❌ Offline (90 초 안에 하트비트 없음) |
| 시골 현장에서 Wi‑Fi가 끊김 | ✅ Connected (TCP 살아 있음) | ❌ Offline (2 분 안에 데이터 없음) |
| 명령을 보냈지만 장치가 실행 중에 크래시 | ✅ Command delivered | ❌ ACK 없음 → 재시도/안전 실패 |
이것은 이론이 아니다. 나는 이 시스템을 내 집 센서에 적용하고 있는데—LWT가 놓치는 실패를 매일 잡아낸다.
직접 해보기
git clone https://github.com/AnilSaithana/hear-beat
cd hear-beat
docker-compose up # Spring Boot + MQTT 브로커 실행
ESP32 펌웨어 예제는 /firmware 폴더에 포함되어 있다.
나는 이걸 만들었다. 왜냐하면 프로덕션 IoT는 클라우드와 하드웨어 사이의 틈새에서 실패하기 때문이다.