Nodejs

[Nodejs] Socket.IO를 사용한 간단한 웹 채팅 만들기

어렵지만 2025. 2. 24. 22:02

Socket.IO는 웹 소켓 연결을 통해 클라이언트와 서버간에 통신을 가능하게 하는 자바스크립트 라이브러리 입니다.

사용하기 쉬운 인터페이스를 제공하며 업데이트나 통신이 필요한 응용프로그램을 구축하는데 널리 사용

 

그렇다면 Socket이란 무엇일까요?

 

  • Socket: 네트워크상에서 두 개의 프로그램이 통신(데이터를 주고받음)을 연결해주는 지점(endPoint)입니다
    • EndPoint는 뭘까?
      • 통신의 종단점을 나타내는 포괄적인 언어입니다.
        • 통신의 종단점이 뭘까?
          • 통신이 이루어지는 처음 ~ 끝지점을 말합니다.
            구체적인 구현방식(소켓, REST API, 메세지큐 등)은 중요하지 않습니다.
      • 사용되는 엔드포인트 뜻
        • 네트워크 프로그래밍: 소켓과 거의 동의어로 사용됩니다. (특히 TCP/IP 통신에서)
        • 웹 서비스 (Web Services): REST API의 URL, SOAP 서비스의 WSDL 주소 등 특정 서비스를 제공하는 지점을 나타냅니다.
        • 클라우드 컴퓨팅: 특정 리소스(VM 인스턴스, 데이터베이스, 스토리지 등)에 접근하기 위한 주소를 나타냅니다.
        • 보안: 네트워크에서 접근 제어 정책이 적용되는 지점을 나타낼 수 있습니다.
      • 비유
        • 엔드포인트: "전화 통화를 하는 사람의 입이나 귀" (추상적 개념)
        • 소켓: "전화기" (구체적인 도구, 전화 통화를 가능하게 하는 인터페이스)
        • "전화 통화를 하는 사람(엔드포인트)"은 "전화기(소켓)"를 사용할 수도 있고, "휴대폰"을 사용할 수도 있고, "화상 통화 앱"을 사용할 수도 있습니다. "전화 통화를 하는 행위" 자체가 중요한 것이지, 어떤 도구를 사용하느냐는 부차적인 문제입니다.
        • "소켓"은 "전화기"와 같이, "전화 통화를 하는 사람"이 실제로 통화를 할 수 있게 해주는 구체적인 도구입니다.
      • 즉, 소켓은 엔드포인트를 구현하는 방법중 하나이며 네트워크 상에서 두 데이터가 주고 받음을 할 수 있게하는 인터페이스입니다.

그럼 Socket.IO를 왜 사용할까???

  • 일반적인 소켓 프로그래밍의 복잡성을 줄이고 실시간, 양방향 통신을 쉽게 구현해주는 자바스크립트의 라이브러입니다.
  • WebSocket 및 폴링(Polling) 자동 처리:
    • WebSocket 지원: Socket.IO는 기본적으로 WebSocket 프로토콜을 사용합니다. WebSocket은 실시간 양방향 통신을 위한 표준 프로토콜입니다.
    • 폴백(Fallback) 메커니즘: WebSocket을 지원하지 않는 오래된 브라우저나 환경에서는 Socket.IO가 자동으로 폴링(Polling) 방식으로 전환합니다. 폴링은 클라이언트가 주기적으로 서버에 요청을 보내 새로운 데이터가 있는지 확인하는 방식입니다. Socket.IO는 Long Polling, JSONP Polling 등 다양한 폴링 방식을 지원하여 호환성을 높입니다. 개발자는 WebSocket 지원 여부를 직접 신경 쓰지 않고도 실시간 통신을 구현할 수 있습니다.

 

vscode의 터미널에서

터미널에서 초기화 및 필요한 패키지 설치

npm init -y npm
install express socket.io

 

코드입력 후

 

server.js

// Express 모듈을 가져옴. 웹 서버를 쉽게 만들 수 있게 도와주는 프레임워크.
const express = require('express');
// Node.js 기본 모듈인 http를 가져옴. 웹 서버를 직접 만들 때 필요요.
const http = require('http');
// Socket.IO 모듈에서 Server 클래스를 가져옴. 실시간 통신을 위해 사용.
const { Server } = require('socket.io');
// 경로 처리를 쉽게 해주는 Node.js 기본 모듈.
const path = require('path');

// Express 앱을 생성. 이걸로 웹 요청을 처리.
const app = express();
// Express 앱을 기반으로 HTTP 서버를 만듦. Socket.IO가 이 서버 위에서 동작.
const server = http.createServer(app);
// Socket.IO 서버를 만듦. 실시간 양방향 통신을 가능.
const io = new Server(server);

// 서버가 사용할 포트 번호를 3000으로 설정. 브라우저에서 접속할 때 이 번호를 사용.
const port = 3000;

// '/' 경로로 GET 요청이 오면 index.html 파일을 클라이언트에게 보냄. 채팅 UI를 보여주는 기본 페이지.
app.get('/', (req, res) => {
  // __dirname은 현재 파일의 경로를 뜻하고, path.join으로 index.html 파일 경로를 안전하게.
  res.sendFile(path.join(__dirname, 'index.html'));
});

