ํ์ด์ฌ์ ์ฌ์ฉํ์ฌ IoT์ฉ MQTT ํ๋กํ ์ฝ์ ๋ง์คํฐํ์ธ์. ์ด ์ฌ์ธต ๊ฐ์ด๋๋ ๊ธฐ๋ณธ ์๋ฆฌ, Paho-MQTT ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ๋ณด์ ๋ฐ ์ค์ ํ๋ก์ ํธ ๊ตฌํ์ ๋ค๋ฃน๋๋ค.
IoT๋ฅผ ์ํ ํ์ด์ฌ: MQTT ๊ตฌํ ์ข ํฉ ๊ฐ์ด๋
์ฐ๊ฒฐ๋ ์ธ์: IoT ํ๋กํ ์ฝ์ด ์ค์ํ ์ด์
์ฐ๋ฆฌ๋ ์ ๋ก ์๋ ์ฐ๊ฒฐ์ ์๋์ ์ด๊ณ ์์ต๋๋ค. ์ฌ๋ฌผ ์ธํฐ๋ท(IoT)์ ๋ ์ด์ ๋ฏธ๋์ ๊ฐ๋ ์ด ์๋๋๋ค. ๊ทธ๊ฒ์ ์ฐ๋ฆฌ์ ํ๊ฒฝ์ ๋ชจ๋ํฐ๋งํ๊ณ , ๊ฐ์ ์ ์๋ํํ๋ฉฐ, ์ฐ์ ์ ์ต์ ํํ๊ณ , ๋์๋ฅผ ํจ์จํํ๋ ์์ญ์ต ๊ฐ์ ์ค๋งํธ ๊ธฐ๊ธฐ ๋คํธ์ํฌ๋ฅผ ์กฐ์ฉํ ์ฎ์ด๋ด๋ ์ธ๊ณ์ ์ธ ํ์ค์ ๋๋ค. ์์ธ์ ๊ฐ์ ์ ์๋ ์ค๋งํธ ์จ๋ ์กฐ์ ๊ธฐ๋ถํฐ ์ผ๋ ์๊ณจ ๋คํ์ ๋์ ์ผ์์ ์ด๋ฅด๊ธฐ๊น์ง, ์ด๋ฌํ ์ฅ์น๋ค์ ์์ฒญ๋ ์์ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๊ณ ์์ต๋๋ค. ํ์ง๋ง ์ข ์ข ์๊ณ , ์ ์ ๋ ฅ์ด๋ฉฐ, ์ ๋ขฐํ ์ ์๋ ๋คํธ์ํฌ์์ ์๋ํ๋ ์ด ์ฅ์น๋ค์ด ์ด๋ป๊ฒ ์๋ก, ๊ทธ๋ฆฌ๊ณ ํด๋ผ์ฐ๋์ ํต์ ํ ์ ์์๊น์? ํด๋ต์ ํนํ๋ ํต์ ํ๋กํ ์ฝ์ ์์ต๋๋ค.
์ฐ๋ฆฌ๊ฐ ๋งค์ผ ์ฌ์ฉํ๋ ์น์ ๋๋ถ๋ถ์ HTTP ํ๋กํ ์ฝ๋ก ๊ตฌ๋๋์ง๋ง, ์ด๋ ์ ํ๋ IoT ์ธ๊ณ์๋ ๋๋ฌด ๋ฌด๊ฒ๊ณ ์ ๋ ฅ ์๋ชจ๊ฐ ๋ง์ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ๋ฐ๋ก ์ด ์ง์ ์์ ๊ธฐ๊ณ ๋ ๊ธฐ๊ณ(M2M) ํต์ ์ ์ํด ํน๋ณํ ์ค๊ณ๋ ํ๋กํ ์ฝ์ด ๋น์ ๋ฐํฉ๋๋ค. ๊ทธ์ค์์๋ MQTT๋ ์ง๋ฐฐ์ ์ธ ํ์ผ๋ก ๋ถ์ํ์ต๋๋ค.
์ด ์ข ํฉ ๊ฐ์ด๋๋ IoT ๋ถ์ผ์์ ๊ฐ์ฅ ๋ค์ฌ๋ค๋ฅํ๊ณ ์ธ๊ธฐ ์๋ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด ์ค ํ๋์ธ ํ์ด์ฌ์ ์ฌ์ฉํ์ฌ MQTT์ ํ์ ํ์ฉํ๊ณ ์ ํ๋ ์ ์ธ๊ณ์ ๊ฐ๋ฐ์, ์์ง๋์ด, ๊ทธ๋ฆฌ๊ณ ์ทจ๋ฏธ ํ๋๊ฐ๋ค์ ์ํด ์ค๊ณ๋์์ต๋๋ค. ์ฐ๋ฆฌ๋ MQTT์ ๊ธฐ๋ณธ ๊ฐ๋ ๋ถํฐ ์์ํ์ฌ ์์ ํ๊ณ , ๊ฒฌ๊ณ ํ๋ฉฐ, ํ์ฅ ๊ฐ๋ฅํ IoT ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ์ฌ์ ์ ๋ ๋ ๊ฒ์ ๋๋ค.
MQTT๋ ๋ฌด์์ธ๊ฐ? ์ ์ฝ์ ์ํด ๋ง๋ค์ด์ง ํ๋กํ ์ฝ
MQTT๋ Message Queuing Telemetry Transport์ ์ฝ์์ ๋๋ค. 1999๋ IBM์ ์ค๋ ์คํ ํฌ๋-ํด๋ฝ ๋ฐ์ฌ์ Arcom(ํ Cirrus Link)์ ์๋ ๋ํผ๊ฐ ์ ๋ขฐํ ์ ์๋ ์์ฑ ๋คํธ์ํฌ๋ฅผ ํตํด ์ก์ ๊ด์ ๋ชจ๋ํฐ๋งํ๊ธฐ ์ํด ๋ฐ๋ช ํ์ต๋๋ค. ๊ทธ ํ์ ๋ฐฐ๊ฒฝ์ MQTT์ ๋ชฉ์ ์ ์๋ฒฝํ๊ฒ ์์ฝํฉ๋๋ค: ์๋นํ ์ ์ฝ ํ์์ ์๋ํ๋ ์ฅ์น๋ฅผ ์ํ ๊ฐ๋ณ๊ณ , ์ ๋ขฐํ ์ ์์ผ๋ฉฐ, ํจ์จ์ ์ธ ๋ฉ์์ง ํ๋กํ ์ฝ์ด ๋๋ ๊ฒ.
๋ฐํ/๊ตฌ๋ (Pub/Sub) ๋ชจ๋ธ ์ค๋ช
MQTT์ ํต์ฌ์๋ ์ฐ์ํ ๋ฐํ/๊ตฌ๋ ์ํคํ ์ฒ ํจํด์ด ์์ต๋๋ค. ์ด๋ ๋ง์ ๊ฐ๋ฐ์์๊ฒ ์ต์ํ HTTP์ ์์ฒญ/์๋ต ๋ชจ๋ธ๊ณผ๋ ๊ทผ๋ณธ์ ์ผ๋ก ๋ค๋ฆ ๋๋ค. ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์ ์ง์ ์ ๋ณด๋ฅผ ์์ฒญํ๋ ๋์ , ํต์ ์ด ๋ถ๋ฆฌ(decoupled)๋ฉ๋๋ค.
๊ธ๋ก๋ฒ ๋ด์ค ์์ด์ ์๋ฅผ ์์ํด ๋ณด์ญ์์ค. ๊ธฐ์(๋ฐํ์)๋ ์์ ์ ๊ธฐ์ฌ๋ฅผ ๋ชจ๋ ๋ ์์๊ฒ ์ง์ ๋ณด๋ด์ง ์์ต๋๋ค. ๋์ , ๊ทธ๋ค์ ๊ธฐ์ฌ๋ฅผ ์์ด์ ์์ ์ค์ ํ๋ธ(๋ธ๋ก์ปค)๋ก ๋ณด๋ด๊ณ "์ธ๊ณ ์ ์น"๋ "๊ธฐ์ "๊ณผ ๊ฐ์ ํน์ ์ฃผ์ ๋ก ๋ถ๋ฅํฉ๋๋ค. ๋ ์(๊ตฌ๋ ์)๋ ๊ธฐ์์๊ฒ ์ ๋ฐ์ดํธ๋ฅผ ์์ฒญํ ํ์ ์์ด, ๋จ์ง ์์ด์ ์์๊ฒ ์ด๋ค ์ฃผ์ ์ ๊ด์ฌ์ด ์๋์ง ์๋ ค์ฃผ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค. ๊ทธ๋ฌ๋ฉด ์์ด์ ์๋ ํด๋น ์ฃผ์ ์ ๋ํ ์๋ก์ด ๊ธฐ์ฌ๋ฅผ ๊ด์ฌ ์๋ ๋ ์์๊ฒ ์๋์ผ๋ก ์ ๋ฌํฉ๋๋ค. ๊ธฐ์์ ๋ ์๋ ์๋ก์ ์กด์ฌ, ์์น ๋๋ ์ํ๋ฅผ ์ ํ์๊ฐ ์์ต๋๋ค.
MQTT์์ ์ด ๋ชจ๋ธ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ ์ฅ์น(๋ฐํ์)์ ์ด๋ฅผ ์์ ํ๋ ์ฅ์น ๋๋ ์ ํ๋ฆฌ์ผ์ด์ (๊ตฌ๋ ์)์ ๋ถ๋ฆฌํฉ๋๋ค. ์ด๋ ๋ค์๊ณผ ๊ฐ์ ์ด์ ๋ก IoT์ ๋งค์ฐ ๊ฐ๋ ฅํฉ๋๋ค:
- ๊ณต๊ฐ ๋ถ๋ฆฌ: ๋ฐํ์์ ๊ตฌ๋ ์๋ ์๋ก์ IP ์ฃผ์๋ ์์น๋ฅผ ์ ํ์๊ฐ ์์ต๋๋ค.
- ์๊ฐ ๋ถ๋ฆฌ: ๊ทธ๋ค์ ๋์์ ์คํ๋ ํ์๊ฐ ์์ต๋๋ค. ์์คํ ์ด ๊ทธ๋ ๊ฒ ์ค๊ณ๋์๋ค๋ฉด ์ผ์๋ ์ธก์ ๊ฐ์ ๋ฐํํ๊ณ , ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ช ์๊ฐ ํ์ ๊ทธ๊ฒ์ ์์ ํ ์ ์์ต๋๋ค.
- ๋๊ธฐํ ๋ถ๋ฆฌ: ์์ธก์ ์์ ์ ๋ฉ์์ง ๊ตํ์ ์๋ฃํ๊ธฐ ์ํด ๋ค๋ฅธ ์ชฝ์ ๊ธฐ๋ค๋ฆฌ๋๋ผ ์ค๋จ๋ ํ์๊ฐ ์์ต๋๋ค.
MQTT ์ํ๊ณ์ ํต์ฌ ๊ตฌ์ฑ ์์
MQTT ์ํคํ ์ฒ๋ ๋ช ๊ฐ์ง ํต์ฌ ๊ตฌ์ฑ ์์๋ก ์ด๋ฃจ์ด์ ธ ์์ต๋๋ค:
- ๋ธ๋ก์ปค(Broker): ์ค์ ํ๋ธ ๋๋ ์๋ฒ์ ๋๋ค. MQTT ์ธ๊ณ์ ์ฐ์ฒด๊ตญ๊ณผ ๊ฐ์ต๋๋ค. ๋ธ๋ก์ปค๋ ๋ฐํ์๋ก๋ถํฐ ๋ชจ๋ ๋ฉ์์ง๋ฅผ ์์ ํ๊ณ , ์ฃผ์ ๋ณ๋ก ํํฐ๋งํ์ฌ ์ ์ ํ ๊ตฌ๋ ์์๊ฒ ๋ณด๋ด๋ ์ญํ ์ ๋ด๋นํฉ๋๋ค. ์ธ๊ธฐ ์๋ ๋ธ๋ก์ปค๋ก๋ Mosquitto ๋ฐ VerneMQ์ ๊ฐ์ ์คํ ์์ค ์ต์ ๊ณผ AWS IoT Core, Azure IoT Hub, Google Cloud IoT Core์ ๊ฐ์ ๊ด๋ฆฌํ ํด๋ผ์ฐ๋ ์๋น์ค๊ฐ ์์ต๋๋ค.
- ํด๋ผ์ด์ธํธ(Client): ๋ธ๋ก์ปค์ ์ฐ๊ฒฐํ๋ ๋ชจ๋ ์ฅ์น ๋๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋๋ค. ํด๋ผ์ด์ธํธ๋ ๋ฐํ์, ๊ตฌ๋ ์ ๋๋ ๋ ๋ค์ผ ์ ์์ต๋๋ค. IoT ์ผ์๋ ํด๋ผ์ด์ธํธ์ด๋ฉฐ, ์ผ์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ์๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ๋ ํด๋ผ์ด์ธํธ์ ๋๋ค.
- ์ฃผ์ (Topic): ๋ฉ์์ง์ ์ฃผ์ ๋๋ ๋ ์ด๋ธ ์ญํ ์ ํ๋ UTF-8 ๋ฌธ์์ด์
๋๋ค. ๋ธ๋ก์ปค๋ ์ฃผ์ ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฉ์์ง๋ฅผ ๋ผ์ฐํ
ํฉ๋๋ค. ์ฃผ์ ๋ ํ์ผ ์์คํ
๊ฒฝ๋ก์ฒ๋ผ ์ฌ๋์(/)๋ฅผ ๊ตฌ๋ถ ๊ธฐํธ๋ก ์ฌ์ฉํ๋ ๊ณ์ธต์ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋๋ค. ์๋ฅผ ๋ค์ด, ๋ฐ๋์ ํ ๊ฑด๋ฌผ ๊ฑฐ์ค์ ์๋ ์จ๋ ์ผ์๋ฅผ ์ํ ์ข์ ์ฃผ์ ๋
UK/London/Building-A/Floor-1/LivingRoom/Temperature๊ฐ ๋ ์ ์์ต๋๋ค. - ํ์ด๋ก๋(Payload): ์ด๊ฒ์ ๋ฉ์์ง์ ์ค์ ๋ฐ์ดํฐ ๋ด์ฉ์ ๋๋ค. MQTT๋ ๋ฐ์ดํฐ์ ๊ตฌ์ ๋ฐ์ง ์์ผ๋ฏ๋ก ํ์ด๋ก๋๋ ๋จ์ํ ๋ฌธ์์ด, ์ ์, JSON, XML ๋๋ ์ํธํ๋ ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ ๋ฑ ๋ฌด์์ด๋ ๋ ์ ์์ต๋๋ค. JSON์ ์ ์ฐ์ฑ๊ณผ ๊ฐ๋ ์ฑ์ผ๋ก ์ธํด ๋งค์ฐ ํํ ์ ํ์ ๋๋ค.
MQTT๊ฐ IoT ํต์ ์ ์ง๋ฐฐํ๋ ์ด์
MQTT์ ์ค๊ณ ์์น์ IoT์ ๊ณผ์ ์ ํนํ ์ ํฉํฉ๋๋ค:
- ๊ฒฝ๋์ฑ: MQTT ๋ฉ์์ง๋ ํค๋๊ฐ ๋งค์ฐ ์์(์ต์ 2๋ฐ์ดํธ) ๋คํธ์ํฌ ๋์ญํญ ์ฌ์ฉ์ ์ต์ํํฉ๋๋ค. ์ด๋ ๋น์ผ ์ ๋ฃฐ๋ฌ ์๊ธ์ ๋ LoRaWAN๊ณผ ๊ฐ์ ์ ๋์ญํญ ๋คํธ์ํฌ์ ์ฅ์น์ ๋งค์ฐ ์ค์ํฉ๋๋ค.
- ํจ์จ์ฑ: ํ๋กํ ์ฝ์ ๋ฎ์ ์ค๋ฒํค๋๋ ์ ๋ ฅ ์๋น ๊ฐ์๋ก ์ง์ ์ด์ด์ ธ ๋ฐฐํฐ๋ฆฌ๋ก ๊ตฌ๋๋๋ ์ฅ์น๊ฐ ๋ช ๋ฌ ๋๋ ๋ช ๋ ๋์ ์๋ํ ์ ์๊ฒ ํฉ๋๋ค.
- ์ ๋ขฐ์ฑ: ๋ถ์์ ํ๊ณ ์ง์ฐ ์๊ฐ์ด ๊ธด ๋คํธ์ํฌ์์๋ ๋ฉ์์ง ์ ๋ฌ์ ๋ณด์ฅํ๋ ๊ธฐ๋ฅ์ ํฌํจํฉ๋๋ค. ์ด๋ ์๋น์ค ํ์ง(QoS) ์์ค์ ํตํด ๊ด๋ฆฌ๋ฉ๋๋ค.
- ํ์ฅ์ฑ: ๋จ์ผ ๋ธ๋ก์ปค๊ฐ ์์ฒ ๋๋ ์๋ฐฑ๋ง ๊ฐ์ ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ์ ๋์์ ์ฒ๋ฆฌํ ์ ์์ด ๋๊ท๋ชจ ๋ฐฐํฌ์ ์ ํฉํฉ๋๋ค.
- ์๋ฐฉํฅ์ฑ: MQTT๋ ์ฅ์น์์ ํด๋ผ์ฐ๋๋ก(ํ ๋ ๋ฉํธ๋ฆฌ) ๊ทธ๋ฆฌ๊ณ ํด๋ผ์ฐ๋์์ ์ฅ์น๋ก(๋ช ๋ น)์ ํต์ ์ ํ์ฉํ๋ฉฐ, ์ด๋ ์๊ฒฉ์ผ๋ก ์ฅ์น๋ฅผ ์ ์ดํ๋ ๋ฐ ํ์์ ์ธ ์๊ตฌ ์ฌํญ์ ๋๋ค.
์๋น์ค ํ์ง(QoS) ์ดํดํ๊ธฐ
MQTT๋ ๊ฐ๋ฐ์๊ฐ ํน์ ์ฌ์ฉ ์ฌ๋ก์ ๋ง์ถฐ ์ ๋ขฐ์ฑ๊ณผ ์ค๋ฒํค๋ ์ฌ์ด์ ์ ์ ํ ๊ท ํ์ ์ ํํ ์ ์๋๋ก ์ธ ๊ฐ์ง ์์ค์ ์๋น์ค ํ์ง(QoS)์ ์ ๊ณตํฉ๋๋ค.
- QoS 0 (์ต๋ ํ ๋ฒ): ์ด๊ฒ์ "์๊ณ ์์ด๋ฒ๋ฆฌ๋" ์์ค์ ๋๋ค. ๋ฉ์์ง๋ ํ ๋ฒ๋ง ์ ์ก๋๋ฉฐ ๋ธ๋ก์ปค๋ ์ต์ข ๊ตฌ๋ ์๋ก๋ถํฐ ์์ ํ์ธ์ ๋ฐ์ง ์์ต๋๋ค. ๊ฐ์ฅ ๋น ๋ฅธ ๋ฐฉ๋ฒ์ด์ง๋ง ์ ๋ฌ์ ๋ณด์ฅํ์ง ์์ต๋๋ค. ์ฌ์ฉ ์ฌ๋ก: 10์ด๋ง๋ค ์ ์ก๋๋ ์ค๋ด ์ฃผ๋ณ ์จ๋ ์ธก์ ๊ฐ๊ณผ ๊ฐ์ด ์ค์ํ์ง ์๊ณ ๋น๋๊ฐ ๋์ ์ผ์ ๋ฐ์ดํฐ. ํ๋์ ์ธก์ ๊ฐ์ ์์ด๋ ๋ฌธ์ ๊ฐ ๋์ง ์์ต๋๋ค.
- QoS 1 (์ต์ ํ ๋ฒ): ์ด ์์ค์ ๋ฉ์์ง๊ฐ ์ต์ ํ ๋ฒ์ ์ ๋ฌ๋ ๊ฒ์ ๋ณด์ฅํฉ๋๋ค. ๋ฐ์ ์๋ ์์ ์๋ก๋ถํฐ ํ์ธ ์๋ต(PUBACK ํจํท)์ ๋ฐ์ ๋๊น์ง ๋ฉ์์ง๋ฅผ ์ ์ฅํฉ๋๋ค. ํ์ธ ์๋ต์ ๋ฐ์ง ๋ชปํ๋ฉด ๋ฉ์์ง๋ฅผ ์ฌ์ ์กํฉ๋๋ค. ์ด๋ก ์ธํด ํ์ธ ์๋ต์ด ์์ค๋ ๊ฒฝ์ฐ ๋๋๋ก ์ค๋ณต ๋ฉ์์ง๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ์ฌ์ฉ ์ฌ๋ก: ์ค๋งํธ ์กฐ๋ช ์ ์ผ๋ผ๋ ๋ช ๋ น. ๋ช ๋ น์ด ์์ ๋์๋์ง ํ์ธํด์ผ ํ๋ฉฐ, ๋ ๋ฒ ์์ ํด๋ ํด๊ฐ ๋์ง ์๋ ๊ฒฝ์ฐ์ ์ฌ์ฉ๋ฉ๋๋ค.
- QoS 2 (์ ํํ ํ ๋ฒ): ๊ฐ์ฅ ์ ๋ขฐํ ์ ์์ง๋ง ๊ฐ์ฅ ๋๋ฆฐ ์์ค์ ๋๋ค. 4๋จ๊ณ ํธ๋์ ฐ์ดํฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฉ์์ง๊ฐ ์ค๋ณต ์์ด ์ ํํ ํ ๋ฒ๋ง ์ ๋ฌ๋๋๋ก ๋ณด์ฅํฉ๋๋ค. ์ฌ์ฉ ์ฌ๋ก: ๊ธ์ต ๊ฑฐ๋, ์ ํํ ์์ ์ฝ๋ฌผ ํฌ์ฌ ๋ช ๋ น, ๋๋ ๊ณต์ฅ์ ๋ก๋ด ํ ์ ์ด์ ๊ฐ์ด ์ค๋ณต์ด ์น๋ช ์ ์ผ ์ ์๋ ์ค์ํ ์์ ์ ์ฌ์ฉ๋ฉ๋๋ค.
ํ์ด์ฌ MQTT ํ๊ฒฝ ์ค์ ํ๊ธฐ
์ด์ ์ค์ฉ์ ์ธ ๋ถ๋ถ์ผ๋ก ๋์ด๊ฐ ๋ณด๊ฒ ์ต๋๋ค. ํ์ด์ฌ์ผ๋ก MQTT ์ ํ๋ฆฌ์ผ์ด์ ๊ตฌ์ถ์ ์์ํ๋ ค๋ฉด ๋ ๊ฐ์ง๊ฐ ํ์ํฉ๋๋ค: MQTT ํด๋ผ์ด์ธํธ๋ฅผ ์ํ ํ์ด์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํต์ ํ MQTT ๋ธ๋ก์ปค์ ๋๋ค.
ํ์ด์ฌ MQTT ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ํ: Paho-MQTT
ํ์ด์ฌ์ฉ์ผ๋ก ๊ฐ์ฅ ๋๋ฆฌ ์ฌ์ฉ๋๊ณ ์ฑ์ํ MQTT ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ดํด๋ฆฝ์ค ์ฌ๋จ์ Paho-MQTT์ ๋๋ค. ์ด๋ ๋ธ๋ก์ปค์ ์ฐ๊ฒฐํ๊ณ ์ฃผ์ ๋ฅผ ๋ฐํํ๊ฑฐ๋ ๊ตฌ๋ ํ๋ ํด๋ผ์ด์ธํธ ํด๋์ค๋ฅผ ์ ๊ณตํ๋ ๊ฒฌ๊ณ ํ๊ณ ๊ธฐ๋ฅ์ด ํ๋ถํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ํ์ด์ฌ์ ํจํค์ง ๊ด๋ฆฌ์์ธ pip๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ๋จํ๊ฒ ์ค์นํ ์ ์์ต๋๋ค.
ํฐ๋ฏธ๋์ด๋ ๋ช ๋ น ํ๋กฌํํธ๋ฅผ ์ด๊ณ ๋ค์์ ์คํํ์ธ์:
pip install paho-mqtt
์ด ๋จ์ผ ๋ช ๋ น์ด๋ก ํ์ด์ฌ์์ MQTT ํด๋ผ์ด์ธํธ๋ฅผ ์์ฑํ๋ ๋ฐ ํ์ํ ๋ชจ๋ ๊ฒ์ด ์ค์น๋ฉ๋๋ค.
MQTT ๋ธ๋ก์ปค ์ค์ ํ๊ธฐ
๋ธ๋ก์ปค์๋ ๊ฐ๋ฐ์ ์ํด ๋ก์ปฌ ๋จธ์ ์์ ์คํํ๋ ๊ฒ๋ถํฐ ํ๋ก๋์ ์ ์ํด ๊ฐ๋ ฅํ ํด๋ผ์ฐ๋ ์๋น์ค๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๊น์ง ์ฌ๋ฌ ์ต์ ์ด ์์ต๋๋ค.
- ๋ก์ปฌ ๋ธ๋ก์ปค (๊ฐ๋ฐ ๋ฐ ํ์ต์ฉ): ๋ก์ปฌ ๋ธ๋ก์ปค๋ก ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ์ ํ์ ๋ ๋ค๋ฅธ ์ดํด๋ฆฝ์ค ํ๋ก์ ํธ์ธ Mosquitto์
๋๋ค. ๊ฐ๋ณ๊ณ , ์คํ ์์ค์ด๋ฉฐ, ์ค์น๊ฐ ์ฝ์ต๋๋ค.
- Debian ๊ธฐ๋ฐ ๋ฆฌ๋
์ค(Ubuntu, Raspberry Pi OS ๋ฑ):
sudo apt-get update && sudo apt-get install mosquitto mosquitto-clients - macOS (Homebrew ์ฌ์ฉ):
brew install mosquitto - Windows: Mosquitto ์น์ฌ์ดํธ์์ ๋ค์ดํฐ๋ธ ์ค์น ํ๋ก๊ทธ๋จ์ ๋ค์ด๋ก๋ํฉ๋๋ค.
127.0.0.1๋๋localhost)๋ฅผ ๊ฐ๋ฆฌํค๋๋ก ํ์ฌ ์ฌ์ฉํ ์ ์์ต๋๋ค. - Debian ๊ธฐ๋ฐ ๋ฆฌ๋
์ค(Ubuntu, Raspberry Pi OS ๋ฑ):
- ๊ณต๊ฐ/ํด๋ผ์ฐ๋ ๋ธ๋ก์ปค (๋น ๋ฅธ ํ
์คํธ์ฉ): ์๋ฌด๊ฒ๋ ์ค์นํ์ง ์๊ณ ์ด๊ธฐ ์คํ์ ํ๋ ค๋ฉด ๋ฌด๋ฃ ๊ณต๊ฐ ๋ธ๋ก์ปค๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ธ๊ธฐ ์๋ ๋ ๊ฐ์ง๋
test.mosquitto.org์broker.hivemq.com์ ๋๋ค. ์ค์: ์ด๋ค์ ๊ณต๊ฐ์ ์ด๊ณ ์ํธํ๋์ง ์์์ต๋๋ค. ๋ฏผ๊ฐํ๊ฑฐ๋ ์ฌ์ ์ธ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด์ง ๋ง์ญ์์ค. ํ์ต ๋ฐ ํ ์คํธ ๋ชฉ์ ์ผ๋ก๋ง ์ฌ์ฉํด์ผ ํฉ๋๋ค.
์ค์ต: ํ์ด์ฌ์ผ๋ก ๋ฐํ ๋ฐ ๊ตฌ๋ ํ๊ธฐ
์ฒซ ๋ฒ์งธ ํ์ด์ฌ MQTT ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ฑํด ๋ด ์๋ค. ๋ฉ์์ง๋ฅผ ๋ณด๋ด๋ ๋ฐํ์์ ๋ฉ์์ง๋ฅผ ๋ฐ๋ ๊ตฌ๋ ์, ๋ ๊ฐ์ ๋ณ๋ ์คํฌ๋ฆฝํธ๋ฅผ ๋ง๋ค ๊ฒ์ ๋๋ค. ์ด ์์ ์์๋ ๋ก์ปฌ Mosquitto ๋ธ๋ก์ปค๋ฅผ ์คํํ๊ณ ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค.
๊ฐ๋จํ MQTT ๋ฐํ์ ๋ง๋ค๊ธฐ (publisher.py)
์ด ์คํฌ๋ฆฝํธ๋ ๋ธ๋ก์ปค์ ์ฐ๊ฒฐํ์ฌ 2์ด๋ง๋ค `python/mqtt/test` ์ฃผ์ ๋ก "Hello, MQTT!"๋ผ๋ ๋ฉ์์ง๋ฅผ ๋ฐํํฉ๋๋ค.
`publisher.py`๋ผ๋ ํ์ผ์ ๋ง๋ค๊ณ ๋ค์ ์ฝ๋๋ฅผ ์ถ๊ฐํ์ธ์:
import paho.mqtt.client as mqtt
import time
# --- ์ค์ ---
BROKER_ADDRESS = "localhost" # ๊ณต๊ฐ ๋ธ๋ก์ปค๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด 'test.mosquitto.org'๋ฅผ ์ฌ์ฉํ์ธ์
PORT = 1883
TOPIC = "python/mqtt/test"
# --- ์ฐ๊ฒฐ ์ฝ๋ฐฑ ---
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("MQTT ๋ธ๋ก์ปค์ ์ฐ๊ฒฐ๋์์ต๋๋ค!")
else:
print(f"์ฐ๊ฒฐ ์คํจ, ๋ฐํ ์ฝ๋ {rc}")
# --- ๋ฉ์ธ ์คํฌ๋ฆฝํธ ---
# 1. ํด๋ผ์ด์ธํธ ์ธ์คํด์ค ์์ฑ
client = mqtt.Client("PublisherClient")
# 2. on_connect ์ฝ๋ฐฑ ํ ๋น
client.on_connect = on_connect
# 3. ๋ธ๋ก์ปค์ ์ฐ๊ฒฐ
client.connect(BROKER_ADDRESS, PORT, 60)
# 4. ๋คํธ์ํฌ ๋ฃจํ๋ฅผ ์ํ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋ ์์
client.loop_start()
try:
count = 0
while True:
count += 1
message = f"Hello, MQTT! Message #{count}"
# 5. ๋ฉ์์ง ๋ฐํ
result = client.publish(TOPIC, message)
# ๋ฐํ ์ฑ๊ณต ์ฌ๋ถ ํ์ธ
status = result[0]
if status == 0:
print(f"`{message}`๋ฅผ `{TOPIC}` ์ฃผ์ ๋ก ๋ณด๋์ต๋๋ค")
else:
print(f"{TOPIC} ์ฃผ์ ๋ก ๋ฉ์์ง ์ ์ก ์คํจ")
time.sleep(2)
except KeyboardInterrupt:
print("๋ฐํ์ด ์ค์ง๋์์ต๋๋ค.")
finally:
# 6. ๋คํธ์ํฌ ๋ฃจํ ์ค์ง ๋ฐ ์ฐ๊ฒฐ ํด์
client.loop_stop()
client.disconnect()
print("๋ธ๋ก์ปค์์ ์ฐ๊ฒฐ์ด ๋์ด์ก์ต๋๋ค.")
๊ฐ๋จํ MQTT ๊ตฌ๋ ์ ๋ง๋ค๊ธฐ (subscriber.py)
์ด ์คํฌ๋ฆฝํธ๋ ๋์ผํ ๋ธ๋ก์ปค์ ์ฐ๊ฒฐํ์ฌ `python/mqtt/test` ์ฃผ์ ๋ฅผ ๊ตฌ๋ ํ๊ณ ์์ ํ๋ ๋ชจ๋ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํฉ๋๋ค.
`subscriber.py`๋ผ๋ ๋ ๋ค๋ฅธ ํ์ผ์ ๋ง๋์ธ์:
import paho.mqtt.client as mqtt
# --- ์ค์ ---
BROKER_ADDRESS = "localhost"
PORT = 1883
TOPIC = "python/mqtt/test"
# --- ์ฝ๋ฐฑ ํจ์ ---
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("MQTT ๋ธ๋ก์ปค์ ์ฐ๊ฒฐ๋์์ต๋๋ค!")
# ์ฑ๊ณต์ ์ผ๋ก ์ฐ๊ฒฐ๋๋ฉด ์ฃผ์ ๋ฅผ ๊ตฌ๋
client.subscribe(TOPIC)
else:
print(f"์ฐ๊ฒฐ ์คํจ, ๋ฐํ ์ฝ๋ {rc}")
def on_message(client, userdata, msg):
# ๋ฉ์์ง ํ์ด๋ก๋๋ฅผ ๋ฐ์ดํธ์์ ๋ฌธ์์ด๋ก ๋์ฝ๋ฉ
payload = msg.payload.decode()
print(f"์์ ๋ ๋ฉ์์ง: `{payload}` (์ฃผ์ : `{msg.topic}`)")
# --- ๋ฉ์ธ ์คํฌ๋ฆฝํธ ---
# 1. ํด๋ผ์ด์ธํธ ์ธ์คํด์ค ์์ฑ
client = mqtt.Client("SubscriberClient")
# 2. ์ฝ๋ฐฑ ํ ๋น
client.on_connect = on_connect
client.on_message = on_message
# 3. ๋ธ๋ก์ปค์ ์ฐ๊ฒฐ
client.connect(BROKER_ADDRESS, PORT, 60)
# 4. ๋คํธ์ํฌ ๋ฃจํ ์์ (๋ธ๋กํน ํธ์ถ)
# ์ด ํจ์๋ ์ฌ์ฐ๊ฒฐ ๋ฐ ๋ฉ์์ง ์ฒ๋ฆฌ๋ฅผ ์๋์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค.
print("๊ตฌ๋ ์๊ฐ ๋ฃ๊ณ ์์ต๋๋ค...")
client.loop_forever()
์์ ์คํํ๊ธฐ
- ๋ ๊ฐ์ ๋ณ๋ ํฐ๋ฏธ๋ ์ฐฝ์ ์ฝ๋๋ค.
- ์ฒซ ๋ฒ์งธ ํฐ๋ฏธ๋์์ ๊ตฌ๋
์ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํฉ๋๋ค:
python subscriber.py - "๊ตฌ๋ ์๊ฐ ๋ฃ๊ณ ์์ต๋๋ค..."๋ผ๋ ๋ฉ์์ง๊ฐ ํ์๋์ด์ผ ํฉ๋๋ค. ์ด์ ๋ฉ์์ง๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์ค์ ๋๋ค.
- ๋ ๋ฒ์งธ ํฐ๋ฏธ๋์์ ๋ฐํ์ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํฉ๋๋ค:
python publisher.py - ๋ฐํ์๊ฐ 2์ด๋ง๋ค ๋ฉ์์ง๋ฅผ ๋ณด๋ด๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค. ๋์์ ์ด ๋ฉ์์ง๋ค์ด ๊ตฌ๋ ์์ ํฐ๋ฏธ๋ ์ฐฝ์ ๋ํ๋ ๊ฒ์ ๋๋ค.
์ถํํฉ๋๋ค! ๋ฐฉ๊ธ ํ์ด์ฌ์ ์ฌ์ฉํ์ฌ ์์ ํ ์๋ํ๋ MQTT ํต์ ์์คํ ์ ๋ง๋ค์์ต๋๋ค.
๊ธฐ๋ณธ์ ๋์ด์: ๊ณ ๊ธ Paho-MQTT ๊ธฐ๋ฅ
์ค์ IoT ์์คํ ์ ์ฐ๋ฆฌ์ ๊ฐ๋จํ ์์ ๋ณด๋ค ๋ ๋ง์ ๊ฒฌ๊ณ ํจ์ด ํ์ํฉ๋๋ค. ํ๋ก๋์ ์ค๋น๊ฐ ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๋ฐ ํ์์ ์ธ ๋ช ๊ฐ์ง ๊ณ ๊ธ MQTT ๊ธฐ๋ฅ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
์ ์ธ(Last Will and Testament, LWT)
๋ณด์ ์นด๋ฉ๋ผ๋ ์ฌ์ฅ ๋ชจ๋ํฐ์ ๊ฐ์ ์ค์ํ ์ฅ์น๊ฐ ์ ์ ์ด๋ ๋คํธ์ํฌ ์์ค๋ก ์๊ธฐ์น ์๊ฒ ์ฐ๊ฒฐ์ด ๋์ด์ง๋ฉด ์ด๋ป๊ฒ ๋ ๊น์? LWT ๊ธฐ๋ฅ์ด MQTT์ ํด๊ฒฐ์ฑ ์ ๋๋ค. ํด๋ผ์ด์ธํธ๊ฐ ์ฐ๊ฒฐํ ๋, ๋ธ๋ก์ปค์ "์ ์ธ" ๋ฉ์์ง๋ฅผ ๋ฑ๋กํ ์ ์์ต๋๋ค. ํด๋ผ์ด์ธํธ๊ฐ ๋น์ ์์ ์ผ๋ก ์ฐ๊ฒฐ์ด ๋์ด์ง๋ฉด(DISCONNECT ํจํท์ ๋ณด๋ด์ง ์๊ณ ), ๋ธ๋ก์ปค๋ ์๋์ผ๋ก ์ด ์ ์ธ ๋ฉ์์ง๋ฅผ ์ง์ ๋ ์ฃผ์ ๋ก ๋์ ๋ฐํํฉ๋๋ค.
์ด๊ฒ์ ์ฅ์น ์ํ ๋ชจ๋ํฐ๋ง์ ๋งค์ฐ ์ ์ฉํฉ๋๋ค. ์ฅ์น๊ฐ ์ฐ๊ฒฐ๋ ๋ `devices/device-123/status` ๋ฉ์์ง๋ฅผ ํ์ด๋ก๋ `"online"`๊ณผ ํจ๊ป ๋ฐํํ๊ฒ ํ๊ณ , ๊ฐ์ ์ฃผ์ ์ ํ์ด๋ก๋๊ฐ `"offline"`์ธ LWT ๋ฉ์์ง๋ฅผ ๋ฑ๋กํ ์ ์์ต๋๋ค. ์ด ์ฃผ์ ๋ฅผ ๊ตฌ๋ ํ๋ ๋ชจ๋ ๋ชจ๋ํฐ๋ง ์๋น์ค๋ ์ฆ์ ์ฅ์น์ ์ํ๋ฅผ ์๊ฒ ๋ฉ๋๋ค.
Paho-MQTT์์ LWT๋ฅผ ๊ตฌํํ๋ ค๋ฉด ์ฐ๊ฒฐํ๊ธฐ ์ ์ ์ค์ ํฉ๋๋ค:
client.will_set('devices/device-123/status', payload='offline', qos=1, retain=True)
client.connect(BROKER_ADDRESS, PORT, 60)
์ ์ง ๋ฉ์์ง(Retained Messages)
์ผ๋ฐ์ ์ผ๋ก ๊ตฌ๋ ์๊ฐ ํน์ ์ฃผ์ ์ ์ฐ๊ฒฐํ๋ฉด, ๊ตฌ๋ ํ ์ดํ์ ๋ฐํ๋ ๋ฉ์์ง๋ง ์์ ํฉ๋๋ค. ํ์ง๋ง ๊ฐ์ฅ ์ต๊ทผ์ ๊ฐ์ ์ฆ์ ๋ฐ์์ผ ํ๋ค๋ฉด ์ด๋จ๊น์? ์ด๊ฒ์ด ๋ฐ๋ก ์ ์ง ๋ฉ์์ง๊ฐ ํ์ํ ์ด์ ์ ๋๋ค. `retain` ํ๋๊ทธ๊ฐ `True`๋ก ์ค์ ๋ ๋ฉ์์ง๊ฐ ๋ฐํ๋๋ฉด, ๋ธ๋ก์ปค๋ ํด๋น ํน์ ์ฃผ์ ์ ๋ํด ๊ทธ ๋ฉ์์ง๋ฅผ ์ ์ฅํฉ๋๋ค. ์๋ก์ด ํด๋ผ์ด์ธํธ๊ฐ ๊ทธ ์ฃผ์ ๋ฅผ ๊ตฌ๋ ํ ๋๋ง๋ค, ์ฆ์ ๋ง์ง๋ง์ผ๋ก ์ ์ง๋ ๋ฉ์์ง๋ฅผ ๋ฐ๊ฒ ๋ฉ๋๋ค.
์ด๊ฒ์ ์ํ ์ ๋ณด์ ์๋ฒฝํฉ๋๋ค. ์ฅ์น๋ ์์ ์ ์ํ(์: `{"state": "ON"}`)๋ฅผ `retain=True`๋ก ๋ฐํํ ์ ์์ต๋๋ค. ์์๋์ด ๊ตฌ๋ ํ๋ ๋ชจ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค์ ์ ๋ฐ์ดํธ๋ฅผ ๊ธฐ๋ค๋ฆด ํ์ ์์ด ์ฆ์ ์ฅ์น์ ํ์ฌ ์ํ๋ฅผ ์ ์ ์์ต๋๋ค.
Paho-MQTT์์๋ ๋ฐํ ํธ์ถ์ `retain` ํ๋๊ทธ๋ฅผ ์ถ๊ฐํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค:
client.publish(TOPIC, payload, qos=1, retain=True)
์๊ตฌ ์ธ์ ๊ณผ ํด๋ฆฐ ์ธ์
ํด๋ผ์ด์ธํธ์ ์ฐ๊ฒฐ ์์ฒญ์ ์๋ `clean_session` ํ๋๊ทธ๋ ๋ธ๋ก์ปค๊ฐ ํด๋ผ์ด์ธํธ์ ์ธ์ ์ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง๋ฅผ ์ ์ดํฉ๋๋ค.
- ํด๋ฆฐ ์ธ์
(
clean_session=True, ๊ธฐ๋ณธ๊ฐ): ํด๋ผ์ด์ธํธ์ ์ฐ๊ฒฐ์ด ๋์ด์ง๋ฉด, ๋ธ๋ก์ปค๋ ๊ตฌ๋ ์ ๋ณด์ ๋๊ธฐ ์ค์ธ QoS 1 ๋๋ 2 ๋ฉ์์ง๋ฅผ ํฌํจํ ๋ชจ๋ ์ ๋ณด๋ฅผ ํ๊ธฐํฉ๋๋ค. ๋ค์ ์ฐ๊ฒฐํ๋ฉด ์์ ํ ์๋ก์ด ํด๋ผ์ด์ธํธ์ฒ๋ผ ์ทจ๊ธ๋ฉ๋๋ค. - ์๊ตฌ ์ธ์
(
clean_session=False): ๊ณ ์ ํ ํด๋ผ์ด์ธํธ ID๋ฅผ ๊ฐ์ง ํด๋ผ์ด์ธํธ๊ฐ ์ด ๋ฐฉ์์ผ๋ก ์ฐ๊ฒฐํ๋ฉด, ๋ธ๋ก์ปค๋ ์ฐ๊ฒฐ์ด ๋์ด์ง ํ์๋ ์ธ์ ์ ์ ์งํฉ๋๋ค. ์ฌ๊ธฐ์๋ ๊ตฌ๋ ์ ๋ณด์ ์คํ๋ผ์ธ ์ํ์ผ ๋ ๋ฐํ๋ ๋ชจ๋ QoS 1 ๋๋ 2 ๋ฉ์์ง๊ฐ ํฌํจ๋ฉ๋๋ค. ํด๋ผ์ด์ธํธ๊ฐ ๋ค์ ์ฐ๊ฒฐํ๋ฉด ๋ธ๋ก์ปค๋ ๋์น ๋ชจ๋ ๋ฉ์์ง๋ฅผ ์ ์กํฉ๋๋ค. ์ด๋ ์ค์ํ ๋ช ๋ น์ ๋์น ์ฌ์ ๊ฐ ์๋ ์ ๋ขฐํ ์ ์๋ ๋คํธ์ํฌ์ ์ฅ์น์ ๋งค์ฐ ์ค์ํฉ๋๋ค.
์๊ตฌ ์ธ์ ์ ์ค์ ํ๋ ค๋ฉด, ์์ ์ ์ด๊ณ ๊ณ ์ ํ ํด๋ผ์ด์ธํธ ID๋ฅผ ์ ๊ณตํ๊ณ ํด๋ผ์ด์ธํธ ์ธ์คํด์ค๋ฅผ ์์ฑํ ๋ `clean_session=False`๋ก ์ค์ ํด์ผ ํฉ๋๋ค:
client = mqtt.Client(client_id="my-persistent-device-001", clean_session=False)
๋ณด์์ ์ ํ์ด ์๋๋๋ค: ํ์ด์ฌ์ผ๋ก MQTT ๋ณด์ํ๊ธฐ
๋ชจ๋ ์ค์ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ณด์์ ๊ฐ์ฅ ์ค์ํฉ๋๋ค. ๋ณด์๋์ง ์์ MQTT ๋ธ๋ก์ปค๋ ์ ์์ ์ธ ํ์์๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋์ฒญํ๊ณ , ์ฅ์น์ ๊ฑฐ์ง ๋ช ๋ น์ ๋ณด๋ด๊ฑฐ๋, ์๋น์ค ๊ฑฐ๋ถ ๊ณต๊ฒฉ์ ์์ํ๋๋ก ์ด๋ํ๋ ๊ฒ๊ณผ ๊ฐ์ต๋๋ค. MQTT ๋ณด์์ ์ธ์ฆ, ์ํธํ, ๊ถํ ๋ถ์ฌ๋ผ๋ ์ธ ๊ฐ์ง ํต์ฌ ๊ธฐ๋ฅ์ ํฌํจํฉ๋๋ค.
์ธ์ฆ: ๋น์ ์ ๋๊ตฌ์ ๋๊น?
์ธ์ฆ์ ๋ธ๋ก์ปค์ ์ฐ๊ฒฐํ๋ ํด๋ผ์ด์ธํธ์ ์ ์์ ํ์ธํฉ๋๋ค. ๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ์ฌ์ฉ์ ์ด๋ฆ๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค. Mosquitto ๋ธ๋ก์ปค๊ฐ ์๊ฒฉ ์ฆ๋ช ์ ์๊ตฌํ๋๋ก ๊ตฌ์ฑํ ๋ค์, ํ์ด์ฌ ํด๋ผ์ด์ธํธ์์ ์ด๋ฅผ ์ ๊ณตํ ์ ์์ต๋๋ค.
ํ์ด์ฌ ํด๋ผ์ด์ธํธ์์ `username_pw_set()` ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ธ์:
client.username_pw_set(username="myuser", password="mypassword")
client.connect(BROKER_ADDRESS, PORT, 60)
์ํธํ: TLS/SSL๋ก ์ ์ก ์ค ๋ฐ์ดํฐ ๋ณดํธํ๊ธฐ
์ฌ์ฉ์ ์ด๋ฆ๊ณผ ๋น๋ฐ๋ฒํธ๊ฐ ๋คํธ์ํฌ๋ฅผ ํตํด ํ๋ฌธ์ผ๋ก ์ ์ก๋๋ค๋ฉด ๊ฑฐ์ ์์ฉ์ด ์์ต๋๋ค. ์ํธํ๋ ํด๋ผ์ด์ธํธ์ ๋ธ๋ก์ปค ๊ฐ์ ๋ชจ๋ ํต์ ์ด ๋ค์์ฌ ๋คํธ์ํฌ๋ฅผ ์ผํํ๋ ๋๊ตฌ์๊ฒ๋ ์ฝ์ ์ ์๋๋ก ๋ณด์ฅํฉ๋๋ค. ์ด๋ ์น์ฌ์ดํธ(HTTPS)๋ฅผ ๋ณดํธํ๋ ๊ฒ๊ณผ ๋์ผํ ๊ธฐ์ ์ธ ์ ์ก ๊ณ์ธต ๋ณด์(TLS)์ ์ฌ์ฉํ์ฌ ๋ฌ์ฑ๋ฉ๋๋ค.
MQTT์ ํจ๊ป TLS(์ข ์ข MQTTS๋ผ๊ณ ํจ)๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด, ๋ธ๋ก์ปค๊ฐ ์ด๋ฅผ ์ง์ํ๋๋ก ๊ตฌ์ฑํ๊ณ (๋ณดํต ํฌํธ 8883) ํด๋ผ์ด์ธํธ์ ํ์ํ ์ธ์ฆ์๋ฅผ ์ ๊ณตํด์ผ ํฉ๋๋ค. ์ด๋ ์ผ๋ฐ์ ์ผ๋ก ๋ธ๋ก์ปค์ ์ ์์ ํ์ธํ๊ธฐ ์ํ ์ธ์ฆ ๊ธฐ๊ด(CA) ์ธ์ฆ์๋ฅผ ํฌํจํฉ๋๋ค.
Paho-MQTT์์๋ `tls_set()` ๋ฉ์๋๋ฅผ ์ฌ์ฉํฉ๋๋ค:
client.tls_set(ca_certs="path/to/ca.crt")
client.connect(BROKER_ADDRESS, 8883, 60)
๊ถํ ๋ถ์ฌ: ๋ฌด์์ ํ ์ ์์ต๋๊น?
ํด๋ผ์ด์ธํธ๊ฐ ์ธ์ฆ๋๋ฉด, ๊ถํ ๋ถ์ฌ๋ ํ์ฉ๋ ์์ ์ ๊ฒฐ์ ํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ์จ๋ ์ผ์๋ ์์ ์ ์ฃผ์ (์: `sensors/temp-A/data`)์๋ง ๋ฐํํ ์ ์์ด์ผ ํ์ง๋ง, ๊ณต์ฅ ๊ธฐ๊ณ๋ฅผ ์ ์ดํ๋ ๋ฐ ์ฌ์ฉ๋๋ ์ฃผ์ (์: `factory/floor-1/robot-arm/command`)์๋ ๋ฐํํ ์ ์์ด์ผ ํฉ๋๋ค. ์ด๋ ์ผ๋ฐ์ ์ผ๋ก ๋ธ๋ก์ปค์์ ์ ๊ทผ ์ ์ด ๋ชฉ๋ก(ACL)์ ์ฌ์ฉํ์ฌ ์ฒ๋ฆฌ๋ฉ๋๋ค. ์ด๋ค ์ฌ์ฉ์๊ฐ ํน์ ์ฃผ์ ํจํด์ ๋ํด `์ฝ๊ธฐ`(๊ตฌ๋ ) ๋๋ `์ฐ๊ธฐ`(๋ฐํ)๋ฅผ ํ ์ ์๋์ง ์ ์ํ๋ ๊ท์น์ผ๋ก ๋ธ๋ก์ปค๋ฅผ ๊ตฌ์ฑํฉ๋๋ค.
๋ชจ๋ ํฉ์น๊ธฐ: ๊ฐ๋จํ ์ค๋งํธ ํ๊ฒฝ ๋ชจ๋ํฐ ํ๋ก์ ํธ
์ด๋ฌํ ๊ฐ๋ ์ ๊ณต๊ณ ํ ํ๊ธฐ ์ํด ์ฝ๊ฐ ๋ ํ์ค์ ์ธ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด ๋ด ์๋ค. ํ๊ฒฝ ๋ฐ์ดํฐ๋ฅผ JSON ๊ฐ์ฒด๋ก ๋ฐํํ๋ ์ผ์ ์ฅ์น์ ์ด ๋ฐ์ดํฐ๋ฅผ ๊ตฌ๋ ํ์ฌ ํ์ํ๋ ๋ชจ๋ํฐ๋ง ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋ฎฌ๋ ์ด์ ํ ๊ฒ์ ๋๋ค.
ํ๋ก์ ํธ ๊ฐ์
- ์ผ์ (๋ฐํ์): ์จ๋์ ์ต๋๋ฅผ ์ฝ๋ ์ผ์๋ฅผ ์๋ฎฌ๋ ์ด์
ํ๋ ํ์ด์ฌ ์คํฌ๋ฆฝํธ. ์ด ๋ฐ์ดํฐ๋ฅผ JSON ํ์ด๋ก๋๋ก ํจํค์งํ์ฌ 5์ด๋ง๋ค
smart_env/device01/telemetry์ฃผ์ ๋ก ๋ฐํํฉ๋๋ค. - ๋ชจ๋ํฐ (๊ตฌ๋
์):
smart_env/device01/telemetry๋ฅผ ๊ตฌ๋ ํ๊ณ , JSON ๋ฐ์ดํฐ๋ฅผ ์์ ํ์ฌ ํ์ฑํ๊ณ , ์ฌ์ฉ์ ์นํ์ ์ธ ์ํ ์ ๋ฐ์ดํธ๋ฅผ ์ถ๋ ฅํ๋ ํ์ด์ฌ ์คํฌ๋ฆฝํธ.
์ผ์ ์ฝ๋ (sensor_publisher.py)
import paho.mqtt.client as mqtt
import time
import json
import random
BROKER_ADDRESS = "localhost"
PORT = 1883
TOPIC = "smart_env/device01/telemetry"
client = mqtt.Client("SensorDevice01")
client.connect(BROKER_ADDRESS, PORT, 60)
client.loop_start()
print("์ผ์ ๋ฐํ์๊ฐ ์์๋์์ต๋๋ค...")
try:
while True:
# ์ผ์ ์ธก์ ๊ฐ ์๋ฎฌ๋ ์ด์
temperature = round(random.uniform(20.0, 30.0), 2)
humidity = round(random.uniform(40.0, 60.0), 2)
# JSON ํ์ด๋ก๋ ์์ฑ
payload = {
"timestamp": time.time(),
"temperature": temperature,
"humidity": humidity
}
payload_str = json.dumps(payload)
# QoS 1๋ก ๋ฉ์์ง ๋ฐํ
result = client.publish(TOPIC, payload_str, qos=1)
result.wait_for_publish() # ๋ฐํ์ด ํ์ธ๋ ๋๊น์ง ๋ธ๋กํน
print(f"๋ฐํ๋จ: {payload_str}")
time.sleep(5)
except KeyboardInterrupt:
print("์ผ์ ๋ฐํ์๋ฅผ ์ค์งํฉ๋๋ค...")
finally:
client.loop_stop()
client.disconnect()
๋ชจ๋ํฐ๋ง ๋์๋ณด๋ ์ฝ๋ (monitor_subscriber.py)
import paho.mqtt.client as mqtt
import json
import datetime
BROKER_ADDRESS = "localhost"
PORT = 1883
TOPIC = "smart_env/device01/telemetry"
def on_connect(client, userdata, flags, rc):
print(f"๊ฒฐ๊ณผ ์ฝ๋ {rc}๋ก ์ฐ๊ฒฐ๋์์ต๋๋ค")
client.subscribe(TOPIC)
def on_message(client, userdata, msg):
print("--- ์ ๋ฉ์์ง ์์ ---")
try:
# ํ์ด๋ก๋ ๋ฌธ์์ด์ ๋์ฝ๋ฉํ๊ณ JSON์ผ๋ก ํ์ฑ
payload = json.loads(msg.payload.decode())
timestamp = datetime.datetime.fromtimestamp(payload.get('timestamp'))
temperature = payload.get('temperature')
humidity = payload.get('humidity')
print(f"์ฅ์น: {msg.topic}")
print(f"์๊ฐ: {timestamp.strftime('%Y-%m-%d %H:%M:%S')}")
print(f"์จ๋: {temperature}ยฐC")
print(f"์ต๋: {humidity}%")
except json.JSONDecodeError:
print("JSON ํ์ด๋ก๋ ๋์ฝ๋ฉ ์ค๋ฅ.")
except Exception as e:
print(f"์ค๋ฅ ๋ฐ์: {e}")
client = mqtt.Client("MonitoringDashboard")
client.on_connect = on_connect
client.on_message = on_message
client.connect(BROKER_ADDRESS, PORT, 60)
print("๋ชจ๋ํฐ๋ง ๋์๋ณด๋๊ฐ ์คํ ์ค์ ๋๋ค...")
client.loop_forever()
ํ๋กํ ํ์ ์์ ํ๋ก๋์ ์ผ๋ก: MQTT ๋ชจ๋ฒ ์ฌ๋ก
ํ๋ก์ ํธ๋ฅผ ๊ฐ๋จํ ์คํฌ๋ฆฝํธ์์ ๊ฒฌ๊ณ ํ๊ณ ํ์ฅ ๊ฐ๋ฅํ ํ๋ก๋์ ์์คํ ์ผ๋ก ์ฎ๊ธฐ๋ ค๋ฉด ์ ์คํ ๊ณํ์ด ํ์ํฉ๋๋ค. ๋ค์์ ๋ช ๊ฐ์ง ํ์์ ์ธ ๋ชจ๋ฒ ์ฌ๋ก์ ๋๋ค:
- ๋ช
ํํ ์ฃผ์ ๊ณ์ธต ๊ตฌ์กฐ ์ค๊ณ: ์ฒ์๋ถํฐ ์ฃผ์ ๊ตฌ์กฐ๋ฅผ ์ ์คํ๊ฒ ๊ณํํ์ญ์์ค. ์ข์ ๊ณ์ธต ๊ตฌ์กฐ๋ ์ค๋ช
์ ์ด๊ณ ํ์ฅ ๊ฐ๋ฅํ๋ฉฐ, ์์ผ๋์นด๋๋ฅผ ์ฌ์ฉํ ์ ์ฐํ ๊ตฌ๋
์ ํ์ฉํฉ๋๋ค. ์ผ๋ฐ์ ์ธ ํจํด์
<์ฌ์ดํธ>/<์์ญ>/<์ฅ์น_์ ํ>/<์ฅ์น_ID>/<์ธก์ ๊ฐ>์ ๋๋ค. - ๋คํธ์ํฌ ๋จ์ ์ ์ฐ์ํ๊ฒ ์ฒ๋ฆฌํ๊ธฐ: ๋คํธ์ํฌ๋ ์ ๋ขฐํ ์ ์์ต๋๋ค. ํด๋ผ์ด์ธํธ ์ฝ๋๋ ๊ฒฌ๊ณ ํ ์ฌ์ฐ๊ฒฐ ๋ก์ง์ ๊ตฌํํด์ผ ํฉ๋๋ค. Paho-MQTT์ `on_disconnect` ์ฝ๋ฐฑ์ ์ด๋ฅผ ์์ํ๊ธฐ์ ์๋ฒฝํ ์ฅ์์ด๋ฉฐ, ์ฌ์ฐ๊ฒฐ ์๋๋ก ๋คํธ์ํฌ๋ฅผ ๋ฒ๋์ํค๋ ๊ฒ์ ํผํ๊ธฐ ์ํด ์ง์ ๋ฐฑ์คํ์ ๊ฐ์ ์ ๋ต์ ๊ตฌํํฉ๋๋ค.
- ๊ตฌ์กฐํ๋ ๋ฐ์ดํฐ ํ์ด๋ก๋ ์ฌ์ฉ: ๋ฉ์์ง ํ์ด๋ก๋์๋ ํญ์ JSON์ด๋ Protocol Buffers์ ๊ฐ์ ๊ตฌ์กฐํ๋ ๋ฐ์ดํฐ ํ์์ ์ฌ์ฉํ์ญ์์ค. ์ด๋ ๋ฐ์ดํฐ๋ฅผ ์์ฒด ์ค๋ช ์ ์ผ๋ก ๋ง๋ค๊ณ , ๋ฒ์ ๊ด๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ฉฐ, ๋ค๋ฅธ ์ ํ๋ฆฌ์ผ์ด์ (์ด๋ค ์ธ์ด๋ก ์์ฑ๋์๋ )์ด ์ฝ๊ฒ ํ์ฑํ ์ ์๋๋ก ํฉ๋๋ค.
- ๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋ ๊ฒ์ ๋ณด์: ๋ณด์ ์์ด IoT ์์คํ ์ ๋ฐฐํฌํ์ง ๋ง์ญ์์ค. ์ต์ํ ์ฌ์ฉ์ ์ด๋ฆ/๋น๋ฐ๋ฒํธ ์ธ์ฆ๊ณผ TLS ์ํธํ๋ฅผ ์ฌ์ฉํ์ญ์์ค. ๋ ๋์ ๋ณด์์ด ํ์ํ ๊ฒฝ์ฐ, ํด๋ผ์ด์ธํธ ์ธ์ฆ์ ๊ธฐ๋ฐ ์ธ์ฆ์ ํ์ํ์ญ์์ค.
- ๋ธ๋ก์ปค ๋ชจ๋ํฐ๋ง: ํ๋ก๋์ ํ๊ฒฝ์์ MQTT ๋ธ๋ก์ปค๋ ์ค์ํ ์ธํ๋ผ ๊ตฌ์ฑ ์์์ ๋๋ค. ๋ชจ๋ํฐ๋ง ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ CPU/๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋, ์ฐ๊ฒฐ๋ ํด๋ผ์ด์ธํธ ์, ๋ฉ์์ง ์๋ ๋ฐ ๋๋ฝ๋ ๋ฉ์์ง๋ฅผ ํฌํจํ ์ํ๋ฅผ ์ถ์ ํ์ญ์์ค. ๋ง์ ๋ธ๋ก์ปค๊ฐ ์ด ์ํ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ ํน๋ณํ `$SYS` ์ฃผ์ ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ๋ ธ์ถํฉ๋๋ค.
๊ฒฐ๋ก : ํ์ด์ฌ๊ณผ MQTT์ ํจ๊ปํ๋ ๋น์ ์ ์ฌ์
์ฐ๋ฆฌ๋ MQTT์ ๊ทผ๋ณธ์ ์ธ "์"์์๋ถํฐ ํ์ด์ฌ์ผ๋ก ๊ตฌํํ๋ ์ค์ฉ์ ์ธ "์ด๋ป๊ฒ"๊น์ง ์ฌํํ์ต๋๋ค. ์ฌ๋ฌ๋ถ์ ๋ฐํ/๊ตฌ๋ ๋ชจ๋ธ์ ํ, QoS์ ์ค์์ฑ, ๊ทธ๋ฆฌ๊ณ ๋ณด์์ ์ค์ํ ์ญํ ์ ๋ฐฐ์ ์ต๋๋ค. Paho-MQTT ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ผ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํ๊ณ ๋ช ๋ น์ ๊ตฌ๋ ํ๋ ์ ๊ตํ ํด๋ผ์ด์ธํธ๋ฅผ ์ผ๋ง๋ ๊ฐ๋จํ๊ฒ ๋ง๋ค ์ ์๋์ง ๋ณด์์ต๋๋ค.
MQTT๋ ๋จ์ํ ํ๋กํ ์ฝ ๊ทธ ์ด์์ ๋๋ค; ๊ทธ๊ฒ์ ์ฌ๋ฌผ ์ธํฐ๋ท์ ์ํ ๊ธฐ์ด ๊ธฐ์ ์ ๋๋ค. ๊ทธ ๊ฒฝ๋์ฑ๊ณผ ๊ฒฌ๊ณ ํ ๊ธฐ๋ฅ์ ์ค๋งํธ ์ํฐ์์ ์ปค๋ฅํฐ๋ ๋์ , ์ฐ์ ์๋ํ์ ์ด๋ฅด๊ธฐ๊น์ง ์ ์ธ๊ณ ์๋ฐฑ๋ง ๊ฐ์ ์ฅ์น์์ ์ ํ๋ฐ๋ ์ด์ ๊ฐ ๋์์ต๋๋ค.
์ฌ์ ์ ์ฌ๊ธฐ์ ๋๋์ง ์์ต๋๋ค. ๋ค์ ๋จ๊ณ๋ ์ด๋ฌํ ๊ฐ๋ ์ ์ค์ ํ๋์จ์ด์ ์ ์ฉํ๋ ๊ฒ์ ๋๋ค. ๋ผ์ฆ๋ฒ ๋ฆฌ ํ์ด, ESP32 ๋๋ ๋ค๋ฅธ ๋ง์ดํฌ๋ก์ปจํธ๋กค๋ฌ๋ก ์คํํด ๋ณด์ญ์์ค. ๋ฌผ๋ฆฌ์ ์ผ์๋ฅผ ์ฐ๊ฒฐํ๊ณ , ํด๋ผ์ฐ๋ IoT ํ๋ซํผ๊ณผ ํตํฉํ๋ฉฐ, ๋ฌผ๋ฆฌ์ ์ธ๊ณ์ ์ํธ ์์ฉํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ์ญ์์ค. ํ์ด์ฌ๊ณผ MQTT๋ฅผ ํตํด ์ฌ๋ฌ๋ถ์ ์ฐจ์ธ๋ ์ปค๋ฅํฐ๋ ์๋ฃจ์ ์ ๊ตฌ์ถํ ๊ฐ๋ ฅํ ๋๊ตฌ ํคํธ๋ฅผ ๊ฐ๊ฒ ๋์์ต๋๋ค.