Back-end/Node.js

[Node.js] ws 모듈과 Socket.IO 로 웹 소켓 사용하기

poppy 2021. 8. 6. 11:39
반응형
웹 소켓이란?
웹 소켓은 HTML5에 새로 추가된 스펙으로 실시간 양방향 데이터 전송을 위한 기술입니다. 최신 브라우저는 대부분 웹 소켓을 지원하고 노드에서는 ws나 Socket.IO 같은 패키지를 통해 웹 소켓을 사용할 수 있습니다. 웹 소켓이 생기기 전에는 주기적으로 서버에 새로운 업데이트가 있는지 확인해서 새로운 내용을 가져오는 방법을 사용했습니다. 웹 소켓은 연결이 이루어지고 나면 계속 연결된 상태이므로 업데이트가 있는지 요청을 보낼 필요가 없고 업데이트가 있으면 서버에서 클라이언트에게 바로 알려줍니다. HTTP 프로토콜과 포트를 공유할 수 있으므로 다른 포트에 연결할 필요도 없습니다.

 

1. ws 모듈로 웹 소켓 사용하기

socket.js 파일을 생성하고 웹 소켓 로직 코드를 작성합니다. 익스프레스 서버와 웹 소켓 서버 연결 후 웹 소켓 서버에 이벤트 리스너를 붙입니다. 웹 소켓은 이벤트 기반으로 작동합니다. 로컬 호스트로 접속한 경우 ip 가 ::1 로 뜹니다. ws 는 웹 소켓 객체이며 이벤트 리스너를 붙입니다. 웹 소켓에는 4가지 상태가 있는데 CONNECTING(연결 중), OPEN(열림), CLOSING(닫는 중), CLOSED(닫힘) 입니다. OPEN 상태일 때만 에러없이 메시지를 보낼 수 있습니다.

// socket.js
const WebSocket = require('ws');

module.exports = (server) => {
  const wss = new WebSocket.Server({ server }); // 익스프레스 서버와 웹 소켓 서버 연결

  wss.on('connection', (ws, req) => { // 클라이언트가 서버와 웹소켓 연결 시 발생하는 이벤트
    const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; // 클라이언트의 IP
    console.log('새로운 클라이언트 접속', ip);
    ws.on('message', (message) => { // 클라이언트로부터 메시지가 왔을 때
      console.log(message);
    });
    ws.on('error', (error) => { // 웹 소켓 연결 중 문제가 생겼을 때
      console.error(error);
    });
    ws.on('close', () => { // 클라이언트와 연결이 끊겼을 때
      console.log('클라이언트 접속 해제', ip);
      clearInterval(ws.interval); // interval 정리
    });

    ws.interval = setInterval(() => { // 3초마다 연결된 모든 클라이언트에게 메시지 전송
      if (ws.readyState === ws.OPEN) { // 상태가 OPEN일 경우 메세지 전송
        ws.send('서버에서 클라이언트로 메시지를 보냅니다.');
      }
    }, 3000);
  });
};

 

app.js 에 웹 소켓을 연결합니다.

// app.js
// .. 생략
const webSocket = require('./socket'); 

// .. 생략
webSocket(server);

 

웹 소켓은 단순히 서버에서 설정한다고 해서 작동하지는 않습니다. 클라이언트에도 웹 소켓을 사용해야 하는데 index.html 파일에 웹 소켓을 설정하겠습니다.

// views/index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>GIF 채팅방</title>
</head>
<body>
<div>F12를 눌러 console 탭과 network 탭을 확인하세요.</div>
<script>
  const webSocket = new WebSocket("ws://localhost:8005"); // 연결할 서버 주소를 넣어 webSocket 객체 생성
  webSocket.onopen = function () { // 서버와 연결된 경우
    console.log('서버와 웹소켓 연결 성공!');
  };
  webSocket.onmessage = function (event) { // 서버로부터 메시지가 오는 경우
    console.log(event.data);
    webSocket.send('클라이언트에서 서버로 답장을 보냅니다');
  };
</script>
</body>
</html>

 

콘솔에서 npm start 를 입력한 후 "http://localhost:8005" 에 접속하여 Network 탭의 Messages 를 확인하면 다음과 같은 화면을 볼 수 있습니다.

 

 

2. Socket.IO로 웹 소켓 사용하기

웹 소켓을 간단하게 사용할 때는 ws 모듈을 사용해도 되지만 구현하려는 서비스가 복잡해지면 Socket.IO 를 사용하는 것이 더 편리합니다. socket.js 파일을 생성하고 웹 소켓 로직 코드를 작성합니다. SocketIO 의 path는 클라이언트가 접속할 경로를 설정한 것입니다. connection 이벤트는 콜백으로 소켓 객체(socket) 을 제공합니다. reply 는 사용자가 직접 만든 이벤트로 클라이언트에서 reply 라는 이벤트명으로 데이터를 보내면 서버에서 받는 부분입니다. 이렇게 이벤트명을 사용하는 것이 ws모듈과 다른 점입니다. 

// socket.js
const SocketIO = require('socket.io');

module.exports = (server) => {
  const io = SocketIO(server, { path: '/socket.io' }); // 익스프레스 서버와 웹 소켓 서버 연결

  io.on('connection', (socket) => { // 클라이언트가 서버와 웹소켓 연결 시 발생하는 이벤트
    const req = socket.request; // 요청 객체
    const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; // 클라이언트의 IP
    console.log('새로운 클라이언트 접속!', ip, socket.id, req.ip);
    socket.on('disconnect', () => { // 클라이언트가 연결을 끊었을 때
      console.log('클라이언트 접속 해제', ip, socket.id);
      clearInterval(socket.interval);
    });
    socket.on('error', (error) => { // 통신 과정에서 에러가 발생했을 때
      console.error(error);
    });
    socket.on('reply', (data) => { // 클라이언트로부터 메시지를 받을 때 (사용자가 직접 만든 이벤트)
      console.log(data);
    });
    socket.interval = setInterval(() => { // 3초마다 클라이언트로 메시지 전송
      socket.emit('news', 'Hello Socket.IO'); // 첫번째 인수 - 이벤트 이름, 두번째 인수 - 데이터
    }, 3000);
  });
};

 

클라이언트에 웹 소켓을 설정하겠습니다. index.html 파일을 작성합니다.  socket 객체를 생성할 때 서버에서 path를 설정했으므로 클라이언트에도 똑같이 path를 설정합니다. path 가 똑같아야 통신할 수 있습니다.

// views/index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>GIF 채팅방</title>
</head>
<body>
<div>F12를 눌러 console 탭과 network 탭을 확인하세요.</div>
<script src="/socket.io/socket.io.js"></script> // Socket.IO 에서 클라이언트로 제공하는 스크립트, 실제 파일은 아님
<script>
  const socket = io.connect('http://localhost:8005', { // 연결할 서버 주소를 넣어 socket 객체 생성
    path: '/socket.io',
    transports: ['websocket'], // 웹 소켓만 사용
  });
  socket.on('news', function (data) { // 서버에서 보내는 news 이벤트를 받기 위해 이벤트 리스너 붙임
    console.log(data);
    socket.emit('reply', 'Hello Node.JS'); // 서버에게 reply로 메시지를 보냄
  });
</script>
</body>
</html>

 

콘솔에서 npm start 를 입력한 후 "http://localhost:8005" 에 접속하여 Network 탭을 확인하면 다음과 같은 화면을 볼 수 있습니다.

 

 

반응형