150_MQTT_경량IoT메시징_발행구독과_초고속통신 📮⚡

도로시와 지니가 함께하는 IoT 글로벌 표준 프로토콜 MQTT의 브로커(Broker)와 발행/구독(Pub/Sub) 실전 구축
- 👧 도로시: “지니야! 수백 개의 온습도 센서가 1초마다 웹 서버로 HTTP 요청을 보내면 서버가 버벅대고 느려지지 않을까? 더 가볍고 번개처럼 빠른 통신 방식은 없어?”
- 🐱 지니: “아주 날카로운 질문이야 도로시! 그래서 전 세계 모든 IoT 산업계는 무거운 HTTP 대신 초경량 메시징 프로토콜인 MQTT (Message Queuing Telemetry Transport) 를 표준으로 사용한단다! 헤더 크기가 단 2바이트에 불과해 수만 대의 장치가 동시 접속해도 끄떡없지!”
-
🐶 토토: “바우! 우체국 편지 배달부처럼 정확하게 날아온다 멍!”
- 🖥️ 강의 슬라이드: slides.md
1. HTTP vs MQTT: 왜 사물인터넷은 MQTT를 쓸까?

[ HTTP 방식 : 요청-응답 (1:1 유선 전화) ] [ MQTT 방식 : 발행-구독 (방송국과 우체국) ]
클라이언트 ──── (무거운 헤더 요청) ──▶ 서버 센서(발행) ───▶ [MQTT 브로커] ───▶ 스마트폰(구독)
클라이언트 ◀─── (HTML 데이터 응답) ── 서버 온도계(발행) ──▶ (중앙 우체국) ──▶ PC 대시보드(구독)
* 연결을 맺고 끊을 때마다 오버헤드 큼! * 영구 TCP 세션 유지, 2바이트 초경량 패킷!
| 비교 항목 | HTTP (HyperText Transfer Protocol) | MQTT (Message Queuing Telemetry Transport) |
|---|---|---|
| 통신 패러다임 | 요청-응답 (Request-Response, 1:1) | 발행-구독 (Publish-Subscribe, 1:N) |
| 메시지 헤더 크기 | 수백 바이트 ~ 수 킬로바이트(KB) | 최소 2 바이트 (초경량!) |
| 통신 지연 (Latency) | 매 요청마다 TCP 핸드셰이크로 느림 | 연결 유지 상태에서 즉각 푸시(Push) |
| 전력 소모 | 큼 (배터리 구동 IoT에 불리) | 극히 적음 (수면 모드 슬립 최적화) |
2. MQTT 핵심 3대 구성 요소: 브로커, 발행자, 구독자
- 브로커 (MQTT Broker):
- 모든 메시지를 중계하는 중앙 허브 우체국. 클라이언트들의 연결을 관리하고 토픽별로 편지를 분류해 배달합니다. (예: Mosquitto, HiveMQ, EMQX)
- 발행자 (Publisher):
- 특정 ‘주제(Topic)’에 메시지(Payload)를 실어 브로커로 쏘아 올리는 장치 (예: 온습도 센서 노드)
- 구독자 (Subscriber):
- 관심 있는 ‘주제(Topic)’를 브로커에 등록해 두고, 새 편지가 오면 즉시 수신하는 장치 (예: 스마트폰 앱, LED 조명)
3. 계층형 토픽(Topic) 주소 설계법
MQTT 주소는 슬래시(/)를 기준으로 나뭇가지처럼 계층을 나눕니다:
home (최상위 분류)
├── livingroom (거실)
│ ├── temperature (온도) ──▶ 발행: "24.5"
│ └── light (조명) ──▶ 구독: "ON"
└── kitchen (주방)
└── gas (가스) ──▶ 발행: "SAFE"
- 와일드카드(Wildcard) 구독의 마법:
+(단일 레벨 와일드카드):home/+/temperature➔ 거실, 주방, 안방 등 모든 방의 온도를 한 번에 구독!#(다중 레벨 와일드카드):home/#➔home아래에서 일어나는 모든 장치의 메시지를 일괄 도청!
4. 실습: ESP32 + 무료 공용 브로커(HiveMQ) 기반 센서 발행 및 LED 원격 제어
아두이노 IDE [라이브러리 관리자]에서 PubSubClient (by Nick O’Leary) 라이브러리를 설치합니다.
#include <WiFi.h>
#include <PubSubClient.h>
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// 무료 오픈 공용 MQTT 브로커
const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;
// 토픽 이름 정의 (중복 방지를 위해 자신만의 고유 이름 사용!)
const char* topic_sensor = "dorothy/smartlab/temp";
const char* topic_led = "dorothy/smartlab/led";
WiFiClient espClient;
PubSubClient client(espClient);
const int LED_PIN = 2; // 내장 LED
// 브로커로부터 구독(Subscribe)한 메시지가 도착했을 때 실행되는 콜백 함수
void callback(char* topic, byte* payload, unsigned int length) {
String message = "";
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.print("[수신 토픽] ");
Serial.print(topic);
Serial.print(" ➔ 명령: ");
Serial.println(message);
if (String(topic) == topic_led) {
if (message == "ON") digitalWrite(LED_PIN, HIGH);
if (message == "OFF") digitalWrite(LED_PIN, LOW);
}
}
// 브로커 재접속 및 토픽 구독 등록
void reconnect() {
while (!client.connected()) {
Serial.print("MQTT 브로커 접속 중...");
String clientId = "ESP32Client-" + String(random(0xffff), HEX);
if (client.connect(clientId.c_str())) {
Serial.println("접속 성공!");
client.subscribe(topic_led); // LED 제어 토픽 구독 개시!
} else {
delay(2000);
}
}
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) delay(500);
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback); // 콜백 등록
}
unsigned long lastMsg = 0;
void loop() {
if (!client.connected()) reconnect();
client.loop(); // MQTT 패킷 수신 루프
// 5초마다 온도 데이터 발행(Publish)
unsigned long now = millis();
if (now - lastMsg > 5000) {
lastMsg = now;
float fakeTemp = 24.0 + random(0, 30) / 10.0;
char tempString[8];
dtostrf(fakeTemp, 1, 1, tempString);
client.publish(topic_sensor, tempString); // 브로커로 쏘아올림!
Serial.print(">> [발행 성공] 온도: ");
Serial.println(tempString);
}
}
🌟 도로시와 지니의 핵심 요약 노트!

- 👧 도로시: “센서는 브로커에 온도만 던져두고, 스마트폰은 토픽만 구독하고 있으니까 0.01초 만에 데이터가 바로바로 도착해!”
- 🐱 지니: “그게 바로 MQTT의 발행/구독 마법이란다! 이제 이 데이터를 인터넷 클라우드에 24시간 저장하고 예쁜 꺾은선 그래프로 시각화하는 ‘151_IoT클라우드_ThingSpeak’로 나아가보자!”
- 🐶 토토: “바우! 구름(Cloud) 위에 내 기록을 남긴다 멍!”
서브목차