티스토리 뷰

반응형

이 글을 읽는 사람은 frontend과 backend의 차이에 정도는 알고 있을 것이다. 그리고 client인 frontend에서 server인 backend로 요청을 보내면 응답을 받는 부분도 알고 있을 것이다. 예를 들면, react에서 spring으로 요청을 보내면, 응답이 오는 것과 같다. 그렇다면, server에서 client로 바로 요청을 보내는 방법 없을까? 이러한 방법을 해결하기 위해서 나온 것이 바로 Websoket이다. Websoket은 상호 간의 데이터를 전달할 수 있어서, 카카오톡과 같은 채팅이나 영상회의 같은 것들을 구현할 때 사용이 가능하다. 이 글을 통해 WebSoket에 대해 알아보고, Websoket과 Socket.io의 차이에 대해서도 알아보도록 하자.

 

WebSoket과 Socket.io의 차이

사실상 목표는 같다. 그러나 WebSoket은 모든 브라우저/모바일 환경에서 같은 조건으로 되지 않는 반면에 Socket.io는 가능하다. 따라서 WebSocket은 잘 안쓴다. 따라서 onmessage, onclose, onopen 이러한 메서드들이 나온다면, WebSocket에 관련된 이야기이다. 대부분 블로그에서는 둘의 차이를 명시하지 않는다. 여기서는 바로 여러분들이 프로젝트에 활용할 수 있도록 Socket.io에 대해 알아보도록 하자.

 

 

 

Socket.io 구현 순서

구현하는 방법을 간단하다.

  1. 데이터를 주고받기위해 socket들을 연결한다.
  2. 보내는 socket을 설정한다.
  3. 받는 socket을 설정한다.
  4. 데이터를 주고받는다.

위의 4가지가 사실 끝이다. 그러면 순서에 맞게 구현을 시작해 보자.

 

 

server 만들기

간단한 서버를 만들기 위해 nodejs의 express를 사용해 본다.

$ npm init
(전부 엔터)
$ npm install express@4
$ touch index.js

위와 같이 명령어를 치고 index.js에 아래의 코드를 넣어준다.

 

// index.js
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);

app.get('/', (req, res) => {
  res.send('<h1>Hello world</h1>');
});

server.listen(3000, () => {
  console.log('listening on *:3000');
});

 

서버 실행은 아래와 같다.

 

$ node index.js

 

그러면 서버가 실행된다. 간단한 서버를 만들었다.

 

http://localhost:3000

에 접속해보면 Hello World가 뜰 것이다!

 

 

client 만들기

먼저 html 파일을 만들어 서버에 연결하자. 위의 서버를 아래와 같이 만든다.

 

// index.js
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

server.listen(3000, () => {
  console.log('listening on *:3000');
});

 

그리고 app.get 부분의 html을 만들어주자. index.html 파일을 만들고 아래와 같이 적어준다.

 

<!DOCTYPE html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      body { margin: 0; padding-bottom: 3rem; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }

      #form { background: rgba(0, 0, 0, 0.15); padding: 0.25rem; position: fixed; bottom: 0; left: 0; right: 0; display: flex; height: 3rem; box-sizing: border-box; backdrop-filter: blur(10px); }
      #input { border: none; padding: 0 1rem; flex-grow: 1; border-radius: 2rem; margin: 0.25rem; }
      #input:focus { outline: none; }
      #form > button { background: #333; border: none; padding: 0 1rem; margin: 0.25rem; border-radius: 3px; outline: none; color: #fff; }

      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages > li { padding: 0.5rem 1rem; }
      #messages > li:nth-child(odd) { background: #efefef; }
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form id="form" action="">
      <input id="input" autocomplete="off" /><button>Send</button>
    </form>
  </body>
</html>

 

지금까지 내용을 정리하면 아래의 그림과 같다.

 

파일 중에 node modules는 express를 설치하면 생기는 파일들이다. package.json은 초반에 npm init를 하면 생기는 파일이다.

 

node index.js로 서버를 실행 후에 localhost:3000 들어가면 아래와 같이 나오면 세팅 끝이다.

 

 

물론, react를 사용하거나, 다른 백엔드를 사용한다면, 위의 세팅은 pass하면 된다.

 

 

Socket.IO server 셋팅

우선 설치를 하자.

$ npm install socket.io

 

index.js를 아래와 같이 변경해준다.

 

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

io.on('connection', (socket) => {
  console.log('a user connected');
});

server.listen(3000, () => {
  console.log('listening on *:3000');
});

