TIL #10 WebSocket [22.07.10, 22.07.18]

Ellie·2022년 7월 10일
0

TodayILearned

목록 보기
13/24

공부한 날짜 22.07.10, 22.07.18

❓ websocket?

유저와 서버간의 양방향 데이터 통신이 가능한 컴퓨터 통신 프로토콜이다. 전통적 통신 방식인 HTTP와는 구별된다. 브라우저에 내장되어 있는 웹 소켓은 사용자가 서버로 메시지를 보내면 이벤트 중심으로 응답을 받을 수 있다.

서버와 유저가 데이터를 주고 받으려면 전통적인 방식으로는 http통신이 있다. (자바스크립트의 fetch같은 문법) 전통적인 방식의 경우 유저가 서버에게 요청할 수 있을 뿐 서버 측은 요청할 수 없다.

그런데 채팅이나 주식같이 실시간 데이터 처리를 해야하는 곳에서 http방식은 굉장히 비효율적인데, 거의 1초마다 변동하는 주식가격데이터를 보내달라고 서버에 요청해야 하기 때문이다.

그래서 해결책으로 Server-Sent EventWebSocket이 있다.

먼저 Server-Sent Event는 http통신을 중단하지 않고 계속 연결해놓는 것이다. 그런데 서버가 일방적으로 데이터를 보내고 사용자는 그 데이터를 수신만 할 수 있다.(라디오에 비유할 수 있다.)

그러나 WebSocket은 Server-Sent Event와 달리 양방향 데이터 통신이 가능하다. 브라우저와 서버간의 통신은 항상 열려있는데, 유저가 요청하는 순간 발생한다. 거버는 업데이트를 원하는 때에 가능하다. 일단 먼저 유저가 서버에 웹소켓에 대한 http 요청을 보내고 서버가 수락하면 WebSocket 통신을 할 수 있다.

🔎 웹소켓 여는 방법

// 터미널 패키지 다운
npm init
npm i express //express는 서버를 만드는 라이브러리
npm i ws //ws는 백엔드에서 웹소켓 이벤트 형식을 연결하는 라이브러리

아래는 간단하게 스크립트에 웹소켓을 연결하는 코드만을 작성한 것이며, 만약 더 다양한 기능을 가진 코드를 구현할 때 다른 파일(서버측이 아닌 프론트측 파일)로 구현하면 될 것이다! 그리고 그 파일을 연결하면 된다.

🔎 클라이언트에서 서버로 데이터 보내기

  • 클라이언트의 socket은 서버로의 연결을 뜻한다.

클라이언트 파일 app.js

// STEP1: html에 연결할 app.js 파일 만들기
const socket = new WebSocket(`ws://${window.location.host}`); // 프론트엔드에서 백엔드 연결하기

socket.addEventListener("open", () => {
  console.log("Connected to Server ✅");
}); // 서버의 connection이벤트를 open함

socket.addEventListener("message", (message) => {
  const li = document.createElement("li");
  li.innerText = message.data;
  messageList.append(li);
}); // message이벤트는 message 정보를 줌

socket.addEventListener("close", () => {
  console.log("Disconnected from the Server ❌"); 
}); // 서버와의 연결이 끊어짐

function handleSubmit(event) {
  event.preventDefault();
  const nickInput = form.querySelector("#nick");
  const msgInput = form.querySelector("#msg");

  socket.send(
    makeMessage("new_message", {
      nickInput: nickInput.value,
      msgInput: msgInput.value,
    }) 
  ); // 백엔드로 메시지를 보내고 있다.
  nickInput.value = "";
  msgInput.value = "";
} 

form.addEventListener("submit", handleSubmit);

프론트엔드가 서버에서 보낸 메시지를 받으려면 이벤트함수를 구현하면 된다. socket을 상수로 서버와 연결했기 때문에 socket.addEventListener()를 했을 때 프론트엔드에서 제공되는 함수는 아래와 같다.

  • open
    • 서버와의 connection 개시 이벤트
  • message
    • 메시지 데이터 정보 수신 이벤트
  • error
    • 에러 이벤트
  • close
    • 서버와의 연결 끊김 이벤트

반대로 프론트엔드에서 서버로 메시지를 보내려면

socket.send(
  makeMessage("new_message", {
    nickInput: nickInput.value,
    msgInput: msgInput.value,
  }) 
);

