[Just Site] 8. socket.io 공부 (2) - 튜토리얼 숙제 풀이

2022. 2. 12. 20:55프로젝트/Just Site

728x90

socket.io 튜토리얼 1편은 아래의 배너를 클릭하자.

 

[Just Site] 8. socket.io 공부 (1) - 튜토리얼 따라하기

이번에 회사에서 소켓 통신을 한다고 해서 한번 찍먹해보기로 했다. 그동안 서버파트를 오지게 기피했는데, 생각보다 단순해서 놀랐다. Socket.io란? Socket.io는 웹소켓을 활용해 만든 라이브러리다

goldfishdiary.tistory.com


연결 / 해제될 때 메시지 보내기

기본적으로 유저가 사이트에 접속하면 'connect',
탭을 닫거나 브라우저를 닫는 등의 경우에 'disconnect' 메시지가 온다.

그럼 이제 다음의 순서대로 작성을 해야겠다.

0. 유저가 연결 / 해제 된다 (자동. 개발 필요 x)

1. 서버에서 연결 / 해제 이벤트를 감지한다.
2. 서버에서 메시지를 클라이언트로 전송한다.

3. 클라이언트에서 메시지를 수신한다.
4. 클라이언트에서 받은 메시지를 표시한다.


1. 서버에서 연결 / 해제 이벤트를 감지한다.
2. 서버에서 메시지를 클라이언트로 전송한다.

index.js를 수정하자.

...

io.on('connection', (socket) => {
  // 유저 연결 메시지
  io.emit('announce', 'user connected')

  socket.on('disconnect', () => {
    // 유저 해제 메시지
    io.emit('announce', 'user disconnected')
  });

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

...

다음과 같이 

- io.on('connection')으로 유저의 연결을 감지한다.
- socket.on('disconnect')으로 유저의 연결 해제를 감지한다.


3. 클라이언트에서 메시지를 수신한다.
4. 클라이언트에서 받은 메시지를 표시한다.

index.html을 수정하자

...

socket.on('announce', function(msg) {
    var item = document.createElement('li');
    item.textContent = msg;
    messages.appendChild(item);
    window.scrollTo(0, document.body.scrollHeight);
})

...

별 건 없고, announce 메시지에 대해
메시지와 같이 대화창에 추가할 수 있게 해줬다.

여러 창을 열고 닫아보았다


'...가 작성중입니다' 표시

우리는 input의 Change 이벤트를 받아서,
서버에 정보를 전송해 줄 것이다.

우선 ~가 작성중입니다를 표시할 공간을 만들어주자.
index.html의 <body> 부분을 수정해준다.

...

  <body>
    <div id="editing"></div>
    <ul id="messages"></ul>
    <form id="form" action="">
      <input id="input" autocomplete="off" /><button>Send</button>
    </form>
    <script src="/socket.io/socket.io.js"></script>
    
 ...

별 거 없고, editing이라는 id를 가진 <div>를 생성했다.

이제 입력 이벤트를 받아보자
index.html의 <script> 부분을 수정한다.

...

      input.addEventListener('change', function(e) {
      	socket.emit('editing');
      })

...

input의 change 이벤트가 실행되면,
서버에 'editing' 메시지를 보내게 했다.


이제 서버에서 'editing' 이벤트를 처리해보자.

index.js의 io.on('connection') 구절 안에 다음의 내용을 넣자.

...

  socket.on('editing', () => {
    io.emit('editing', `${socket.id} is editing`);
  })

...

이제 다시 클라이언트에서 editing 메시지를 처리하자.

index.html의 <script> 부분을 수정하자.

...
      var editingDiv = document.getElementById('editing');

      socket.on('editing', function(msg) {
        editingDiv.innerText = msg;
      })

...

와! 샌즈!


온라인 상태의 유저 보여주기

왠지 현재 연결중인 유저들을 전부 조회할 수 있지 않을까 생각을 했다.
그런 기능을 안 만들어놨을리 없다.

Object.keys(io.sockets.sockets)

위의 방법으로 가져올 수 있다고 한다.

index.js의 io.on('connection') 구절을 수정하자.

...

let connected = [];

io.on('connection', (socket) => {
  io.emit('announce', 'user connected');
  
  connected.push(socket.id);
  io.emit('online', connected);

  socket.on('disconnect', () => {
    io.emit('announce', 'user disconnected')

    connected = connected.filter(id => id !== socket.id);
    io.emit('online', connected);
  });

...

이제 클라이언트에서 'online' 이벤트를 처리하자.

index.html를 다음과 같이 수정한다.

...

  <body>
    <!-- online div 생성 -->
    <div id="online"></div>
    <div id="editing"></div>
    <ul id="messages"></ul>
    <form id="form" action="">
      <input id="input" autocomplete="off" /><button>Send</button>
    </form>
    <script src="/socket.io/socket.io.js"></script>
    <script>
      var socket = io();

      var form = document.getElementById('form');
      var input = document.getElementById('input');
      var editingDiv = document.getElementById('editing');
      // online div 가져오기
      var onlineDiv = document.getElementById('online');

      // online 메시지 수정하기
      socket.on('online', function(msg) {
        onlineDiv.innerText = `online: ${msg.join(', ')}`;
      })
      
 ....

닉네임 설정

각 소켓의 닉네임은 socket.data.username을 설정하면 된다.

index.js를 수정해보자.

  ...  
  
  socket.on('nickname', (msg) => {
    socket.data.username = msg;
  })

  socket.on('chat message', (msg) => {
    io.emit('chat message', { name: socket.data.username || socket.id, msg });
    console.log('message: ' + msg);
  });
  
  ...

'nickname' 이벤트가 들어오면 username을 바꿔주고,
채팅 메시지를 보내줄 때 name과 msg로 나눠서 보내주기로 했다.


index.html을 수정해보자.

...

  <body>
    <div id="online"></div>
    <div id="editing"></div>
    <ul id="messages"></ul>
    <form id="nicknameForm" action="">
      <input id="nicknameInput" autocomplete="off" /><button>Change</button>
    </form>
    <form id="form" action="">
      <input id="input" autocomplete="off" /><button>Send</button>
    </form>
    <script src="/socket.io/socket.io.js"></script>
    <script>
      var socket = io();
      
      ...

      var nicknameForm = document.getElementById('nicknameForm');
      var nicknameInput = document.getElementById('nicknameInput');

...

      nicknameForm.addEventListener('submit', function(e) {
        e.preventDefault();

        if (nicknameInput.value) {
          socket.emit('nickname', nicknameInput.value);
        }
      })
      
      ...

      socket.on('chat message', function(msg) {
        var item = document.createElement('li');
        // 채팅 내용 바꿔주기
        item.textContent = `${msg.name}: ${msg.msg}`;
        messages.appendChild(item);
        window.scrollTo(0, document.body.scrollHeight);
      })
      
      ...

이런 식으로 이름이 정해졌다.
(song은 다른 탭에서 정한 이름)


이제 확실히 감도 잘 잡히긴 하는데
socket.io의 문서가 아주 불친절한 것 같다.
인덱스가 있는게 아니라 줄글 형식으로 되어있어서
원하는거 찾기가 쉽지가 않다. ㅠㅠ