위 코드에서 주의할 점은, socket.io인 Server가 server를 통과하면서 io가 된다는 것이다. 관련 코드는 아래와 같다.

const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);

 

그리고 io.on 내부에 'connection'이라는 event를 적어두면, 이 서버를 통과하는 socket에 대해 탐지를 시작한다.

 

 

Socket.io Client 세팅

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();
</script>

client 쪽은 간단하다. 위의 코드만 적으면 연결이 된다. 왜냐하면, src="/socket.io/socket.io.js를 호출하기만 하면, 서버를 지나가면서 자동으로 연결된다.

 

연결 후에 페이지를 새로고침 하면, console이 찍히면서 연결된 것을 확인할 수 있다.

 

 

socket.io  해제

io.on('connection', (socket) => {
  console.log('a user connected');
  socket.on('disconnect', () => {
    console.log('user disconnected');
  });
});

socket 해제는 'disconnect' 이벤트를 추가해 주면 해제가 가능하다.

 

 

socket.io 데이터 보내기

index.html을 아래와 같이 추가한다.

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();

  var form = document.getElementById('form');
  var input = document.getElementById('input');

  form.addEventListener('submit', function(e) {
    e.preventDefault();
    if (input.value) {
      socket.emit('chat message', input.value);
      input.value = '';
    }
  });
</script>

 

index.js는 아래와 같이 추가를 한다.

 

io.on('connection', (socket) => {
  socket.on('chat message', (msg) => {
    console.log('message: ' + msg);
  });
});

 

여기서의 핵심은 client의 socket.emit에 들어간 chat message와 server 부분에 들어간 socket.on의 chat mesage가 같다는 게 포인트다.

 

event 이름이 같은 부분끼리 데이터를 주고받을 수 있다. 그리고 emit 부분이 보내는 부분이고, on 부분이 받는 부분이다. event 이름은 자유롭게 설정이 가능하다.

 

 

아래와 같이 정리를 할 수 있다.

 

// index.html
<!DOCTYPE html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      body { margin: 0; padding-bottom: 3rem; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }

      #form { background: rgba(0, 0, 0, 0.15); padding: 0.25rem; position: fixed; bottom: 0; left: 0; right: 0; display: flex; height: 3rem; box-sizing: border-box; backdrop-filter: blur(10px); }
      #input { border: none; padding: 0 1rem; flex-grow: 1; border-radius: 2rem; margin: 0.25rem; }
      #input:focus { outline: none; }
      #form > button { background: #333; border: none; padding: 0 1rem; margin: 0.25rem; border-radius: 3px; outline: none; color: #fff; }

      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages > li { padding: 0.5rem 1rem; }
      #messages > li:nth-child(odd) { background: #efefef; }
    </style>
  </head>
  <body>
    <script src="/socket.io/socket.io.js"></script>
    <ul id="messages"></ul>
    <form id="form" action="">
      <input id="input" autocomplete="off" /><button>Send</button>
    </form>
    <script>
        var socket = io();

        var messages = document.getElementById('messages');
        var form = document.getElementById('form');
        var input = document.getElementById('input');

        form.addEventListener('submit', function(e) {
            e.preventDefault();
            if (input.value) {
            socket.emit('chat message', input.value);
            input.value = '';
            }
        });

        socket.on('chat message', function(msg) {
            var item = document.createElement('li');
            item.textContent = msg;
            messages.appendChild(item);
            window.scrollTo(0, document.body.scrollHeight);
        });
    </script>
  </body>
</html>

 

//index.js
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

io.on('connection', (socket) => {
  socket.on('chat message', (msg) => {
    io.emit('chat message', msg);
  });
});

server.listen(3000, () => {
  console.log('listening on *:3000');
});

 

위의 코드는 브라우저를 여러 개 열고 채팅을 치면, 서로의 코드들이 동시에 발생한다. 즉, 단체 채팅 기능이라고도 할 수 있다.

 

만약 내가 보낸 것은 나만 볼 수 있도록 만들려면, 아래의 코드처럼 server 부분의 socket을 변경하면 된다.

 

io.on('connection', (socket) => {
  socket.on('chat message', (msg) => {
    socket.emit('chat message', msg);
  });
});

 

io.emit를 socket.emit로 변경한 것이다. io는 연결되어 있는 전체 socket에 보내는 것이고, socket.emit는 현재 보낸 socket에게만 보내는 코드이다.

 

여기까지가, socket의 기본이다. 사실, 연결하고, emit으로 내보내고, on으로 받는 구조가 끝이다. 이러한 것들을 이용하면, 다양한 것들을 구현 가능하다.

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함