MQTT로 카톡서버 만들어보자 - 2/10. 톡을 날려보자

빙고리우스·2024년 3월 19일
3
post-thumbnail

썰 풀기 전, 먼저 딱 2개만 외우자.

  • talkpub이다.
  • 읽고 쓰기는 DB다.

엄마는 여자고, 아빠는 남자다

만큼 쉽지 아니한가😁😁😁

Talk Talk Talk

자,자, 이제 톡을 날려보자.
사실 톡하는 건, socket.io와 별 반 다를 게 없다.

  • client가 server/login으로 로그인하고, server/talk으로 talkjson을 날린다.
  • talkjson 안에는 어떤 room 에다가 어떤 내용으로 날렸는지 다 들어 있다.
  • server는 talk을 받으면, 받아볼 다른 client들한테 차례차례 전달한다.

여기서 핵심은 이 모든 것을 server 를 통해서 한다는 것이다.
broker로 이를 구현하려면 개발 인생😵 말린다.

먼저 뭘 짤건지 설명해보자.

그래야 코딩 이해가 쉽다.

다음은 client1 과 client2 가 room1 에서 대화하는 내용이다.

clientroom1
client1안녕하세요 저는 client1입니다.
client2아, 네...안녕하세요 저는 cleint2 예요....

client1 실행 캡쳐


client1 이란 이름은 아래처럼 command line 변수로 받는다.

$> node client.js client1

2번째 라인이 자기가 날린 톡이고,
3번째 라인이 client2로 부터 받은 톡이다.

client2 실행 캡쳐

$> node client.js client2

이건 설명 안해도 이해가 되죠잉😏😏😏

server 실행 캡쳐

receivedmessage
1번째 receivedclient1 로그인
2번째 receivedclient2 로그인
3번째 receivedclient1 톡
4번째 receivedclient2 톡

대충 돌아가는 걸 봤으니, 코드로 이를 확인해보자.
코드부터 들이 밀면 울렁증 느끼더라.

토발즈 형님도 말씀하셨다. 이빨 그만 좀 털어라~

서버님에게 추가된 Code

server/login로 로그인 요청 들어오면 process_login function 불러주고,
server/talk로 톡 요청 들어오면 process_talk function 호출하면 끝.

야호🚀 프로그램 개쉽네.

server.js

const mqtt = require("mqtt");
const server = mqtt.connect("mqtt://localhost");
const rooms = { 'room1': ['client1', 'client2', 'client3'] };

server.on("connect", () => {
    server.subscribe(["server/login", "server/talk"]);
});

// login인지, talk인지 구분
server.on("message", (topic, message) => {
    console.log('received: ', topic, JSON.parse(message));
    if (topic.endsWith('/login')) process_login(message);
    else if (topic.endsWith('/talk')) process_talk(message);
});

// login 처리
function process_login(message) {
    let json = JSON.parse(message);
    let client_topic = 'clients/' + json.data.id + '/login';
    if (json.data.pwd === 'pwd') {
        let res = { data: { msg: 'login ok' } };
        server.publish(client_topic, JSON.stringify(res));
    } else {
        let res = { error: { code: 404, msg: 'pwd mismatch' } };
        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 => {
        if (talk.data.from === client) return false;
        let client_topic = 'clients/' + client + '/talk';
        server.publish(client_topic, message);
    });
}

앗! 잠깐, velog 운영자님 코드블럭 문법 강조 기능 좀 넣어줘요.
Code Reading 힘들어요. 쓰는 나도 보기 힘든데😥😥😥

요로코롬 나오게...

클라이언트에 코드 추가

유튜버 생각해보자.
자신만의 채널이 있다.

클라이언트도 자신만의 채널이 있어야 한다.
서버와 비밀스럽게 통신할 채널.

자 내 아이디는 client1 이라 해보자.
그러면 내 채널은 clients/client1/# 이 되는 거다.
이렇게 해야 client2client1의 내용을 볼 수 없지 않겠는가.

물론 실 서비스에서 cleint1 같이 쉽게 추측 가능한 이름 쓰면 안된다.
UUID 이런 거 써야 한다.
프로젝트에서 그냥 client1 이렇게 따라하면 폭망💣이다.

하는 일은 서버랑 똑 같다.

clients/client/login로 로그인 응답이 들어오면 process_login function 불러주고,
clients/client/talk로 톡 응답이 들어오면 process_talk function 호출하면 끝이다.

다만 마치 톡 앱처럼 보이려고(고민 많이했다) 사용자 입력을 받는 readline 추가했다.

client.js

const mqtt = require("mqtt");
const client = mqtt.connect("mqtt://localhost");
const readline = require("readline");

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

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

