삽질기록 — Vue.js에서 mqtt 토픽 구독하기
vue.js 에서 실시간 통신을 하기 위해서는 많은 방법이 있습니다. 저는 그 중, mqtt를 사용하려고 하는데요, mqtt는 IoT를 위한 프로토콜 메시징입니다.
mqtt는 publisher, broker, subscriber가 있습니다.
퍼블리셔에서 토픽을 발행(publish)하고, 구독자가 해당 토픽을 구독(subscribe)합니다. 그리고 브로커는 이들을 중계합니다.
MQTT를 구현하는 브로커들은 많은데, 저는 그 중 RabbitMQ를 사용하려 합니다.
이 글은 mqtt 구독하기 위한 단계를 포스팅하는 글이기 때문에, mqtt 구독부터 적겠습니다.
vue에서 mqtt를 사용하기 위한 방법에는 vue-mqtt, Paho의 라이브러리 사용 등이 있습니다. vue-mqtt의 경우에는 개발 문서가 적어 나중을 위하여 Paho에서 만든 라이브러리를 사용하려 합니다.
Paho를 사용하기 위해서 mqttws31.js라는 파일을 import 해 줘야 합니다. 해당 파일은 아래 링크에서 다운로드 받아 주세요.
이제 시작하면 되는데요, 아래 링크에서 예시를 확인할 수 있습니다.
프로젝트의 최상단 파일인 main.js에서 작업을 해도 되지만, 저는 mqtt라는 플러그인을 따로 만들어 주겠습니다.
src/plugins/mqtt.js 라는 파일을 생성해 주시고, main.js에 아래와 같이 mqtt를 등록해 주세요.
그리고 mqtt.js에 위 예시(RabbitMQ의 예시)를 보고 내용을 추가하겠습니다.
다운받은 mqttws31 파일을 임포트 해 주시고, plugin 설정을 해 주세요. 브로커로 설정해 놓은 계정의 username과 password를 넣어 주고, 브로커의 호스트 주소와 포트 번호도 넣어 주세요. (RabbitMQ 문서에 따르면 Web MQTT 플러그인이 사용 가능한 경우, 포트는 15675라고 합니다.)
그리고, 프로젝트를 실행해 주세요.
브로커로 설정된 서버가 정상적으로 동작한다면, 아래와 같이 표출될 것입니다.
이제 토픽을 구독해야 하는데요, 이 작업을 플러그인에서 해 줘도 됩니다. 하지만 저는, 각 페이지마다 구독하는 토픽이 다를 것이라고 생각했기 때문에 각 페이지마다 구독하도록 코드를 짰습니다.
이제 토픽을 구독하려는 페이지에서 구독을 해 줘야 되는데요, 이를 해 주기 위해 방금 작성한 mqtt plugin에 구독 메소드를 추가하겠습니다.
mqttData라는 객체 안에 subscribe 메소드를 생성해 준 뒤, $_subscribe 플러그인 메소드에 해당 메소드를 설정해 주세요.
client.subscribe 관련 메소드는 아래 페이지에서 자세하게 확인할 수 있습니다.
그리고 토픽을 구독해야 하는 페이지가 렌더링 될 때 해당 메소드를 호출해 주세요.
저는 mounted 훅에서 구독 메소드를 호출했습니다. 그리고, 실행해 주세요.
콘솔을 확인하면, 아마 “Invalid state not connected.” 에러가 뜰 것입니다 (저는 Test.vue를 따로 만들어 주지 않고 App.vue에서 호출하여 생긴 에러일 수도 있습니다.)
삽질을 한 결과, mqtt가 성공적으로 셋팅되기 전에 컴포넌트에서 호출하여 생긴 에러라고 판단하였습니다. 해당 내용을 콘솔로 확인해 보겠습니다.
먼저 mqtt.js의 onSuccess 콜백 함수에 콘솔을 찍어 주세요. (성공적으로 셋팅 되었을 때를 확인하기 위함입니다.)
그리고, 컴포넌트의 mounted 훅에서도 콘솔을 찍어 주세요.
프로젝트를 실행한 뒤, 콘솔을 확인해 주세요.
보시면 app render가 더 먼저 되는 것을 확인할 수 있습니다. mqtt가 성공적으로 셋팅되기 전에, client subscribe 메소드가 먼저 실행되어 발생한 에러입니다.
해당 에러를 해결해 주기 위한 방법이 여러가지 있을 텐데요, 저는 플러그인에 isSuccess라는 변수를 선언해 준 뒤, onSuccess가 되었을 때 isSuccess를 true로 변경해 주겠습니다.
이제 컴포넌트에서 isSuccess를 이용하여 구독을 하면 되는데요, isSuccess의 변경 시점을 잡아 구독을 해도 되지만, 플러그인의 변수를 watch에 할당하는 경우는 찾지 못하여 store를 동원하였습니다. (남이 안 한 데에는 이유가 있을 거라고 판단했습니다… ^^;; 만약 가능하시다면 watch에 넣으셔도 될 것 같습니다.)
이제 store를 이용하겠습니다. store 이용 방법은 여러가지 포스트가 있을 텐데요, 아래에서도 확인할 수 있습니다.
저는 컴포넌트가 마운트 될 때 isSuccess가 true일 경우 구독을 실행하고, 아닐 경우 store의 wait subs list에 push 해 줄 것입니다. (토픽이 여러개일 경우를 대비하여 array로 만들었습니다.)
if (this.$_isSuccess) { this.$_subscribe("topic");} else { this.PUSH_WAIT_SUBS_LIST("topic");}
위 코드를 컴포넌트의 mounted 훅에 넣었습니다. PUSH_WAIT_SUBS_LIST는 store의 mutation 함수입니다. store 쪽의 자세한 설명이 필요하시다면 댓글 달아 주세요.
플러그인에서 store를 이용하기 위해 main.js를 살짝 변경하였습니다.
그리고, 플러그인에서 store를 이용하여 토픽 리스트의 토픽들을 구독하는 코드를 넣어 주세요.
watSubList에 값들이 있을 경우 해당 토픽들을 구독한 후 reset 시켜 주는 코드입니다.
그리고 실행하면, 에러 없이 정상적으로 작동하는 것을 볼 수 있습니다.
큐가 정상적으로 도착하는지 확인하기 위하여, 콘솔에 찍어 보겠습니다.
publisher가 있다면 토픽에 컴포넌트와 같은 토픽 값을 넣어 준 후 실행해 주시고, 아닐 경우 rabbitMQ 사이트에 가서 username 과 password를 넣어 로그인 한 후 큐를 보내 주세요.
publish message를 누르면,
콘솔에 찍히는 걸 확인할 수 있습니다.
이제 플러그인에 도착한 메시지를 컴포넌트에 보내 줘야 하는데요, 해당 내용은 이벤트버스로 구현하겠습니다.
src/utils/eventBus.js를 생성해 주시고, 안에 아래처럼 내용을 넣어 주세요.
import Vue from 'vue'export const EventBus = new Vue()
그리고 이 이벤트 버스를 main.js에 전역으로 등록시켜 줘도 되지만, 저는 쓰는 곳마다 각각 등록해 주겠습니다.
mqtt 플러그인에 방금 만든 eventbus를 추가한 후 메시지가 도착할 경우 ‘messageArrived’ 이벤트를 보내 주겠습니다.
그리고 컴포넌트에서도 그 이벤트버스를 추가한 후 messageArrived를 받아 주겠습니다.
실행하면 메시지가 정상적으로 도착하는 것을 확인할 수 있습니다.
이제 각각의 destinationName을 이벤트 이름으로 설정하여 보내 주면, 각각의 컴포넌트에서 구독한 토픽들을 나눠서 큐를 받을 수 있습니다.
박수와 댓글은 저를 힘나게 합니다!