투케이2K

478. (javaScript) [간단 소스] 자바스크립트 paho-mqtt 라이브러리 사용해 구독 된 특정 토픽 unSubscribe 구독 해제 수행 본문

JavaScript

478. (javaScript) [간단 소스] 자바스크립트 paho-mqtt 라이브러리 사용해 구독 된 특정 토픽 unSubscribe 구독 해제 수행

투케이2K 2026. 1. 6. 21:39
728x90

[개발 환경 설정]

개발 툴 : Edit++

개발 언어 : JavaScript

 

[소스 코드]

-----------------------------------------------------------------------------------------
[사전 설명 및 설정 사항]
-----------------------------------------------------------------------------------------

- 개발 환경 : Web


- 개발 기술 : JavaScript (자바스크립트) / paho-mqtt / MQTT / Connection


- 사전) MQTT (Message Queuing Telemetry Transport) 설명 : 

  >> MQTT 는 경량 메시지 프로토콜로, 주로 IoT(사물인터넷) 환경에서 사용됩니다

  >> MQTT 목적 : 제한된 네트워크 환경(저속, 불안정)에서 효율적으로 메시지를 주고받기 위해 설계

  >> MQTT 기반 : TCP/IP 위에서 동작

  >> MQTT 패턴 : Publish/Subscribe 모델을 사용

    - Publisher : 메시지를 발행하는 클라이언트

    - Subscriber : 특정 주제(topic)를 구독하는 클라이언트

    - Broker: 메시지를 중개하는 서버 (예: Mosquitto)

  >> MQTT 주요 특징 : 

    - 경량성 : 헤더가 매우 작음(2바이트부터 시작)

    - QoS (Quality of Service) : 
      $ QoS 0: 최대 한 번 전달(보장 없음)
      $ QoS 1: 최소 한 번 전달(중복 가능)
      $ QoS 2: 정확히 한 번 전달(가장 안전)

    - 지속 연결 : KeepAlive로 연결 상태 유지

    - Last Will and Testament (LWT) : 클라이언트 비정상 종료 시 브로커가 메시지 발행

    - 토픽 기반 라우팅 : 계층적 구조(/home/temperature 등)

-----------------------------------------------------------------------------------------





-----------------------------------------------------------------------------------------
[소스 코드]
-----------------------------------------------------------------------------------------

