[node.js] 게시판에서 채팅 프로그램 만들기
2022. 12. 6. 11:20
-- server.js

// ********* server.js 파일

// 전역 변수
const colors = ["red", "green", "blue", "magenta", "purple", "plum", "orange"];
const clients = [];
let history = [];

// HTTP 서버(express) 생성 및 구동
// 1. express 객체 생성
const express = require("express");
const app = express();

// 2. "/" 경로 라우팅 처리
app.use("/", (req, res) => {
  res.sendFile(__dirname + "/chat.php");
}); // index.html 파일 응답

// 3. 8080 port에서 서버 구동
const HTTPServer = app.listen(8080, () => {
  console.log("Server is open at port:8080");
});

const wsModule = require("ws");
// 2. WebSocket 서버 생성/구동
const wsServer = new wsModule.Server({
  server: HTTPServer,
  // port: 8081,
  // WebSocket서버에 연결할 HTTP서버를 지정한다.
  // port: 30002 // WebSocket연결에 사용할 port를 지정한다(생략시, http서버와 동일한 port 공유 사용)
});

// connection(클라이언트 연결) 이벤트 처리
wsServer.on("connection", (ws, request) => {
  const index = clients.push(ws) - 1;
  let userName = false;
  let userColor = false;

  // 1) 연결 클라이언트 IP 취득
  const ip =
    request.headers["x-forwarded-for"] || request.connection.remoteAddress;

  console.log(`새로운 클라이언트[${ip}] 접속`);

  // 2) 클라이언트에게 메시지 전송
  if (ws.readyState === ws.OPEN) {
    // 연결 여부 체크
    ws.send(`클라이언트[${ip}] 접속을 환영합니다 from 서버`); // 데이터 전송
  }

  // 3) 클라이언트로부터 메시지 수신 이벤트 처리
  ws.on("message", (msg) => {
    if (userName === false) {
      userName = htmlEntities(msg);
      userColor = colors.shift();
      ws.send(makeResponse("color", userColor));

      console.log(`User is known as: ${userName} with ${userColor} color`);
    } else {
      console.log(`Received Message from ${userName}: ${msg}`);

      const obj = {
        time: new Date().getTime(),
        text: htmlEntities(msg),
        author: userName,
        color: userColor,
      };

      history.push(obj);
      history = history.slice(-100);

      clients.forEach((client) => client.send(makeResponse("message", obj)));
      // ws.send(makeResponse("message", obj));
    }
    console.log(`클라이언트[${ip}]에게 수신한 메시지 : ${msg}`);
  });

  // 4) 에러 처리
  ws.on("error", (error) => {
    console.log(`클라이언트[${ip}] 연결 에러발생 : ${error}`);
  });

  // 5) 연결 종료 이벤트 처리
  ws.on("close", () => {
    console.log(`클라이언트[${ip}] 웹소켓 연결 종료`);
    if (userName !== false && userColor !== false) {
      console.log(`Peer ${ws.remoteAddress} disconnected`);

      clients.splice(index, 1);
      colors.push(userColor);
    }
  });
});

// utility
const htmlEntities = (str) =>
  String(str)
    .replace(/&/g, "&")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;");
const makeResponse = (type, data) => JSON.stringify({ type, data });

- server.js 코딩 후

터미널에서 node server.js 로 웹서버 켜주기

-- client.js

$(function () {
  "use strict";

  var content = $("#content");
  var input = $("#input");
  var status = $("#status");

  var myColor = false;
  var myName = false;

  window.WebSocket = window.WebSocket || window.MozWebSocket;

  if (!window.WebSocket) {
    content.html(
      $("<p>", { text: "Sorry, but your browser doesn't support WebSocket." })
    );
    input.hide();
    $("span").hide();
    return;
  }

  var connection = new WebSocket("ws://127.0.0.1:8080");

  connection.onopen = function () {
    input.removeAttr("disabled");
    status.text("이름 입력:");
  };

  connection.onerror = function (error) {
    content.html(
      $("<p>", {
        text:
          "Sorry, but there's some problem with your " +
          "connection or the server is down.",
      })
    );
  };

  connection.onmessage = function (message) {
    try {
      console.log(message);
      var json = JSON.parse(message.data);
    } catch (e) {
      console.log("Invalid JSON: ", message.data);
      return;
    }

    if (json.type === "color") {
      myColor = json.data;
      status.text(myName + ": ").css("color", myColor);
      input.removeAttr("disabled").focus();
    } else if (json.type === "history") {
      for (var i = 0; i < json.data.length; i++) {
        addMessage(
          json.data[i].author,
          json.data[i].text,
          json.data[i].color,
          new Date(json.data[i].time)
        );
      }
    } else if (json.type === "message") {
      input.removeAttr("disabled");
      addMessage(
        json.data.author,
        json.data.text,
        json.data.color,
        new Date(json.data.time)
      );
    } else {
      console.log("Hmm..., I've never seen JSON like this:", json);
    }
  };

  input.keydown(function (e) {
    if (e.keyCode === 13) {
      var msg = $(this).val();

      if (!msg) {
        return;
      }

      connection.send(msg);

      $(this).val("");

      input.attr("disabled", "disabled");

      if (myName === false) {
        myName = msg;
      }
    }
  });

  setInterval(function () {
    if (connection.readyState !== 1) {
      status.text("Error");
      input
        .attr("disabled", "disabled")
        .val("Unable to communicate with the WebSocket server.");
    }
  }, 3000);

  function addMessage(author, message, color, dt) {
    content.prepend(
      '<p><span style="color:' +
        color +
        '">' +
        author +
        "</span> @ " +
        (dt.getHours() < 10 ? "0" + dt.getHours() : dt.getHours()) +
        ":" +
        (dt.getMinutes() < 10 ? "0" + dt.getMinutes() : dt.getMinutes()) +
        ": " +
        message +
        "</p>"
    );
  }
});

 

- front

<?php  
    include("layout/header.php");  ?>
<style>
p {
    line-height: 18px;
}

div {
    width: 500px;
    margin-left: auto;
    margin-right: auto;
}

#content {
    padding: 5px;
    background: #ddd;
    border-radius: 5px;
    overflow-y: scroll;
    border: 1px solid #CCC;
    margin-top: 10px;
    height: 160px;
}

#input {
    border-radius: 2px;
    border: 1px solid #ccc;
    margin-top: 10px;
    padding: 5px;
    width: 400px;
}

#status {
    width: 88px;
    display: block;
    float: left;
    margin-top: 15px;
}
</style>
</head>

<body>
    <div id="content"></div>

    <div>
        <span id="status">Connecting...</span>
        <input type="text" id="input" disabled="disabled" />
    </div>

    <script src="./client.js"></script>
</body> 

</html>
<?php include("layout/footer.php");?>

 

- 결과화면

 


참고 페이지

https://sisiblog.tistory.com/269

https://curryyou.tistory.com/348