본문 바로가기
프로젝트

24/10/01 - [개인] 점핑 액션 게임(6): Socket.IO

by Jini_Lamp 2024. 10. 1.

오늘부터는 이전에 실습했던 점핑 액션 게임을 실습 영상 없이 개인이 알아서 코드를 작성해야 한다.

따라서 제목도 말머리에 "실습"이 아닌, "개인"으로 작성하기로 했다.

 

 

먼저, 점핑 액션 게임 프로젝트를 진행하다보면 다음과 같은 코드들을 볼 수 있다.

io.on('connection', (socket) => {});
socket.on('message', (data) => {});

socket.emit('connection', {});
io.emit('event_name', {});

 

프로젝트를 조금 더 진행하기에 앞서, 해당 부분을 한번쯤 짚고 넘어가는 게 좋을 듯 하여 이번 게시물을 작성하게 되었다.

 

 

 

Socket.IO

Socket.IO는 실시간 웹 애플리케이션에서 클라이언트와 서버 간의 양방향 통신을 쉽게 구현할 수 있게 해주는 JS 라이브러리이다. 해당 라이브러를 사용하면 클라이언트와 서버 간의 실시간 데이터 전송을 쉽게 구현할 수 있다.

주로 웹 애플리케이션에서 채팅, 알림, 실시간 업데이트 등을 구현할 때 사용된다.

이전 게시물에서 잠깐 설명을 했었는데, 해당 내용은 링크를 통해 확인 바람(링크)

 

Socket.IO는 HTTP 기반의 전통적인 요청/응답 방식과 달리, WebSocket 프로토콜을 사용하여 양방향 통신을 수행한다.

주요 특징은 다음과 같다.

  • 양방향 실시간 통신
    클라이언트와 서버가 서로 이벤트를 발생시키고, 데이터를 주고 받을 수 있다.
  • 자동 전송 매커니즘
    브라우저나 네트워크 환경에 따라 XHR Polling 같은 다른 프로토콜을 자동으로 선택하여 안정적인 연결을 보장한다.
  • 네임스페이스와 룸 지원
    클라이언트와 서버 간의 이벤트 통신을 네임스페이스로 구분할 수 있으며, 특정 그룹(룸) 내에서 통신할 수도 있다.
  • 브로드캐스팅
    한 클라이언트가 보낸 메시지를 여러 클라이언트에게 동시에 전달하는 기능을 제공한다.

XHR Polling의 설명은 아래를 참고 바람.

더보기

웹 소켓 같은 지속적인 연결이 불가능하거나, 지원되지 않을 때, 실시간 통신을 구현하는 대체 기술이다. 동작 방식은 다음과 같다.

  1. 클라이언트가 서버에 요청을 보냄
    클라이언트는 특정 주기마다 서버에 요청을 보내며, 서버에서 새로운 데이터가 있는지 확인한다.
  2. 서버 응답
    서버는 새로운 데이터가 있을 때마다 해당 데이터를 응답으로 클라이언트에 반환한다. 만약 새로운 데이터가 없다면, 서버는 빈 응답을 보낼 수 있다.
  3. 반복
    클라이언트는 일정 주기로 이러한 요청을 반복적으로 서버에 보낸다. 이로 인해 서버와 클라이언트 간의 지속적인 통신이 가능한 것처럼 보이게 된다.

 

이렇게 XHR Polling은 실시간 통신을 위해 클라이언트가 일정 시간마다 서버에게 새로운 데이터를 요청한다.

그렇다보니 새로운 데이터가 발생하더라도 클라이언트가 바로 수신하지 않고, 다음 요청 시에 데이터를 수신할 수 있는데, 이는 데이터를 수신하는데 지연 시간이 발생함을 의미한다. 또한 새로운 데이터가 없더라도 계속 요청을 보내는 것이라 불필요한 네트워크 리소스가 발생한다. 이는 트래픽이 많을 경우, 서버에 부담을 줄 수 있다.

 

웹 소켓과의 차이점은 웹 소켓과 달리 지속적인 연결을 유지하며 상시 양방향 통신이 이루어지지 않는다는 점이다.