<!DOCTYPE HTML>
<html lang="ko">
<head>
    <title>javaScriptTest</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">


    <!-- 반응형 구조 만들기 -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">


    <!-- 내부 CSS 스타일 지정 -->
    <style>

        html, body {
            width: 100%;
            height: 100%;
            margin : 0 auto;
            padding : 0;
            border : none;
            background-color: #666;
        }

    </style>





    <!-- [CDN 주소 설정] : https://www.jsdelivr.com/package/npm/paho-mqtt -->
    <script src="https://cdn.jsdelivr.net/npm/paho-mqtt@1.1.0/paho-mqtt.min.js"></script>






    <!-- [자바스크립트 코드 지정] -->
    <script type="module">

        // --------------------------------------------------------------------------------------------------------------

        // [모듈 import]
        import aws4 from "https://esm.sh/aws4fetch@1.0.17";

        const { AwsClient } = aws4;    

        // --------------------------------------------------------------------------------------------------------------

        // [전역 변수 선언]        
        var mqttClient = null;
        var connectTimer = null;

        const QOS = 0; // 네트워크 서비스 품질
        const CONNECT_TIME_OUT = 20; // Seconds
        const SUBSCRIBE_TIME_OUT = 20; // Seconds


        // [AWS Iam 계정 정보]
        const endpoint = 'a1..d9-ats.iot.ap-northeast-1.amazonaws.com'; // [AWS iot:Data-ATS 도메인]
        const region = 'ap-northeast-1'; // [AWS 리전]
        const accessKey = 'AK..7Q'; // [IAM 액세스 키]
        const secretKey = 'Zz..xj'; // [IAM 시크릿 키]


        // [AWS 생성 된 사물 Thing]
        const thingName = "T_TWOK_0000000001"; 

        // 구독 토픽 : 와일드 카드 예시 : $aws/things/T_TWOK_0000000001/shadow/name/common/update/#
        const subscribe_topic_common = `$aws/things/${thingName}/shadow/name/common/update/accepted`;
        
        // --------------------------------------------------------------------------------------------------------------

        // [html 최초 로드 및 이벤트 상시 대기 실시]
        window.onload = async function() {

          try {

            // --------------------------------------
            // AWS : SigV4 인증 URL 생성
            // --------------------------------------
            const aws = new AwsClient({
              accessKeyId: accessKey,
              secretAccessKey: secretKey,
              service: "iotdevicegateway", // ✅ 중요!
              region: region,
            });

            const url = `wss://${endpoint}/mqtt`; // [AWS iot:Data-ATS 도메인]

            
            // [SigV4 서명된 URL 생성]
            const signed = await aws.sign(url, { 
              method: "GET",
              signQuery: true // ✅ signQuery: true 옵션 추가
            });

            
            // ✅ wss://a1y..pd9-ats.iot.ap-northeast-1.amazonaws.com/mqtt?X-Amz-Date=20251123T234432Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AK..H7Q%2F20251123%2Fap-northeast-1%2Fiotdevicegateway%2Faws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=fcc..d7a
            const signUrl = signed.url;


            if (signUrl == null || String(typeof signUrl).toLowerCase() == "undefined" || JSON.stringify(signUrl) == "null" || signUrl == "") {
                
              alert("[window onload] : [Error] : aws.sign url is null");
                
            }
            else {
              // --------------------------------------
              // Call : Mqtt Connect
              // --------------------------------------
              mqttConnect(signUrl); // AWS WebSocket Url
            } 

          }

          catch (exception) {
            console.error("[window onload] : [Exception] : 예외 상황 발생 : ", exception.message);
          }
        };





        // [Create : Mqtt Object]
        function mqttConnect(url) {

          try {
              
            // --------------------------------------
            // Set : Client Id
            // --------------------------------------
            const clientId = "clientId-" + Math.random();


            // --------------------------------------
            // Set : Paho.Client
            // --------------------------------------
            mqttClient = new Paho.Client(url, clientId); // ✅ AWS WebSocket : wss:// 443 자동 할당


            // --------------------------------------
            // Set : callback handlers
            // --------------------------------------
            mqttClient.onConnectionLost = onConnectionLost;
            mqttClient.onMessageArrived = onMessageArrived;


            // --------------------------------------
            // Connect : mqttClient
            // --------------------------------------
            mqttClient.connect({ // Custom Option
                    
              useSSL: true, // ✅ SSL/TLS 사용 여부 : 반드시 true 설정
              mqttVersion: 4, // MQTT 프로토콜 버전 (MQTT v3.1.1)

              timeout: CONNECT_TIME_OUT, // 연결 시도 타임아웃 (초) → 기본값은 30초
              keepAliveInterval: 30, // ✅ Ping 주기 (초) → 기본값은 60초    
              
              cleanSession: true, // ✅ 이전 세션 구독 정보 삭제

              /*
              invocationContext: { // 연결 시 컨텍스트 객체 → 콜백에서 context로 접근 가능
                  clientId: "mqtt-client"
              },
              // */

              onSuccess: () => { // 연결에 따른 콜백
                console.warn("");
                console.warn("[AWS] Connection Success : ", url);
                console.warn("");

                onConnect(); // Call Connect Success
              }, 
              onFailure: (err) => { // 연결에 따른 에러 메시지
                console.error("");
                console.error("[AWS] Connection failed : ", err)
                console.error("");

                onConnectionFail(err); // Call Connect Fail
              } 
            }); 

          }
          catch (exception) {
            console.error("[mqttConnect] : [Exception] : 예외 상황 발생 : ", exception.message);

            if (exception.message.indexOf("Failed to") >= 0){
              onConnectionFail(exception);
            }
          }
        }





        // [Connection : Callback Handler]
        function onConnect() {

          // --------------------------------------
          // Check : Paho Client
          // --------------------------------------
          if (mqttClient == null){
            return;
          }


          // --------------------------------------
          // Logic : Procedure
          // --------------------------------------
          try {
                               
            // ---------------------------------
            // ✅ [Test] : AWS : Subscribe
            // ---------------------------------                
            onSubscribe(subscribe_topic_common);              
            // ---------------------------------

          }
          catch (exception) {
            console.error("[onConnect] : [Exception] : 예외 상황 발생 : ", exception);
          }
            
        }





        // ❌ [Connection : Callback Handler]
        function onConnectionLost(responseObject) {
          console.error("");
          console.error("=========================================");
          console.error("[Mqtt] : connect : onConnectionLost");
          console.error("---------------------------------------");
          console.error("[responseObject] : ", JSON.stringify(responseObject));
          console.error("---------------------------------------");
          console.error("[errorCode] : ", responseObject.errorCode);
          console.error("---------------------------------------");
          console.error("[errorMessage] : ", responseObject.errorMessage);
          console.error("=========================================");
          console.error("");

          /*
          -----------------------------------------
          [errorCode] : 종류
          -----------------------------------------
          0 : 정상 종료 (예: 클라이언트에서 명시적으로 disconnect() 호출)
          -----------------------------------------
          1 : 소켓 오류 (네트워크 문제로 연결이 끊김)
          -----------------------------------------
          2 : 프로토콜 오류 (MQTT 프로토콜 위반)
          -----------------------------------------
          3 : 메시지 전송 오류 (메시지 처리 중 내부 오류)
          -----------------------------------------
          4 : KeepAlive 타임아웃 (브로커 응답 없음)
          -----------------------------------------
          5 : 내부 오류 (예: AMQJS0005E와 같은 내부 예외)
          -----------------------------------------
          // */


          // --------------------------------------
          // Clear : Mqtt Object

          // --------------------------------------

          try {
              if (mqttClient != null){
                mqttClient.disconnect(); 
                mqttClient = null;
              }                        
          }
          catch (exception) { // disconnect error
              console.error("[Mqtt] : onConnectionLost : disconnect : Exception : ", exception.message);  
              mqttClient = null;              
          }


          // --------------------------------------
          // Logic : Procedure
          // --------------------------------------
          try {

            var onConnectionLostReason = "";

            if (responseObject.errorCode == 0){
                onConnectionLostReason = String(responseObject.errorCode) + " : 정상 종료 (예: 클라이언트에서 명시적으로 disconnect() 호출)";
            }
            else if (responseObject.errorCode == 1){
                onConnectionLostReason = String(responseObject.errorCode) + " : 소켓 오류 (네트워크 문제로 연결이 끊김)";
            }
            else if (responseObject.errorCode == 2){
                onConnectionLostReason = String(responseObject.errorCode) + " : 프로토콜 오류 (MQTT 프로토콜 위반)";
            }
            else if (responseObject.errorCode == 3){
                onConnectionLostReason = String(responseObject.errorCode) + " : 메시지 전송 오류 (메시지 처리 중 내부 오류)";
            }
            else if (responseObject.errorCode == 4){
                onConnectionLostReason = String(responseObject.errorCode) + " : KeepAlive 타임아웃 (브로커 응답 없음)";
            }
            else if (responseObject.errorCode == 5){
                onConnectionLostReason = String(responseObject.errorCode) + " : 내부 오류 (예: AMQJS0005E와 같은 내부 예외)";
            }
            else {
                onConnectionLostReason = String(responseObject.errorCode) + " : ELSE";
            }

            console.error("[Mqtt] : onConnectionLostReason : ", onConnectionLostReason);
          }
          catch (exception) {                
            console.error("[onConnectionLost] : [Exception] : 예외 상황 발생 : ", exception.message);
          }
        }






        // ❌ [Connection : Callback Handler]
        function onConnectionFail(error) {
          console.error("");
          console.error("=========================================");
          console.error("[Mqtt] : connect : onConnectionFail");
          console.error("---------------------------------------");
          console.error("[errorName] : ", error.name);
          console.error("---------------------------------------");
          console.error("[errorMessage] : ", error.message);
          console.error("=========================================");
          console.error("");

          // --------------------------------------
          // Clear : Mqtt Object
          // --------------------------------------
          try {
            if (mqttClient != null){
              mqttClient.disconnect(); 
              mqttClient = null;
            }                        
          }
          catch (exception) { // disconnect error
            console.error("[Mqtt] : onConnectionFail : disconnect : Exception : ", exception.message);      
            mqttClient = null;          
          }
        }






        // [Topic : Subscribe]
        function onSubscribe(topic) {

          // --------------------------------------
          // Check : Paho Client
          // --------------------------------------
          if (mqttClient == null){
            return;
          }

          console.log("[Mqtt] : onSubscribe : Start : ", topic);


          // --------------------------------------
          // Logic : Procedure
          // --------------------------------------
          try {
              
            mqttClient.subscribe(topic, { // Custom Set Topic Subscribe

              qos: QOS, // 네트워크 서비스 품질
              timeout: SUBSCRIBE_TIME_OUT, // SUBACK 대기 시간 (초)
              invocationContext: { topic }, // ✅ 토픽을 컨텍스트에 담아둠

              onSuccess: (resp) => {

                // 브로커가 승인한 QoS와 함께, 내가 담아둔 topic 을 확인 가능
                console.log("");
                console.log("=========================================");
                console.log("[Mqtt] : onSubscribe : onSuccess");
                console.log("---------------------------------------");
                console.log("[topic] : ", resp.invocationContext.topic);
                console.log("---------------------------------------");
                console.log("[QOS] : ", resp.grantedQos);
                console.log("=========================================");
                console.log("");

                const timeAlert = setTimeout(() => { // 타이머
                                    
                  onUnSubscribe(resp.invocationContext.topic); // 토픽 구독 해제 ✅

                }, 5000 ); // 5초 뒤에 수행
              },
              onFailure: (err) => {
                console.error("");
                console.error("=========================================");
                console.error("[onSubscribe] : [구독 수행] : onFailure : ", err);
                console.error("=========================================");
                console.error("");
              }

            });

            // --------------------------------------

          }
          catch (exception) {                
            console.error("[onSubscribe] : [Exception] : 예외 상황 발생 : ", exception.message);
          }
        }







        // [Topic : onUnSubscribe]
        function onUnSubscribe(topic) {

          // --------------------------------------
          // Check : Paho Client
          // --------------------------------------
          if (mqttClient == null){
            return;
          }

          console.log("[Mqtt] : onUnSubscribe : Start : ", topic);


          // --------------------------------------
          // Logic : Procedure
          // --------------------------------------
          try {
              
            mqttClient.unsubscribe(topic, {

              timeout: 5, // 초
              onSuccess: (ctx) => {
                console.warn("unsubscribed : success : ", ctx?.invocationContext);
              },
              onFailure: (e) => {
                console.error("unsubscribe failed : ", e.errorMessage);
              }

            });

            // --------------------------------------

          }
          catch (exception) {                
            console.error("[onUnSubscribe] : [Exception] : 예외 상황 발생 : ", exception.message);
          }
        }






        // [Topic : Publish]
        function onPublish(topic, message) {

          // --------------------------------------
          // Check : Paho Client
          // --------------------------------------
          if (mqttClient == null){
            return;
          }

          console.log("[Mqtt] : onPublish : Start : ", topic);


          // --------------------------------------
          // Logic : Procedure
          // --------------------------------------
          try {

            const pahoMessage = new Paho.Message(message);
            pahoMessage.destinationName = topic; // Set Topic Publish
            
            pahoMessage.qos = QOS; // 네트워크 서비스 품질
            pahoMessage.retained = false; // ✅ 보관하지 않고 현재 메시지만 전송 : 현재 연결된 구독자에게만 전송되고, 브로커는 이 메시지를 저장하지 않습니다

            mqttClient.send(pahoMessage);

            console.log("");
            console.log("=========================================");
            console.log("[Mqtt] : onPublish : Success");
            console.log("---------------------------------------");
            console.log("[topic] : ", topic);
            console.log("---------------------------------------");
            console.log("[message] : ", message);
            console.log("=========================================");
            console.log("");
          }
          catch (exception) {  
            console.error("[onPublish] : [Exception] : 예외 상황 발생 : ", exception.message);
          }
        }





        // [Receive : Message]
        function onMessageArrived(message) {

          // --------------------------------------
          // Check : Paho Client
          // --------------------------------------
          if (mqttClient == null){
            return;
          }

          console.log("");
          console.log("=========================================");
          console.log("[Mqtt] : receive : onMessageArrived");
          console.log("---------------------------------------");
          console.log("[topic] : ", message.destinationName);
          console.log("---------------------------------------");
          console.log("[qos] : ", message.qos);
          console.log("---------------------------------------");
          console.log("[retained] : ", message.retained);
          console.log("---------------------------------------");
          console.log("[payloadString] : ", message.payloadString);
          console.log("=========================================");
          console.log("");

        }

        // --------------------------------------------------------------------------------------------------------------

    </script>