"new_message"라는 이벤트를 생성하고 리스너 함수를 만들어서 socket의 send메소드를 사용하면 된다.


🔎 서버에서 프론트에서 온 데이터 수신하고 또 다시 프론트로 데이터 보내기

  • 서버의 socket은 연결된 브라우저를 뜻한다.

서버 파일 server.js

// STEP2: 웹서버만들기
import express from "express";
import http from "http";
import WebSocket from "ws";

const app = express();

app.set("view engine", "pug");
app.set("views", __dirname + "/views");
app.use("/public", express.static(__dirname + "/public"));
app.get("/", (req, res) => res.render("home"));
app.get("/*", (req, res) => res.redirect("/"));

const server = http.createServer(app);
const wss = new WebSocket.Server({ server }); // http와 websocket이 같은 서버에 돌아가기 하기 위해 {server}를 안에 넣음

const sockets = []; // fake socket database 
// 여러개의 다른 브라우저에서 websocket을 사용하면 각각의 socket이 나타나서 각각 답장을 해주기 때문에 각각의 브라우저가 서로 연결되지 못한다. 
// 그래서 배열로 socket을 넣어줘서 하나로 관리하는 fake database를 만들었다.

// 실시간 소통을 위한 코드
wss.on("connection", (socket) => {
  console.log("connected to Browser ✅");

  sockets.push(socket); // push fake socket database 
  socket.on("close", () => console.log("Disconnected from the Browser ❌"));
  socket.on("message", (message) => {
    const parsedMessage = JSON.parse(message);
    sockets.forEach((aSocket) =>
      aSocket.send(
        `${parsedMessage.payload.nickInput}: ${parsedMessage.payload.msgInput}`
      ) // 모든 브라우저에 메시지를 보낼 수 있게 되었음.
    );
  });
});

const handleListen = () => console.log(`Listening on http://localhost:3000`);
server.listen(3000, handleListen);
  • on 메소드로 이벤트를 시작할 수 있다. 아래는 대표 이벤트이다.
    • connection
      • 브라우저와의 연결
    • close
      • 브라우저 연결이 끊겼을 때의 이벤트
    • error
      • 에러 이벤트
    • message
      • 브라우저가 메시지를 보냈을 때 수신하는 이벤트
  • send 메소드로 메시지를 프론트엔드로 전송할 수 있다.
    • socket.send("hello");

✅ .on("connection", fn)

브라우저와 백엔드 통신은 아래 "connection"라는 이벤트 안에서 이루어진다.
socket.on("connection", (socket) => {})
connection 이벤트가 발생하면 on메소드는 백엔드에 연결된 다른 브라우저의 정보(socket!)를 두번째 파라미터 함수에 제공해준다. 함수 안에서 인자로 제공되며, 함수 안에서 socket의 메소드(send 등등)들을 자유롭게 사용할 수 있다.

🔎 웹소켓의 단점

  1. 사람이 많으면 안된다.
    모든 통신을 추적해야 하기 때문에 메모리 성능이 좋아야 한다. 그래서 유저가 많으면 많을수록 다른 사람'들'에게 전달해야 하기 때문에 딜레이가 발생할 수 있다. 그래서 이런 단점을 보완하기 위해 브라우저와 브라우저가 연결되는 webRTC가 나타났다.

  2. 부수적인 기능이 없다.
    앞에서 배열로 fake database를 구현했듯 로직을 내가 일일이 만들어야 한다.

  3. 비효율적이다.
    내가 메세지를 보냈는데 나의 서버에 내가 보낸 메시지가 온다. 다른 브라우저에만 가면 될 텐데 말이다. 또한 웹소켓에서는 프론트엔드에서 메시지를 보낼 때 string 형식만 보낼 수 있기 때문에 만약 객체데이터를 보냈다면 그것을 JSON.parse해야한다.


그래서 웹소켓보다는 Socket.io라는 라이브러리를 쓰면 더 쉽고 다양한 기능들을 이용할 수 있다.

  • 연결 끊기면 자동재접속 기능
  • 웹소켓 접속자마다 자동 id 부여
  • 모든 웹소켓유저에게 전체 메시지 전송 가능
  • 웹소켓방 생성가능


참고

코딩애플 websocket
노마드 코더 webRTC? WebSockets? 5분 개념정리!
노마드 코더 줌 클론코딩
MDN websocket

profile
정말로 아는 것인지 항상 의심하기

0개의 댓글