테이블링이 테이블오더 주문을 POS Agent 로 넘길 때 쓰던 WebSocket 연결이 간헐적으로 끊겨 주문 전송이 실패하는 문제를 겪고, 대안으로 MQTT 를 학습하고 테스트한 기록이다. Amazon MQ 의 ActiveMQ 를 브로커로 두고 NestJS 로 publisher 와 subscriber 를 만들어, 끊겨 있던 동안 발행된 메시지가 재연결 시 전달되는지를 중점 확인했다.
핵심 포인트- 문제는 테이블오더 주문 정보를 POS Agent 에 전달하던 WebSocket 이 간헐적으로 끊기며 메시지를 수신하지 못한 것이다. WebSocket 은 QoS 옵션과 메시지 대기열이 없어 전달 보장이 어렵다.
- MQTT 는 클라이언트와 브로커만으로 구성되는 topic 기반 pub/sub 프로토콜이고, QoS 0/1/2 로 전달 보장 수준을 고른다.
- 영구 세션은 clean session 을 비활성화하고 QoS 1 이상으로 보낼 때 성립한다. 이 조합에서 미수신 메시지가 재전달된다.
- 테스트 구성은 Amazon MQ 의 ActiveMQ 와 NestJS @nestjs/microservices 의 Transport.MQTT 다. ActiveMQ 는 MQTT v3.1.1 과 v3.1 만 지원한다.
- 결과적으로 이전 clientId 로 재연결하자 끊겨 있던 동안 발행된 메시지들이 그대로 수신됐다.
상세 정리- MQTT 는 낮은 대역폭과 제한된 리소스 환경을 위한 경량 메시지 프로토콜이다. 작은 패킷을 쓰고 오버헤드와 전력 소비가 작아 IoT 와 M2M 에서 많이 쓴다.
- 모든 클라이언트가 발행과 구독을 다 할 수 있어 publisher 와 subscriber 구분이 고정돼 있지 않다.
- Topic 은 슬래시로 구분하는 계층 구조이고, 브로커가 이를 기준으로 연결된 클라이언트에게 보낼 메시지를 필터링한다.
- QoS 0 은 fire and forget 으로 보내고 끝, 1 은 at least once 라 수신 확인이 올 때까지 재전송하며 중복이 허용되는 상황용, 2 는 exactly once 로 가장 안정적이다.
- MQTT 5.0 에서 도입된 responseTopic 과 correlationData 로 Request/Response 패턴을 구성할 수 있다. 처리 완료 여부를 사용자에게 알릴 때 유용하다.
- 클라이언트 옵션은 clientId(중복 ID 로 연결하면 이전 연결이 끊김), clean false(영구 세션), subscribeOptions.qos 2 로 잡았다.
- Publisher 는 ClientProxyFactory.create 로 Transport.MQTT 프록시를 만들고, MqttRecordBuilder 로 QoS 를 지정한 record 를 emit 한다.
- Subscriber 는 app.connectMicroservice 로 마이크로서비스를 붙이고 @MessagePattern('tabling/lounge/light', Transport.MQTT) 로 토픽 메시지를 받는다.
- WebSocket 과의 차이: 목적 자체가 다르고, 브라우저는 MQTT 를 기본 지원하지 않아 브라우저에서 쓰려면 WebSocket 위에 얹어야 한다.
- Message Queue 와의 차이: MQ 는 전달 보장을 위해 1:1 통신을 하지만 MQTT 는 pub/sub 이라 여러 구독자가 같은 메시지를 받는다. ActiveMQ 는 Topic 과 Queue 를 모두 지원해 목적에 맞게 고를 수 있다.
- Kafka 와의 차이: Kafka 는 대용량 스트림 처리와 저장에 적합하다. 대량 IoT 데이터를 정밀 분석하려고 MQTT 와 Kafka 를 결합해 쓰기도 한다.
- 글 시점은 적용 여부를 검토 중인 단계이고, 가벼운 통신 테스트에는 무료 Public Broker 도 쓸 수 있다고 덧붙인다.
왜 읽나연결이 자주 끊기는 단말이나 에이전트와 통신하며 전달 보장이 필요한 백엔드 개발자에게 MQTT 옵션 조합과 NestJS 연동 코드를 짚어주는 글.