// 클라이언트가 소켓으로 연결되면 이 코드가 실행.
io.on('connection', (socket) => {
  // 새로운 사용자가 접속했음을 서버 콘솔에 출력. 디버깅이나 상태 확인.
  console.log('새로운 사용자가 연결되었습니다!');

  // 클라이언트가 'user joined' 이벤트를 보내면 실행. 유저 이름을 받아 처리.
  socket.on('user joined', (username) => {
    // 현재 소켓에 유저 이름을 저장. 나중에 퇴장할 때 사용용.
    socket.username = username;
    // 서버 콘솔에 누가 입장했는지 출력. 어떤 유저가 들어왔는지 확.
    console.log(`${username}님이 입장했습니다!`);
    // 모든 클라이언트에게 시스템 메시지로 입장 알림을 보냄. 채팅창에 표시.
    io.emit('chat message', { username: '시스템', message: `${username}님이 입장했습니다!` });
  });

  // 클라이언트가 'chat message' 이벤트를 보내면 실행. 채팅 메시지를 받아 처리.
  socket.on('chat message', (data) => {
    // 서버 콘솔에 누가 어떤 메시지를 보냈는지 출력. 로그로 남겨서 확인 가능.
    console.log(`${data.username}: ${data.message}`);
    // 모든 클라이언트에게 받은 메시지를 그대로 전달. 실시간 채팅이 가능.
    io.emit('chat message', { username: data.username, message: data.message });
  });

  // 클라이언트가 연결을 끊으면 실행. 접속 종료를 감지.
  socket.on('disconnect', () => {
    // 서버 콘솔에 누가 나갔는지 출력. username이 없으면 '익명'으로 표시.
    console.log(`${socket.username || '익명'}님이 연결을 끊었습니다.`);
    // 모든 클라이언트에게 시스템 메시지로 퇴장 알림을 보냄. 채팅창에 표시.
    io.emit('chat message', { username: '시스템', message: `${socket.username || '익명'}님이 퇴장했습니다!` });
  });
});

// 서버를 포트 3000에서 실행 시작. 성공하면 콘솔에 메시지를 띄움.
server.listen(port, () => {
  // 서버가 잘 시작됐는지 확인하기 위해 콘솔에 출력. URL도 알림림.
  console.log(`서버가 http://localhost:${port}에서 실행 중입니다!`);
});

 

html.js

<!-- HTML5 문서임을 선언. 최신 표준으로 브라우저가 인식 -->
<!DOCTYPE html>
<!-- HTML 문서의 시작. lang 속성은 생략했지만 기본적으로 영어로 간주 -->
<html>
<head>
  <!-- 페이지 제목. 브라우저 탭에 표시 -->
  <title>실시간 채팅</title>
  <!-- CSS 스타일을 정의. 페이지 모양 꾸밈 -->
  <style>
    /* body 요소의 글꼴을 Arial로 설정.  */
    body { font-family: Arial, sans-serif; }
    /* 메시지 목록의 스타일. 기본 목록 점을 없애고 여백을 조정 */
    #messages { list-style-type: none; padding: 0; }
    /* 각 메시지 항목에 여백을 줘서 보기 좋게함함 */
    #messages li { padding: 5px; }
  </style>
</head>
<body>
  <!-- 메시지가 표시될 unordered list. 채팅 내용을 담는 공간 -->
  <ul id="messages"></ul>
  <!-- 메시지 입력 폼. 사용자가 텍스트를 입력하고 전송 -->
  <form id="form" action="">
    <!-- 텍스트 입력 필드. autocomplete 끄고, ID로 쉽게 접근 가능 -->
    <input id="input" autocomplete="off" />
    <!-- 전송 버튼. 클릭하면 폼이 제출출 -->
    <button>전송</button>
  </form>

  <!-- Socket.IO 클라이언트 스크립트를 서버에서 가져옴. 실시간 통신의 핵심 -->
  <script src="/socket.io/socket.io.js"></script>
  <!-- 클라이언트 측 JavaScript 코드 시작 -->
  <script>
    // 사용자에게 이름 입력창을 띄우고 입력값을 저장. 채팅에서 사용할 닉네임
    const username = prompt('채팅에서 사용할 이름을 입력하세요:');
    // Socket.IO로 서버와 연결 시작. 실시간 통신을 위한 소켓 객체를 만듬듬
    const socket = io();

    // 서버에 'user joined' 이벤트를 보내 유저 이름을 전달. 입장 처리 시작
    socket.emit('user joined', username);

    // HTML에서 form 요소를 가져옴. 메시지 전송을 처리
    const form = document.getElementById('form');
    // 입력 필드를 가져옴. 사용자가 메시지를 입력하는 곳
    const input = document.getElementById('input');
    // 메시지 목록을 가져옴. 새 메시지를 여기 추가
    const messages = document.getElementById('messages');

    // 폼 제출 이벤트 리스너 추가. 메시지를 보내는 트리거
    form.addEventListener('submit', (e) => {
      // 기본 폼 제출 동작(페이지 새로고침)을 막음. 실시간 앱에선 필요 없음
      e.preventDefault();
      // 입력값이 비어있지 않으면 실행
      if (input.value) {
        // 서버에 'chat message' 이벤트로 이름과 메시지를 보냄
        socket.emit('chat message', { username, message: input.value });
        // 입력 필드를 비움. 다음 메시지를 위해 준비
        input.value = '';
      }
    });

    // 서버에서 'chat message' 이벤트를 받으면 실행. 새 메시지를 표시
    socket.on('chat message', (data) => {
      // 새로운 li 요소를 만듦. 메시지를 담을 공간
      const li = document.createElement('li');
      // 메시지 형식으로 텍스트 설정. "이름: 메시지" 형태
      li.textContent = `${data.username}: ${data.message}`;
      // 메시지 목록에 새 li를 추가. 채팅창에 메시지 나타남
      messages.appendChild(li);
    });
  </script>
</body>
</html>

 

시연영상