
'불안정한 네트워크 환경에서 메시지 전송을 보장받을 수 있는 프로토콜'을 찾던 중에 IoT에서 많이 쓰이는 MQTT Protocol에 대해서 공부해 보았다.
어떤 특징을 가졌고 어떻게 불안정한 네트워크 환경에서 IoT의 통신에 도움을 주는지 간단하게 정리해 본다.
MQTT Protocol이란?
MQTT 프로토콜에 대해 찾아보면 대략 아래와 같은 특징들을 찾아볼 수 있다.
- Message Queuing Telemetry Transport의 약자이다.
- IoT를 위해 설계된 매우 가벼운 메세징 프로토콜이다.
- 불안정한 네트워크 환경에서도 동작한다.
- TCP/IP 위에서 동작하고 양방향 통신이다.
- Publish/Subscribe 구조로 동작하기 때문에 Broker가 필요하다.
🧐 그래서 이게 어떻게 도움을 준다는 건데..?
기능을 하나하나 살펴보자.
MQTT Protocol은 불안정한 네트워크 환경을 어떻게 극복하는가?
1. Publish/Subscribe 구조

MQTT는 Publish/Subscribe 구조(💡줄여서 pub/sub이라고 한다)로 동작한다.
이해를 돕기 위해 간단한 그림을 그려 보았는데, 그림에서 화살표 방향으로 데이터가 흘러간다.
간단하게 용어와 흐름에 대해서 설명을 해보자면...
- 위 그림에서 Broker는 메시지를 보관하는 Queue(저장소)이다.
- 양쪽에 MQTT Client라고 표시된 Broker에 메시지를 넣거나 가져가는 녀석들이다.
메시지를 Broker에 넣는 행위를 Publish(발행), Broker에서 메시지를 가져가는 행위를 Subscribe(구독)이라고 한다. - Topic은 Broker에서 메시지를 넣거나 가져가는 주소를 구분하기 위한 값이다.
위의 예시에서 '/IoT/status'라는 Topic으로 Publish 한 메시지는 같은 Topic을 Subscribe 하고 있는 'Backend server'와 'Backend server2 ' 모두에게 전달된다. - Broker와 MQTT Client는 '양방향 통신'을 한다. 서로 메시지를 주고받을 수 있다는 의미이다.
그렇다면 pub/sub 구조를 통해 얻을 수 있는 장점이 무엇일까?
- Reliability
먼저 Publish 한 메시지는 Broker에 저장되기 때문에 모종의 이유로 Subscribe가 잠시 불안정하더라도 메시지를 안정적으로 가져갈 수 있다. 메시지가 누락되지 않고 전송할 수 있음을 어느 정도 보장할 수 있다는 것이다. - Decoupling
Broker에 메시지를 넣는 주체(Publisher)와 메시지를 읽는 주체(Subscriber)가 직접적으로 연결되어 있지 않기 때문에 데이터 전송을 비동기적으로 처리할 수 있고, 서로의 존재를 알지 못해도 Broker만 알고 있으면 메시지를 주고받을 수 있다. - Scalability
방금 언급한 것처럼 Publisher와 Subscriber가 서로 독립적이기 때문에 기능에 따라 필요한 만큼 확장이나 축소를 할 수 있다.
예를 들어, IoT 기기가 너무 많은 메시지를 Publish 한다면 Subscriber를 여러 개로 늘려서 message를 처리할 수 있다.
2. QoS (Quality Of Service)
위에서 Broker에 메시지를 저장해두면 Client가 안정적으로 메시지를 가져갈 수 있다고 했다.
🧐 그렇다면, Client에서 Broker로 메시지가 저장되는 것은 어떻게 보장하고, Broker에서 Client로 메시지가 전송되는 것을 어떻게 보장하나??
MQTT에서는 메시지 전송자와 수신자 사이의 등급을 3가지로 분류해서 제공하고 있고 이를 QoS (Quality Of Service)라고 부른다.
QoS 0 (At most once)
- 메시지를 전송(Publish)하기만 한다.
- Broker에 메시지가 저장되지 않기 때문에 Overhead가 적다
- 메시지가 유실될 수 있다.
QoS 1 (At least once)
- 메시지를 전송(Publish)한 뒤 PUBACK 메시지를 기다린다.
- PUBACK 메시지가 오지 않으면 올 때까지 메시지를 재전송하기 때문에 중복된 메시지를 받을 가능성이 있다.
- 일반적으로 가장 많이 사용되는 레벨의 QoS이다.
QoS 2 (Exactly once)
- 메시지를 전송(Publish)한 뒤 PUBREC메시지를 받으면 PUBREL을 전송하고 PUBCOMP 메시지를 기다린다.
- 메시지가 딱 1번 전송됨을 보장할 수 있지만 하나의 메시지를 보내기 위해 총 4번 왔다 갔다 하기 때문에 Overhead가 매우 크다.
- 대부분의 클라우드 서비스의 MQTT에서도 지원하지 않는 레벨의 QoS이다.
QoS는 메시지를 Broker에 Publish 할 때와 Subscribe 할 때 지정할 수 있다.
💡 참고로, Subscribe 할 때의 QoS는 Publish 할 때의 QoS보다 낮을 수는 있지만 높을 수 없다.
만약 Publish 했을 때보다 더 낮은 레벨의 QoS로 Subscribe 하게 될 경우에는 QoS가 Downgrade 된다.
예를 들어, QoS2로 Publish 한 메시지를 Client가 QoS1로 Subscribe 하게 되면 이 메시지는 QoS1으로 Subscribe 된다.
3. Keep alive & Client takeover
위에서 MQTT Broker와 MQTT Client는 양방향 통신을 한다고 했다.
양방향 통신을 한다는 것은 양쪽 다 Connection을 맺고 있다는 뜻이다.
🧐 만약 한쪽의 Connection이 끊어지면 어떻게 될까?
🧐 혹은 불안정한 네트워크 상황때문에 아주 잠깐 연결이 끊어지게 되면 어떻게 될까?
MQTT에서 이런 상황을 대비해서 Keep alive와 Client takeover라는 걸 제공한다.
keep alive (aka healthcheck)

