728x90

Reference

더보기

해당 포스팅은 노마드코더의 '줌 클론코딩' 강좌를 보고 공부한 내용을 작성한 포스팅입니다.

 

줌 클론코딩 – 노마드 코더 Nomad Coders

WebSockets, SocketIO, WebRTC

nomadcoders.co

 

구현 예정 기능

  1. 실시간 채팅 보내기 / 받기
  2. 닉네임 설정
  3. 방 설정
  4. 채팅 이벤트

 

  • HTTP protocol
    • http://...
    • Client - Server
      • request <-> response 과정
      • Real-Time 은 아니다.
      • request가 이루어져야만 서버는 답하는 형식
      • 응답이 이루어지고 나서 요청 및 응답 데이터는 소멸
  • WebSocket protocol
    • ws://...
    • 웹 브라우저에만 국한되는 시스템은 아니다.
    • Client - Server 연결뿐만 아니라 Server - Server 연결도 가능하다.
    • Client - Server
      • request <-> accept/non accept
      • Client와 Server가 연결
      • Server가 요청을 받으면 Client와 Server는 연결이 된다.
        → (연결된 동안은 데이터 계속 유지)
        → 연결이 된 동안에는 Client 요청이 없어도 Server 에서도 데이터 전송 가능

 

  • ws npm 패키지
 

ws

Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js

www.npmjs.com

 


1. WebSocket 이벤트 메서드

해당 부분 전체 소스 코드
 

works2 - Websocket chatting(1) · olive-su/Zoom_Clone_Coding@6a313d5

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

github.com

 

📢 코드 이해도를 위해 이벤트 함수는 모두 익명 함수로 작성했습니다!

 

  • WebSocket 에도 js 처럼 특정 이벤트를 발생시키는 키워드가 존재한다.
  • http 프로토콜은 프론트엔드의 요청이 있어야지만 백엔드가 응답을 할 수 있는 구조이지만 ws 프로토콜은 프론트엔드의 요청이 없어도 백엔드 응답 가능(양방향 통신)
const wss = new WebSocket.Server({ server }); // webSocket 서버 생성

// param socket : 서버와 브라우저간의 연결 정보
function handleConnection(socket) {
    console.log(socket)
}

wss.on("connection" , handleConnection); // on method : 이벤트를 기다림

 

| 📄 server.js

import http from "http";
import WebSocket from "ws";
import express from "express";

const app = express();

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

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

const server = http.createServer(app);
const wss = new WebSocket.Server({ server });


const sockets = [];// fake database 
// 어느 브라우저에서 연결했는지를 알기 위함

// socket : 브라우저와의 연결 정보
wss.on("connection", (socket) => { // 브라우저와 연결 성공 시
  sockets.push(socket); // 다른 브라우저 연결시, socketsArray에 추가해줌
  console.log("Connected to Browser ✅");
  socket.on("close", () => { // 브라우저 연결 실패 시 
      console.log("Disconnected from the Browser ❌");
  });
  socket.on("message", (message) => { // 브라우저로 부터 메시지 받았을 시
    sockets.forEach((aSocket) => aSocket.send(message));
      // 각 브라우저 각각을 aSocket으로 놓고 메시지 전송(sockets에서 꺼내와서 전송)

  });
});

server.listen(3000, handleListen);

 

| 📄 app.js

const messageList = document.querySelector("ul");
const messageForm = document.querySelector("form");
const socket = new WebSocket(`ws://${window.location.host}`);

socket.addEventListener("open", () => {
  console.log("Connected to Server ✅");
});

socket.addEventListener("message", (message) => { // 백엔드로부터 메시지를 받았을 경우
  console.log("New message: ", message.data);
});

socket.addEventListener("close", () => {
  console.log("Disconnected from Server ❌");
});

function handleSubmit(event) {
  event.preventDefault();
  const input = messageForm.querySelector("input");
  socket.send(input.value);
  input.value = "";
}

messageForm.addEventListener("submit", handleSubmit);

🌟 socket의 의미

  • app.js(프론트엔드) : 생성한 ws 객체 (브라우저 당 개별적으로 존재)
  • server.js(백엔드) : 프론트엔드와의 연결상태

 

📢 ws 버전에 따라 문자가 나타나지 않는 문제 해결!
➡ 서버측에서 message.toString('utf-8') 로 String으로 변환해준다.

 


2. 닉네임 설정

해당 부분 전체 소스 코드
 

works3 - Websocket chatting(2) · olive-su/Zoom_Clone_Coding@384526e

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

github.com

 

  • nickname, message를 구분하기 위해 nickname 타입과 message 타입으로 넘겨준다.
{
    type: "nickname", (닉네임)
    payload: "tom" (닉네임으로 할 이름)
}

{
    type: "message", (채팅)
    payload: "Hello!" (채팅 내용)
}
  1. 🌟 socket.send 메서드의 데이터 타입이 String 이므로 변환 작업을 수행한다!
  2. Json 으로 데이터를 주고 받으면 안좋은 이유 : 데이터를 받는 서버가 js 코드가 아닐 경우 충돌이 발생할 우려가 있다.
    webSocket은 어디에서든 사용할 수 있는 API이기 때문에 데이터 타입에 영향을 받아서는 안된다.
  • 프론트엔드 : StringJson 변환 ➡ 다시 Json -> String
  • 백엔드 : StringJson (Json에서 payload 항목만 화면에 출력)

| 📄 app.js

// 들어온 메시지(string)를 -> json -> string 으로 변환
function makeMessage(type, payload) {
    const msg = {type, payload}
    return JSON.stringify(msg)
}

...

function handleSubmit(event) {
  event.preventDefault();
  const input = messageForm.querySelector("input");
  socket.send(makeMessage("new_message", input.value));
  input.value = "";
}

function handleNickSubmit(event) {
    event.preventDefault();
    const input = nickForm.querySelector("input");
    socket.send(makeMessage("nickname", input.value));
 }

 

| 📄 server.js

wss.on("connection", (socket) => {
  sockets.push(socket);
  socket["nickname"] = "anonymous";
  console.log("Connected to Browser ✅");
  socket.on("close", onSocketClose);
  socket.on("message", (msg) => {
    const parsed = JSON.parse(msg);
    switch (parsed.type) {
      case "new_message":
        sockets.forEach((aSocket) =>
          aSocket.send(
            `${socket.nickname} : ${parsed.payload.toString("utf-8")}`
          )
        );
      case "nickname":
        socket["nickname"] = parsed.payload; // socket의 닉네임 항목에 사용자 지정 닉네임 설정
    }
  });
});
 
728x90