Kimsora✨
article thumbnail
320x100
반응형

채팅방의 구현전 간단하게 Socket.IO 대해 알아보자

Socket.IO란
웹 소켓 연결을 통해 클라이언트와 서버간에 실시간 양방향 통신을 가능하게하는 JavaScript 라이브러리이다
클라이언트와 서버간에 webSocket 연결을 설정하여 작동한다
서버는 들어오는 연결을 확인하고 클라이언트가 방출하는 이벤트를 처리하고,  클라이언트는 서버에 연결하고 이벤트를 방출하거나 이벤트를 확인할 수 있다
클라이언트가 이벤트를 방출하면 서버는 이벤트를 수신하여 클라이언트로 응답을 다시 보내거나 동일한 네임 스페이스 또는 방에 있는 다른 클라이언트로 이벤트를 브로드 캐스트 한다

📌브로드 캐스트란

  • 로컬 랜 상에 붙어있는(브로드캐스트 도메인 안에 있는) 모든 네트워크 장비들에게 보내는 통신

 

 

 npm i socket.io //소켓IO 설치

Server.js

import express from "express";
import http from "http";
import { Server } from "socket.io";

const app = express();// Express.js 모듈을 불러오기
const httpServer = http.createServer(app)//// HTTP 서버 생성
const io = new Server(httpServer);// 웹 소켓 서버 생성

 

app.js

-Socket.IO 초기화:클라이언트는 서버와 통신을 시작 할수 있다

⭐️socket.emit()

:클라이언트에서 서버로 데이터나 이벤트를 전송하는 메서드

socket.emit(eventName, data, callback),이벤트 명은 어떤이름이든 상관 없다

  • eventName:전송하려는 이벤트를 이름,서버 측에서 이벤트를 읽고 해당 이벤트를 식별하는데 사용(어떤 이름이든 상관❌)
  • data:문자,객체,배열 다른유형의 데이터일 수 있다=>무제한으로 인자를 넘겨줄수 있다
  • callback:클라이언트에서 서버로 데이터를 전송한 후에 서버에서 처리가 완료되었을 때 실행할 콜백함수=>꼭 마지막 자리에 들어가야한다

=>서버에서 처리된 결관 추가 작업을 수핼 할 수 있다
⭐️socket.on()
:클라이언트가 서버로부터 이벤트를 수신하기 위해 사용되는 메서드

서버에서 발생한 특정 이벤트를 받고 해당 이벤트가 발생할 때 실행할 콜백 함수를 정의 할수 있다

socket.on(eventName, callback)

  • eventName:서버 측에서 이벤트를 발생시킬 떄 사용되며,클라이언트는 이 이름을 통해 어떤이벤트를 수신할지 식별한다
  • callback:서버가 해당 이벤트가 발생할 떄 실행 할 함수, 서버로 부터 데이터와 함께 호출된다
// Socket.IO 초기화
const socketIo = io();

const welcome = document.getElementById("welcome");
const room = document.getElementById("room");
const form = welcome.querySelector("form");

let roomName;
// 메시지 제출 처리 함수
function handleMsgSubmit(e) {
  e.preventDefault();
  const input = room.querySelector("#msg input");
  const value = input.value;
  // 새 메시지를 서버로 전송
  socketIo.emit("new_msg", input.value, roomName, () =>
    addMessage(`you: ${value}`)
  );
  input.value = "";
}

// 방을 보여주는 함수
function showRoom() {
  welcome.hidden = true;
  room.hidden = false;
  const h3 = room.querySelector("h3");
  h3.innerText = `방 ${roomName} (1)`;

  const msgForm = room.querySelector("#msg");
  msgForm.addEventListener("submit", handleMsgSubmit);
}

// 방 이름과 인원 수를 업데이트하는 함수
function roomNameChange(count) {
  console.log(count);
  const h3 = room.querySelector("h3");
  h3.innerText = `방 ${roomName} (${count})`;
}

// 폼 제출 처리 함수
function handleSubmit(e) {
  e.preventDefault();
  const input = form.querySelector("input");
  const nickInput = form.querySelector("#nick");
  // 새 닉네임과 방 입장 이벤트를 서버로 전송
  socketIo.emit("new_nick", nickInput.value);
  socketIo.emit("enter_room", input.value, showRoom);
  // roomName 설정 및 입력 필드 초기화
  roomName = input.value;
  input.value = "";
  nickInput.value = "";
}

// 채팅 방에 메시지를 추가하는 함수
function addMessage(msg) {
  const ul = room.querySelector("ul");
  const li = document.createElement("li");
  li.innerText = msg;
  ul.appendChild(li);
}

// 폼 제출 이벤트 리스너
form.addEventListener("submit", handleSubmit);

// Socket.IO 이벤트 리스너
socketIo.on("welcome", (nickName, count) => {
  roomNameChange(count);
  addMessage(`${nickName}님이 입장하셨습니다`);
});

socketIo.on("bye", (left, count) => {
  roomNameChange(count);
  addMessage(`${left}님이 퇴장하셨습니다 🥲 (${count}명 남음)`);
});

socketIo.on("new_msg", addMessage);

