게시글

5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭
강좌12 - NodeJS - 8년 전 등록 / 6년 전 수정

Socket.io로 실시간 업데이트

웹소켓

안녕하세요. 이번 시간에는 socket.io로 실시간 업데이트를 구현해보겠습니다. socket.io는 웹소켓을 사용해서 클라이언트에 실시간으로 데이터를 전송합니다(웹소켓이 없을 시, xhr이나 flash를 사용합니다). 클라이언트에서는 이벤트 리스너로 대기하고 있으면 새로운 정보가 들어옴에 따라 보이는 정보를 업데이트할 수 있습니다. 게임이 끝났을 때의 레벨 정보를 다른 클라이언트에 실시간으로 전송해봅시다.

npm install --save socket.io

서버에서 쓸 socket.io입니다. 클라이언트 용은 따로 html 스크립트로 불러와야 합니다. socket.js를 만들어줍시다.

socket.js

module.exports = (io) => {
  io.on('connection', (socket) => { // 웹소켓 연결 시
    console.log('Socket initiated!');
    socket.on('newScoreToServer', (data) => { // 클라이언트에서 newScoreToServer 이벤트 요청 시
      console.log('Socket: newScore');
      io.emit('newScoreToClient', data);
    });
  });
};

설명은 나중에 한 번에 하겠습니다. 이벤트 리스너 형식으로 되어있는 것이 포인트입니다. 아래와 같이 서버와 연결해줍시다. 서버와 연결하는 순간 socket.js의 이벤트 리스너들이 서버에 부착됩니다.

server.js

// ... 기존 코드
const passport = require('./passport.js');
const SocketIo = require('socket.io'); // 추가
const socketEvents = require('./socket.js'); // 추가
// ... 기존 코드
const server = app.listen(8080, () => { // const server 부분 추가
  console.log('Express App on port 8080!');
});
const io = SocketIo(server); // socket.io와 서버 연결하는 부분
socketEvents(io); // 아까 만든 이벤트 연결

유의할 점은 마지막에 app.listen을 변수에 담아서 socket.io 패키지와 한 번 더 연결해준다는 겁니다. 그 결과물인 io 변수를 이제 socket 모듈에 전달하는 거죠. 이렇게 전달된 io는 socket.js 모듈에서 사용됩니다. io.on('connection', 콜백) 은 서버와 클라이언트의 소켓이 연결되었을 때 실행됩니다. 아래 클라이언트 부분을 보시면 connect 메소드를 호출하는 데 그 부분이 서버와 연결을 시도하는 부분입니다. callback 안에 이제 진짜 socket을 가지고 할 일들을 넣어주면 됩니다.

코드 중에 socket.on('이벤트명', 콜백)이 있는데요. 이 부분이 클라이언트에서 서버로 오는 요청을 처리할 이벤트 리스너입니다. 보면 newScoreToServer라는 요청이 왔을 때 io.emit('newScoreToClient', data)를 실행합니다. io.emit은 다시 클라이언트로 데이터를 보내는 겁니다. 정확히는 자신을 포함한 모든 클라이언트로 소켓 요청을 보내는 겁니다.

정리하자면, 한 클라이언트에서 소켓을 통해 서버로 데이터를 보냅니다. 서버는 그 데이터를 받아 모든 클라이언트에게 다시 보내는 거죠. 이렇게 보냄과 동시에 모든 클라이언트가 같은 데이터를 전송받을 수 있습니다. 즉 한 사람이 점수 기록을 경신한 정보가 플레이 중인 모든 사람들에게 전달됩니다.

이제 클라이언트를 볼까요? 그 전에 클라이언트를 좀 수정해봅시다. 전 시간에 사용한 파일들을 계속 사용합니다.

layout.pug

// ... 기존 코드
// turn.js 불러오는 스크립트 위에 다음 줄들 추가: 여기부터
script(src='/socket.io/socket.io.js')
script.
  var socket = io.connect('http://localhost:8080');
  var scores = [];
  function updateScoreboard(scores) {
    var scoreboard = document.getElementById('scoreboard');
    scoreboard.innerHTML = '';
    var frag = document.createDocumentFragment();
    for (var i = 0; i < scores.length; i++) {
      var div = document.createElement('div');
      var name = document.createElement('b');
      var lev = document.createElement('span');
      name.innerHTML = scores[i].name;
      lev.innerHTML = scores[i].lev;
      div.appendChild(name);
      div.appendChild(lev);
      frag.appendChild(div);
    }
    scoreboard.appendChild(frag);
  }
  socket.on('newScoreToClient', function (data) {
    scores.push(data);
    updateScoreboard(scores);
  });
// 여기까지 추가
script(src='turn.js')

main.pug

// #screen 태그 아래에 추가
#screen
  // ... 기존 태그
  #monster-stat
    span#monster-name
    span#monster-hp
    span#monster-att
#scoreboard

turn.js

// gameOver 메소드를 다음과 같이 변경
gameOver: function () {
  document.getElementById('screen').innerHTML = hero.name + '은 레벨' + hero.lev + '에서 죽었습니다. 새로 시작하려면 새로고침하세요';
  socket.emit('newScoreToServer', { name: hero.name, lev: hero.lev }); // 추가
  return false;
},

일단 layout.pug에 socket.io를 불러오고, io.connect 메소드로 서버와의 연결을 시도했습니다. 또한 서버에서 오는 이벤트를 받을 이벤트리스너를 연결해주었습니다. newScoreToClient 이벤트가 오면 #scoreboard 태그 안에 점수를 그립니다. 따라서 #scoreboard 태그를 main.pug에 만들어주었고요. turn.js의 gameOver 메소드에는 게임오버시 서버로 socket 요청을 할 메소드를 작성했습니다.

