<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>mq_expert.log</title>
        <link>https://velog.io/</link>
        <description>다할줄아는 사람보다 뭔가 한가지 똑부러지게하는 사람이되자.</description>
        <lastBuildDate>Fri, 14 Jun 2024 23:10:28 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>mq_expert.log</title>
            <url>https://velog.velcdn.com/images/mqtt_expert/profile/cb59baeb-9d00-47f3-a31b-edd1beb478de/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. mq_expert.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/mqtt_expert" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[배달의종족 쭈문 - 1/10. 엄마와 비동기]]></title>
            <link>https://velog.io/@mqtt_expert/baedal1</link>
            <guid>https://velog.io/@mqtt_expert/baedal1</guid>
            <pubDate>Fri, 14 Jun 2024 23:10:28 GMT</pubDate>
            <description><![CDATA[<p>치킨집 가면 울려 퍼지는 소리</p>
<blockquote>
<p>배달의 종족 쭈<del>문</del></p>
</blockquote>
<p>저거 들으며 생각했다.
나라면 어떻게 만들까?</p>
<blockquote>
<p>주문을 넣으면, 가게에 설치된 POS기나 단말기로 주문이 전송되 <code>배달의 종족 쭈문</code> 이렇게 울리는 것 같은데...</p>
</blockquote>
<p>무책임하게 들리겠지만, 실재 어떻게 만들어져 있는지, 나도 모른다.
다만 Message Queue로 만들었을거란 생각이 들어 한 번 만들어 보려고 한다.</p>
<h2 id="어떻게-만들것인가">어떻게 만들것인가?</h2>
<ul>
<li>앞 시리즈 <a href="https://velog.io/@mqtt_expert/series/MQTT%EB%A1%9C-%EC%B9%B4%ED%86%A1%EC%84%9C%EB%B2%84-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EC%9E%90">MQTT로 카톡서버 만들어보자</a> 와 동일한 기술을 사용하겠다.</li>
<li>다만 MQ(Message Queue)를 Mosquitto/MQTT -&gt; RabbitMQ/AMQP 로 바꾸어서 해보려 한다.</li>
</ul>
<blockquote>
<p>RabbitMQ 설치, AMQP 설명은 인터넷에 차고 넘치니 최소한으로만 언급하겠다.</p>
</blockquote>
<h2 id="알고-있게지만-복습-겸해서-뇌리에-박아야-할-것">알고 있게지만, 복습 겸해서 뇌리에 박아야 할 것.</h2>
<ul>
<li>비동기</li>
<li>RR(Request-Response, Request-Reply) 패턴</li>
</ul>
<h2 id="엄마와-비동기">엄마와 비동기</h2>
<p>사실 비동기가 무엇인지 다 잘 알고 있을 것이다.</p>
<p>다만 여기서 언급하는 이유는 Broker 끼고 RR 패턴으로 구현하다보면, 코딩을 동기식으로 하고 있는 나 자신을 발견하기 때문이다.</p>
<p>디버깅하다 깨닫는다.</p>
<blockquote>
<p>아 MQ는 비동기지...</p>
</blockquote>
<p>그래서 다시 한 번 짚고 넘어가자는 의미로 글 써본다.</p>
<h3 id="sync-엄마가-청소하면-동기다">Sync: 엄마가 청소하면 동기다.</h3>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/bea561d7-3bd6-478f-ab44-9e7f3c79e25d/image.PNG" alt=""></p>
<ol>
<li>아침 준비한다.</li>
<li>설겆이한다.</li>
<li>청소한다.</li>
</ol>
<p>순서대로 하면 된다.
이건 엄마식 동기 처리 방식이다.</p>
<h3 id="async-각자-하고-엄마한테-보고하면-비동기다">Async: 각자 하고, 엄마한테 보고하면 비동기다.</h3>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/b9fd5b27-7240-40d1-a4f1-10dc7d1431b1/image.PNG" alt=""></p>
<p>엄마가 다 큰 자식 방청소하다가 화 났다.
어느 날 자식 새끼들 다 모아 놓고 엄마가 선언한다.</p>
<blockquote>
<p>이제부터 각자 방은 스스로 청소하고 엄마한테 <code>다 했다</code>고 보고해라.</p>
</blockquote>
<p>어떤 놈이 청소 먼저 끝낼 지 엄마는 모른다.</p>
<p>이게 비동기다.</p>
<p>정정하던 엄마가 어느 날 친구 장례식장 갔다 와서 그런다.</p>
<blockquote>
<p>에구...불쌍한 것....오는 건 순서가 있어도, 가는 건 순서가 없다더만...</p>
</blockquote>
<p>인생🚶 비동기다.</p>
<p>✅ 다음 글에서는 RR 패턴에 대해 얘기해보겠다.</p>
<p>✅ 이 글은 <a href="https://velog.io/@mqtt_expert/series/baedal">시리즈</a> 글입니다. 앞에서 부터 차근차근 읽으세요.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MQTT로 카톡서버 만들어보자 - Github 소스]]></title>
            <link>https://velog.io/@mqtt_expert/kakaotalkgithub</link>
            <guid>https://velog.io/@mqtt_expert/kakaotalkgithub</guid>
            <pubDate>Fri, 10 May 2024 06:46:07 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>개인 소스랑 섞이지 않도록 따로 Github 계정 따서 올린다.😁😁</p>
