06. MQTT νλ‘ν μ½
06. MQTT νλ‘ν μ½¶
νμ΅ λͺ©ν¶
- MQTT νλ‘ν μ½μ μ리μ νΉμ§ μ΄ν΄
- Mosquitto λΈλ‘컀 μ€μΉ λ° μ€μ
- Topic ꡬ쑰μ QoS λ 벨 μ΄ν΄
- paho-mqtt λΌμ΄λΈλ¬λ¦¬ μ¬μ©λ² μ΅λ
- λ©μμ§ λ°ν λ° κ΅¬λ ꡬν
1. MQTT νλ‘ν μ½ κ°μ¶
1.1 MQTTλ?¶
MQTT (Message Queuing Telemetry Transport)λ κ²½λ λ©μμ§ νλ‘ν μ½λ‘, IoT νκ²½μ μ΅μ νλμ΄ μμ΅λλ€.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MQTT μν€ν
μ² β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Publisher Broker β
β (μΌμ) (μ€κ³μ) β
β βββββββββββ βββββββββββ β
β β μ¨λ β ββPUBLISHβββββΆ β β β
β β μΌμ β (topic: β Mosquittoβ β
β βββββββββββ home/temp) β β β
β β β β
β βββββββββββ β β βββββββββββ β
β β μ΅λ β ββPUBLISHβββββΆ β β βββΆβ λͺ¨λ°μΌ β β
β β μΌμ β (topic: β β β μ± β β
β βββββββββββ home/humid) β β βββββββββββ β
β β β Subscriber β
β β β β
β β β βββββββββββ β
β β β βββΆβ μΉ β β
β β β β λμ보λβ β
β βββββββββββ βββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
1.2 MQTT νΉμ§¶
| νΉμ§ | μ€λͺ |
|---|---|
| κ²½λ | μ΅μ 2λ°μ΄νΈ ν€λ (HTTP λλΉ λ§€μ° μμ) |
| Pub/Sub | λ°ν/ꡬλ ν¨ν΄ (λμ¨ν κ²°ν©) |
| QoS | 3κ°μ§ λ©μμ§ μ λ¬ λ³΄μ₯ μμ€ |
| Last Will | λΉμ μ μ°κ²° μ’ λ£ μ μλ¦Ό |
| Retained | λ§μ§λ§ λ©μμ§ μ μ₯ |
| Keep Alive | μ°κ²° μν λͺ¨λν°λ§ |
1.3 MQTT vs HTTP λΉκ΅¶
# νλ‘ν μ½ λΉκ΅
comparison = {
"Header Size": {
"MQTT": "2 bytes (μ΅μ)",
"HTTP": "~800 bytes (νκ· )"
},
"Pattern": {
"MQTT": "Pub/Sub (λΉλκΈ°)",
"HTTP": "Request/Response (λκΈ°)"
},
"Connection": {
"MQTT": "μ§μ μ°κ²°",
"HTTP": "λΉμ°κ²° (HTTP/1.1) λλ μ§μ (HTTP/2)"
},
"Bidirectional": {
"MQTT": "μ§μ (μλ°©ν₯)",
"HTTP": "μλ² νΈμ μ νμ "
},
"Use Case": {
"MQTT": "μ€μκ° μΌμ, μ μ λ ₯, μ λμν",
"HTTP": "μΉ API, λμ©λ λ°μ΄ν°"
}
}
2. Mosquitto λΈλ‘컀¶
2.1 μ€μΉ¶
# Ubuntu/Debian (λΌμ¦λ² 리νμ΄)
sudo apt update
sudo apt install mosquitto mosquitto-clients
# μλΉμ€ μμ λ° νμ±ν
sudo systemctl start mosquitto
sudo systemctl enable mosquitto
# μν νμΈ
sudo systemctl status mosquitto
2.2 κΈ°λ³Έ μ€μ ¶
# μ€μ νμΌ νΈμ§
sudo nano /etc/mosquitto/mosquitto.conf
# /etc/mosquitto/mosquitto.conf
# κΈ°λ³Έ μ€μ
pid_file /run/mosquitto/mosquitto.pid
# 리μ€λ μ€μ
listener 1883
protocol mqtt
# μ΅λͺ
μ μ (ν
μ€νΈμ©)
allow_anonymous true
# λ‘κ·Έ μ€μ
log_dest file /var/log/mosquitto/mosquitto.log
log_type all
# μ§μμ± (λ©μμ§ μ μ₯)
persistence true
persistence_location /var/lib/mosquitto/
# μΆκ° μ€μ νμΌ ν¬ν¨
include_dir /etc/mosquitto/conf.d
2.3 μΈμ¦ μ€μ ¶
# λΉλ°λ²νΈ νμΌ μμ±
sudo mosquitto_passwd -c /etc/mosquitto/passwd iotuser
# μΆκ° μ¬μ©μ
sudo mosquitto_passwd /etc/mosquitto/passwd anotheruser
# /etc/mosquitto/conf.d/auth.conf
# μ΅λͺ
μ μ λΉνμ±ν
allow_anonymous false
# λΉλ°λ²νΈ νμΌ
password_file /etc/mosquitto/passwd
# μ€μ μ μ©
sudo systemctl restart mosquitto
2.4 TLS μ€μ (보μ μ°κ²°)¶
# μΈμ¦μ μμ± (μ체 μλͺ
)
mkdir -p ~/mqtt-certs && cd ~/mqtt-certs
# CA ν€ λ° μΈμ¦μ
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 365 -key ca.key -out ca.crt
# μλ² ν€ λ° μΈμ¦μ
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365
# /etc/mosquitto/conf.d/tls.conf
listener 8883
protocol mqtt
cafile /home/pi/mqtt-certs/ca.crt
certfile /home/pi/mqtt-certs/server.crt
keyfile /home/pi/mqtt-certs/server.key
require_certificate false
2.5 CLI ν μ€νΈ¶
# ν°λ―Έλ 1: ꡬλ
mosquitto_sub -h localhost -t "test/topic" -v
# ν°λ―Έλ 2: λ°ν
mosquitto_pub -h localhost -t "test/topic" -m "Hello MQTT!"
# μΈμ¦ ν¬ν¨
mosquitto_pub -h localhost -t "test/topic" -m "Hello" -u iotuser -P password
# QoS μ§μ
mosquitto_pub -h localhost -t "test/topic" -m "QoS 1" -q 1
3. Topicκ³Ό QoS¶
3.1 Topic ꡬ쑰¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MQTT Topic ꡬ쑰 β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β κ³μΈ΅μ ꡬ쑰 (μ¬λμλ‘ κ΅¬λΆ) β
β β
β home/ β
β βββ living-room/ β
β β βββ temperature β κ±°μ€ μ¨λ β
β β βββ humidity β κ±°μ€ μ΅λ β
β β βββ light β κ±°μ€ μ‘°λͺ
β
β βββ bedroom/ β
β β βββ temperature β
β β βββ motion β μΉ¨μ€ λͺ¨μ
μΌμ β
β βββ kitchen/ β
β βββ smoke β μ£Όλ°© μ°κΈ° κ°μ§ β
β β
β μμΌλμΉ΄λ: β
β β’ + (λ¨μΌ λ 벨): home/+/temperature β
β β home/living-room/temperature, home/bedroom/temperatureβ
β β
β β’ # (λ€μ€ λ 벨): home/# β
β β home μλ λͺ¨λ ν ν½ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
3.2 Topic μ€κ³ κ°μ΄λ¶
# μ’μ Topic μ€κ³ μμ
topic_examples = {
# μμΉ/μ₯μΉ/μΌμ
"home/living-room/temperature": "κ±°μ€ μ¨λ",
"office/floor1/room101/hvac/status": "μ¬λ¬΄μ€ HVAC μν",
# μ₯μΉID/λ°μ΄ν°νμ
"sensor/abc123/data": "μΌμ λ°μ΄ν°",
"sensor/abc123/status": "μΌμ μν",
# λͺ
λ Ή λ° μλ΅
"device/led001/command": "LED λͺ
λ Ή",
"device/led001/response": "LED μλ΅",
# ν΄λΌμ°λ μ°λ
"aws/things/sensor001/shadow/update": "AWS IoT μλμ°",
}
# νΌν΄μΌ ν ν¨ν΄
bad_patterns = [
"/leading/slash", # μ ν μ¬λμ λΆνμ
"space in topic", # 곡백 νΌνκΈ°
"UpperCase/Mixed", # μλ¬Έμ κΆμ₯
"too/deep/hierarchy/a/b/c/d/e", # κ³Όλν κΉμ΄
]
3.3 QoS (Quality of Service)¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MQTT QoS λ 벨 β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β QoS 0: At most once (μ΅λ 1ν) β
β ββββββββββ ββββββββββ β
β βPublisherβββPUBLISHβββΆβBrokerβ β
β ββββββββββ ββββββββββ β
β β’ μ μ‘ νμΈ μμ β
β β’ κ°μ₯ λΉ λ¦, λ©μμ§ μμ€ κ°λ₯ β
β β’ μ©λ: μΌμ λ°μ΄ν° (μμ€ νμ©) β
β β
β QoS 1: At least once (μ΅μ 1ν) β
β ββββββββββ ββββββββββ β
β βPublisherβββPUBLISHβββΆβBrokerβ β
β β ββββPUBACKββββ β β
β ββββββββββ ββββββββββ β
β β’ νμΈ μλ΅, μ¬μ μ‘ κ°λ₯ β
β β’ μ€λ³΅ κ°λ₯, μμ€ μμ β
β β’ μ©λ: μ€μ μλ¦Ό β
β β
β QoS 2: Exactly once (μ νν 1ν) β
β ββββββββββ ββββββββββ β
β βPublisherβββPUBLISHβββΆβBrokerβ β
β β ββββPUBRECββββ β β
β β βββPUBRELβββΆβ β β
β β ββββPUBCOMPβββ β β
β ββββββββββ ββββββββββ β
β β’ 4-way handshake β
β β’ κ°μ₯ λλ¦Ό, μ νν μ λ¬ λ³΄μ₯ β
β β’ μ©λ: κ²°μ , μ€μ λͺ
λ Ή β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
3.4 Retained λ©μμ§¶
# Retained λ©μμ§ κ°λ
"""
Retained Message:
- λΈλ‘μ»€κ° λ§μ§λ§ λ©μμ§λ₯Ό μ μ₯
- μ ꡬλ
μκ° μ°κ²° μ μ¦μ μμ
- μΌμ νμ¬ μν μ λ¬μ μ μ©
μ:
1. μΌμκ° μ¨λ 25λλ₯Ό retain=Trueλ‘ λ°ν
2. λΈλ‘μ»€κ° λ©μμ§ μ μ₯
3. μ ꡬλ
μ μ°κ²° μ μ¦μ 25λ μμ
4. μΌμ μ€νλΌμΈμ΄μ΄λ λ§μ§λ§ κ° μ μ§
"""
# μ¬μ© μ
retained_use_cases = {
"μ₯μΉ μν": "device/sensor01/status (online/offline)",
"νμ¬ κ°": "home/temperature (λ§μ§λ§ μΈ‘μ κ°)",
"μ€μ ": "device/config (νμ¬ μ€μ )",
}
4. paho-mqtt λΌμ΄λΈλ¬λ¦¬¶
4.1 μ€μΉ¶
pip install paho-mqtt
4.2 κΈ°λ³Έ Publisher¶
#!/usr/bin/env python3
"""MQTT Publisher κΈ°λ³Έ μμ """
import paho.mqtt.client as mqtt
import json
import time
# λΈλ‘컀 μ€μ
BROKER_HOST = "localhost"
BROKER_PORT = 1883
TOPIC = "sensor/temperature"
def on_connect(client, userdata, flags, rc):
"""μ°κ²° μ½λ°±"""
if rc == 0:
print("λΈλ‘컀 μ°κ²° μ±κ³΅")
else:
print(f"μ°κ²° μ€ν¨: {rc}")
def on_publish(client, userdata, mid):
"""λ°ν μλ£ μ½λ°±"""
print(f"λ©μμ§ λ°νλ¨: mid={mid}")
def publish_sensor_data():
"""μΌμ λ°μ΄ν° λ°ν"""
client = mqtt.Client(client_id="temperature_sensor_01")
client.on_connect = on_connect
client.on_publish = on_publish
# μ°κ²°
client.connect(BROKER_HOST, BROKER_PORT, keepalive=60)
client.loop_start()
try:
while True:
# μΌμ λ°μ΄ν° μμ±
data = {
"sensor_id": "temp_01",
"temperature": round(20 + (time.time() % 10), 1),
"timestamp": int(time.time())
}
payload = json.dumps(data)
# λ°ν (QoS 1, Retained μ¬μ©)
result = client.publish(TOPIC, payload, qos=1, retain=True)
if result.rc == mqtt.MQTT_ERR_SUCCESS:
print(f"λ°ν: {payload}")
else:
print(f"λ°ν μ€ν¨: {result.rc}")
time.sleep(5)
except KeyboardInterrupt:
print("\nμ’
λ£")
finally:
client.loop_stop()
client.disconnect()
if __name__ == "__main__":
publish_sensor_data()
4.3 κΈ°λ³Έ Subscriber¶
#!/usr/bin/env python3
"""MQTT Subscriber κΈ°λ³Έ μμ """
import paho.mqtt.client as mqtt
import json
BROKER_HOST = "localhost"
BROKER_PORT = 1883
TOPICS = [
("sensor/temperature", 1),
("sensor/humidity", 1),
]
def on_connect(client, userdata, flags, rc):
"""μ°κ²° μ½λ°±"""
if rc == 0:
print("λΈλ‘컀 μ°κ²° μ±κ³΅")
# ν ν½ κ΅¬λ
for topic, qos in TOPICS:
client.subscribe(topic, qos)
print(f"ꡬλ
: {topic} (QoS {qos})")
else:
print(f"μ°κ²° μ€ν¨: {rc}")
def on_message(client, userdata, msg):
"""λ©μμ§ μμ μ½λ°±"""
try:
payload = json.loads(msg.payload.decode())
print(f"[{msg.topic}] {payload}")
except json.JSONDecodeError:
print(f"[{msg.topic}] {msg.payload.decode()}")
def on_disconnect(client, userdata, rc):
"""μ°κ²° ν΄μ μ½λ°±"""
print(f"μ°κ²° ν΄μ : {rc}")
def subscribe_sensors():
"""μΌμ λ°μ΄ν° ꡬλ
"""
client = mqtt.Client(client_id="sensor_monitor_01")
client.on_connect = on_connect
client.on_message = on_message
client.on_disconnect = on_disconnect
client.connect(BROKER_HOST, BROKER_PORT, keepalive=60)
try:
client.loop_forever()
except KeyboardInterrupt:
print("\nμ’
λ£")
client.disconnect()
if __name__ == "__main__":
subscribe_sensors()
4.4 μΈμ¦ μ¬μ©¶
#!/usr/bin/env python3
"""MQTT μΈμ¦ μ°κ²°"""
import paho.mqtt.client as mqtt
import ssl
BROKER_HOST = "mqtt.example.com"
BROKER_PORT = 8883 # TLS
def create_secure_client(username: str, password: str) -> mqtt.Client:
"""보μ MQTT ν΄λΌμ΄μΈνΈ μμ±"""
client = mqtt.Client(client_id="secure_client_01")
# μΈμ¦ μ€μ
client.username_pw_set(username, password)
# TLS μ€μ
client.tls_set(
ca_certs="/path/to/ca.crt",
certfile="/path/to/client.crt", # ν΄λΌμ΄μΈνΈ μΈμ¦μ (μ΅μ
)
keyfile="/path/to/client.key", # ν΄λΌμ΄μΈνΈ ν€ (μ΅μ
)
tls_version=ssl.PROTOCOL_TLS
)
# νΈμ€νΈλͺ
κ²μ¦ λΉνμ±ν (μ체 μλͺ
μΈμ¦μμ©)
# client.tls_insecure_set(True)
return client
def connect_secure():
"""보μ μ°κ²°"""
client = create_secure_client("iotuser", "password123")
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("보μ μ°κ²° μ±κ³΅")
else:
print(f"μ°κ²° μ€ν¨: {rc}")
client.on_connect = on_connect
client.connect(BROKER_HOST, BROKER_PORT, keepalive=60)
client.loop_forever()
4.5 Last Will and Testament (LWT)¶
#!/usr/bin/env python3
"""MQTT Last Will (λΉμ μ μ’
λ£ μλ¦Ό)"""
import paho.mqtt.client as mqtt
import time
def create_client_with_lwt(client_id: str) -> mqtt.Client:
"""LWTκ° μ€μ λ ν΄λΌμ΄μΈνΈ μμ±"""
client = mqtt.Client(client_id=client_id)
# Last Will μ€μ
# λΉμ μ μ°κ²° μ’
λ£ μ μ΄ λ©μμ§κ° λ°νλ¨
client.will_set(
topic=f"device/{client_id}/status",
payload="offline",
qos=1,
retain=True
)
return client
def run_sensor_with_lwt():
"""LWTκ° μλ μΌμ μ€ν"""
client_id = "sensor_with_lwt"
client = create_client_with_lwt(client_id)
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("μ°κ²°λ¨")
# μ¨λΌμΈ μν λ°ν
client.publish(
f"device/{client_id}/status",
"online",
qos=1,
retain=True
)
client.on_connect = on_connect
client.connect("localhost", 1883, keepalive=60)
client.loop_start()
try:
while True:
client.publish(f"device/{client_id}/data", "sensor data")
time.sleep(5)
except KeyboardInterrupt:
# μ μ μ’
λ£ μ μ€νλΌμΈ μν λ°ν
client.publish(f"device/{client_id}/status", "offline", qos=1, retain=True)
client.disconnect()
5. κ³ κΈ ν¨ν΄¶
5.1 λ©μμ§ λΌμ°ν ¶
#!/usr/bin/env python3
"""ν ν½ κΈ°λ° λ©μμ§ λΌμ°ν
"""
import paho.mqtt.client as mqtt
import json
from typing import Callable
class MQTTRouter:
"""MQTT λ©μμ§ λΌμ°ν°"""
def __init__(self, broker_host: str, broker_port: int = 1883):
self.client = mqtt.Client()
self.broker_host = broker_host
self.broker_port = broker_port
self.routes: dict[str, Callable] = {}
self.client.on_connect = self._on_connect
self.client.on_message = self._on_message
def route(self, topic_pattern: str):
"""λΌμ°νΈ λ°μ½λ μ΄ν°"""
def decorator(func: Callable):
self.routes[topic_pattern] = func
return func
return decorator
def _on_connect(self, client, userdata, flags, rc):
if rc == 0:
print("λΌμ°ν° μ°κ²°λ¨")
for topic in self.routes.keys():
client.subscribe(topic)
print(f"λΌμ°νΈ λ±λ‘: {topic}")
def _on_message(self, client, userdata, msg):
# λ§€μΉλλ νΈλ€λ¬ μ°ΎκΈ°
for pattern, handler in self.routes.items():
if mqtt.topic_matches_sub(pattern, msg.topic):
try:
payload = json.loads(msg.payload.decode())
except json.JSONDecodeError:
payload = msg.payload.decode()
handler(msg.topic, payload)
break
def run(self):
"""λΌμ°ν° μ€ν"""
self.client.connect(self.broker_host, self.broker_port)
self.client.loop_forever()
# μ¬μ© μ
router = MQTTRouter("localhost")
@router.route("sensor/+/temperature")
def handle_temperature(topic: str, payload: dict):
sensor_id = topic.split('/')[1]
print(f"μ¨λ [{sensor_id}]: {payload}")
@router.route("sensor/+/humidity")
def handle_humidity(topic: str, payload: dict):
sensor_id = topic.split('/')[1]
print(f"μ΅λ [{sensor_id}]: {payload}")
@router.route("device/+/command")
def handle_command(topic: str, payload: dict):
device_id = topic.split('/')[1]
print(f"λͺ
λ Ή [{device_id}]: {payload}")
if __name__ == "__main__":
router.run()
5.2 λΉλκΈ° MQTT (asyncio)¶
#!/usr/bin/env python3
"""λΉλκΈ° MQTT ν΄λΌμ΄μΈνΈ (asyncio-mqtt)"""
import asyncio
import aiomqtt # pip install aiomqtt
import json
async def publish_sensor_data():
"""λΉλκΈ° λ°ν"""
async with aiomqtt.Client("localhost") as client:
while True:
data = {
"temperature": 25.5,
"timestamp": asyncio.get_event_loop().time()
}
await client.publish("sensor/temp", json.dumps(data))
print(f"λ°ν: {data}")
await asyncio.sleep(5)
async def subscribe_sensor_data():
"""λΉλκΈ° ꡬλ
"""
async with aiomqtt.Client("localhost") as client:
async with client.messages() as messages:
await client.subscribe("sensor/#")
async for message in messages:
print(f"[{message.topic}] {message.payload.decode()}")
async def main():
"""λ°νκ³Ό ꡬλ
λμ μ€ν"""
await asyncio.gather(
publish_sensor_data(),
subscribe_sensor_data()
)
if __name__ == "__main__":
asyncio.run(main())
5.3 μ¬μ°κ²° λ‘μ§¶
#!/usr/bin/env python3
"""μλ μ¬μ°κ²° MQTT ν΄λΌμ΄μΈνΈ"""
import paho.mqtt.client as mqtt
import time
class RobustMQTTClient:
"""μλ μ¬μ°κ²°μ μ§μνλ MQTT ν΄λΌμ΄μΈνΈ"""
def __init__(self, broker_host: str, broker_port: int = 1883):
self.broker_host = broker_host
self.broker_port = broker_port
self.client = mqtt.Client()
self.connected = False
self.reconnect_delay = 1
self.max_reconnect_delay = 60
self.client.on_connect = self._on_connect
self.client.on_disconnect = self._on_disconnect
def _on_connect(self, client, userdata, flags, rc):
if rc == 0:
print("μ°κ²°λ¨")
self.connected = True
self.reconnect_delay = 1 # 리μ
else:
print(f"μ°κ²° μ€ν¨: {rc}")
def _on_disconnect(self, client, userdata, rc):
print(f"μ°κ²° ν΄μ : {rc}")
self.connected = False
if rc != 0:
self._reconnect()
def _reconnect(self):
"""μ¬μ°κ²° μλ"""
while not self.connected:
try:
print(f"μ¬μ°κ²° μλ... ({self.reconnect_delay}μ΄ ν)")
time.sleep(self.reconnect_delay)
self.client.reconnect()
except Exception as e:
print(f"μ¬μ°κ²° μ€ν¨: {e}")
# μ§μ λ°±μ€ν
self.reconnect_delay = min(
self.reconnect_delay * 2,
self.max_reconnect_delay
)
def connect(self):
"""μ΄κΈ° μ°κ²°"""
self.client.connect(self.broker_host, self.broker_port)
def run(self):
"""ν΄λΌμ΄μΈνΈ μ€ν"""
self.connect()
self.client.loop_forever()
if __name__ == "__main__":
client = RobustMQTTClient("localhost")
client.run()
μ°μ΅ λ¬Έμ ¶
λ¬Έμ 1: μ¨μ΅λ λͺ¨λν°¶
- μ¨λμ μ΅λ λ°μ΄ν°λ₯Ό 5μ΄λ§λ€ λ°ννλ Publisherλ₯Ό μμ±νμΈμ.
- ν΄λΉ λ°μ΄ν°λ₯Ό ꡬλ νμ¬ μ½μμ μΆλ ₯νλ Subscriberλ₯Ό μμ±νμΈμ.
λ¬Έμ 2: μ₯μΉ μν κ΄λ¦¬¶
- LWTλ₯Ό μ¬μ©νμ¬ μ₯μΉ μ¨λΌμΈ/μ€νλΌμΈ μνλ₯Ό κ΄λ¦¬νμΈμ.
- μμΌλμΉ΄λλ₯Ό μ¬μ©νμ¬ λͺ¨λ μ₯μΉ μνλ₯Ό λͺ¨λν°λ§νμΈμ.
λ¬Έμ 3: λͺ λ Ή-μλ΅ μμ€ν ¶
- λͺ λ Ή ν ν½μΌλ‘ LED μ μ΄ λͺ λ Ήμ μμ νμΈμ.
- μλ΅ ν ν½μΌλ‘ μ€ν κ²°κ³Όλ₯Ό λ°ννμΈμ.
λ€μ λ¨κ³¶
- 07_HTTP_REST_for_IoT.md: REST APIμ MQTT ν΅ν©
- 10_Home_Automation_Project.md: MQTT κΈ°λ° μ€λ§νΈν
μ΅μ’ μ λ°μ΄νΈ: 2026-02-01