</head>


<body>

</body>

</html>

-----------------------------------------------------------------------------------------





-----------------------------------------------------------------------------------------
[참고 사이트]
-----------------------------------------------------------------------------------------

[Web/JavaScript] paho-mqtt 라이브러리 및 aws4fetch 사용해 AWS IOT Core MQTT 구독 , 메시지 발생 수행

https://kkh0977.tistory.com/8418

https://blog.naver.com/kkh0977/224085422841


[간단 소스] 자바스크립트 paho-mqtt 라이브러리 사용해 MQTT 기본 Connection 연결 수행 실시

https://kkh0977.tistory.com/8414

https://blog.naver.com/kkh0977/224083727154


MQTT (Message Queueing Telemetry Transport) 통신 설명

https://kkh0977.tistory.com/3631

https://blog.naver.com/kkh0977/222971771381?trackingCode=blog_bloghome_searchlist


[라이브러리] [Android] paho.mqtt.android - MqttAndroidClient 안드로이드 MQTT 통신 라이브러리

https://blog.naver.com/kkh0977/223557263865?trackingCode=blog_bloghome_searchlist


[MQTT] mosquitto 사용해 MQTT 통신 테스트 환경 구축

https://blog.naver.com/kkh0977/222971768497?trackingCode=blog_bloghome_searchlist


[mqtt] 온라인 MQTT 테스트 수행 참고 사이트

https://blog.naver.com/kkh0977/223557261164?trackingCode=blog_bloghome_searchlist

-----------------------------------------------------------------------------------------
 
728x90
반응형
Comments