socket.emit('이벤트명', 데이터)가 그것입니다. 해당 이벤트명으로 데이터를 서버로 보냅니다. 위의 예에서는 newScoreToServer의 이름으로 서버에 영웅의 name과 lev 정보를 보냈네요. 서버의 이벤트 리스너에 data 매개변수로 전송됩니다.

서버에 data 객체가 보내지면 서버는 이벤트 리스너 콜백에 따라 io.emit('newScoreToClient', data)를 통해 다시 모든 클라이언트로 데이터를 보내게 됩니다. 이렇게 모든 클라이언트에 실시간으로 방금 죽은 영웅의 이름과 데이터 정보가 전송됩니다. 브라우저를 여러 개 켜놓고 실행해보세요. 다음과 같이 작동하는 것을 볼 수 있습니다.

크롬와 엣지를 동시에 실행시킨 후 게임을 진행했습니다(다른 브라우저여도 상관없습니다). 크롬에서 게임오버가 되자 엣지에 실시간으로 게임오버 정보가 전달됩니다.

undefined

더 다양한 정보를 전송해보세요! 이번 시간은 맛보기였고, 다음 시간에는 socket.io API에 대해 구체적으로 알아봅시다.

조회수:
0
목록
투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.
5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭

댓글

9개의 댓글이 있습니다.
2년 전
안녕하세요 어제 pm2 cluster 모드에서 socket.io 관련 문의드린 사람입니다.
로컬 redis 서버를 cloud redis 서버로 분리하였으나 동일 증상이 발생되어, 크롬 f12 네트워크 탭을 보니 요청이 실패한 경우 provisional headers are shown 경고가 발생하는걸 확인했습니다. 보니까 button의 onclick 클릭 이벤트에 함수를 만들어서 요청한게 실수였던거 같습니다. 교재 방법 대로 \u003cform> 태그와 \u003cbutton type="submit"> 으로 해보겠습니다.
2년 전
안녕하세요 pm2 cluster 모드에서 socket.io 가 제대로 동작하지 않는 경우가 있어
https://www.npmjs.com/package/@socket.io/redis-adapter
링크를 참조하여 socket.js 코드를 수정했습니다.

const socket_io = require('socket.io');
const { createClient } = require('redis');
const { createAdapter } = require('@socket.io/redis-adapter');
const axios = require('axios');
const cookieParser = require('cookie-parser');

module.exports = (server, app, sessionMiddleware) => {
const io = socket_io(server, {path: '/socket.io'});
const pubClient = createClient({
host: `${process.env.REDIS_HOST}`,
port: `${process.env.REDIS_PORT}`,
password:`${process.env.REDIS_PASSWORD}`,
});
const subClient = pubClient.duplicate();
io.adapter(createAdapter(pubClient, subClient));
app.set('io', io);
const room = io.of('/workspace');
const chat = io.of('/content')
....

이렇게 하면 처음 몇 개 채팅만 정상적으로 올라갔다가
계속 채팅을 하면 사이트가 응답이 없어집니다.
pm2 클러스터모드 에서 socke.io 를 어떻게 구현할지 문의드립니다...
2년 전
redis 서버를 별도로 두셔야하는데 노드서버랑 레디스 서버가 분리되어있는 게 맞나요?
3년 전
최근 socket io v4 부터는 socket 서버 생성이 new SocketIo.Server(server, option) 으로 바꼈습니다.
4년 전
io.on("connection", (socket)=>{
console.log(socket.id)
})
를 해보니깐 마지막 연결된 socket이나 마지막으로 이벤트를 발생시킨 소켓의 id값이 나오더라구요. 클라이언트에서 socket.emit()을 하게되면 클라이언트 소켓이 서버 소켓에게 socket이라는 request 객체를 주게되고 이 socket에는 보낸 클라이언트의 id 값(socket.id)과 api들이 내장되어 있어서 socket.on, socket.broadcast.emit등을 사용할 수 있는건가요?

위에 질문이 맞다면 클라이언트 소켓이 서버 소켓으로 보내는 request socket객체는 클라이언트 소켓의 정보를 담고 있는 클라이언트 소켓과는 전혀 다른 객체겠죠?
4년 전
const server = app.listen(PORT, handleListening);
const io = socketIO.listen(server);
이코드가 서버의 소켓을 만드는 과정이고 io가 서버의 소켓이라고 보면 될까요?
4년 전
네 http 서버에 같은 포트로 소켓을 더합니다. io는 연결된 소켓을 핸들링할 수 있는 객체입니다.
4년 전
연결된 소켓이라는 것은 클라이언트 소켓을 말씀하시는건가요? 어떤식으로 핸들링할수있을까요
4년 전
socket.io는 브라우저와 호환되는 Sokcet 통신을 위한 기술을 자동으로 선택해주는 라이브러리로 알고있습니다. 하지만 WebSocket API를 지원하지 않는 브라우저인 경우 ajax polling이나 streaming, flash socket 등을 활용하여 통신시켜준다고 하는데 이러한 경우에는 http를 통해서 데이터를 주고받는지 궁금합니다.
4년 전
네 websocket이 지원 안 되는 구형브라우저의 경우 http를 통해서 데이터를 주고받습니다. long polling으로 검색하시면 됩니다.
5년 전
작동하지않습니다
socket으로 모듈을 뺀 후에 bin/www에서 attach한 socket으로 해보았지만 역시 작동하지 않았습니다.
6년 전
socket.js4번라인에 주석처리가 있어야 할것같아요.
6년 전
감사합니다!
7년 전
main.pug 윗부분에 예제에서 에러가좀 있는거같습니다~