Socket IO
Socket IO ๐
Socket?
-
์์ผ โ ํ๋กํ ์ฝ, ip์ฃผ์, ํฌํธ๋๋ฒ๋ก ์ ์.
-
๋จ์ด์ ธ ์๋ ๋ ํธ์คํธ๋ฅผ ์ฐ๊ฒฐํด์ฃผ๋ ๋๊ตฌ๋ก์จ ์ธํฐํ์ด์ค ์ญํ .
-
๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์๋ ๊ตฌ์กฐ์ฒด๋ก ์์ผ์ ํตํด ๋ฐ์ดํฐ ํต๋ก๊ฐ ๋ง๋ค์ด ์ง๋ค.
-
์์ผ์ ์ญํ ์ ๋ฐ๋ผ ํด๋ผ์ด์ธํธ ์์ผ, ์๋ฒ์์ผ์ผ๋ก ๊ตฌ๋ถ๋๋ค.
โ ์น ์ ํ๋ฆฌ์ผ์ด์ : ์ฅ์น์ ์ด์ ์ฒด์ (OS)์์ ๋ก์ปฌ๋ก ์คํ๋๋ ์ปดํจํฐ ๊ธฐ๋ฐ ์ํํธ์จ์ด ํ๋ก๊ทธ๋จ๊ณผ ๋ฌ๋ฆฌ ์น ์๋ฒ์์ ์คํ๋๋ ์์ฉ ํ๋ก๊ทธ๋จ ์ํํธ์จ์ด
โ Websocket : ๋ฐ์ดํฐ๊ฐ ๋๋ฝ๋์ง ์๊ฒํ๋ tcp๊ธฐ๋ฐ์ ์๋ฐฉํฅํต์ ์ ์ ๊ณตํ๋ ์ปดํจํฐ ํ๋กํ ์ฝ
์ฌ์ฉ ์ด์
Http ํต์ ์ ๋จ๋ฐฉํฅ ๋ฐฉ์์ด๊ณ ์ฐ๊ฒฐ์ด ์ ์ง๊ฐ ์๋๊ธฐ ๋๋ฌธ์ ๋งค๋ฒ ์ ๋ฐ์ดํธ ์ /๋ฌด๋ฅผ ํ์ธํ๊ธฐ์ํด์ ์๋ฒ์ ์์ฒญ์ ๋ถ๋ฌ์์ผ ํ๋ค.๊ทธ๋ฆฌ๊ณ ์์ฒญ์ ํ ๋ ๋ง๋ค ์ ์ฒด ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๊ฒ ๋์ด์๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์๋ฒ์ ๋ง์ ๋ถํ๊ฐ ์๊ธฐ๊ฒ ๋ฉ๋๋ค
ํ์ง๋ง ์น์์ผ์ ์๋ฐฉํฅ ํต์ ์ ์ง์ํ๊ฒ ๋์ด์๊ณ ๋ ์๋ฒ์์ ์ฐ๊ฒฐ์ด ์ ์ง๊ฐ ๋ ์ํ์์ ๋ฐ์ดํฐ๊ฐ ์ค๊ฐ์์๊ฒ ํด์ฃผ๊ธฐ ๋๋ฌธ์ ์ค์๊ฐ ๋ฐ์ดํฐ ์ ์ก์ด ๊ฐ๋ฅํด์ง๊ฒ ๋๋ค. ์ด๋ด ๋ Websocket์ ์ด์ฉํ๋ค.
(์์ฝํ์๋ฉด ์๋ฒ์ ํด๋ผ์ด์ธํธ๊ฐ ์ฐ๊ฒฐ๋์๋ ์ํ๋ฅผ ๋ง๋ค์ด ์ฃผ๊ธฐ ์ํด์ ์ฌ์ฉ)
WebSocket vs socket.io
์ฌ์ค ์ ์ด์ ๋์ ๋ค๋ฅธ ๊ฐ๋ ์ด๋ค.
์น์์ผ์ ์๋ฐฉํฅ ์ํต์ ์ํ ํ๋กํ ์ฝ์ด๋ค. ํ๋กํ ์ฝ์ ์ฝ๊ฒ ๋งํ์๋ฉด ์๋ก ๋ค๋ฅธ ์ปดํจํฐ๋ผ๋ฆฌ ์ํตํ๊ธฐ ์ํ ์ฝ์์ด๋ค.
๋ฐ๋ฉด์, socket.io๋ ์๋ฐฉํ ํต์ ์ ํ๊ธฐ์ํด ์น์์ผ ๊ธฐ์ ์ ํ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. ์ด์ฐ๋ณด๋ฉด ์๋ฐ์คํฌ๋ฆฝํธ์ jQuery์ ๊ด๊ณ์ ๋น์ทํ๋ค๊ณ ํ ์ ์๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ socket.io๊ฐ ๊ฐ์ ๊ธฐ๋ฅ์ ๊ตฌํํ๋๋ผ๋ ์ฝ๊ฐ ๋๋ฆฌ์ง๋ง, ๋ง์ ํธ์์ฑ์ ์ ๊ณตํ๋ค. ๋ํ Java, C++, Python ๋ฑ ์ฌ๋ฌ ์ธ์ด๋ค์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ํ ์ง์๋ฉ๋๋ค.
WebSocket
- HTML5 ์น ํ์ค ๊ธฐ์
- ๋งค์ฐ ๋น ๋ฅด๊ฒ ์๋ํ๋ฉฐ ํต์ ํ ๋ ์์ฃผ ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ด์ฉํจ
- ์ด๋ฒคํธ๋ฅผ ๋จ์ํ ๋ฃ๊ณ , ๋ณด๋ด๋ ๊ฒ๋ง ๊ฐ๋ฅํจ
Socket.io
- ํ์ค ๊ธฐ์ ์ด ์๋๋ฉฐ, ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
- ์์ผ ์ฐ๊ฒฐ ์คํจ ์ fallback์ ํตํด ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ์์์ ํด๋น ํด๋ผ์ด์ธํธ์ ์ฐ๊ฒฐ์ ์๋ํจ
- ๋ฐฉ ๊ฐ๋ ์ ์ด์ฉํด ์ผ๋ถ ํด๋ผ์ด์ธํธ์๊ฒ๋ง ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๋ ๋ธ๋ก๋์บ์คํ ์ด ๊ฐ๋ฅํจ
์๋ฒ์์ ์ฐ๊ฒฐ๋ ์์ผ(์ฌ์ฉ์)๋ค์ ์ธ๋ฐํ๊ฒ ๊ด๋ฆฌํด์ผํ๋ ์๋น์ค์ธ ๊ฒฝ์ฐ์๋ Broadcasting ๊ธฐ๋ฅ์ด ์๋ socket.io์ ์ฐ๋๊ฒ ์ ์ง๋ณด์ ์ธก๋ฉด์์ ํจ์ฌ ์ด์ ์ด ๋ง๋ค.
๋ฐ๋ฉด ๊ฐ์ํํ ๊ฑฐ๋์๊ฐ์ด ๋ฐ์ดํฐ ์ ์ก์ด ๋ง์ ๊ฒฝ์ฐ์๋ ๋น ๋ฅด๊ณ ๋น์ฉ์ด ์ ์ ํ์ค WebSocket์ ์ด์ฉํ๋๊ฒ ๋ฐ๋์งํ๋ค.
์ค์ ๋ก ์ ๋นํธ๋ ๋ฐ์ด๋ธ์ค ์์ผ API๋ฅผ ์ฌ์ฉํด๋ณด๋ฉด ์ ๋ง ์์ฒญ๋๊ฒ ๋ง์ ๋ฐ์ดํฐ๊ฐ ๋ค์ด์จ๋ค.
๊ตฌํํ๊ธฐ
Back
const realTimeChat = io => {
io.on('connection', socket => {
socket.on('joinRoom', ({ username, room }) => {
const user = userJoin(socket.id, username, room);
insertUesrInfo(user);
socket.join(user.room);
socket.emit('message', formatMessage(botName, `${user.username}๋ ํ์ํฉ๋๋ค. ๐`));
socket.broadcast
.to(user.room)
.emit('message', formatMessage(botName, `${user.username}๋์ด ์ฐธ๊ฐํ์
จ์ต๋๋ค.`));
io.to(user.room).emit('roomUsers', {
room: user.room,
users: getRoomUsers(user.room),
});
});
socket.on('chatMessage', msg => {
const user = getCurrentUser(socket.id);
insertMsg(msg, user);
io.to(user.room).emit('message', formatMessage(user.username, msg));
});
socket.on('disconnect', () => {
const user = userLeave(socket.id);
if (user) {
io.to(user.room).emit(
'message',
formatMessage(botName, `${user.username}๋์ด ๋๊ฐ์
จ์ต๋๋ค.`)
);
io.to(user.room).emit('roomUsers', {
room: user.room,
users: getRoomUsers(user.room),
});
}
user && deleteUserInfo(user);
});
});
};
export default realTimeChat;
Front
<script>
const chatForm = document.getElementById('chat-form');
const chatMessages = document.querySelector('.chat-messages');
const roomName = document.getElementById('room-name');
const userList = document.getElementById('users');
const { username, room } = Qs.parse(location.search, {
ignoreQueryPrefix: true,
});
var socket = io.connect('https://www2.wecode.buzzntrend.com');
socket.emit('joinRoom', { username, room });
socket.on('roomUsers', ({ room, users }) => {
roomName.innerText = room;
userList.innerHTML = '';
users.forEach((user) => {
const li = document.createElement('li');
li.innerText = user.username;
userList.appendChild(li);
});
});
socket.on('message', (message) => {
outputMessage(message);
chatMessages.scrollTop = chatMessages.scrollHeight;
});
chatForm.addEventListener('submit', (e) => {
e.preventDefault();
let msg = e.target.elements.msg.value;
msg = msg.trim();
if (!msg) {
return false;
}
socket.emit('chatMessage', msg);
e.target.elements.msg.value = '';
e.target.elements.msg.focus();
});
function outputMessage(message) {
const div = document.createElement('div');
div.classList.add('message');
const p = document.createElement('p');
p.classList.add('meta');
p.innerText = message.username;
p.innerHTML += `<span>${message.time}</span>`;
div.appendChild(p);
const para = document.createElement('p');
para.classList.add('text');
para.innerText = message.text;
div.appendChild(para);
document.querySelector('.chat-messages').appendChild(div);
}
</script>
๊ฐ๊ฐ ์์ฑ๋ ๋ฐฉ์์ ๋ชจ๋ ๋ํ๋ด์ฉ๋ค์ Mongo DB์ ์ ์ฅ๋๋๋ก ๊ตฌํํ์๋ค.
์ญ์ ๋์ ์ธ๊ฑธ ํ๋๊น ๋๋ฌด ์ฌ๋ฐ๋ค..
์ฐธ๊ณ
์์ ์ฝ๋๋๋ก ํ๋ค๋ฉด ๋ก๋๋ฐธ๋ฐ์ฑ์ ์ํด ์๋ฒ๊ฐ ์ฌ๋ฌ๊ฐ ์์ฑ๋ ์
์ ์ ๋ค๋ผ๋ฆฌ ์ฑํ ์ด ๋ถ๊ฐ๋ฅํ ๊ฒ์ด๋ค.
ex. ๋๋ A๋ฐฉ์ ๋ค์ด๊ฐ๊ณ ๋ค๋ฅธ์ฌ๋๋ A๋ฐฉ์ ๋ค์ด๊ฐ๋๋ฐ ์ํต์ด์๋
๊ทธ๋ด๋ ํ์ํ๊ฒ์ด socket.io-redis์ด๋ค.
redis adapter๋ ๋์ถฉ ์๋๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ๋์ํ๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก redis์ pub/sub๊ธฐ๋ฅ์ ํ์ฉํ ๊ฒ์ธ๋ฐ ๊ฐ๋จํ ๋งํ์๋ฉด
์ฑ๋์ ๊ตฌ๋ ํ subscribe์๊ฒ ๋ชจ๋ ๋ฉ์ธ์ง๋ฅผ ์ ์ก ํ๋๊ฒ์ด๋ค.
redis๋ฅผ ํตํด ์คํ์ ํด๋ดค๋๋ฐ
A๋ฅผ ๊ตฌ๋ ํ ํฐ๋ฏธ๋์๋ A์ ๋ฉ์ธ์ง๋ง B๋ฅผ ๊ตฌ๋ ํ ํฐ๋ฏธ๋์๋ B์ ๋ฉ์ธ์ง๋ง ๋์ค๋๊ฒ์ ๋ณผ ์ ์๋ค.
์ด์ ์ด๊ฒ์ ๋ด๊ฐ๋ง๋ socket.io์๋ฒ์ ํ์ฉํด๋ณด์.
์ ๋ง ๊ฐ๋จํ๋ค
const redisAdapter = require("socket.io-redis");
io.adapter(redisAdapter({ host: "localhost", port: 6379 }));
์ด๊ฒ๋ง ์ ์ธํด์ฃผ๋ฉด ์์์ to(โ๋ฐฉ์ด๋ฆโ).emit์ redis์ pub/sub๊ธฐ๋ฅ์ด ๋ถ์ฐฉ๋๋ค.
(๋ง๊ทธ๋๋ก ์ด๋ํฐ๋ค..์ ๋ง ํธ๋ฆฌํจ)
โ๋ง์ฝ redis๊ฐ ์ฃฝ์ด๋ฒ๋ฆฌ๋ฉดโ๋ผ๋ ๊ฐ์ ์ ์์ธ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์ง ์๋๋ค๋ฉด server ๋ํ ์ฃฝ์ด๋ฒ๋ฆฌ๊ฒ๋๋ค.
io.of('/').adapter.on('error', error => {
console.log('redisAdapter: ', error);
});
์์๊ฐ์ด redis๊ฐ ์ฃฝ์์๋์ ์์ธ์ฒ๋ฆฌ๋ฅผ ํด์ค์ server๋ ๊บผํธ๋ฆฌ์ง ์๋๋กํ์
(๋ฌผ๋ก ์ด๋ ๊ฒ ๋์๋ redis๊ฐ ์ด์๋ ๋๊น์ง ํ์ฌ์ ์๋ฒ์ ์๋ ํด๋ผ์ด์ธํธ๋ผ๋ฆฌ๋ง ์ฑํ ์ด ๊ฐ๋ฅํ๋ค)
์ฐธ๊ณ ์ฌํญ์ socket.io-redis, ์๋ฌ์ฒ๋ฆฌ ๋ชจ๋ ๊ตฌ๋ฌธ๋ฒ์ด๋ค.
ํ์ฌ๋ @socket.io/redis-adapter๋ผ๋ ์ด๋ฆ์ผ๋ก ๋ฐ๋์๋ค.
๋ด๊ฐ ๊ตฌ๋ฌธ๋ฒ์ ์ด ์ด์ ๋ ์ฌ์ฉํ๊ธฐ์๋ ํธํ๊ณ ์ง๊ด์ ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
https://www.opensourceagenda.com/projects/socketio-redis/versions
https://socket.io/docs/v4/redis-adapter/
asdasdasdasds asdasdasd