</blockquote>
<h2 id="시리즈">시리즈</h2>
<p>글 : <a href="https://velog.io/@mqtt_expert/series/MQTT%EB%A1%9C-%EC%B9%B4%ED%86%A1%EC%84%9C%EB%B2%84-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EC%9E%90">https://velog.io/@mqtt_expert/series/MQTT로-카톡서버-만들어보자</a>
Github 소스 : <a href="https://github.com/MQ-Expert/mqtt_chatting">https://github.com/MQ-Expert/mqtt_chatting</a></p>
<h3 id="1mqtt로-카톡서버-만들어보자-인트로">1.MQTT로 카톡서버 만들어보자-인트로</h3>
<p>글 : <a href="https://velog.io/@mqtt_expert/kakaotalk0">https://velog.io/@mqtt_expert/kakaotalk0</a></p>
<h3 id="2mqtt로-카톡서버-만들어보자---110-pubsub으로-로그인">2.MQTT로 카톡서버 만들어보자 - 1/10. Pub/Sub으로 로그인?</h3>
<p>글 : <a href="https://velog.io/@mqtt_expert/kakaotalk1">https://velog.io/@mqtt_expert/kakaotalk1</a>
Github 소스 : <a href="https://github.com/MQ-Expert/mqtt_chatting/tree/main/01">https://github.com/MQ-Expert/mqtt_chatting/tree/main/01</a></p>
<h3 id="3mqtt로-카톡서버-만들어보자---210-톡을-날려보자">3.MQTT로 카톡서버 만들어보자 - 2/10. 톡을 날려보자</h3>
<p>글 : <a href="https://velog.io/@mqtt_expert/kakaotalk2">https://velog.io/@mqtt_expert/kakaotalk2</a>
Github 소스 : <a href="https://github.com/MQ-Expert/mqtt_chatting/tree/main/02">https://github.com/MQ-Expert/mqtt_chatting/tree/main/02</a></p>
<h3 id="4mqtt로-카톡서버-만들어보자---310-사용자-상태">4.MQTT로 카톡서버 만들어보자 - 3/10. 사용자 상태</h3>
<p>글 : <a href="https://velog.io/@mqtt_expert/kakaotalk3">https://velog.io/@mqtt_expert/kakaotalk3</a>
Github 소스 : <a href="https://github.com/MQ-Expert/mqtt_chatting/tree/main/03">https://github.com/MQ-Expert/mqtt_chatting/tree/main/03</a></p>
<h3 id="5mqtt로-카톡서버-만들어보자---410-내전화-받았늬">5.MQTT로 카톡서버 만들어보자 - 4/10. 내전화 받았늬?</h3>
<p>글 : <a href="https://velog.io/@mqtt_expert/kakaotalk4">https://velog.io/@mqtt_expert/kakaotalk4</a>
Github 소스 : <a href="https://github.com/MQ-Expert/mqtt_chatting/tree/main/04">https://github.com/MQ-Expert/mqtt_chatting/tree/main/04</a></p>
<h3 id="6mqtt로-카톡서버-만들어보자---510-qos-번외">6.MQTT로 카톡서버 만들어보자 - 5/10. QoS 번외</h3>
<p>글 : <a href="https://velog.io/@mqtt_expert/kakaotalk5">https://velog.io/@mqtt_expert/kakaotalk5</a></p>
<h3 id="7mqtt로-카톡서버-만들어보자---610-입구컷-안-당하기">7.MQTT로 카톡서버 만들어보자 - 6/10. 입구컷 안 당하기</h3>
<p>글 : <a href="https://velog.io/@mqtt_expert/kakaotalk6">https://velog.io/@mqtt_expert/kakaotalk6</a>
Github 소스 : <a href="https://github.com/MQ-Expert/mqtt_chatting/tree/main/06">https://github.com/MQ-Expert/mqtt_chatting/tree/main/06</a></p>
<h3 id="8mqtt로-카톡서버-만들어보자---710-acl">8.MQTT로 카톡서버 만들어보자 - 7/10. ACL</h3>
<p>글 : <a href="https://velog.io/@mqtt_expert/kakaotalk7">https://velog.io/@mqtt_expert/kakaotalk7</a>
Github 소스 : <a href="https://github.com/MQ-Expert/mqtt_chatting/tree/main/07">https://github.com/MQ-Expert/mqtt_chatting/tree/main/07</a></p>
<h3 id="9mqtt로-카톡서버-만들어보자---810-idc에-불이-났어요">9.MQTT로 카톡서버 만들어보자 - 8/10. IDC에 불이 났어요</h3>
<p>글 : <a href="https://velog.io/@mqtt_expert/kakaotalk8">https://velog.io/@mqtt_expert/kakaotalk8</a>
Github 소스 : <a href="https://github.com/MQ-Expert/mqtt_chatting/tree/main/08">https://github.com/MQ-Expert/mqtt_chatting/tree/main/08</a></p>
<h3 id="10mqtt로-카톡서버-만들어보자---910-serverjs-이중화">10.MQTT로 카톡서버 만들어보자 - 9/10. server.js 이중화</h3>
<p>글 : <a href="https://velog.io/@mqtt_expert/kakaotalk9">https://velog.io/@mqtt_expert/kakaotalk9</a>
Github 소스 : <a href="https://github.com/MQ-Expert/mqtt_chatting/tree/main/09">https://github.com/MQ-Expert/mqtt_chatting/tree/main/09</a></p>
<h3 id="11mqtt로-카톡서버-만들어보자---1010-서버-쪼개기">11.MQTT로 카톡서버 만들어보자 - 10/10. 서버 쪼개기</h3>
<p>글 : <a href="https://velog.io/@mqtt_expert/kakaotalk10">https://velog.io/@mqtt_expert/kakaotalk10</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MQTT로 카톡서버 만들어보자 - 10/10. 서버 쪼개기]]></title>
            <link>https://velog.io/@mqtt_expert/kakaotalk10</link>
            <guid>https://velog.io/@mqtt_expert/kakaotalk10</guid>
            <pubDate>Thu, 18 Apr 2024 02:15:18 GMT</pubDate>
            <description><![CDATA[<h2 id="처음-시작은">처음 시작은</h2>
<p>우리 아버지, 삼촌 세대는 그랬다러라.
한 방에서 다 잤다구.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/38a70c94-fed6-4c5a-8edf-c351d4cd2bfc/image.jpg" alt=""></p>
<p>세상은 변했다.
1인 가구가 대세다.</p>
<p>이번 글에서는 코딩은 없다.</p>
<blockquote>
<p>서비스를 어떻게 구성할 것인가</p>
</blockquote>
<p>라는 전략 얘기만 할거다.</p>
<h2 id="토픽별로-프로그램-쪼개기">토픽별로 프로그램 쪼개기</h2>
<p>이것은 한 서버 안에 토픽별로 프로그램을 나누는 것이다.
즉 토픽별 전담 프로그램이 있는 것이다.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/a4c816ce-d9de-4100-89f8-9e152312745e/image.png" alt=""></p>
<p>34평 아파트에 아버지 방, 누나 방, 내 방, 동생 방 ... 이렇게 있는 거다.</p>
<p>살다보니 내가 투잡을 뛰게 되었다. 
그래서 방에 책상을 하나씩 두고 한쪽에서는 A 일을 다른 쪽에서는 B 일을 하게 되었다.</p>
<p><code>server/login</code>  토픽도 로그인 처리 해주는 Job<code>login_proc</code>와 DB에 저장하는 Job<code>login_save</code>으로 나눠 처리할 수 있다.
동일 토픽에 대해서 말이다.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/b6940242-6f3e-4068-8fa0-9d8119fcdd31/image.png" alt=""></p>
<h2 id="토픽별로-서버-쪼개기">토픽별로 서버 쪼개기</h2>
<p>이것은 서버별로 쪼개는 거로 토픽별로 전담 서버가 있는 것이다.
즉 위의 전담 프로그램을 서버 당 하나씩 집어 넣는거다.</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/267b6784-6bf5-4077-ace0-3496f6c17021/image.png" alt=""></p>
<p>비유하자면 아버지 집🏠, 누나 집🏠, 내 집🏠 ... 이렇게 쪼개는 거다.
말 그대로 1인 가구.</p>
<h2 id="장단점">장.단점</h2>
<p>돈만 있다면 전담 서버가 있는게 왔다다.
관리 비용이 더 들어가겠지만.😂😂</p>
<h2 id="이거-두개를-합치면">이거 두개를 합치면?</h2>
<p>topic 별로 전담 서버가 있고 AND 똑 같은 일을 하는 프로그램을 여러개 만드는 거다.
동일한 일을 하기 때문에 앞에서 배웠던 <a href="https://velog.io/@mqtt_expert/kakaotalk9">shared subscription</a>을 사용해야 한다.
그래야 Round Robin으로 일을 받아서 처리한다.</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/b3dc2fb5-22d6-48a6-98fc-f21fbc77dca0/image.png" alt=""></p>
<p>우리의 목표는 이거다. 기능별로 서버별로 분리하는 거다.</p>
<h2 id="한걸음-더-나아가">한걸음 더 나아가</h2>
<p>IDC에 장애가 날 경우를 대비해 다음과 같이 이중화할 수 있다.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/83b3b9c6-b103-4796-9a79-5c1be860c935/image.png" alt=""></p>
<h2 id="뱀다리">뱀다리</h2>
<p>앞에서 하나의 토픽에 대해 2개로 나누어서 처리할려면 shared scription Group을 분리해야 한다.
<code>server/login</code>  토픽을 예로 들면 다음과 같이 Login Proc와 Login Save를 분리해야 한다..</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/9efcc70e-521d-42e2-85c6-c9c2e2f12810/image.png" alt=""></p>
<p>동일 토픽 <code>server/login</code> 에 대해서 각자 처리하는 역할이 다르므로 그룹을 GroupA, GroupB 이렇게 나눠줘야 한다.
물론 GroupA, GroupB는 임으로 지어준 이름이다.
GroupLoginProc,GroupLoginSave 이렇게 의미 전달될 수 있도록 맘대로 정해주면 된다.</p>
<p>이걸 위 그림에다가 그리려니 너무 복잡하다.
점심 먹고 할 일 없으면 GroupA, GroupB 도 그려넣고, 앞에서 배운 broker 이중화도 그려 넣어보자. 
과제다.</p>
<p>여기까지다.
끝, End, 쫑, 시마이~</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MQTT로 카톡서버 만들어보자 - 9/10. server.js 이중화]]></title>
            <link>https://velog.io/@mqtt_expert/kakaotalk9</link>
            <guid>https://velog.io/@mqtt_expert/kakaotalk9</guid>
            <pubDate>Fri, 12 Apr 2024 04:37:21 GMT</pubDate>
            <description><![CDATA[<h2 id="broker만-이중화하냐">broker만 이중화하냐?</h2>
<blockquote>
<p>실재 서비스는 server.js 가 하는데, server.js가 죽으면 어떡하죠?</p>
</blockquote>
<p>앞에서 broker가 죽으면 다른 broker가 받을 수 있도록 했다.
그래 broker는 이제 안심이다.</p>
<p>그런데 server.js가 죽으면 어떡할 건데.
어떡하긴 뭘 어떡해.
이것도 똑 같다.</p>
<blockquote>
<p>server.js 도 여러개 띄워야 한다.</p>
</blockquote>
<p>아시다시피 nodejs 는 <strong>Single Process / Single Thread</strong> 다.
죽으면 서비스 끝이다.</p>
<p>담당자는 팀장한테 끌려간다.😓</p>
<p>그래서 예전엔 죽으면 살리고, 또 죽으면 또 살리고 하는 forever.js 같은 모듈을 사용하기도 하였다.
요즈음은 프로세스 매니저라고 해서 pm2 를 사용한다.
보통 CPU 코어 수 만큼 server.js를 띄우고 요청이 들어오면 돌아가면서 처리하도록 하는거다.
유식한 말로 이거 scale out이라 한다.</p>
<p>이거 해보자.</p>
<h2 id="serverjs를-2-개-띄워보자">server.js를 2 개 띄워보자.</h2>
<p>앞에서 계속 봐 왔던 server.js 이다.
다만 2개를 띄울 거니까, 어떤 프로세스가 반응했는지 보기 위해 </p>
<pre><code class="language-javascript">    console.log(process.pid, &#39;가 반응하였음.&#39;);</code></pre>
<p>를 추가했다.</p>
<p><strong>server.js</strong></p>
<pre><code class="language-javascript">const mqtt = require(&quot;mqtt&quot;);
const server = mqtt.connect(&quot;mqtt://localhost&quot;);

server.on(&quot;connect&quot;, () =&gt; {
    server.subscribe(&quot;server/login&quot;);
});

server.on(&quot;message&quot;, (topic, message) =&gt; {
    console.log(process.pid, &#39;가 반응하였음.&#39;);
    var data = JSON.parse(message);
    var client_topic = &#39;clients/&#39; + data.id;
    if (data.pwd === &#39;pwd&#39;) {
        var res = { msg: &#39;login ok&#39; };
        server.publish(client_topic, JSON.stringify(res));
    } else {
        var res = { msg: &#39;login fail&#39; };
        server.publish(client_topic, JSON.stringify(res));
    }
});
</code></pre>
<p>자 이 상태에서 <code>server.js</code>를 2개 실행해보자.
<strong>1번 server.js</strong> 실행</p>
<pre><code class="language-bash">$&gt; node server.js</code></pre>
<p><strong>2번 server.js</strong> 실행</p>
<pre><code class="language-bash">$&gt; node server.js</code></pre>
<p>여기서 <code>client.js</code>를 실행하면 어떡게 될까?</p>
<pre><code class="language-bash">$&gt; node client.js</code></pre>
<p><code>client.js</code> 소스는 아래처럼 바뀐게 없다.</p>
<p><strong>client.js</strong></p>
<pre><code class="language-javascript">const mqtt = require(&quot;mqtt&quot;);
const client = mqtt.connect(&quot;mqtt://localhost&quot;);

const user = { id: &#39;client1&#39;, pwd: &#39;pwd&#39; };

client.on(&quot;connect&quot;, () =&gt; {
    client.subscribe(&quot;clients/client1&quot;);
    client.publish(&quot;server/login&quot;, JSON.stringify(user));
});

client.on(&quot;message&quot;, (topic, message) =&gt; {
    console.log(message.toString());
    client.end();
});
</code></pre>
<p><strong>client.js</strong> 실행</p>
<pre><code class="language-bash">$&gt; node client.js</code></pre>
<p><strong>client.js</strong> 실행 결과
<img src="https://velog.velcdn.com/images/mqtt_expert/post/b15a72e8-a655-49ed-b066-e0caea2604a3/image.PNG" alt=""></p>
<blockquote>
<p>어이쿠😨😨😨, <code>login ok</code> 메세지가 2번 떨어진다.</p>
</blockquote>
<p>아니나 다를까, 1번, 2번 <code>server.js</code> 가 둘 다 반응하였다.😨😨😨</p>
<p><strong>1번 server.js</strong> 실행 결과
<img src="https://velog.velcdn.com/images/mqtt_expert/post/323a073f-9beb-456b-82ea-323214c48e67/image.PNG" alt="">
<strong>2번 server.js</strong> 실행 결과
<img src="https://velog.velcdn.com/images/mqtt_expert/post/bd69212a-a009-4666-a9c6-c25ad4f69925/image.PNG" alt=""></p>
<p>뭐 생각해보면 당연한 결과다.
2개의 server.js가 <code>server/login</code> topic에 sub 하고 있으니까 당연히 이렇게 반응한 것인데.</p>
<p>그런데 우리가 원하는 건 이게 아니다.
한 번에 한 서버만 반응해야지.
그래야 로드밸런싱도 되고, 한 놈이 죽어도 다른 놈이 일을 하니까 이중화라고 말할 수 있고...</p>
<p>라운드로빈 방식으로 한 번에 한 놈한테만 일을 시키는 방법은 없을까?</p>
<p>화투판에서 화투는 선이 한 장씩 줘야한다.</p>
<h2 id="shared-subscriptions">Shared subscriptions</h2>
<p>방법이 있다.
제목대로 일반 sub을 <strong>공유 sub</strong>으로 바꾸면 된다.
어떡해 바꾸냐? 
방법은 간단하다.</p>
<p>topic 이름을 <code>server/login</code> 에서 <code>$share/GroupA/server/login</code> 로 바꾸면 된다.</p>
<p><strong>server.js</strong></p>
<pre><code class="language-javascript">...
server.on(&quot;connect&quot;, () =&gt; {
    server.subscribe(&quot;$share/GroupA/server/login&quot;);
});
...</code></pre>
<p><code>GroupA</code> 라고 썼는데, 이것은 그룹핑을 하기 위한 이름으로 이름이 같으면 하나로 묶인다.</p>
<p>client.js 는 그대로 <code>server/login</code>에 pub 하면 된다.</p>
<p>자 그 결과를 보자.
귀찮아서 그림을 하나로 묶었다.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/0ae5a237-95ac-4413-a350-0c08726e1797/image.PNG" alt="">
제일 위에 <code>node clinet.js</code> 실행한 결과를 보면 알겠지만, 1번 server.js만 반응하였다.
한번 더 실행하면 2번 server.js가 반응할 것이다.</p>
<h2 id="pm2">pm2</h2>
<p>server.js를 하나씩 띄우기도 귀찮을뿐만 아니라, 죽었는지 확인하는 것도 그렇고, 죽으면 다시 살리는 것도 번거럽고.
그래서 나온게 pm2다.</p>
<blockquote>
<p>라떼엔 <code>forever.js</code> 이런 걸로 짰지.</p>
</blockquote>
<p>이러면 연식이 좀 된 개발자라 생각하면 된다.</p>
<p><strong>ecosystem.config.js</strong></p>
<pre><code class="language-javascript">module.exports = {
  apps: [{
    name: &#39;server&#39;,
    script: &#39;./server.js&#39;,
    instances: &#39;max&#39;,
    exec_mode: &#39;cluster&#39;
  }]
}</code></pre>
<p><code>instances : &#39;max&#39;</code> 로 주면 CPU 코어 수만큼 프로세스가 실행된다.</p>
<p>다음은 client.js를 실행한 결과다.</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/c9f82b44-4ae2-43cc-bd9d-e82b3ba5d129/image.PNG" alt=""></p>
<p>8 Core PC라 8개 프로세스가 떴지만, <code>client.js</code> 에는 하나만 반응하였다.</p>
<p>Cloud1, Cloud2 2개 있다고 하자.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/6525a9d9-d6df-432d-b01e-2cad0e3cfe70/image.png" alt=""></p>
<p>Cloud1에도 <code>GroupA</code> 를 만들고, Cloud2에도 <code>GroupA</code>를  만들어 두면, Cloud1에 문제가 생겨도 서비스에는 지장이 없다.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/c1a695db-7902-45c3-a3fa-146db774aaf1/image.png" alt=""></p>
<p>멋지지 아니한가~</p>
<p>사실 내가 그린 그림보다 아래 나오지만, <code>Roger Light</code>의 이 그림이 이해하는 더 도움이 된다.
한 번에 한 장씩.🎴🎴🎴
<img src="https://cedalo.com/wp-content/uploads/2023/03/ezgif.com-optimize-1.gif" alt=""></p>
<hr>
<p><strong>Shared subscriptions</strong> 은 <code>Roger Light</code>의 <a href="https://cedalo.com/blog/mqtt-shared-subscriptions-guide/">https://cedalo.com/blog/mqtt-shared-subscriptions-guide/</a> 글을 읽어 보기 바란다.
정확한 스펙은 <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901250">https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901250</a> 를 참고.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MQTT로 카톡서버 만들어보자 - 8/10. IDC에 불이 났어요]]></title>
            <link>https://velog.io/@mqtt_expert/kakaotalk8</link>
            <guid>https://velog.io/@mqtt_expert/kakaotalk8</guid>
            <pubDate>Tue, 09 Apr 2024 03:11:44 GMT</pubDate>
            <description><![CDATA[<h2 id="broker가-죽으면-어떡해">broker가 죽으면 어떡해?</h2>
<p>어떡하긴 뭘 어떡해</p>
<blockquote>
<p>broker를 여러개 만들어 놓아야지.</p>
</blockquote>
<p>우리가 집을 내놔도 부동산 한 군데 내놓나?
아니지 않나? 
몇 군데 내놓지.
집 구하러 손님이 오면, 일단 자기가 가지고 있는 매물부터 소개해주고, 
손님이 맘에 안들어하면 옆 부당산 매물도 보여준다.(수수료가 반띵이라는 말이 있긴한데...)</p>
<p>Broker도 마찬가지다.
앞 <a href="https://velog.io/@mqtt_expert/kakaotalk1">1장</a> 읽어 보신 분들은 알겠지만, broker도 부동산처럼 중간에서 연결해주는 역할을 한다.</p>
<p>하나만 있어선 불안하다.</p>
<blockquote>
<p>얘가 죽으면...어떡하지</p>
</blockquote>
<p>라는 걱정이 드는 것은 당연하다.</p>
<p>우리도 여러군데 부동산에 집을 내놓듯, broker도 여러개 등록할 수 있다.</p>
<p>여러개라고 해서 client가 pub 하면 broker들이 모두 반응하는 것은 아니다.
등록된 순서대로 한 놈만 반응한다.
만약 1번 broker가 반응이 없다면 그 다음 2번 broker가 그 역할을 한다.</p>
<p>완벽한 Active-Standby 구조다.</p>
<p>정말 그렇게 동작하는지 테스트해보자.</p>
<p>VSCode를 4개로 나누어서 </p>
<table>
<thead>
<tr>
<th>#</th>
<th>cmd</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>$&gt; mosquitto.exe -c mq_01.conf</td>
<td>1번째 broker port 1883</td>
</tr>
<tr>
<td>2</td>
<td>$&gt; mosquitto.exe -c mq_02.conf</td>
<td>2번째 broker port 2883</td>
</tr>
<tr>
<td>3</td>
<td>$&gt; node server.js</td>
<td>1장에 있는 server.js</td>
</tr>
<tr>
<td>4</td>
<td>$&gt; node client.js</td>
<td>1장에 있는 client.js</td>
</tr>
</tbody></table>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/d8159f6a-1fce-477b-a39b-b2eeb8ea59a2/image.png" alt=""></p>
<p>먼저 broker를 2 개 만들어 보자.</p>
<p><strong>1번</strong> broker
<strong>mq_01.conf</strong></p>
<pre><code>per_listener_settings false
listener 1883
allow_anonymous true

log_dest stderr
log_type all</code></pre><p>실행</p>
<pre><code class="language-bash">$&gt; mosquitto.exe -c mq_01.conf</code></pre>
<p><strong>2번</strong> broker
<strong>mq_02.conf</strong></p>
<pre><code>per_listener_settings false
listener 2883
allow_anonymous true

log_dest stderr
log_type all</code></pre><p>실행</p>
<pre><code class="language-bash">$&gt; mosquitto.exe -c mq_02.conf</code></pre>
<p><strong>3번</strong>  <strong>server.js</strong></p>
<pre><code class="language-javascript">const mqtt = require(&quot;mqtt&quot;);

const options = {
    servers: [{
        host: &#39;localhost&#39;,
        port: 1883
    }, {
        host: &#39;localhost&#39;,
        port: 2883
    }]
};

const server = mqtt.connect(options);

server.on(&quot;connect&quot;, () =&gt; {
    server.subscribe(&quot;server/login&quot;);
});

server.on(&quot;message&quot;, (topic, message) =&gt; {
    var data = JSON.parse(message);
    console.log(&#39;server&#39;, data);  
    var client_topic = &#39;clients/&#39; + data.id;
    if (data.pwd === &#39;pwd&#39;) {
        var res = { msg: &#39;login ok&#39; };
        server.publish(client_topic, JSON.stringify(res));
    } else {
        var res = { msg: &#39;login fail&#39; };
        server.publish(client_topic, JSON.stringify(res));
    }
});</code></pre>
<p><strong>4번</strong>  <strong>client.js</strong></p>
<pre><code class="language-javascript">const mqtt = require(&quot;mqtt&quot;);

const options = {
    servers: [{
        host: &#39;localhost&#39;,
        port: 1883
    }, {
        host: &#39;localhost&#39;,
        port: 2883
    }]
};

const client = mqtt.connect(options);

const user = { id: &#39;client1&#39;, pwd: &#39;pwd&#39; };

client.on(&quot;connect&quot;, () =&gt; {
    client.subscribe(&quot;clients/client1&quot;);
    client.publish(&quot;server/login&quot;, JSON.stringify(user));
});

client.on(&quot;message&quot;, (topic, message) =&gt; {
    console.log(message.toString());
    client.end();
});</code></pre>
<p>시나리오는 이렇다.</p>
<ol>
<li><strong>1번</strong>, <strong>2번</strong>에 각각 broker를 띄워놓고,</li>
<li><strong>3번</strong> server.js를 실행하면 <strong>1번</strong>에 붙는다.</li>
<li><strong>4번</strong> client.js를 실행하면 이것도  <strong>1번</strong>에 붙고, <strong>3번</strong> server.js가 반응한다.</li>
<li>이 상태에서 <strong>1번</strong>을 Ctrl-C로 죽이면,  <strong>3번</strong> server.js가 <strong>2번</strong> broker에 붙는다.</li>
<li><strong>4번</strong> client.js를 다시 실행하면, <strong>2번</strong> broker에 연결되어 <strong>3번</strong> server.js가 반응한다.</li>
</ol>
<p>시나리오대로 실행한 동영상이다.
PC에서 봐야 눈이 안 아플것이다.</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/a6073ea8-e9eb-4f7a-bd2f-3986b4848b94/image.gif" alt=""></p>
<p>알흠답지 아니한가~</p>
<h2 id="그래서-idc에-불이-나면">그래서 IDC에 불이 나면?</h2>
<p>broker를 서로 다른 cloud 에 두면 IDC에 불이 나도 서비스에는 지장이 없을 것이다.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/53000c61-b3ac-4aaa-a7ec-86667aa3fb0b/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MQTT로 카톡서버 만들어보자 - 7/10. ACL]]></title>
            <link>https://velog.io/@mqtt_expert/kakaotalk7</link>
            <guid>https://velog.io/@mqtt_expert/kakaotalk7</guid>
            <pubDate>Wed, 03 Apr 2024 02:52:33 GMT</pubDate>
            <description><![CDATA[<h2 id="내가-딱-정해줄께">내가 딱 정해줄께</h2>
<p>문 앞에서 민증 깠다고 다 된 건 아니다.
20대는 홀로, 30대는 룸으로~
선글라스 아저씨가 안내하는 곳으로 가야 한다.
노는 물이 다르다.</p>
<p>pub/sub 도 마찬가지다.
server는 <strong>server</strong> 토픽에 붙고, client는 <strong>client</strong> 토픽에 붙어야지.
client가 <strong>server</strong> 토픽에 붙으면 안된다.
그랬다가는 다른 사람 대화 내용을 다 들여다 볼 수 있다.
개폭망이다.</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/1086d58d-26e2-497f-aa84-45afbf1c09fe/image.webp" alt=""></p>
<p>저 아저씨 귀에 꽂고 있는 거 뺏긴 것과 같은 거다.
몰래 다 듣고 있다는 뜻이다.</p>
<p>자 그럼 어떡해야 하나?</p>
<p>앞 글 <a href="https://velog.io/@mqtt_expert/kakaotalk1">1. Pub/Sub으로 로그인?</a> 에 나와 있는 아래 그림을 보자.</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/cfa703d9-3c3c-430c-843b-ca735a0cda2e/image.png" alt=""></p>
<blockquote>
<ol>
<li>sub server/login : server/login으로 들어오는 건 내가 처리할께</li>
<li>sub clients/client1 : 결과는 clients/client1 로 주세요</li>
<li>pub server/login : server님, 저 login할래요</li>
<li>pub clients/client1 : clients/client1 DB에 있는 패스워드랑 다른데...너 누구냐?</li>
</ol>
</blockquote>
<p>즉 server는 <strong>server/#</strong> 을 subscribe(read)하고, <strong>clients/#</strong> 에 publish(write)한다.
반대로 client는 <strong>clients/#</strong> 을 subscribe(read)하고, <strong>server/#</strong> 에 publish(write)한다.</p>
<p>이게 우리의 룰이다.</p>
<p>client가 <strong>server/#</strong> 을 subscribe(read) 하면 안된다.
어떻게 해야할까?</p>
<p>즉 누가 어떤 topic에 sub할지 pub할지 정해주는게 필요하다.
자율에 맡기면 X된다.
그래서 나온게 ACL이다.</p>
<h2 id="acl">ACL</h2>
<p><strong>ACL</strong>(Access Control List)은 프로토콜 영역이 아니다.
<a href="https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html">MQTT 3.1 Spec</a>에서 검색해봐라. 안 나온다.</p>
<p>누가 어디에 붙을 수 있는지 정하는 건 broker 영역이다.
이건 <a href="https://mosquitto.org/man/mosquitto-conf-5.html">mosquitto conf</a> 에서 찾아야 한다.</p>
<p>위 문서에서 <strong>acl</strong>로 검색해보면 다음과 같이 나온다.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/c1a396a2-21f7-4277-8b46-27e3b7a24ca1/image.PNG" alt="acl_file"></p>
<p>우리 규칙에 맞게 시키는대로 acl_file을 만들자.</p>
<p><strong>acl</strong></p>
<pre><code class="language-javascript"># deny anonymous access
topic deny $SYS/#

user server
topic read server/#
topic write clients/#

user client
topic write server/#
topic read clients/#</code></pre>
<p>일단 <strong>$SYS/#</strong> topic은 시스템 정보이므로 아무나 접근 못하게 막고,</p>
<blockquote>
<p>server는 <strong>server/#</strong> 을 subscribe(read)하고, <strong>clients/#</strong> 은 publish(write)한다.
client는 <strong>clients/#</strong> 을 subscribe(read)하고, <strong>server/#</strong> 에 publish(write)한다.</p>
</blockquote>
<p>멋지지 않은가</p>
<p>설정 파일에 acl을 등록하자.</p>
<p><strong>mosquitto.conf</strong></p>
<pre><code class="language-javascript">acl_file C:\Program Files\mosquitto\conf.d\acl</code></pre>
<h2 id="백문이불여일테스트">백문이불여일테스트</h2>
<p>아래 코드는 앞에서 봤던 server.js로, 제대로 된 자격증명(server/pwd)으로 로그인한 거다.
다만 TLS/SSL 들어가면 복잡하니까, 최대한 단순하게 하기 위해 뺐자.
제대로 동작하는지 확인하기 위해 message를 받으면 출력하도록 바꿨다.
개발자가 가장 많이하는 디버깅 아닌가.</p>
<p><span style="color:black; background:yellow">1번</span> <strong>server.js</strong></p>
<pre><code class="language-javascript">const mqtt = require(&quot;mqtt&quot;);

const options = {
    port: 1883,
    host: &#39;stark&#39;,
    username: &#39;server&#39;,
    password: &#39;pwd&#39;
}

const server = mqtt.connect(options);

server.on(&quot;connect&quot;, () =&gt; {
    server.subscribe(&quot;server/login&quot;);
});

server.on(&quot;message&quot;, (topic, message) =&gt; {
    var data = JSON.parse(message);
    console.log(&#39;server&#39;, data);
    var client_topic = &#39;clients/&#39; + data.id;
    if (data.pwd === &#39;pwd&#39;) {
        var res = { msg: &#39;login ok&#39; };
        server.publish(client_topic, JSON.stringify(res));
    } else {
        var res = { msg: &#39;login fail&#39; };
        server.publish(client_topic, JSON.stringify(res));
    }
});</code></pre>
<p>아래는 fake 자격증명(client/pwd)으로 클라이언트 중 한 놈이 마치 server인양 server.js 를 실행한거다.
로그인은 통과하겠지만, 진짜 server가 아니다.
제대로 동작하는지 확인하기 위해 message를 받으면 출력하도록 했다.</p>
<p><span style="color:black; background:yellow">2번</span> <strong>fake_server.js</strong></p>
<pre><code class="language-javascript">const mqtt = require(&quot;mqtt&quot;);

const options = {
    port: 1883,
    host: &#39;stark&#39;,
    username: &#39;client&#39;,
    password: &#39;pwd&#39;
}

const server = mqtt.connect(options);

server.on(&quot;connect&quot;, () =&gt; {
    server.subscribe(&quot;server/login&quot;);
});

server.on(&quot;message&quot;, (topic, message) =&gt; {
    var data = JSON.parse(message);
    console.log(&#39;fake_server&#39;, data);
    var client_topic = &#39;clients/&#39; + data.id;
    if (data.pwd === &#39;pwd&#39;) {
        var res = { msg: &#39;login ok&#39; };
        server.publish(client_topic, JSON.stringify(res));
    } else {
        var res = { msg: &#39;login fail&#39; };
        server.publish(client_topic, JSON.stringify(res));
    }
});</code></pre>
<p>자 이 상태에서 <span style="color:black; background:yellow">3번</span> client.js를 실행하면 어떻게 될까?</p>
<pre><code class="language-javascript">$&gt; node client.js</code></pre>
<p>당연히 <span style="color:black; background:yellow">1번</span> server.js는 반응하지만, <span style="color:black; background:yellow">2번</span> fake_server.js는 반응하지 않는다.</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/b3e63885-7922-47af-a02a-d38ec06f69a1/image.PNG" alt=""></p>
<blockquote>
<p>우쒸! fake_server.js에서 오류라도 떨어져야되는 거 아니예요.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/00e1a52e-b567-4773-99c8-fa65f78a8765/image.jpg" alt=""></p>
<p>Web 로그인에서도 <strong>아이디가 틀렸다, 패스워드가 틀렸다</strong> 이렇게 세세하게 안 알려준다.
뭔가를 알려주면 해커는 그것으로 유추하기 때문에, 걍 무응답이 답이다.😁</p>
<hr>
<p>acl 파일의 server 부분은 다음과 같이 하나 하나 써주는게 보안에 도움이 된다.</p>
<p><strong>acl</strong></p>
<pre><code class="language-javascript"># deny anonymous access
topic deny $SYS/#

user server
topic read server/login
topic read server/logout
topic read server/signin
topic read server/signout
topic read server/talk
topic write clients/#

user client
topic write server/login
topic write server/logout
topic write server/signin
topic write server/signout
topic write server/talk
topic read clients/#</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[MQTT로 카톡서버 만들어보자 - 6/10. 입구컷 안 당하기]]></title>
            <link>https://velog.io/@mqtt_expert/kakaotalk6</link>
            <guid>https://velog.io/@mqtt_expert/kakaotalk6</guid>
            <pubDate>Fri, 29 Mar 2024 05:41:32 GMT</pubDate>
            <description><![CDATA[<h2 id="누군가-당신의-대화를-엿듣는다면">누군가 당신의 대화를 엿듣는다면?</h2>
<p>보안에는 4단계가 있다<del>(고하더라)</del>.</p>
<ul>
<li>네트워크 수준 보안</li>
<li>전송 수준 보안</li>
<li>애플리케이션 수준 보안</li>
<li>페이로드 암호화</li>
</ul>
<p>일단 영어가 섞여 있으니 용어가 어질어질하다.</p>
<table>
<thead>
<tr>
<th>#</th>
<th>단계</th>
<th>보안 방법</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>네트워크 수준 보안</td>
<td><strong>VPN</strong> 터널을 사용하여 네트워크 연결 보호</td>
</tr>
<tr>
<td>2</td>
<td>전송 수준 보안</td>
<td>HTTP와 마찬가지로 <strong>TLS/SSL</strong>로 전송 계층 보호</td>
</tr>
<tr>
<td>3</td>
<td>애플리케이션 수준 보안</td>
<td>고유한 클라이언트 식별자, 또는 <strong>이름/비밀번호</strong>로 자격 증명</td>
</tr>
<tr>
<td>4</td>
<td>페이로드 암호화</td>
<td>애플리케이션 수준에서 <strong>페이로드 자체를 암호화</strong></td>
</tr>
</tbody></table>
<h2 id="아무리-못해도-web만큼은-해줘야하지-않겠니">아무리 못해도 Web만큼은 해줘야하지 않겠니?</h2>
<p>그래서 딱 2개, 2번 <strong>TLS/SSL 보안</strong>과 3번 <strong>자격증명</strong> 해보자. 
나머지는 이 글 범위를 넘어선다<del>(라고 쓰고 줄행랑💨💨💨)</del>.</p>
<h2 id="자격증명-▶-민쯩-까봐">자격증명 ▶ 민쯩 까봐!</h2>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/ae451080-b502-4694-83b5-60595800e61d/image.jpg" alt=""></p>
<p>맨 먼저 내가 누군지 확인 안 된 놈은 입구컷이다.
그래야 어장 관리가 되지 안 그러면 broker 거덜난다.
mosquitto는 기본적으로 localhost 로 붙을 땐 자격증명 없어도 붙지만, 외부에서 붙을 땐 자격증명이 필요하다.
localhost고 뭐고 무조건 내가 누군지 까야 붙을 수 있게 하려면 mosquitto.conf 파일에 다음과 같이 하면 된다.</p>
<p>그리고 mosquitto 2.0 이상에서는 localhost가 아닌 다른 곳에서도 붙게 하려면 listener를 <a href="https://stackoverflow.com/a/65278769">정의</a>해야 한다. 보안 때문에 강화된 거란다.</p>
<p><strong>mosquitto.conf</strong></p>
<pre><code class="language-javascript">listener 1883
allow_anonymous false</code></pre>
<p>그 다음 입장자 명단을 만들고, 해킹을 대비해 암호화 한다.
<strong>password</strong></p>
<pre><code class="language-javascript">server:pwd
client:pwd</code></pre>
<p>이 파일은 mosquitto_passwd 프로그램으로 다음의 명령으로 암호화하라.</p>
<pre><code class="language-javascript">$&gt; mosquitto_passwd.exe password</code></pre>
<p>그러면 파일이 아래와 같이 변하는 기적을 맛볼 것이다.</p>
<p><strong>password</strong></p>
<pre><code class="language-javascript">server:$7$101$p4wSJs3pD1lwGE2X$HeddQkc56jh2ZehHeCe/W+dq3EgynumOmMdYTkw8hdu8rKXbOjWPfYiqmM/wmTh3oIyu6fcVyH4w84E/SRIMjw==
client:$7$101$QZizFrfQBO53ZAcq$J6GR7oh5UqeK3UGOVy7EpG0Sf7TIJObl6pkRTyjA8j4xy3jdbS0kJEjOSt7IQ6K5tsqpgwHkmubbxKf0gg97/w==</code></pre>
<h3 id="brokermosquitto-설정">broker(mosquitto) 설정</h3>
<p>password 파일을 만들었으면 준비는 다 된거다.
이 파일을 mosquitto.conf 파일에 추가하고 함 껏다 켜준다.</p>
<p><strong>mosquitto.conf</strong></p>
<pre><code class="language-javascript">listener 1883
allow_anonymous false
password_file C:\Program Files\mosquitto\conf.d\password</code></pre>
<p>이제 우리는 정문에 <strong>기도</strong>를 세워 둔거다.
기본 어장 관리는 된다고 보면 된다.</p>
<h3 id="serverjs-clientjs-설정">server.js, client.js 설정</h3>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/7b0769c4-51d9-4906-81c2-7bb6409423ef/image.png" alt=""></p>
<p>client 입장에서는, 이제 물관리하는 이 아저씨를 통과해야 한다.</p>
<p>당연히 민증 까야지.</p>
<p><strong>server.js</strong></p>
<pre><code class="language-javascript">const options = {
    host: &#39;stark&#39;,
    port: 1883,
    username: &#39;server&#39;,
    password: &#39;pwd&#39;
}
const client = mqtt.connect(options);</code></pre>
<p><strong>server</strong>도 borker 입장에서는 client다.</p>
<p><strong>client.js</strong></p>
<pre><code class="language-javascript">const options = {
    host: &#39;stark&#39;,
    port: 1883,
    username: &#39;client&#39;,
    password: &#39;pwd&#39;
}
const client = mqtt.connect(options);</code></pre>
<p><strong>&#39;stark&#39;</strong>가 뭐냐구?
hosts 파일에 등록한 mosquitto 서버 이름이다.</p>
<p><strong>C:\Windows\System32\drivers\etc\hosts</strong></p>
<pre><code class="language-javascript">192.168.219.101 stark</code></pre>
<p>이름이 왜 stark 냐구? <a href="https://github.com/mqttjs/MQTT.js/blob/main/examples/tls%20client/mqttclient.js">mosquitto example</a> 에 있는 이름 그대로 썼다.</p>
<h2 id="tlsssl-암호화">TLS/SSL 암호화</h2>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/85cbc3dd-032b-4648-ac6c-756281d7a8ce/image.jpg" alt="">
네트웍 중간에 김선생이 청진기 대고 있으면 어떡할건가?</p>
<p>다음은 wireshark으로 패킷 캡쳐한 것이다.
암호화하지 않았을 경우 <strong>server/login</strong> 으로 넘긴 데이터가 다 보인다.
<strong>뜨악😱</strong>이다.</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/1e6b7570-d024-4c57-b78a-e0f6a07ad226/image.PNG" alt=""></p>
<p>이 상태로 플젝 끝했다가는 개폭망이라 봐도 무방하다.</p>
<p>김선생 못 알아보게 만들어야 한다.
다음은 TLS로 암호화한 것이다.
뚫어져라봐도 당췌 뭐가 오고 간 것인지 추측도 하기 어렵지 아니한가 우하하하.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/8d01bd50-aa4f-43bd-a6d4-8e9f90f7f739/image.PNG" alt="">
심지어 <strong>topic</strong>명도 알아 볼 수 없다.</p>
<p><strong>이거 해보자</strong> 했는데, 이미 한 분들 많더라.😂
인증서 만드는 것은  <a href="https://m.blog.naver.com/powercm/221735407930">이 글</a>을 따라하면 된다.
다만 nodejs 관련 설정은 다음과 같이 하면 된다.</p>
<p><strong>server.js</strong></p>
<pre><code class="language-javascript">const fs = require(&#39;fs&#39;)
const path = require(&#39;path&#39;)
const KEY = fs.readFileSync(path.join(__dirname, &#39;/tls/server.key&#39;))
const CERT = fs.readFileSync(path.join(__dirname, &#39;/tls/server.crt&#39;))
const TRUSTED_CA_LIST = fs.readFileSync(path.join(__dirname, &#39;/tls/ca.crt&#39;))

const options = {
    port: 1883,
    host: &#39;stark&#39;,
    username: &#39;server&#39;,
    password: &#39;pwd&#39;,
    key: KEY,
    cert: CERT,
    rejectUnauthorized: true,
    ca: TRUSTED_CA_LIST,
    protocol: &#39;mqtts&#39;
}

const server = mqtt.connect(options);</code></pre>
<p><strong>client.js</strong></p>
<pre><code class="language-javascript">const fs = require(&#39;fs&#39;)
const path = require(&#39;path&#39;)
const KEY = fs.readFileSync(path.join(__dirname, &#39;/tls/client.key&#39;))
const CERT = fs.readFileSync(path.join(__dirname, &#39;/tls/client.crt&#39;))
const TRUSTED_CA_LIST = fs.readFileSync(path.join(__dirname, &#39;/tls/ca.crt&#39;))

const options = {
    port: 1883,
    host: &#39;stark&#39;,
    username: &#39;client&#39;,
    password: &#39;pwd&#39;,
    key: KEY,
    cert: CERT,
    rejectUnauthorized: true,
    ca: TRUSTED_CA_LIST,
    protocol: &#39;mqtts&#39;
}

const server = mqtt.connect(options);</code></pre>
<p>사실 보안을 다룰 때 <strong>&#39;완벽&#39;</strong> 이란 없다.
외부 침투를 아무리 완벽하게 막았다고 하더라도, 대부분 구멍은 내부자에게 있다.
스노든 아저씨를 봐라.
<img src="https://upload.wikimedia.org/wikipedia/ko/thumb/9/92/%EC%98%81%ED%99%94_%EC%8A%A4%EB%85%B8%EB%93%A0.jpg/220px-%EC%98%81%ED%99%94_%EC%8A%A4%EB%85%B8%EB%93%A0.jpg" alt=""></p>
<p>따라서 자격증명 파일, 인증서 파일은 은밀한 곳에 따로 보관하자.
적어도 <strong>production</strong> 환경이라도...🙏🙏🙏</p>
<hr>
<small>기도: 기도는 일본말 木戸：きど（gido）로 우리말은 문지기쯤 되지 않을까. 영어로는 Bouncer라고 한다. 등판에 security 라고 쓴 미드도 있긴 하더라.</small>]]></description>
        </item>
        <item>
            <title><![CDATA[MQTT로 카톡서버 만들어보자 - 5/10. QoS 번외]]></title>
            <link>https://velog.io/@mqtt_expert/kakaotalk5</link>
            <guid>https://velog.io/@mqtt_expert/kakaotalk5</guid>
            <pubDate>Thu, 28 Mar 2024 01:38:55 GMT</pubDate>
            <description><![CDATA[<h2 id="qos는-맞춰라">QoS는 맞춰라</h2>
<p>MQTT 코딩할 때 가장 많이 고민되는게 있다.
보내는 놈의 QoS 랑, 받는 놈의 QoS.</p>
<blockquote>
<p>나는 QoS=1로 보내고, 받는 놈이 QoS=2로 받으면 확실하지 않을까?</p>
</blockquote>
<p>이거에 대한 썰을 풀고자 한다.</p>
<p>자, 자 각설하고
우리야 client와 server 라고 하지만, broker 입장에서는 둘 다 client 아니겠나.</p>
<p>다음을 생각해보자.
client와 server 가 서로 다른 QoS를 사용할 때 QoS는 어느 쪽을 따를까?</p>
<blockquote>
<p>세상에 당신이 최초로 고민한 문제는 없다.
구글신한테 물어봐라.</p>
</blockquote>
<p>사수한테 물어보면 위와 같은 대꾸를 듣는다.
사수님의 말이 맞다.
당연히 이것도 테스트한 사람이 있다.</p>
<p><a href="https://github.com/ralight">Roger Light</a> 가 만든 다음 시나리오 표를 보자.
앞에 2개는 설정(setting)이고, 뒤 2개는 결과다.</p>
<table>
<thead>
<tr>
<th>#</th>
<th>QoS publishing</th>
<th>QoS subscribing</th>
<th>QoS Publisher &gt;&gt; Broker</th>
<th>QoS Broker &gt;&gt; Subscriber</th>
</tr>
</thead>
<tbody><tr>
<td><strong>1</strong></td>
<td>0</td>
<td>1 or 2</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td><strong>2</strong></td>
<td>1 or 2</td>
<td>0</td>
<td>1 or 2</td>
<td>0</td>
</tr>
<tr>
<td><strong>3</strong></td>
<td>2</td>
<td>1</td>
<td>2</td>
<td>1</td>
</tr>
<tr>
<td><strong>4</strong></td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td><small>출처: <a href="https://cedalo.com/blog/understanding-mqtt-qos/">https://cedalo.com/blog/understanding-mqtt-qos/</a></small></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<p>1번은 받는 놈이 아무리 QoS를 1, 2로 하더라도 보내는 QoS가 0 이면 받는 QoS도 0 이라는 거다.
2,3번도 같은 맥락으로 이해할 수 있다.</p>
<p>즉 아무리 내가 보낼 때 높은 QoS로 보내도, 받는 쪽이 어떻게 받도록 설정하느냐에 따라 달라진다는 거다.</p>
<blockquote>
<p>유식한 말로 <strong>QoS Downgrade</strong> 현상이라고 한다.</p>
</blockquote>
<p>따라서 4번과 같이 <strong>보내는 QoS</strong>와 <strong>받는 QoS</strong>를 맞추는 것이 정신 건강에 이롭다.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/e006ee03-5fb8-42ce-851c-a5fbb096fe85/image.jpg" alt=""></p>
<h2 id="qos별-속도-차이">QoS별 속도 차이</h2>
<p>QoS가 높아질수록 신뢰도는 높아지지만 속도는 느려질 것같다는 느낌적 느낌은 받는데....</p>
<blockquote>
<p>그렇다 이건 느낌이다. 숫자가 아니다.</p>
</blockquote>
<p>회의 시간에 느낌만 이야기 하면 초급 취급 받는다.
숫자를 제시해야 한다.
QoS별로 초당 몇 개의 Packet을 보낼 수 있는지에 대한 다음 벤치마킹 데이터를 보자.</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/ea785493-f651-4ede-a159-3ee6c82e8496/image.webp" alt=""></p>
<p><small><center>출처: <a href="https://blog.softwaretoolbox.com/mqtt-quality-of-service-datahub">https://blog.softwaretoolbox.com/mqtt-quality-of-service-datahub</a></center></small></p>
<p>보내는 건 별 차이가 없는데, 받는 건 드라마틱한 차이가 보인다. 
솔직히 이 자료보고 나도 놀랐다.
생각보다 QoS=1이 QoS=2보다 안 빠르네 😰
이거 보니 괜히 QoS=0 으로 프로그램 고치고 싶어진다😝</p>
<hr>
<small>QoS=0 으로 바꿀려면 server가 talk 쏠 때 보낸 놈한테도 쏘도록 수정하면 되지 않을까..솔직히 안해봤다😰</small>

<p><small>해보고 잘되면 목에 힘주고 아무한테도 알려주지 말고 연봉협상💰 때 넌지시 얘기하라.</small></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MQTT로 카톡서버 만들어보자 - 4/10. 내전화 받았늬?]]></title>
            <link>https://velog.io/@mqtt_expert/kakaotalk4</link>
            <guid>https://velog.io/@mqtt_expert/kakaotalk4</guid>
            <pubDate>Wed, 27 Mar 2024 08:14:36 GMT</pubDate>
            <description><![CDATA[<h2 id="늬-내-전화-아이-받늬">늬 내 전화 아이 받늬?</h2>
<p>말을 하는데, 상대가 &#39;응&#39;, &#39;응&#39; 하며 추임새를 넣어주면 말하는 사람도 신이 난다.</p>
<p>그런데 내 말엔 신경도 안 쓰고 지 할 일만 하는 놈도 있다.
&#39;늬 내 말 듣고 있니?&#39; 라고 답답해서 중간에 물으면 &#39;야...다 듣고 있어!&#39; 이러면 짜증 제대로다.</p>
<p>이번 글에서는 내가 <strong>talk</strong>을 날렸을 때, 상대편 대응 방식에 따라 어떻게 톡 프로그램을 코딩해야 하는 지 알아보자.</p>
<p>즉 대꾸하는 분과 대꾸없는 놈, 두 케이스에 대해서...</p>
<p>pub/sub 통신에서는 보내는 사람과 받는 사람, 즉 종단간의 연결 보다는 중간에 있는 <strong>broker</strong> 와의 대화에 집중한다.
broker와의 통화 품질을 <strong>QoS</strong>라고 하는데, 여기에는 다음 3가지가 있다.</p>
<h2 id="qos">QoS</h2>
<table>
<thead>
<tr>
<th>QoS</th>
<th>어려운 말</th>
<th>쉬운 말</th>
<th>ㅈㄹ 쉬운 말</th>
</tr>
</thead>
<tbody><tr>
<td>0</td>
<td>At most once</td>
<td>Fire and Forget</td>
<td>쏘고 잊으세요</td>
</tr>
<tr>
<td>1</td>
<td>At least once</td>
<td>Acknowledged delivery</td>
<td>최소 한 번은 보내라</td>
</tr>
<tr>
<td>2</td>
<td>Exactly once</td>
<td>Assured delivery</td>
<td>딱 한 번만 보내라</td>
</tr>
<tr>
<td><small>Ref: <a href="https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718099">MQTT Spec v3.1.1 4.3 Quality of Service levels and protocol flows</a></small></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<p>이건 client인 나와 broker인 너와의 관계다. 
pub하는 client와 sub하는 server와 관계는 제 3자인 broker가 끼여드는데, 이것도 여기에 기초한다.</p>
<p>다음은 QoS 별 응답 프로토콜이다. 자세한 내용은 참고를 클릭하면 되는데....
슬프게도 영어다😭😭😭</p>
<table>
<thead>
<tr>
<th>QoS</th>
<th>응답</th>
<th>참고</th>
</tr>
</thead>
<tbody><tr>
<td>0</td>
<td>없음</td>
<td></td>
</tr>
<tr>
<td>1</td>
<td>PUBACK</td>
<td><a href="https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718043">MQTT Spec v3.1.1 3.4 PUBACK – Publish acknowledgement</a></td>
</tr>
<tr>
<td>2</td>
<td>PUBREC</td>
<td><a href="https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718048">MQTT Spec v3.1.1 3.5 PUBREC – Publish received</a></td>
</tr>
<tr>
<td></td>
<td>PUBREL</td>
<td><a href="https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718053">MQTT Spec v3.1.1 3.6 PUBREL – Publish release</a></td>
</tr>
<tr>
<td></td>
<td>PUBCOMP</td>
<td><a href="https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718058">MQTT Spec v3.1.1 3.7 PUBCOMP – Publish complete </a></td>
</tr>
</tbody></table>
<p><del>지면도 남고 하니</del> 하나씩 알아보자.</p>
<h3 id="qos-level-0">Qos Level 0</h3>
<blockquote>
<p>쏘고 잊으세요.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/5b536241-c338-4d08-888d-d6aebe951ecd/image.PNG" alt=""></p>
<p>이거 딱 그거다. 월요일 아침에 하는 <strong>교장 선생님 훈화</strong>
<del>연식 들통나는 건가 😜</del>
자기 말만 하고 땡이다</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/ca158b9e-2451-497e-bf1f-f1de56005173/image.PNG" alt=""></p>
<p><small><center><a href="https://takethenotes.com/mqtt-qos/">출처: The Importance of MQTT QoS: A Comprehensive Guide (Quality of Service)</a></center></small></p>
<p>그림 보면 알겠지만, <strong>쏘기만 한다.</strong>
Broker도 마찬가지다. 
Subscriber한테  <strong>쏘기만 한다.</strong></p>
<p>요 걸 3자 관계로 그려보면 다음과 같다.
<img src="https://miro.medium.com/v2/resize:fit:720/format:webp/0*1NSybQWdhHvVhnTb.png" alt=""></p>
<p><small><center><a href="https://emqx.medium.com/introduction-to-mqtt-5-0-protocol-qos-quality-of-service-e6d9b0aaf9fb">출처: Introduction to MQTT QoS (Quality of Service)</a></center></small></p>
<p>Publisher는 Broker한테 쏘고 Broker도 Subscriber한테 쏘기만 할 뿐 받았는지 관심이 없다.
말 그대로 <strong>쏘고 잊는다.</strong>
메세지 저장 이딴 것 없다.</p>
<p>따라서 ㅈㄹ 빠르다.</p>
<p>소스로는 _{ qos: 0} _ 만 추가하면 된다.</p>
<pre><code class="language-javascript">const mqtt = require(&quot;mqtt&quot;);
const client = mqtt.connect(&quot;mqtt://localhost&quot;);

const user = { id: &#39;client1&#39;, pwd: &#39;pwd&#39; };

client.on(&quot;connect&quot;, () =&gt; {
    client.subscribe(&quot;clients/client1&quot;);
    client.publish(&quot;server/payload&quot;, &#39;hi&#39;, { qos: 0 }, (err, res) =&gt; {
        if (err) console.log(&#39;err&#39;, err);
        else console.dir(res);

        client.end();
    });
});</code></pre>
<p>요걸 실행해보면 </p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/31e3ca02-10fb-4789-ac64-607d7e92db1e/image.PNG" alt="">
<strong>callback</strong> 함수 <strong>res</strong> 가 <strong>undefined</strong>다.</p>
<p>이 딴 걸 어디에다 쓰냐고?
다 필요하니까 만들어 둔 것이다.
온,습도 센서가 온,습도를 pub한다고 했을 때, 받는 쪽 입장에서는 최신 데이터가 젤 중요하다.
이럴 땐 QoS 0 이 딱이다.
중간 것 몇 개 잃어 버려도 개안타~</p>
<h3 id="qos-level-1">Qos Level 1</h3>
<blockquote>
<p>최소 한 번은 보내라.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/be9e6764-f0dd-4da5-a94c-f18e42650339/image.PNG" alt=""></p>
<p>이건 매번 확인하는 거다. 내 말 들었는지.
&#39;응&#39;, &#39;응&#39; 하는 맞장구가 없으면 한 말 또 하는거다.</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/cf24cf53-00e5-4ea9-a372-9c717e381c89/image.PNG" alt=""></p>
<p><small><center><a href="https://takethenotes.com/mqtt-qos/">출처:  The Importance of MQTT QoS: A Comprehensive Guide (Quality of Service)</a></center></small></p>
<p><img src="https://miro.medium.com/v2/resize:fit:720/format:webp/0*b4pvXUWp6MVnAAFy.png" alt=""></p>
<p><small><center><a href="https://emqx.medium.com/introduction-to-mqtt-5-0-protocol-qos-quality-of-service-e6d9b0aaf9fb">출처: Introduction to MQTT QoS (Quality of Service)</a></center></small></p>
<p>Publisher는 Broker한테 쏘고 Broker는 Subscriber한테 쏘고 PUBACK을 돌려준다.
PUBACK을 실패하면 Broker는 저장해두었던 걸 한 번 더 보낼 수 있다.</p>
<p>QoS 0 보다는 아니지만, 나름 빠른데, 1번 이상 Subscriber가 받을 수 있다.</p>
<p>QoS 만 0에서 1로 바꾼 소스다.</p>
<pre><code class="language-javascript">client.on(&quot;connect&quot;, () =&gt; {
    client.subscribe(&quot;clients/client1&quot;);
    client.publish(&quot;server/payload&quot;, &#39;hi&#39;, { qos: 1 }, (err, res) =&gt; {
        if (err) console.log(&#39;err&#39;, err);
        else console.dir(res);

        client.end();
    });
});</code></pre>
<p>요걸 실행해보면 </p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/7c35d4de-eb10-4b61-9f3b-6aea9230f664/image.PNG" alt=""></p>
<p>res.cmd 에 &#39;publish&#39;, 그리고 <strong>broker가</strong> 따 준 <strong>messageId</strong>가 들어왔다.</p>
<p>중복 문제만 없다면 요거이 딱인데...😅</p>
<h3 id="qos-level-2">QoS Level 2</h3>
<blockquote>
<p>딱 한 번만 보내라.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/3331bbd7-2a24-4285-af6f-a2fcdeb58156/image.PNG" alt="">
요건 빼도 박도 못하게 도장 꽉꽉 찍어두는거다.</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/478d1bdd-44a5-42d8-af90-35d1c7c4ba12/image.PNG" alt=""></p>
<p><small><center><a href="https://takethenotes.com/mqtt-qos/">출처:  The Importance of MQTT QoS: A Comprehensive Guide (Quality of Service)</a></center></small></p>
<p><img src="https://miro.medium.com/v2/resize:fit:720/format:webp/0*G6zwc_VftJ4AcueS.png" alt=""></p>
<p><small><center><a href="https://emqx.medium.com/introduction-to-mqtt-5-0-protocol-qos-quality-of-service-e6d9b0aaf9fb">출처: Introduction to MQTT QoS (Quality of Service)</a></center></small></p>
<p>딱 한번만 보내고/받기 위해서는 Broker가 중간에서 보낸 놈과 받는 놈을 잡고 서로 주고 받은 걸 확인 시켜주는 작업을 한다.
그러다 보니 확인하는 프로토콜(PUBREC,PUBREL,PUBCOMP)이 많고, 당연히 통신도 느려진다. 
Broker도 할 일이 많다보니 부하도 많이 걸리고, 그림 보면 알겠지만 Publisher, Subscriber 도 뭔가 복잡하고 바쁘다.</p>
<p>Qos 를 2로 바꾼 소스다.</p>
<pre><code class="language-javascript">client.on(&quot;connect&quot;, () =&gt; {
    client.subscribe(&quot;clients/client1&quot;);
    client.publish(&quot;server/payload&quot;, &#39;hi&#39;, { qos: 2 }, (err, res) =&gt; {
        if (err) console.log(&#39;err&#39;, err);
        else console.dir(res);

        client.end();
    });
});</code></pre>
<p>이를 실행하면</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/e5427dd9-4b4e-4c1b-b697-01d18d496d3f/image.PNG" alt=""></p>
<p>res.cmd 에 &#39;pubrel&#39;, 그리고 <strong>broker가</strong> 따 준 <strong>messageId</strong>가 들어왔다.</p>
<p>요거이 우리가 원하는 바이긴 한데, 느리다는 단점이 있다.</p>
<p>QoS 2와 같이 중복없이 가는데, 빠른 방법은 없을까?</p>
<h2 id="qos--1과-중복-방지">QoS = 1과 중복 방지</h2>
<p>받았다는 걸 확인할 수 있는 PUBACK은 받지만, 중복을 피하고 싶다.
즉 QoS = 2의 효과를 원하지만, 속도가 느린 건 원치 않는다. 이건데...</p>
<p>세상에 공짜가 있나
Broker 부하를 줄이려면 누군가 그 부하를 져야할텐데</p>
<p>누가 그 부담을 질 것인가? </p>
<blockquote>
<p>Database &lt;- 얘 밖에 없다.</p>
</blockquote>
<p>어떻게?</p>
<blockquote>
<p>Insert 대신 Upsert로</p>
</blockquote>
<p>쫑날 수도 있으니, Upsert로 ...
Upsert하려면 PK가 있어야 하는데.</p>
<p>아 그러면 client에서 <strong>talk</strong>을 쏠 때 unique한 talk_id를 만들어서 쏘면 되지 않을까?</p>
<p>talk_id를 unique하게 만드는 방법은 <strong>Date.now()</strong> 를 쓰던 <strong>UUID</strong>를 쓰던 방법은 많을 것이다.
그래도 쫑이 걱정된다면 prefix를 줘서 나미 따듯 쫑 안 나게 비켜가게 해야지.
이는 각자 개발 짬에 따라 코딩하시라.</p>
<h3 id="clientjs-수정">client.js 수정</h3>
<pre><code class="language-javascript">const { v4: uuidv4 } = require(&#39;uuid&#39;);

...

rl.on(&#39;line&#39;, function (line) {
    if (line === &#39;exit&#39;) rl.close();

    const talk = { data: { talk_id: uuidv4(), from: user_id, room: &#39;room1&#39;, msg: line } };
    client.publish(&quot;server/talk&quot;, JSON.stringify(talk));

    rl.prompt()
});</code></pre>
<h3 id="serverjs-수정">server.js 수정</h3>
<pre><code class="language-javascript">    mysql_conn.sql(UPSERT_TALK, [json.data.talk_id, json.data.from, json.data.room, json.data.msg, json.data.talk_id], function(err, results, fields) {
      room.forEach(client =&gt; {
          if (talk.data.from === client) return false;
          let client_topic = &#39;clients/&#39; + client + &#39;/talk&#39;;
          server.publish(client_topic, message);
      });
    }</code></pre>
<p>이건 번외지만 SQL 이 다음과 같이 바뀔 것이다.</p>
<p><strong>Before</strong></p>
<pre><code class="language-javascript">INSERT INTO talk_table (from, room, msg)
VALUES (#{from}, #{room}, #{msg});</code></pre>
<p><strong>After</strong></p>
<pre><code class="language-javascript">INSERT INTO talk_table (id, from, room, msg)
VALUES (#{talk_id}, #{from}, #{room}, #{msg}) ON DUPLICATE KEY
UPDATE id = #{talk_id};</code></pre>
<hr>
<small><strong>PUBACK</strong>: pub back 이 아니라 pub ack 이다. 개인적으론 pub back도 좋다고 생각한다. callback 처럼.</small>]]></description>
        </item>
        <item>
            <title><![CDATA[MQTT로 카톡서버 만들어보자 - 3/10. 사용자 상태]]></title>
            <link>https://velog.io/@mqtt_expert/kakaotalk3</link>
            <guid>https://velog.io/@mqtt_expert/kakaotalk3</guid>
            <pubDate>Mon, 25 Mar 2024 04:01:51 GMT</pubDate>
            <description><![CDATA[<h2 id="내-죽음을-서버에게-알려라">내 죽음을 서버에게 알려라~</h2>
<p>장군께서는</p>
<blockquote>
<p>나의 죽음을 적에게 알리지마라</p>
</blockquote>
<p>라고 하셨지만, client는 자신의 죽음을 server 에게 알려야 한다.
이것을 <strong>LWT</strong><small>(Last will and testament)</small> 라고 하며 고급지게 <strong>유언</strong> 이라 한다.
우리는 죽을 때 <strong>말(言)</strong>을 남기는데, 서양은 <strong>윌(Will)</strong>을 남기나 보다.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/aa6a12a8-c116-4d5b-b64b-ef64ea697817/image.jpg" alt=""></p>
<h3 id="죽었니-살았니">죽었니? 살았니?</h3>
<p>장군의 죽음을 왜군이 알아선 안되지만,
<strong>server</strong>는 <strong>client</strong> 상태를 알아야 한다.
왜?
<strong>server</strong>가 <strong>talk</strong>을 보낼 때(전파할 때) 이 톡을 받는 주체는 <strong>client</strong>의 <strong>mqtt_connect</strong>이다.
<strong>client</strong>의 <strong>mqtt_connect</strong>이 살아 있는지, 아니면 죽었는지에 따라 <strong>pub</strong>할지 <strong>push</strong>할지 결정하기 때문이다.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/1a0c025b-ff0d-49c9-afd8-944a02849c1a/image.jpg" alt=""></p>
<p><strong>client</strong>의 <strong>mqtt_connect</strong>가 죽었는지, 살았는지 알려면 어떡해해야할까?
<strong>will</strong>이 그 답이다.
<strong>client</strong>의 <strong>mqtt_connect</strong> 가 죽으면 <strong>boker</strong>가 <strong>client</strong>의 <strong>will</strong>을 보낼 것이다.
<strong>server</strong>는 이 <strong>will</strong>을 수신하고 있다가, 어느 <strong>client</strong>가 죽었는지? 살았는지 판단하면 된다.</p>
<table>
<thead>
<tr>
<th>상태</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>active</td>
<td>😁 앱에서 <strong>mqtt_connect</strong> 가 살아 있을 때</td>
</tr>
<tr>
<td>inactive</td>
<td>😅 <strong>mqtt_connect</strong> 가 죽었을 때</td>
</tr>
<tr>
<td>uninstall</td>
<td>😰 슬프게도 앱을 삭제했을 때</td>
</tr>
</tbody></table>
<p><strong>mqtt_connect</strong> 생사 여부와 별개로 앱에서 앱 <strong>Noti</strong> 띄우는 건 또 다른 로직이다.
<strong>client</strong> 앱에서는 <strong>mqtt_connect</strong> 가 살아 있고, <strong>foreground</strong> 상태이며, 현재 message와 동일 <strong>room</strong> 에 있다면 앱 <strong>Noti</strong>에 등록하지 않고 톡방에만 쏘면 된다.
그 이외에는 <strong>Noti</strong>에 띄워야 한다. 
앱 개발자라면 이 말을 이해할 것이다. </p>
<table>
<thead>
<tr>
<th>상태</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>App.state == Foreground &amp;&amp; room == message.room</td>
<td>room 화면에 talk 내용을 쓰면 된다.</td>
</tr>
<tr>
<td>App.state == Foreground &amp;&amp; room != message.room</td>
<td>Noti에 등록</td>
</tr>
<tr>
<td>App.state == Background || App을 실행하지 않은 경우</td>
<td>Noti에 등록</td>
</tr>
</tbody></table>
<h3 id="어떻게-나의-죽음을-알릴-것인가">어떻게 나의 죽음을 알릴 것인가?</h3>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/1c973ae7-27ff-46cc-abbd-6bbdf1f876e2/image.jpg" alt=""></p>
<p><strong>lwt</strong>의 기본 개념은 이렇다. 
죽기 전 <strong>lwt</strong>(유언)을 써 놓고, 내가 죽으면 내 죽음에 관심있는 자<small>(여기선 <strong>server</strong>겠지)</small>에게 뿌리라고 borker에게 부탁하는거다.
즉 <strong>client</strong>가 mqtt 연결할 때 <strong>lwt</strong>를 함께 등록하는 거다.
nodejs mqtt에서는 <a href="https://github.com/mqttjs/MQTT.js?tab=readme-ov-file#mqttclientstreambuilder-options">다음</a>과 같다.</p>
<h3 id="클라이언트에-추가되는-코드">클라이언트에 추가되는 코드</h3>
<p><strong>client</strong>에 추가하는 코드는 단순한다. 접속할 때 나의 <strong>will</strong>을 등록하면 된다.
요렇게 해 놓으면 connection이 끊어지면 <strong>broker</strong><small>(브로커라기보단 변호사네😏)</small> 가 나의 죽음에 관심있는(<strong>sub</strong>하고 있는) 이들한테 이를 뿌려준다.</p>
<pre><code class="language-javascript">const user_id = process.argv[2]; //&#39;client1&#39;;

const client = mqtt.connect({
    host: &#39;localhost&#39;,
    will: {
        topic: &#39;server/die&#39;,
        payload: JSON.stringify({ data: { id: user_id } })
    }
});</code></pre>
<p>내 죽음에 관심 있는 자들이 여럿 있겠지만 😂, <strong>server</strong>가 가장 관심이 있을 것이다.
왜냐하면 이를 DB에 저장하고 <strong>pub</strong>할지 <strong>push</strong>할지 결정해야 하기 때문이다.
따라서 서버 토픽인 <strong>server/die</strong>로 이름 짓자.</p>
<h3 id="서버에-추가되는-코드">서버에 추가되는 코드</h3>
<p>이제 서버가 <strong>server/die</strong> topic도 <strong>sub</strong>하도록 추가하면 된다.</p>
<pre><code class="language-javascript">server.on(&quot;connect&quot;, () =&gt; {
    server.subscribe([&quot;server/login&quot;, &quot;server/talk&quot;, &quot;server/die&quot;]);
});</code></pre>
<p>그 다음 <strong>server/die</strong>가 들어왔을 때 처리하는 함수만 짜 넣으면 끝.</p>
<pre><code class="language-javascript">server.on(&quot;message&quot;, (topic, message) =&gt; {
    console.log(&#39;received: &#39;, topic, JSON.parse(message));
    if (topic.endsWith(&#39;/login&#39;)) process_login(message);
    else if (topic.endsWith(&#39;/talk&#39;)) process_talk(message);
    else if (topic.endsWith(&#39;/die&#39;)) process_die(message);
});

...

function process_die(message) {
    let json = JSON.parse(message);
    mysql_conn.sql(UPDATE_USER_STATE, [json.data.id,&#39;inactive&#39;], function(err, results, fields) {
    ...
    }
}
</code></pre>
<p>서버가 있어 너무 행복😁하지 아니한가?</p>
<h3 id="다른-상태는">다른 상태는?</h3>
<p><strong>active</strong>는 따로 로직을 뺄 필요도 없다.
<strong>active</strong>는 로그인 시 active로 DB에 Update하도록 추가하면 된다.
<strong>uninstall</strong>은 앱 삭제 이벤트 받아 Update하면 된다.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[MQTT로 카톡서버 만들어보자 - 2/10. 톡을 날려보자]]></title>
            <link>https://velog.io/@mqtt_expert/kakaotalk2</link>
            <guid>https://velog.io/@mqtt_expert/kakaotalk2</guid>
            <pubDate>Tue, 19 Mar 2024 03:32:37 GMT</pubDate>
            <description><![CDATA[<h2 id="썰-풀기-전-먼저-딱-2개만-외우자">썰 풀기 전, 먼저 딱 2개만 외우자.</h2>
<ul>
<li><strong>talk</strong>은 <strong>pub</strong>이다.</li>
<li>읽고 쓰기는 <strong>DB</strong>다.</li>
</ul>
<blockquote>
<p>엄마는 여자고, 아빠는 남자다 </p>
</blockquote>
<p>만큼 쉽지 아니한가😁😁😁</p>
<h2 id="talk-talk-talk">Talk Talk Talk</h2>
<p>자,자, 이제 톡을 날려보자.
사실 톡하는 건, socket.io와 별 반 다를 게 없다.</p>
<ul>
<li>client가 <strong>server/login</strong>으로 로그인하고, <strong>server/talk</strong>으로 <strong>talk</strong><small>json</small>을 날린다.</li>
<li>이 <strong>talk</strong><small>json</small> 안에는 어떤 <strong>room</strong> 에다가 어떤 <strong>내용</strong>으로 날렸는지 다 들어 있다.</li>
<li>server는 <strong>talk</strong>을 받으면, 받아볼 다른 client들한테 차례차례 전달한다.</li>
</ul>
<blockquote>
<p>여기서 핵심은 이 모든 것을 <strong>server</strong> 를 통해서 한다는 것이다.
<strong>broker</strong>로 이를 구현하려면 개발 인생😵 말린다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/d1695b16-a532-4606-987b-7447df799ae1/image.PNG" alt=""></p>
<h3 id="먼저-뭘-짤건지-설명해보자">먼저 뭘 짤건지 설명해보자.</h3>
<p>그래야 코딩 이해가 쉽다.</p>
<p>다음은 client1 과 client2 가 room1 에서 대화하는 내용이다.</p>
<table>
<thead>
<tr>
<th>client</th>
<th>room1</th>
</tr>
</thead>
<tbody><tr>
<td>client1</td>
<td>안녕하세요 저는 client1입니다.</td>
</tr>
<tr>
<td>client2</td>
<td>아, 네...안녕하세요 저는 cleint2 예요....</td>
</tr>
</tbody></table>
<h4 id="client1-실행-캡쳐">client1 실행 캡쳐</h4>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/038937db-4c62-4f4f-a3b1-6b37ad80f601/image.PNG" alt="">
client1 이란 이름은 아래처럼 command line 변수로 받는다.</p>
<pre><code>$&gt; node client.js client1</code></pre><p>2번째 라인이 자기가 날린 톡이고,
3번째 라인이 client2로 부터 받은 톡이다.</p>
<h4 id="client2-실행-캡쳐">client2 실행 캡쳐</h4>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/c16bf965-9134-499f-8ae6-84334d696491/image.PNG" alt=""></p>
<pre><code class="language-javascript">$&gt; node client.js client2</code></pre>
<p>이건 설명 안해도 이해가 되죠잉😏😏😏</p>
<h4 id="server-실행-캡쳐">server 실행 캡쳐</h4>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/d9bec2f7-4f3e-4bda-9e32-60b83e6d2bb8/image.PNG" alt=""></p>
<table>
<thead>
<tr>
<th>received</th>
<th>message</th>
</tr>
</thead>
<tbody><tr>
<td>1번째 received</td>
<td>client1 로그인</td>
</tr>
<tr>
<td>2번째 received</td>
<td>client2 로그인</td>
</tr>
<tr>
<td>3번째 received</td>
<td>client1 톡</td>
</tr>
<tr>
<td>4번째 received</td>
<td>client2 톡</td>
</tr>
</tbody></table>
<p>대충 돌아가는 걸 봤으니, 코드로 이를 확인해보자.
코드부터 들이 밀면 울렁증 느끼더라.</p>
<p>토발즈 형님도 말씀하셨다. 이빨 그만 좀 털어라~
<img src="https://velog.velcdn.com/images/mqtt_expert/post/f86c86df-5d98-4d07-8fa8-0df49d4ab725/image.jpg" alt=""></p>
<h3 id="서버님에게-추가된-code">서버님에게 추가된 Code</h3>
<p><strong>server/login</strong>로 로그인 요청 들어오면 <strong>process_login</strong> function 불러주고,
<strong>server/talk</strong>로 톡 요청 들어오면 <strong>process_talk</strong> function 호출하면 끝.</p>
<blockquote>
<p>야호🚀 프로그램 개쉽네.</p>
</blockquote>
<h4 id="serverjs">server.js</h4>
<pre><code class="language-javascript">const mqtt = require(&quot;mqtt&quot;);
const server = mqtt.connect(&quot;mqtt://localhost&quot;);
const rooms = { &#39;room1&#39;: [&#39;client1&#39;, &#39;client2&#39;, &#39;client3&#39;] };

server.on(&quot;connect&quot;, () =&gt; {
    server.subscribe([&quot;server/login&quot;, &quot;server/talk&quot;]);
});

// login인지, talk인지 구분
server.on(&quot;message&quot;, (topic, message) =&gt; {
    console.log(&#39;received: &#39;, topic, JSON.parse(message));
    if (topic.endsWith(&#39;/login&#39;)) process_login(message);
    else if (topic.endsWith(&#39;/talk&#39;)) process_talk(message);
});

// login 처리
function process_login(message) {
    let json = JSON.parse(message);
    let client_topic = &#39;clients/&#39; + json.data.id + &#39;/login&#39;;
    if (json.data.pwd === &#39;pwd&#39;) {
        let res = { data: { msg: &#39;login ok&#39; } };
        server.publish(client_topic, JSON.stringify(res));
    } else {
        let res = { error: { code: 404, msg: &#39;pwd mismatch&#39; } };
        server.publish(client_topic, JSON.stringify(res));
    }
}

// talk 처리
function process_talk(message) {
    let talk = JSON.parse(message);
    let room = rooms[talk.data.room];
    room.forEach(client =&gt; {
        if (talk.data.from === client) return false;
        let client_topic = &#39;clients/&#39; + client + &#39;/talk&#39;;
        server.publish(client_topic, message);
    });
}</code></pre>
<blockquote>
<p>앗! 잠깐, velog 운영자님 <a href="https://docs.github.com/ko/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting">코드블럭 문법 강조 기능</a> 좀 넣어줘요. 
Code Reading 힘들어요. 쓰는 나도 보기 힘든데😥😥😥</p>
</blockquote>
<p>요로코롬 나오게...
<img src="https://velog.velcdn.com/images/mqtt_expert/post/19c90c5e-4926-4cd2-bc28-5486a90bf57d/image.PNG" alt=""></p>
<h3 id="클라이언트에-코드-추가">클라이언트에 코드 추가</h3>
<p>유튜버 생각해보자.
자신만의 채널이 있다.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/47ae1615-9879-4822-8450-8ff9db487b74/image.PNG" alt=""></p>
<p>클라이언트도 자신만의 채널이 있어야 한다.
서버와 비밀스럽게 통신할 채널.</p>
<p>자 내 아이디는 <strong>client1</strong> 이라 해보자.
그러면 내 채널은 <strong>clients/client1/#</strong> 이 되는 거다.
이렇게 해야 <strong>client2</strong>가 <strong>client1</strong>의 내용을 볼 수 없지 않겠는가.</p>
<p>물론 실 서비스에서 <strong>cleint1</strong> 같이 쉽게 추측 가능한 이름 쓰면 안된다.
<a href="https://www.npmjs.com/package/uuid">UUID</a> 이런 거 써야 한다.
프로젝트에서 그냥 <strong>client1</strong> 이렇게 따라하면 폭망💣이다.</p>
<p>하는 일은 서버랑 똑 같다.</p>
<p><strong>clients/client/login</strong>로 로그인 응답이 들어오면 <strong>process_login</strong> function 불러주고,
<strong>clients/client/talk</strong>로 톡 응답이 들어오면 <strong>process_talk</strong> function 호출하면 끝이다.</p>
<p>다만 마치 톡 앱처럼 보이려고<small>(고민 많이했다)</small> 사용자 입력을 받는 <a href="https://nodejs.org/api/readline.html">readline</a> 추가했다.</p>
<h4 id="clientjs">client.js</h4>
<pre><code class="language-javascript">const mqtt = require(&quot;mqtt&quot;);
const client = mqtt.connect(&quot;mqtt://localhost&quot;);
const readline = require(&quot;readline&quot;);

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    prompt: &#39;&#39;,
});

const user_id = process.argv[2]; //&#39;client1&#39;;
const user = { data: { id: user_id, pwd: &#39;pwd&#39; } };

client.on(&quot;connect&quot;, () =&gt; {
    client.subscribe([&#39;clients/&#39; + user_id + &#39;/login&#39;, &#39;clients/&#39; + user_id + &#39;/talk&#39;]);
    client.publish(&quot;server/login&quot;, JSON.stringify(user));
});

// login인지, talk인지 구분
client.on(&quot;message&quot;, (topic, message) =&gt; {
    if (topic.endsWith(&#39;/login&#39;)) process_login(message);
    else if (topic.endsWith(&#39;/talk&#39;)) process_talk(message);
});

// login 처리
function process_login(message) {
    let res = JSON.parse(message);
    if (!!res.data) rl.prompt();
    else if (!!res.error) client.end();
}

// talk 처리
function process_talk(message) {
    let talk = JSON.parse(message);
    console.log(`${talk.data.from} : ${talk.data.msg}.`);
}

// 화면에서 메세지 받아 톡을 보낸다.
rl.on(&#39;line&#39;, function (line) {
    if (line === &#39;exit&#39;) rl.close();

    const talk = { data: { from: user_id, room: &#39;room1&#39;, msg: line } };
    client.publish(&quot;server/talk&quot;, JSON.stringify(talk));

    rl.prompt()
});

// 화면에서 &#39;exit&#39; 라고 입력하면 끝낸다.
rl.on(&#39;close&#39;, function () {
    if (!!client) client.end();
    process.exit();
});</code></pre>
<h3 id="login-기록📋을-남겨라">login 기록📋을 남겨라</h3>
<p>분명 첨에 쌍둥이 아기들이 말했다. 2개는 외우라고.
읽고, 쓰기는 <strong>DB</strong>라고.</p>
<p>기록을 남기고, 사용자 정보를 읽어오고, room 에는 누가 누가 있는지 알려면 <strong>DB</strong>를 함 갔다 와야 한다.
<strong>DB</strong>를 갔다오는 건 당연히 <strong>server</strong>가 할일이다.
그리고 <strong>DB</strong>와 뭔 일을 하려면, <strong>callback</strong> 으로 짜던지 <strong>async</strong> 로 코딩해야 한다는 건 코린이들도 다 아는 사실.
대충 아래처럼 해주면 되겠다.</p>
<h4 id="serverjs의-process_login-일부">server.js의 process_login 일부</h4>
<pre><code class="language-javascript">    if (json.data.pwd === &#39;pwd&#39;) {
        mysql_conn.sql(INSERT_USER_LOGIN, [json.data.id], function(err, results, fields) {
          let res = { data: { msg: &#39;login ok&#39; } };
          server.publish(client_topic, JSON.stringify(res));
        }
    } else {</code></pre>
<h3 id="talk-기록📋도-남겨라">talk 기록📋도 남겨라</h3>
<p><strong>talk</strong> 기록도 마찬가지다.
<strong>DB</strong>에 저장하는 코드를 넣으면 된다.
<strong>room</strong>에 누가 있는지, <strong>DB</strong>에 노크해서 문 열고 보면 된다.
그리고 한 명씩 차례로 톡을 보내면 된다.
괜히 짬도 안되면서 남의 <strong>room</strong> 열었다가 기도한테 개맞는 수도 있다.
누구는 이러겠지. 
<strong>rooms/room1</strong> 처럼 만들어서 거기다 쏘면 안되냐구...
된다...</p>
<blockquote>
<p>Just Do it 🚀🚀🚀</p>
</blockquote>
<p>방이 없는 상태에서 개톡 날릴 때, 방 이름 생성하기 위해 <strong>pub</strong> 하고, 클라이언트는 그거 <strong>sub</strong> 하는 거 해봤다.
쉽지 않더라😂
그래서 일관성 있게 전부 이렇게 했다.</p>
<p>방 이름도 <strong>room1</strong> 이렇게 하면 client 이름처럼 폭망💣한다.</p>
<blockquote>
<p>어쨌든 여기서 핵심은 <strong>room</strong> 이 아니라 <strong>room</strong> 안에 있는 <strong>client</strong>에게 직접 <strong>talk</strong>을 날리는 거다.</p>
</blockquote>
<p>요걸 코드로 짜보면 다음과 같다.</p>
<h4 id="serverjs의-process_talk-일부">server.js의 process_talk 일부</h4>
<pre><code class="language-javascript">    mysql_conn.sql(INSERT_TALK, [json.data.from, json.data.room, json.data.msg], function(err, results, fields) {
      room.forEach(client =&gt; {
          if (talk.data.from === client) return false;
          let client_topic = &#39;clients/&#39; + client + &#39;/talk&#39;;
          server.publish(client_topic, message);
      });
    }
</code></pre>
<p>보낸 놈한테는 아래처럼 안 보내는 게 낫다.</p>
<pre><code class="language-javascript">if (talk.data.from === client) return false;</code></pre>
<p>client가 <strong>talk</strong>을 <strong>pub</strong> 하고 <strong>puback</strong>이 안오면 아래처럼 <strong>안갔어요! 다시보낼깝쇼?</strong> 빨간 거 달아주면 되겠다.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/30588bf8-8644-4a92-875c-e971b2a891d5/image.jpg" alt=""></p>
<h3 id="talk-받을-놈이-어디-갔다면">talk 받을 놈이 어디 갔다면?</h3>
<p>요게 요게 문제다.
핸드폰을 켜 놓긴 했는데, 뒷주머니🚶나 가방👜에 둔 상태라던지,
톡은 안 보고 틱톡 보고 있다던지,
백번 양보해서, 우리 앱을 보고는 있는데, 다른 방에서 열심히 톡하고 있을 때 말이다.
보통 이럴 때 <strong>app push</strong> 날린다.</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/c7f2710a-c48f-4021-8012-2d819a165f6d/image.jpg" alt=""></p>
<p><strong>push server</strong> 구축 이런거, 여러분 실력이라면 껌인줄 안다.
하지만 <strong>네카라쿠배</strong> 다니는 분들한테 양보하고 <strong>Android</strong>는 그냥 <strong>FCM</strong> 쓰는 걸로 하자.</p>
<p><strong>FCM</strong> 보내려면 FCM <strong>token</strong>이 필요하다.
앱 개발자님에게 음료수 사주면서 조신하게 물어보면 알려줄끼다.
안 알려주면 <a href="https://firebase.google.com/docs/cloud-messaging/concept-options?hl=ko">FCM Message 보내는 법</a>을 참고하라.</p>
<h3 id="정리">정리</h3>
<p><strong>정리</strong> 이런거 해줘야 뭔가 수미일관되게 글을 썼다 이런 삘😍 받는다.
첨에 말했다.
<strong>talk</strong> 하고, <strong>읽고 쓰기</strong>... 딱 2개 해봤다.</p>
<hr>
<small>- 날리면, 날리다 : <strong>바이든</strong>하고는 아무 상관이 없다.</small>]]></description>
        </item>
        <item>
            <title><![CDATA[MQTT로 카톡서버 만들어보자 - 1/10. Pub/Sub으로 로그인?]]></title>
            <link>https://velog.io/@mqtt_expert/kakaotalk1</link>
            <guid>https://velog.io/@mqtt_expert/kakaotalk1</guid>
            <pubDate>Fri, 15 Mar 2024 11:23:58 GMT</pubDate>
            <description><![CDATA[<h2 id="pubsub으로-로그인-만들라굽쇼">Pub/Sub으로 로그인 만들라굽쇼</h2>
<p>웹서비스 로그인은 전형적인 방식이 있다.
POST로 </p>
<pre><code class="language-javascript">{
  id:&quot;hong_gil_dong&quot;,
  pw:&quot;password1234&quot;,
  ...
}</code></pre>
<p>을 날리면 서버가 DB에서 비번 맞춰보고 맞으면 로그인 아니면 <strong>우리집에 왜 왔니?</strong> 하면 된다.</p>
<blockquote>
<p>그런데 이건 웹 프로그램 짤 때고...
Pub/Sub Broker 세상에서는 어떻게 하지?😒😒</p>
</blockquote>
<h3 id="broker-세상은-그렇게-돌아가는게-아녀">Broker 세상은 그렇게 돌아가는게 아녀</h3>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/1153b21a-f3eb-4074-b3a0-6a73e88d4a64/image.png" alt=""></p>
<p>다 아는 얘기 한번 해보자.
Broker 방식은 복덕방하고 똑같다.
방구하러 온 사람이 떡방 할배에게 말한다.</p>
<blockquote>
<p>여기 현대빌라 203혼데요, 저희 이번에 나가서 집 내놔요. 방 3개고 재작년에 지은 신축이예요.
전번은 010-1234-5678이고, 제가 일을 해서 집은 저녁 7시 이후 보여줄 수 있어요.</p>
</blockquote>
<p>집 내 놓는다는 말이다. 
유식하게 이를 <strong>pub</strong> 이라 한다.</p>
<blockquote>
<p>2인 가족이 살건데요.
채광 좋고, 단독 전세 나오면 연락해주세요.</p>
</blockquote>
<p>집 구하니, 연락 달라는 말이다.
유식하게 <strong>sub</strong>이라 한다.</p>
<p>떡방 할배가 장부 뒤져 그런 집이 있으면 이분한테 연락한다.
즉 <strong>pub</strong> 날리는거다.</p>
<blockquote>
<p>어,  할배가 <strong>sub</strong>도 받고, <strong>pub</strong>도 날리고 혼자 다하네.☎☎</p>
</blockquote>
<p>이제 <strong>pub/sub</strong>이 방향의 문제임을 눈치챘을 것이다.
<strong>sub</strong>한 놈한테 <strong>pub</strong>하는거다.</p>
<p><strong>pub</strong> 받을려면 <strong>sub</strong>해 놔야한다.
같은 말이다.😁😁</p>
<p>자 이제 로그인 문제로 돌아와보자.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/5e718d16-8973-4d6b-a63c-bbc93aecf694/image.png" alt="">
위 그림은 login 요청하고, login 응답하는거다.</p>
<p>Broker는 둘만 있는 세상이 아니다. 
Broker 한테 로그인 처리 해달라고 하면 화낸다.
웹서버 방식이 아니라, 쉽지 않다.</p>
<blockquote>
<p>오똑케하면 이 문제를 해결할 것인가?</p>
</blockquote>
<p>물론 Broker 자체에 로그인 기능이 있지만, 그건 떡방 할배 사무실 이름이랑 전번이다.
우리가 원하는 건, DB에 있는 사용자 정보에 접근해서 거기랑 맞춰보는거다.
<img src="https://velog.velcdn.com/images/mqtt_expert/post/8be5ad24-f9e7-4d51-8c9a-3e471b06dd23/image.png" alt="2"></p>
<h3 id="clientserver-방식은-쉽지-ㅎ">Client/Server 방식은 쉽지 ㅎ</h3>
<p>떡방 아저씨 입장에선 모두가 client다.
집을 구하는 사람도 client고, 집을 내놓는 사람도 cleint다.
왜?
둘 다 돈을 주니까...😁😁😁
<strong>왓어 원더플 월더</strong> 다.</p>
<p>Broker 세상에는 client만 있고, server 는 없다.</p>
<p>다들 돈 낸는 client라고 일은 안하고, 다리 꼬고 앉아 있다.
누가 로그인 처리할 것인가? 방법이 없을까?</p>
<blockquote>
<p>있다.
client 중 한 놈을 패서 server로 만들자.</p>
</blockquote>
<p>그냥 그 client 이름을 server라 붙이면 된다. 인자부터 니가 server 해👊👊👊</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/206bc5ea-ac9d-4702-b4bc-782b82679791/image.PNG" alt=""></p>
<p>진실의 방을 다녀오고 나면 다음과 같이 고분고분해진다.</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/3dded0d4-bed6-46b5-a6cd-f883063e70a8/image.png" alt=""></p>
<blockquote>
<ol>
<li>sub server/login : server/login으로 들어오는 건 내가 처리할께😢</li>
<li>pub server/login : server님, 저 login할래요😄</li>
</ol>
</blockquote>
<p>됐다.
borker로 server/client 만들었다.
사실 한 놈 뚜까 패서 server 시켰다.😁😁😁😁😁😁</p>
<p>이제 server가 비번 맞춰보고 login 해줄지 말지 응답하면 된다.
자, 이제 User는 login 응답만 받으면 된다.
엥. 근데 응답 콜은 어떻게 받지?</p>
<p>맛집 앞에서 줄만 잘 선다고 끝 아니다.
아무리 기다려도 안 부를 수 있다.</p>
<p>뒤에 온 놈이 먼저 들어가는 더러운 꼴 볼 수 있단 말이다.
대기책자📝에 이름이랑 전번 써놔야 여친한테 사랑💏받는다.</p>
<p>Broker 세상도 마찬가지다.
응답 콜 받겠다고 미리 등록(<strong>sub</strong>)해야 한다.</p>
<p><img src="https://velog.velcdn.com/images/mqtt_expert/post/cfa703d9-3c3c-430c-843b-ca735a0cda2e/image.png" alt=""></p>
<blockquote>
<ol>
<li>sub server/login : server/login으로 들어오는 건 내가 처리할께</li>
<li>sub clients/client1 : 결과는 clients/client1 로 주세요</li>
<li>pub server/login : server님, 저 login할래요</li>
<li>pub clients/client1 : clients/client1 DB에 있는 패스워드랑 다른데...너 누구냐?</li>
</ol>
</blockquote>
<p>Broker는 왼손으로 요청 받아서, 오른손으로 전달해주는 역할을 한다.
내용에는 관심없다.
거의 예수님, 부처님 수준이다.
다만 어떤 놈이 어떤 것 받아 본다고 했더라(<strong>sub</strong>) 하는 놓은 노트📝 한권 들고 있다.</p>
<blockquote>
<p>누가 뭘 구독하고 있는지?</p>
</blockquote>
<p>또 있다. <strong>sub</strong>하는 놈들이 여럿일 수 있으니</p>
<blockquote>
<p>누가 지금 연결되어 있는지</p>
</blockquote>
<p>주기적으로 체크✅도 한다. 변심하고 딴 맛집 간 놈 없는지...</p>
<p>어떤 놈은 가면 간다 말을 하기도 한다.
이걸 유식하게 <strong>LWT</strong>라 한다. 나중에 나온다.</p>
<p>글 쓰는 이가 그냥 설렁설렁 대충 주절거리는 것 같지만, 나름 깊은 뜻이 쪼금 있다.</p>
<p>여기까지 온다고 고생들 했다.
이제 코드로 함 만들어 보자.
그래도 명색이 프로_글래머💪인데.</p>
<h3 id="clientjs">client.js</h3>
<pre><code class="language-javascript">const mqtt = require(&quot;mqtt&quot;);
const client = mqtt.connect(&quot;mqtt://localhost&quot;);

const user = { id: &#39;client1&#39;, pwd: &#39;pwd&#39; };

client.on(&quot;connect&quot;, () =&gt; {
    client.subscribe(&quot;clients/client1&quot;);
    client.publish(&quot;server/login&quot;, JSON.stringify(user));
});

client.on(&quot;message&quot;, (topic, message) =&gt; {
    console.log(message.toString());
    client.end();
});</code></pre>
<blockquote>
<p>예외 처리 왜 안하냐구?</p>
</blockquote>
<p>할 줄 몰라서 안한다고 생각하면, 당신은 천재다.
그런 거는 나중에 돈 받고 만들 때 하면 된다. 
괜히 Code Reading만 어렵더라.</p>
<h3 id="serverjs">server.js</h3>
<pre><code class="language-javascript">const mqtt = require(&quot;mqtt&quot;);
const server = mqtt.connect(&quot;mqtt://localhost&quot;);

server.on(&quot;connect&quot;, () =&gt; {
    server.subscribe(&quot;server/login&quot;);
});

server.on(&quot;message&quot;, (topic, message) =&gt; {
    let data = JSON.parse(message);
    let client_topic = &#39;clients/&#39; + data.id;
    if (data.pwd === &#39;pwd&#39;) {
        let res = { msg: &#39;login ok&#39; };
        server.publish(client_topic, JSON.stringify(res));
    } else {
        let res = { msg: &#39;login fail&#39; };
        server.publish(client_topic, JSON.stringify(res));
    }
});</code></pre>
<p>떡방이라고 했다가 혼났다. 
<strong>부동산중계인</strong></p>
<hr/>
<small> - 복덕방 사진: 페이스북 '사진으로 보는 코리아'</small><br>
<small> - 마동석 사진: 영화 '시동'</small>
]]></description>
        </item>
        <item>
            <title><![CDATA[MQTT로 카톡서버 만들어보자-인트로]]></title>
            <link>https://velog.io/@mqtt_expert/kakaotalk0</link>
            <guid>https://velog.io/@mqtt_expert/kakaotalk0</guid>
            <pubDate>Thu, 14 Mar 2024 01:28:03 GMT</pubDate>
            <description><![CDATA[<h3 id="인트로라-쓰고-어그로라-읽는다">인트로라 쓰고 어그로라 읽는다.</h3>
<p>&quot;MQTT&quot; 로 구글링해보면 끝도 없이 나온다.</p>
<blockquote>
<p>사실 끝은 있더라...😂</p>
</blockquote>
<p>대부분 &quot;MQTT는 무엇인가&quot;, &quot;MQTT 작동원리&quot;, &quot;MQTT  패킷 구조&quot; 등 기초적인 원리를 설명하는 글이다.
예제도 MQTT 사이트에 있는 IoT 관련 내용으로 pub/sub 한번 해보는 내용이다.</p>
<p>그 글들을 무시하려는 것이 아니라, 이걸로 서비스를 만들려고 하면 좀 애매하다는거다.
그 막막함 나도 안다.
그래서 이 글을 연재하기로 마음먹었다.</p>
<h3 id="나는-오래전부터-카카오톡-같은-채팅서버를-만들고-싶었다">나는 오래전부터 카카오톡 같은 채팅서버를 만들고 싶었다.</h3>
<p>왜? 
채팅이야말로, 모바일 앱의 기본 중 기본이라 생각했다.</p>
<blockquote>
<p>자네는 뭘 개발하나?
&quot;네, 저는 웹 서비스 개발합니다&quot;
 그러면 게시판 정도는 눈 감고도 짜겠구만.</p>
</blockquote>
<p>그렇다. 나에게 채팅 기능은 웹 개발자의 게시판이라 보면 된다.</p>
<p>처음에는 socket.io 로 만들었지만, 사이트에서 주는 예제를 넘어서기가 쉽지 않았다.
그리고 socket.io는 websocket이기에 최초 연결 시 무겁기도 하고, 스마트 폰에서 실행하기에는 많은 리소스를 사용한다.
심지어 먹통이 되기도 한다.</p>
<blockquote>
<p>...
그래서 포기했다. 
아니 &quot;socket.io는 웹채팅에 양보하자&quot; 라는 말로 정신승리했다.
...</p>
</blockquote>
<p>그러다가 MQTT를 알게 되었다.
MQTT는 IoT를 위해 만들어진 것이기에 채팅서버용 프로토콜로는 딱이라는 생각을 했다.
지하철이나 버스를 타고 이동 중, 하나의 AP에서 다음 AP로 넘어갈 때 연결성도 좋았다.</p>
<p>오호 이것 괜찮은데....</p>
<blockquote>
<p>...
하지만
&quot;늑대 피하려다 호랭이 만난다&quot;는 속담처럼 이것도 쉽지 않았다.
...</p>
</blockquote>
<p>그 호랭이를 어떻게 물리쳤는지(아니 구워 삶았는지)에 대한 글이다.</p>
<h3 id="mosquitto는-웹-서비스가-아니다">Mosquitto는 웹 서비스가 아니다.</h3>
<p>우리가 익히 아는 Client/Server, 웹 서비스 이런게 아니다.
예를 들어 채팅하기 위해 로그인하다고 해보자.</p>
<p>Broker한데 _&quot;로그인 처리 해줘&quot;_하면 안된다. </p>
<p>얘는 말 그대로 중계자이다. 오른손을 받아 왼손으로 넘겨주는 놈이다.
여기서 첫번째 장애를 만났다.</p>
<blockquote>
<p>...
로그인 처리해 줄 놈을 어떻게 만들지?
...</p>
</blockquote>
<h3 id="서비스가-대박나서-사용자가-몰리면">서비스가 대박나서, 사용자가 몰리면?</h3>
<blockquote>
<p>...
이런 문제는 그 때가서 돈 세면서 생각하면 된다.
...</p>
</blockquote>
<p>채팅 사용자가 몇 명 안될 때는 큰 문제가 되지 않는다.
하지만 사용자가 엄청 많아졌을 때, 또는 특정 시간 때에 사용자가 몰리면 어떻게 할 것인가?</p>
<h3 id="나의-메세지는-나만-봐야-하는데">나의 메세지는 나만 봐야 하는데...</h3>
<p>채널에 구독하기만 하면 다 볼 수 있는 환경에서, 내 메세지는 나에게만 전송되어야 한다. </p>
<blockquote>
<p>안 그러면 쪽팔린다.
평생 들을 욕을 하루에 다 들을 수도 있다.</p>
</blockquote>
<h3 id="전송된-메세지는-저장해놔야지-나중에-딴소리-안하지">전송된 메세지는 저장해놔야지, 나중에 딴소리 안하지.</h3>
<p>물론 만들어 달라는 분(돈 주는 사람일 가능성이 높으니까)의 고견에 따라 달라질 수 있다.</p>
<blockquote>
<p>1번고객: 야...우리는 메세지를 DB에 저장할 필요 없어.
2번고객: 우리는 회사 서버에 저장해줘. 압색들어오면 싹 지워버리게.
3번고객: 뭐 중요한 것도 없고, 우린 그냥 클라우드 DB에 저장해줘.</p>
</blockquote>
<p>돈🤑 주시는 분들은 보통 반말을 많이 사용하더라. ㅠㅠ</p>
<h3 id="위의-경험을-공유해보려고-한다">위의 경험을 공유해보려고 한다.</h3>
<p>다음 정도는 알고 있어야 얻어 걸리는 것이 있을 것이다.</p>
<ul>
<li>MQTT를 아는 사람</li>
<li>Node.js를 아는 사람</li>
<li>MySQL을 아는 사람</li>
</ul>
<p>기초적인 것은 여기 velog.io에도 많이 있으니, 여기선 다루지 않겠다.</p>
<p>tl;dr 댓글에 설치법 이런 것 물어보지 마시고...나도 구글링 함ㅠㅠ</p>
]]></description>
        </item>
    </channel>
</rss>