실시간/WebSocket을 이용한 채팅

6. 닉네임

sdafdq 2023. 10. 25. 17:44

솔직히 이것도 쉬워보이긴 한다.

먼저, 이제 클라이언트 쪽에서 데이터를 그냥 문자열이 아닌 json 형태로 보내야 한다.

거기에 nickname, message를 필드로 가지고 있다.

뭐 그냥 바로 해보자.

 

사실 별 문제는 없었다.

클 -> 서버로 가는 데이터가 버퍼로 와서 변환해 줬다.

먼저 일단, 클쪽에서 문자열인 데이터를 Json으로 바꿔보자.

 

$msgForm.addEventListener("submit", (e)=>{
  e.preventDefault();

  const $nickname = $msgForm.querySelector("#nickname");
  const $message = $msgForm.querySelector("#message");
  const data = {
    nickname:$nickname.value,
    message : $message.value
  }
  socket.send(JSON.stringify(data));
  $message.value = "";
});

그냥 input의 값을 받아오고,

그것으로 객체를 만들어 준 다음,

JSON.stringify(data) 해서 그 데이터를 Json 형태의 문자열로 변환시킨다음 보냈다.

 

 

그럼 서버쪽에서

socket.on("message", (m)=>{
const data = m.toString('utf-8');
console.log(data);
for(const sc of sockets){
  sc.send(m.toString('utf-8'))
}
});

버퍼데이터로 받는데, 우선 그걸 utf-8 형식의 문자열로 변환해 준다.

그 다음 그냥 그걸 그대로 보낸다.

내가 알기로 아마 전송간에 데이터는 다 문자열로만 가능한 걸로 안다.

그래서 문자열이지만 json형태인 저걸 그대로 보내면 된다.

 

그러면, 클라이언트 측에서,

socket.addEventListener("message", (m)=>{
  const $li = document.createElement("li");
  const $nickname = document.createElement("span");
  const $message = document.createElement("p");
  const data = JSON.parse(m.data);
  $nickname.textContent = data.nickname;
  $message.textContent = data.message;
  console.log(m.data);
  $li.append($nickname);
  $li.append($message);
  $msgListUl.append($li);
});

html 요소를 추가하는 부분은 일단 무시하고, 

  const data = JSON.parse(m.data);
  $nickname.textContent = data.nickname;
  $message.textContent = data.message;

서버에서 클라이언트측으로 보내면, 클라이언트 측에서 서버로 보내면 버퍼데이터로 내가보낸 문자열 그대로 날려지는 것과 달리 서버에서 클라이언트는 객체로 온다.

그 message객체안의 data라는 객체안에 서버로부터 날라온 데이터가 들어있다.(문자열 형태로)

그거를 우선 JSON.parse()해서 json형태의 문자열이었던 것을 객체로 바꿔버린다.

그 다음 그 데이터를 쓰면 된다.

 

 

나는 그냥 send를 누르면 nickname, message가 있는 객체로 만들어서 보냈는데,

만약 서버에서 메시지별로 다르게 처리하도록 하고 싶다면,

{
    type:"nickname",
    payload : "명수"
}

이런 식으로 클라이언트에서 만들어서 서버에 보내고,

 

서버에서는

swtich(data.type){
    case "nickname":
    	break;
    
    case "message" : 
    	break;
}

이런 식으로도 할 수 있을 것 같다.

 

 

 

참고로, 객체 그대로 전송하는 것은 안 좋다.

내가 javascript로 서버를 만들었다면, 서버에 접속하는 언어가 여러 언어일 수도 있기 때문.

Json이 가장 무난하다.

 

그러니까 서버에서 클라이언트 쪽으로 데이터를 보내든,

클라이언트 쪽에서 서버로 데이터를 보내든,

Json이라는 택배상자로 잘 포장해서 보내야 한다.

 

 

그리고, socket은 기본적으로 객체이기 때문에,

우리가 원하는 값을 저장시켜 놓을 수 있다.

뭐 예를 들어 socket.nickname = "명수";

라던지.

 

즉, 소켓끼리 구분이 가능하다는 말.

 

 

connection 이벤트때는 연결, 즉 초기화 작업을 해준다 그런 느낌이고,

거기에 이벤트 추가하는 건 이벤트에 따라 어떻게 할 것인지.

 

 

내가 이거를 close 할 때 sockets에 있던 걸 제거하려고 했는데,

close로 오는 socket은 좀 다른건가 봄.

뭔가 socket.ip라든지 해서 다른 기준이 있어야 하는건가?

 

여튼 근데 내가 보낸 message는 굳이 내게는 또 다시 보낼 필요 없으니 

function doProcessByMessage(m, socket){
  const msgObj = msgToObject(m);
  switch(msgObj.type){
    case "message":
      const sendNick = socket.nickname;
      const dataForsend = {
        type:"message",
        payload:`${sendNick} : ${msgObj.payload}`
      }
      const scIdx = sockets.indexOf(socket);
      console.log(scIdx);
      for(let i = 0; i < scIdx; i++){
        sockets[i].send(JSON.stringify(dataForsend));
      }
      for(let i = scIdx+1; i < sockets.length; i++){
        sockets[i].send(JSON.stringify(dataForsend));
      }
      break;
}

이렇게 자기 socket 인덱스 찾아서 그거 빼고 보내도록 함.

scIdx전까지 보내고,

scIdx + 1 부터 다시 전송하고.

 

또 close 할 때도, 

socket.on("close", ()=>{
    const idx = sockets.indexOf(socket);
    console.log(sockets.length);
    console.log(socket === sockets[idx]);
    sockets.splice(idx,1);
    for(let i = 0; i < sockets.length; i++){
      console.log(i);
    }
    console.log("Disconnected socket");
});

이렇게 자기 idx 찾아서 제거토록 함.

close 됐는데도 보낼 이유가 없으니까.

 

 

 

프론트엔드 쪽 app.js에서

const socket = new WebSocket("ws://192.168.229.45:3000");

연결하는 서버를 이렇게 하면 다른 컴퓨터에서도 연결이 된다 (주변에서만) LAN 범위 일려나?