위의 이미지처럼 더 이상 Client가 Broker에 Connection을 유지하지 못하고 있는 상황임을 Broker는 어떻게 알 수 있을까?
MQTT에서는 이런 상황을 대비해서 Client에서 Broker로 connection을 맺을 때 Keep alive timeout을 설정할 수 있게 하였다.
Keep alive는 흔히 말하는 Healthcheck 또는 Ping과 같다. Client에서 주기적으로 PINGREG 패킷을 보내면 Broker가 PINGRESP 패킷으로 응답한다.
규칙을 심플하다.
- 만약 Keep alive timeout의 1.5배에 해당하는 시간 동안 PINGREG 패킷을 보내지 않는다면 Broker는 Client의 연결이 끊어졌다고 판단해서 연결을 끊는다. 참고로, 1.5배는 스펙에 적혀있는 값이다.
- 만약 Client가 Broker로부터 일정시간 동안 PINGRESP 패킷을 받지 못한다면 Broker와 통신이 안된다고 판단해서 연결을 끊고 재연결을 시도한다. 스펙에 따로 시간이 적혀있지는 않기 때문에 기다리는 시간은 Client 구현체마다 다르다.
Client takeover
위에서 Client가 PINGRESP 패킷을 받지 못하면 기존 연결을 끊고 Broker로 재연결을 시도한다고 했다.
이 행위에 어떤 문제가 있을까?
- 기존의 Broker에서 Client로의 Connection은 남아있을 수도 있다. (TCP 연결이 한쪽만 열려있는 상황)
- 연결을 맺으면 Broker는 해당 client와의 session 정보들을 가지고 있는데 새로운 연결을 하게 되면 이런 정보들이 다시 초기화된다.
위의 문제를 해결하기 위해서 MQTT는 Client가 새로운 연결을 맺어도 이전에 있던 Session 정보들을 그대로 사용함과 동시에 한쪽만 열려있는 Connection을 정리하는 로직을 추가했고 이를 Client takeover라고 부른다.
이 기능을 사용하기 위해서는 Client에서 Connection을 맺을 때 반드시 ClientID를 지정해야 한다.
4. Sessions
위에서 Broker는 client의 session 정보를 가지고 있다고 했다.
🧐 session 정보는 어떤것들이 있고 Broker는 어떤 방식으로 유지하고 있을까?
Session에 저장되는 정보들
Broker는 Client별로 아래의 데이터를 저장하고 있다. 사실 더 많지만 일부만 적어보았다.
- Session Metadata
connection에 관련된 정보들이다. Keep alive나 ClientID 등을 예로 들 수 있다. - Subscriptions
Client가 구독하고 있는 Topic 정보들을 가지고 있다. - ACK를 받지 않은 메시지들
QoS에서 설명한대로 PUBACK를 받지 못하면 메시지를 다시 전송해야 하기 때문에 ACK를 받지 못한 메시지를 저장해 둔다. - Queueing 된 메시지들
QoS1이나 QoS2인 경우에는 Client가 Offline인 상황에 대비해서 Client 별로 메시지를 저장해 둔다.
Session mode
어떤 정보가 저장되는지는 이제 알았다. 그렇다면 Broker는 위의 정보를 언제까지 가지고 있는 것일까?
MQTT에서는 2가지의 Session mode를 지원하고 있다.
보통 Client에서 Connection을 맺을 때 어떤 모드를 사용할지 선택할 수 있다.
- Clean session
이 모드를 이용하게 되면 connection 끊어지는 순간 Broker가 session 정보들을 모두 삭제해 버린다.
Client가 단순하게 Publish 만을 한다거나, 메시지가 유실되어도 상관없는 경우에는 더 유용할 수 있다. - Persistent session
위와 반대로 Broker가 Client의 session 정보를 모두 기억하고 있다.
연결을 맺을 때 SessionID를 지정해 주어야만 이 모드를 사용할 수 있다.
데이터가 저장되는 장소는 Broker의 구현체마다 다른데 많이 쓰이는 Mosquitto broker의 경우에는 단일 파일에다가 저장해 둔다.
MQTTv5부터는 일정시간이 지난 후의 session data를 지울 수 있는 expire timeout을 설정할 수 있도록 개선되었다.
불안정한 네트워크 상황에서 데이터를 계속해서 받으려면 이 모드를 사용해야 할 것이다.
5. LWT
LWT는 'Last Will and Testament'의 약자이고 Will message라고도 많이 표현한다.
번역하자면 유언장🪦인데 재미있고 유용한 feature라고 생각했다.
동작방식은 이렇다.
- Client A가 Connection을 맺고난 뒤에 Broker에 Topic과 Message를 지정 LWT를 등록한다.
- Client A가 모종의 이유로 Disconnect 되면 Broker가 대신해서 등록된 LWT의 Topic으로 Message를 publish 한다.
- Will Topic을 Subscribe 하고 있는 Client B에서 Client A가 Disconnect 된 것을 파악할 수 있다.
Client가 불안정한 네트워크 때문에 연결이 끊어졌을 때 쉽게 파악할 수 있는 방법을 제시해 주는 기능이다.
6. Retained message
Retained message는 각 토픽별로 가장 마지막에 들어온 메시지를 별도로 저장해 두는 기능이다.
🧐 가장 마지막에 들어오는 메시지를 별도로 저장해서 얻을 수 있는 이점이 뭘까?
이 질문에 답하기 위해서 먼저 2가지를 알고 있어야 한다.
- Session mode와 상관없이 Broker에 처음 연결하는 Client는 연결된 시점 이전의 message를 subscribe 할 수 없다.
- Retained message가 존재하면 연결을 새로 맺을 때마다 Client는 Retained message를 받게 된다.
위의 두 가지 사실을 인지한 채 아래의 예시를 살펴보자.
IoT 기기가 자신의 전원 상태를 Publish 하고 있는 상황을 그림으로 표현해 보았다.

