00. WebSoket / Socket.io

이권석·2022년 10월 1일
2

NodeJS

목록 보기
1/1
post-thumbnail

HTTP → AJAX

HTTP 는 Hyper Text Transfer Protocol의 약자로 오늘날 통신에 가장 널리 사용되고 있는 프로토콜. URLHeader 같은 부과 정보를 포함해 사용자가 원하는 데이터를 정확하게 주고 받을 수 있도록 해준다.

HTTP 통신의 경우 클라이언트 요청 1번에 응답 1번을 보낸 뒤 통신을 종료한다. 따라서 페이지의 일부분만 갱신하고 싶어도 요청을 다시보내야 응답하는 형태로 실행된다.

이런 제약으로부터 조금더 진화한 것이 AJAX 이다. AJAX는 Asynchronous Javascript XML의 약자로 XMLHttpRequest라는 JS 객체를 이용해서 서버와 비동기 방식으로 통신해서 DOM을 조작해 문서 일부분만 갱신하는 것을 가능하게 한다. axios 도 AJAX라고 할 수 있다.

AJAX의 해당 기능 덕분에 HTTP만 사용하는 방식보다 렌더링 속도가 빠르고 오버헤드가 적다는 이점이 있다. 추가로 비동기 방식으로 통신하기 때문에 클라이언트가 불필요하게 대기하는 시간이 줄어든다.

따라서 HTTP 대신 AJAX를 사용하는 경우 이메일 중복 체크나 패스워드 확인 등 페이지 이동이 없는데 변해야 되는 경우에 사용된다.

하지만 AJAX도 결국 HTTP 통신이기 때문에 만약 요청이 오지 않으면 응답을 할 수 없는 문제점이 생긴다. 실시간 알림, 실시간 채팅 등 실시간으로 통신을 해야하는 경우에는 구현하기가 힘들다. 이러한 근본적인 문제를 해결하기 위해 새로운 통신규약 WebSocket이 생겨났다.


WebSocket

웹소켓은 클라이언트와 서버 간의 양방향 통신이 가능하도록 지원하는 프로토콜이다. 즉 클라이언트가 굳이 요청을 먼저 보내지 않아도 서버 측에서 데이터를 보낼 수 있게 되는 것이다. 그렇기 때문에 WS(Websocket Protocol) 는 실시간 채팅이나 게임, 스트리밍 서비스등에서 사용된다. 다음 그림을 통해 HTTP와 무엇이 다른지 비교해보자.

클라이언트가 2번의 요청을 보낸다고 가정하면 HTTP 통신 방식은 2번의 연결이 필요하다. 하지만 WebSocket 통신의 경우 딱 1번만 연결을 맺고 양방향으로 통신이 가능하다.


Express로 구현해보기

const express = require('express');
const http = require('http');
const app = express();
const path = require('path');  // 경로
const socketIO = require('socket.io');
const server = http.createServer(app);

const io = socketIO(server);

io.on('connection', (socket) => {
  socket.on('[채널 이름]', (data) => {  // chat.js에서 받고
    io.emit('[채널 이름]', [보낼 함수]);  // 다시 보내고
  });
})

app.use(express.static(path.join(__dirname, 'src')));

server.listen(PORT, () => console.log(`server is running ${PORT}`));

"use strict"

const socket = io();

socket.emit('[채널 이름]', [보낼 내용])	 // app.js로 보내고
socket.on('[채널 이름]', [받은 내용])  // 다시 받고

<!DOCTYPE html>
<html lang="en">
<head>
  ~~
</head>
<body>
  ~~
  <!-- npm 모듈 socket.io를 절대경로로 불러오기 -->
  <script src="/socket.io/socket.io.js"></script>
  <!-- 먼저 요청할 js 파일 -->
  <script src="js/chat.js"></script>
</body>
</html>

예제] Live Chat App 구현해보기

NodeJS에는 Websocket을 지원하는 여러 모듈이 있다. 그 중 socket.io 모듈을 활용하여 실시간 채팅앱을 구현해봤다.

전체 Directory 구조

app.js
package.json
package-lock.json
node_modules
src
 ├─ index.html
 ├─ css
 │   └─ style.css
 └─ js
     └─ chat.js


Path: live-chat-app/app.js

const express = require('express');
const http = require('http');
const path = require('path');
const app = express();
const moment = require('moment');
const server = http.createServer(app);
const socketIO = require('socket.io');
const io = socketIO(server);

app.use(express.static(path.join(__dirname, 'src')));
const PORT = process.env.PORT || 8080;

io.on('connection', (socket) => {
  socket.on('chatting', (data) => {
    const { name, msg } = data;
    io.emit('chatting', {
      name,
      msg,
      time: moment(new Date()).format('h:mm A')
    });
  });
})

server.listen(PORT, () => console.log(`server is running ${PORT}`));

Path: live-chat-app/src/js/chat.js

"use strict"

const socket = io();

const nickname = document.querySelector('#nickname');
const chatList = document.querySelector('.chatting-list');
const chatInput = document.querySelector('.chatting-input');
const sendButton = document.querySelector('.send-button');
const displayContainer = document.querySelector('.display-container');

chatInput.addEventListener('keypress', (event) => {
  if (event.keyCode === 13) {
    send();
  }
})

function send() {
  const param = {
    name: nickname.value,
    msg: chatInput.value
  }
  socket.emit('chatting', param);
}

sendButton.addEventListener('click', send);


socket.on('chatting', (data) => {
  const { name, msg, time } = data;
  const item = new LiModel(name, msg, time);
  item.makeLi();
  displayContainer.scrollTo(0, displayContainer.scrollHeight);
})

function LiModel(name, msg, time) {
  this.name = name;
  this.msg = msg;
  this.time = time;

  this.makeLi = () => {
    const li = document.createElement('li');
    li.classList.add(nickname.value === this.name ? "sent" : "received")
    const dom = `
          <span class="profile">
            <span class="user">${this.name}</span>
            <img class="image" src="http://placeimg.com/50/50/any" alt="any">
          </span>
          <span class="message">${this.msg}</span>
          <span class="time">${this.time}</span>
          `;
    li.innerHTML = dom;
    chatList.appendChild(li);
  } 
}

Path: live-chat-app/src/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>실시간 채팅 웹 어플리케이션</title>
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <div class="wrapper">
    <div class="user-container">
      <label for="nickname">대화명</label>
      <input type="text" id="nickname">
    </div>
    <div class="display-container">
      <ul class="chatting-list"></ul>
    </div>
    <div class="input-container">
      <span>
        <input type="text" class="chatting-input">
        <button class="send-button">전송</button>
      </span>
    </div>
  </div>
  <script src="/socket.io/socket.io.js"></script>
  <script src="js/chat.js"></script>
</body>
</html>

구현 결과

profile
Junior Node.js developer

0개의 댓글