client.on("connect", () => {
    client.subscribe(['clients/' + user_id + '/login', 'clients/' + user_id + '/talk']);
    client.publish("server/login", JSON.stringify(user));
});

// login인지, talk인지 구분
client.on("message", (topic, message) => {
    if (topic.endsWith('/login')) process_login(message);
    else if (topic.endsWith('/talk')) 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('line', function (line) {
    if (line === 'exit') rl.close();

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

    rl.prompt()
});

// 화면에서 'exit' 라고 입력하면 끝낸다.
rl.on('close', function () {
    if (!!client) client.end();
    process.exit();
});

login 기록📋을 남겨라

분명 첨에 쌍둥이 아기들이 말했다. 2개는 외우라고.
읽고, 쓰기는 DB라고.

기록을 남기고, 사용자 정보를 읽어오고, room 에는 누가 누가 있는지 알려면 DB를 함 갔다 와야 한다.
DB를 갔다오는 건 당연히 server가 할일이다.
그리고 DB와 뭔 일을 하려면, callback 으로 짜던지 async 로 코딩해야 한다는 건 코린이들도 다 아는 사실.
대충 아래처럼 해주면 되겠다.

server.js의 process_login 일부

	if (json.data.pwd === 'pwd') {
        mysql_conn.sql(INSERT_USER_LOGIN, [json.data.id], function(err, results, fields) {
          let res = { data: { msg: 'login ok' } };
          server.publish(client_topic, JSON.stringify(res));
        }
    } else {

talk 기록📋도 남겨라

talk 기록도 마찬가지다.
DB에 저장하는 코드를 넣으면 된다.
room에 누가 있는지, DB에 노크해서 문 열고 보면 된다.
그리고 한 명씩 차례로 톡을 보내면 된다.
괜히 짬도 안되면서 남의 room 열었다가 기도한테 개맞는 수도 있다.
누구는 이러겠지.
rooms/room1 처럼 만들어서 거기다 쏘면 안되냐구...
된다...

Just Do it 🚀🚀🚀

방이 없는 상태에서 개톡 날릴 때, 방 이름 생성하기 위해 pub 하고, 클라이언트는 그거 sub 하는 거 해봤다.
쉽지 않더라😂
그래서 일관성 있게 전부 이렇게 했다.

방 이름도 room1 이렇게 하면 client 이름처럼 폭망💣한다.

어쨌든 여기서 핵심은 room 이 아니라 room 안에 있는 client에게 직접 talk을 날리는 거다.

요걸 코드로 짜보면 다음과 같다.

server.js의 process_talk 일부

    mysql_conn.sql(INSERT_TALK, [json.data.from, json.data.room, json.data.msg], function(err, results, fields) {
      room.forEach(client => {
          if (talk.data.from === client) return false;
          let client_topic = 'clients/' + client + '/talk';
          server.publish(client_topic, message);
      });
	}

보낸 놈한테는 아래처럼 안 보내는 게 낫다.

if (talk.data.from === client) return false;

client가 talkpub 하고 puback이 안오면 아래처럼 안갔어요! 다시보낼깝쇼? 빨간 거 달아주면 되겠다.

talk 받을 놈이 어디 갔다면?

요게 요게 문제다.
핸드폰을 켜 놓긴 했는데, 뒷주머니🚶나 가방👜에 둔 상태라던지,
톡은 안 보고 틱톡 보고 있다던지,
백번 양보해서, 우리 앱을 보고는 있는데, 다른 방에서 열심히 톡하고 있을 때 말이다.
보통 이럴 때 app push 날린다.

push server 구축 이런거, 여러분 실력이라면 껌인줄 안다.
하지만 네카라쿠배 다니는 분들한테 양보하고 Android는 그냥 FCM 쓰는 걸로 하자.

FCM 보내려면 FCM token이 필요하다.
앱 개발자님에게 음료수 사주면서 조신하게 물어보면 알려줄끼다.
안 알려주면 FCM Message 보내는 법을 참고하라.

정리

정리 이런거 해줘야 뭔가 수미일관되게 글을 썼다 이런 삘😍 받는다.
첨에 말했다.
talk 하고, 읽고 쓰기... 딱 2개 해봤다.


- 날리면, 날리다 : 바이든하고는 아무 상관이 없다.
profile
다할줄아는 사람보다 뭔가 한가지 똑부러지게하는 사람이되자.

3개의 댓글

comment-user-thumbnail
2024년 3월 27일

함 시도! 감사합니다^^

1개의 답글
comment-user-thumbnail
2024년 3월 29일

여기 있는 소스 가져다 사용해도 되나요?

답글 달기