엄마는 여자고, 아빠는 남자다
만큼 쉽지 아니한가😁😁😁
자,자, 이제 톡을 날려보자.
사실 톡하는 건, socket.io와 별 반 다를 게 없다.
여기서 핵심은 이 모든 것을 server 를 통해서 한다는 것이다.
broker로 이를 구현하려면 개발 인생😵 말린다.
그래야 코딩 이해가 쉽다.
다음은 client1 과 client2 가 room1 에서 대화하는 내용이다.
client | room1 |
---|---|
client1 | 안녕하세요 저는 client1입니다. |
client2 | 아, 네...안녕하세요 저는 cleint2 예요.... |
client1 이란 이름은 아래처럼 command line 변수로 받는다.
$> node client.js client1
2번째 라인이 자기가 날린 톡이고,
3번째 라인이 client2로 부터 받은 톡이다.
$> node client.js client2
이건 설명 안해도 이해가 되죠잉😏😏😏
received | message |
---|---|
1번째 received | client1 로그인 |
2번째 received | client2 로그인 |
3번째 received | client1 톡 |
4번째 received | client2 톡 |
대충 돌아가는 걸 봤으니, 코드로 이를 확인해보자.
코드부터 들이 밀면 울렁증 느끼더라.
토발즈 형님도 말씀하셨다. 이빨 그만 좀 털어라~
server/login로 로그인 요청 들어오면 process_login function 불러주고,
server/talk로 톡 요청 들어오면 process_talk function 호출하면 끝.
야호🚀 프로그램 개쉽네.
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/# 이 되는 거다.
이렇게 해야 client2가 client1의 내용을 볼 수 없지 않겠는가.
물론 실 서비스에서 cleint1 같이 쉽게 추측 가능한 이름 쓰면 안된다.
UUID 이런 거 써야 한다.
프로젝트에서 그냥 client1 이렇게 따라하면 폭망💣이다.
하는 일은 서버랑 똑 같다.
clients/client/login로 로그인 응답이 들어오면 process_login function 불러주고,
clients/client/talk로 톡 응답이 들어오면 process_talk function 호출하면 끝이다.
다만 마치 톡 앱처럼 보이려고(고민 많이했다) 사용자 입력을 받는 readline 추가했다.
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();
});
분명 첨에 쌍둥이 아기들이 말했다. 2개는 외우라고.
읽고, 쓰기는 DB라고.
기록을 남기고, 사용자 정보를 읽어오고, room 에는 누가 누가 있는지 알려면 DB를 함 갔다 와야 한다.
DB를 갔다오는 건 당연히 server가 할일이다.
그리고 DB와 뭔 일을 하려면, callback 으로 짜던지 async 로 코딩해야 한다는 건 코린이들도 다 아는 사실.
대충 아래처럼 해주면 되겠다.
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 기록도 마찬가지다.
DB에 저장하는 코드를 넣으면 된다.
room에 누가 있는지, DB에 노크해서 문 열고 보면 된다.
그리고 한 명씩 차례로 톡을 보내면 된다.
괜히 짬도 안되면서 남의 room 열었다가 기도한테 개맞는 수도 있다.
누구는 이러겠지.
rooms/room1 처럼 만들어서 거기다 쏘면 안되냐구...
된다...
Just Do it 🚀🚀🚀
방이 없는 상태에서 개톡 날릴 때, 방 이름 생성하기 위해 pub 하고, 클라이언트는 그거 sub 하는 거 해봤다.
쉽지 않더라😂
그래서 일관성 있게 전부 이렇게 했다.
방 이름도 room1 이렇게 하면 client 이름처럼 폭망💣한다.
어쨌든 여기서 핵심은 room 이 아니라 room 안에 있는 client에게 직접 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가 talk을 pub 하고 puback이 안오면 아래처럼 안갔어요! 다시보낼깝쇼? 빨간 거 달아주면 되겠다.
요게 요게 문제다.
핸드폰을 켜 놓긴 했는데, 뒷주머니🚶나 가방👜에 둔 상태라던지,
톡은 안 보고 틱톡 보고 있다던지,
백번 양보해서, 우리 앱을 보고는 있는데, 다른 방에서 열심히 톡하고 있을 때 말이다.
보통 이럴 때 app push 날린다.
push server 구축 이런거, 여러분 실력이라면 껌인줄 안다.
하지만 네카라쿠배 다니는 분들한테 양보하고 Android는 그냥 FCM 쓰는 걸로 하자.
FCM 보내려면 FCM token이 필요하다.
앱 개발자님에게 음료수 사주면서 조신하게 물어보면 알려줄끼다.
안 알려주면 FCM Message 보내는 법을 참고하라.
정리 이런거 해줘야 뭔가 수미일관되게 글을 썼다 이런 삘😍 받는다.
첨에 말했다.
talk 하고, 읽고 쓰기... 딱 2개 해봤다.
함 시도! 감사합니다^^