XHR Polling은 굳이 따지자면 양방향 통신이 아니다. 클라이언트와 서버 간의 비동기적인 데이터 전송을 통해 양방향 통신처럼 보이도록 할 뿐이다.

그래서 웹 소켓에선 서버에서 클라이언트로 데이터를 즉시 푸시할 수 있지만, XHR Polling은 서버가 능동적으로 클라이언트에 데이터를 푸시할 수 없다.

 

서버와 클라이언트는 서로 이벤트 기반으로 소통한다. 특정 이벤트가 발생하면 그에 따라 서버나 클라이언트가 반응하는 것이다.

이때 사용하는 게 .emit()과 .on()이다. 두 메서드의 특징은 다음과 같다.

  • .emit()
    특정 이벤트를 발생시킬 때 사용. 클라이언트/서버에서 데이터를 전송할 수 있다.
  • .on()
    특정 이벤트가 발생했을 때, 그에 반응하여 특정 동작을 실행할 수 있다.

 

아래 예시 코드는 서버가 클라이언트에게 데이터를 보내는 과정이다.

// 서버
io.on('connection', (socket) => {
  // 클라이언트에게 'welcome' 이벤트와 함께 메시지 전송
  socket.emit('welcome', 'Welcome to the server!');
});

// 클라이언트
socket.on('welcome', (msg) => {
  console.log(msg)	// Welcome to the server! 출력
});

 

반대로 클라이언트가 서버에게 데이터를 보낼 수도 있다.

// 서버
io.on('connection', (socket) => {
  // 클라이언트에게 'welcome' 이벤트와 함께 메시지 전송
  socket.on('test', (msg) => {
      console.log(msg);	// Hi! 출력
  });
});

// 클라이언트
socket.emit('test', 'Hi!'});

 

아무리 많은 인수를 보내도 상관 없고, Buffer나 TypeArray 등의 바이너리 객체를 비롯해 직렬화 가능한 데이터 구조는 모두 지원된다고 한다.

// 서버
io.on("connection", (socket) => {
  socket.emit("hello", 1, "2", { 3: '4', 5: Buffer.from([6]) });
});

// 클라이언트
socket.on("hello", (arg1, arg2, arg3) => {
  console.log(arg1); // 1
  console.log(arg2); // "2"
  console.log(arg3); // { 3: '4', 5: ArrayBuffer (1) [ 6 ] }
});

 

여기서 io.on()은 서버 측에서 사용되는 메서드로, 특정 이벤트가 발생했을 때 이를 감지하고 처리하는 콜백 함수를 정의하는 기능을 하며, 주로 클라이언트와 서버 간의 연결 및 이벤트 처리에 사용된다.

  • io.on('connection', (socket) => {});
    클라이언트가 서버에 연결되었을 때 발생하는 connection 이벤트를 처리하는데 사용된다. Socket.IO 서버가 클라이언트의 연결을 감지하고, 클라이언트의 소켓을 통해 상호작용할 수 있게 된다.
    반대로 disconnect 이벤트는 클라이언트가 서버와의 연결을 끊었을 때 발생한다.
io.on('connection', (socket) => {
  // 여기서 socket을 통해 개별 클라이언트와 상호작용할 수 있음

  // 클라이언트의 연결 해제 처리
  socket.on('disconnect', () => {
    console.log('A user disconnected');
  });
});

 

좀 더 쉽게 설명하자면, 서버에 연결된 모든 클라이언트에 대한 이벤트 리스너를 설정할 때 사용된다. 주로 connection과 같은 서버 전역 이벤트를 처리할 때 사용하며, 반대로 socket.on()은 개별 클라이언트 소켓에 대해 특정 이벤트가 발생했을 때 해당 이벤트를 처리하는 리스너를 설정할 때 사용된다.

 

또한 io.emit()이라는 것도 있는데, 마찬가지로 서버에 연결된 모든 클라이언트에게 이벤트를 전송할 때 사용된다.

io.on('connection', (socket) => {
  // 새로운 클라이언트가 접속할 때 모든 클라이언트에게 메시지 전송
  io.emit('announcement', 'A new user has joined the chat');
});