socketIo.on("room_change", (rooms) => {
  const roomList = welcome.querySelector("#room_list");
  roomList.innerHTML = ""; // 방 목록 초기화
  if (rooms.length === 0) {
    return;
  }

  rooms.forEach((el) => {
    const li = document.createElement("li");
    li.innerText = el;
    roomList.appendChild(li);
  });
});

Server.js

⭐️socket.rooms

:클라이언트 소켓이 현재 참여하고 있는 모든 방의 정보를 나타내는 속성이다,Set객체의 형태이며 소켓이 참여한 모든 방의 고유한 이름을 포함한다

기본적으로 자신의 id 로된 private room에 속해있다=>기본적으로 클라이언트와서버 사이에 private room이 있기 때문

Set(2) { 'WZe907uRBxTZAHHlAAAR', '1' }//Set(2) { '[Socket의 아이디]', '[방 이름]'

⭐️socket.sids

:서버에 연결된 모든 클라이언트 소켓의 식별자를(ID)의 아이디를 관리하는 객체이다 클라이언트 소켓의 ID를 해당 소켓 객체로 매핑하고 클라이언트 소켓이 연결될 때 서버는 각클라언트 소켓에 고유한 ID를 할당하고 이 ID를 사용하여 클라이언트 소켓을 식별한다

⭐️socket.join(roomName)

:특정 방에 참여시키는 메서드,클라이언트 소켓을 원하는 방으로 그룹화하고 해당 방에 속한 다른 클라이언트들과 실시간 통신을 할 수 있다

⭐️socket.to(roomName)

:특정 방 으로 메세지를 보내는 메서드,현재 클라이언트 소켓을 제외한 특정 방에 참여한 다른 클라이언트 소켓들에게 메세지를 전송

⭐️disconnecting

:클라언트 소켓이 연결을 해제하기 직전에 발생하는 이벤트 =>클라이언트 소켓이 하나 이상의 방에서 나가려고 할 떄 발생하며 연결 해제 이벤트 disconnect 보다 앞서 발생한다

import express from "express";
import http from "http";
import { Server } from "socket.io";

const app = express();
app.set("view engine", "html");


// HTTP 서버 생성
const httpServer = http.createServer(app);
// 웹 소켓 서버 생성
const io = new Server(httpServer);
// 웹 소켓 서버의 adapter에서 rooms와 sids 추출
const { sids, rooms } = io.sockets.adapter;

// 모든 공개 방 목록을 반환하는 함수=>퍼블릭한 ID찾기
function publicRoomList() {
  const publicRooms = [];
  rooms.forEach((_, key) => {
    if (!sids.get(key)) {
      const count = rooms.get(key).size;
      publicRooms.push(`${key}🏡  총: ${count} 명`);
    }
  });
  return publicRooms;
}

// 방의 현재 인원 수를 반환하는 함수
function countRoom(roomName) {
  return rooms.get(roomName)?.size;
}

// 클라이언트와의 연결이 설정될 때의 처리
io.on("connection", (socket) => {


  // 클라이언트가 방에 입장할 때의 처리
  socket.on("enter_room", (roomName, func) => {
    socket.join(roomName);
    func();
    socket.to(roomName).emit("welcome", socket.nickName, countRoom(roomName));
    io.sockets.emit("room_change", publicRoomList(), countRoom(roomName));
  });

  // 클라이언트가 연결을 종료할 때의 처리
  socket.on("disconnecting", () => {
    socket.rooms.forEach((room) =>
      socket.to(room).emit("bye", socket.nickName, countRoom(room) - 1)
    );
  });

  // 클라이언트가 완전히 연결을 끊을 때의 처리
  socket.on("disconnect", () =>
    io.sockets.emit("room_change", publicRoomList())
  );

  // 새로운 메시지를 받았을 때의 처리
  socket.on("new_msg", (msg, roomName, done) => {
    socket.to(roomName).emit("new_msg", `${socket.nickName}님: ${msg} `);
    done();
  });

  // 닉네임 설정
  socket.on("new_nick", (nickName) => {
    socket["nickName"] = nickName;
  });
});

// 서버 시작
httpServer.listen(3000, handleListen);

 

func() 함수를 실행시키면 back-end에서 이 코드를 실행시키지 않는다,보안 문제가 생기기 때문

=>신뢰하지 못하는 코드를 back-end에서 실행시키면 안된다.)

→ 즉, Front-end에서 실행된 코드는 back-end가 실행을 시킨것 😮

  socket.on("enter_room", (roomName, func) => {
    socket.join(roomName);
    func();
    socket.to(roomName).emit("welcome", socket.nickName, countRoom(roomName));
    io.sockets.emit("room_change", publicRoomList(), countRoom(roomName));
  });

이외에도 많은 메서드와 이벤트들이 있지만 생략... 서버와 클라이언트를 왔다갔다하면서 주고받는데 너무 어지럽다..... 

 

728x90
반응형

'HTTP 네트워크' 카테고리의 다른 글

GraphQL  (0) 2022.12.01
REST API  (0) 2022.10.06
웹 애플리케이션 아키텍처  (0) 2022.10.05
profile

Kimsora✨

@sorarar

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그

WH