왜 MQTT Last Will Testament만으로는 프로덕션 IoT에 충분하지 않은가 (그리고 우리가 대신 만든 것)

발행: (2026년 2월 22일 오후 03:10 GMT+9)
4 분 소요
원문: Dev.to

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 이 표시되지만 장치는 어제부터 데이터를 보내지 않는다.

AWS IoT Core에서 오래된 데이터를 가진 연결된 장치를 표시

우리의 해결책: 애플리케이션‑레벨 하트비트 + 상태 기반 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는 클라우드와 하드웨어 사이의 틈새에서 실패하기 때문이다.

0 조회
Back to Blog

관련 글

더 보기 »

대규모 결제 시스템 설계

마리아가 “Confirm Ride”(확정 탑승)를 누르면 실제로 무슨 일이 일어날까요? 마리아는 15분 안에 중요한 회의가 있습니다. 현금이 없습니다. 그녀는 Uber를 열고, 승차를 요청하고, …