SocketIO

hrj2233·2021년 11월 25일
0

socketio

목록 보기
1/1

프레임워크(나온지 오래되었고 안정적임)
websocket을 실행하는게 아님.
websokcet은 socket IO가 실시간, 양방향, event 기반 통신을 제공하는 방법 중 하나.
websocket에 문제가 생겨도 socketIO는 계속 작동.
socketIO는 "websocket의 부가기능"이 아님.
socketIO는 가끔 websocket을 이용해서 실시간, 양방향, event기반 통신을 제공하는 프레임워크
어떤 브라우저가 websocket을 지원하지 않는 경우, http long polling 같은 것을 사용.
socketio는 프론트와 백엔드 간 실시간 통신을 가능하게 해주는 프레임워크 또는 라이브러리.

rooms

socket.on('enter_room', (roomName, done) => {
  	console.log(socket.id)
    console.log(socket.rooms);
    socket.join(roomName);
    console.log(socket.rooms);
}

두번째 줄에서 기본적으로 user는 이미 방에 혼자 들어가있음
(user의 id는 user가 있는 방의 socket.id와 같음.)
socketio에서 모든 socket은 기본적으로 user와 서버 사이에 private room이 있음.

adapter

app.js

// io는 자동적으로 backend socket.io와 연결해주는 function
const socket = io();
const welcome = document.getElementById('welcome');
const nameForm = welcome.querySelector('#name');
const form = welcome.querySelector('#enter');
const room = document.getElementById('room');
room.hidden = true;

let roomName;

function addMessage(message) {
  const ul = room.querySelector('ul');
  const li = document.createElement('li');
  li.innerText = message;
  ul.appendChild(li);
}

function handleMessageSubmit(event) {
  event.preventDefault();
  const input = room.querySelector('#msg input');
  const value = input.value;
  //백엔드에 emit
  socket.emit('new_message', input.value, roomName, () => {
    addMessage(`You: ${value}`);
  });
  input.value = '';
}

function handleNicknameSubmit() {
  event.preventDefault();
  const input = welcome.querySelector('#name input');
  socket.emit('nickname', input.value);
}

function showRoom() {
  welcome.hidden = true;
  room.hidden = false;
  const h3 = room.querySelector('h3');
  h3.innerText = `Room ${roomName}`;
  const msgForm = room.querySelector('#msg');
  msgForm.addEventListener('submit', handleMessageSubmit);
}

function handleRoomSubmit(event) {
  event.preventDefault();
  const input = form.querySelector('input');
  // 첫번째 arg: event의 이름만들기.
  // 두번째 arg: 보내고 싶은 인수들
  // 인수에 여러개 넣어도 상관없음.ex) socket.emit('enter_room',a,b,c,d,e);
  // websocket과 달리 두번째 args를 json.stringfy안해줘도됨. socketio가 알아서 다 해줌.
  // 마지막 arg: 서버에서 호출하는 function.
  // 마지막 arg가 함수가 아니어도 상관없음.
  // 만약 끝낼 때 실행되는 function을 보내고 싶으면 마지막 자리에 넣어야 함
  socket.emit('enter_room', input.value, showRoom);
  roomName = input.value;
  input.value = '';
}
form.addEventListener('submit', handleRoomSubmit);
nameForm.addEventListener('submit', handleNicknameSubmit);

socket.on('welcome', (user, newCount) => {
  const h3 = room.querySelector('h3');
  h3.innerText = `Room ${roomName} (${newCount})`;
  addMessage(`${user} arrived!`);
});

socket.on('bye', (left, newCount) => {
  const h3 = room.querySelector('h3');
  h3.innerText = `Room ${roomName} (${newCount})`;
  addMessage(`${left} left ㅠㅠ`);
});

socket.on('new_message', addMessage);

socket.on('room_change', (rooms) => {
  const roomList = welcome.querySelector('ul');
  roomList.innerHTML = '';
  if (rooms.length === 0) {
    //if문에서의 return문은 호출한 함수를 종료시킴
    //break는 if를 종료
    return;
  }
  rooms.forEach((room) => {
    const li = document.createElement('li');
    li.innerText = room;
    roomList.append(li);
  });
});

server.js

//http는 nodejs에 내장되어있음.
import http from 'http';
import { Server } from 'socket.io';
//백엔드를 위한 admin ui
import { instrument } from '@socket.io/admin-ui';
import express from 'express';
const app = express();

app.set('view engine', 'pug');
app.set('views', __dirname + '/views');
// static은 유저가 볼 수 있는 폴더를 지정하는거.
app.use('/public', express.static(__dirname + '/public'));

app.get('/', (_, res) => res.render('home'));
app.get('/*', (_, res) => res.redirect('/'));

const httpServer = http.createServer(app);
const wsServer = new Server(httpServer, {
  cors: {
    origin: ['https://admin.socket.io'],
    credentials: true,
  },
});

instrument(wsServer, {
  auth: false,
});

function publicRooms() {
  const {
    sockets: {
      // apater는 다른 서버들 사이에 실시간 어플리케이션을 동기화
      adapter: { sids, rooms },
    },
  } = wsServer;
  const publicRooms = [];
  rooms.forEach((_, key) => {
    // room id를 socket id에서 찾을 수 없다면 public room
    if (sids.get(key) === undefined) {
      publicRooms.push(key);
    }
  });
  return publicRooms;
}

function countRoom(roomName) {
  return wsServer.sockets.adapter.rooms.get(roomName)?.size;
}

wsServer.on('connection', (socket) => {
  socket['nickname'] = 'Anon';
  socket.onAny((event) => {
    console.log(`Socket Event: ${event}`);
  });
  socket.on('enter_room', (roomName, done) => {
    socket.join(roomName);
    //done은 프론트에서 마지막 인수를 뜻함.
    //done func 실행시키면 프론트엔드에서 func 실행
    done();
    socket.to(roomName).emit('welcome', socket.nickname, countRoom(roomName));
    // message를 연결된 모든 socket에게 보내줌
    wsServer.sockets.emit('room_change', publicRooms());
  });
  //disconnecting은 원래 있는 이벤트이름.
  //sokcet이 방을 떠나기 바로 직전에 발생.
  socket.on('disconnecting', () => {
    //프론트에 emit
    socket.rooms.forEach((room) =>
      socket.to(room).emit('bye', socket.nickname, countRoom(room) - 1)
    );
  });
  socket.on('disconnect', () => {
    wsServer.sockets.emit('room_change', publicRooms());
  });
  // 프론트와 백엔드의 이벤트 이름이 같아도 상관없음.
  socket.on('new_message', (msg, room, done) => {
    socket.to(room).emit('new_message', `${socket.nickname}: ${msg}`);
    done();
  });
  socket.on('nickname', (nickname) => (socket['nickname'] = nickname));
});

const handleListen = () => console.log(`Listening on http://localhost:3000`);
httpServer.listen(3000, handleListen);

0개의 댓글