위는 Retained message를 사용하지 않은 상황이다.
'On'이라는 메시지가 계속해서 들어오다가 Client가 Connection을 맺은 후에 모종의 이유로 10초 동안 메시지를 Publish 하지 않았다.
해당 Topic을 Subscribe 하는 Client는 IoT 기기의 상태를 최소 10초는 기다린 후에야 'On'이라는 걸 받을 수 있을 것이다.

만약 IoT 기기가 Retained 옵션을 설정한 채 publish 했다면 'On'이라는 메시지가 Broker에 Retained message로 저장이 되어있을 것이다. 이때, Client가 Connection을 맺는 순간 Client는 'On'이라는 메시지를 받고 시작을 하기 때문에 10초를 기다리지 않아도 IoT기기가 연결이 끊기기 전 마지막으로 Publish 한 상태를 바로 알 수 있게 된다.
마치며
MQTT의 특징들을 살펴보면서 'MQTT Protocol은 불안정한 네트워크 환경을 어떻게 극복하는가?'에 대한 질문의 답은 충분히 얻은 것 같다. 공부하다 보니 스펙이 그리 복잡하지 않고 생각보다 더 성숙한 기술이라는 생강이 들었다.
사실 MQTT의 기능은 위에서 정리한 것 말고도 더 많이 있는데, MQTTv3에서 MQTTv5로 가면서 기존에 있었던 불편함 개선하여서 도움이 되는 기능들이 많이 추가되었다.
실제로 사용되는 Broker들과 자주 사용되는 Client 그리고 실제로 간단하게 사용해 본 예제들을 정리해 보면 좋을 것 같다.
References
- Steves-internet-guide 페이지, 정말 디테일하게 정리해 놓으셔서 도움이 많이 된 페이지이다.
http://www.steves-internet-guide.com/ - MQTT organization 페이지
https://mqtt.org/ - MQTT spec이 정의된 페이지
http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/csprd02/mqtt-v3.1.1-csprd02.html - HiveMQ 유튜브, 이론들을 영상으로 잘 설명해 놓아서 이해하기 쉬웠다.
https://www.youtube.com/@HiveMQ