socket.io에 대한 개념만 있었던지라, 심도있게 공부하여 프로젝트에 '채팅 페이지'를 구현해보았다. 프론트 단에서만 코딩을 하다가 node.js로 백엔드 단을 구현해보니, client와 server 간 데이터가 움직이는 것을 볼 수 있어서 새롭고 흥미로웠다.
앞으로 node로 게시글 작성, 회원탈퇴, 관리자 등 다양한 기능을 만들어 보아야겠다.

1. 서버에서 socket을 실행할 서버를 생성하고 실행한다.
2. 클라이언트가 서버에 접속하면, 클라이언트는 connect 이벤트가 발생하고 서버의 connection 이벤트를 발생시킨다.
3. 서버에서 connection 이벤트를 수신하고, 어떻게 동작할지 정한다.
4. 서버에서 'update'라는 이벤트를 발생시키면
5. 클라이언트에서는 'update' 이벤트를 수신한다.
const http= require('http');
const app= express();
const server= http.createServer(app)
const socket= require('socket.io');
const io= socket(server);
const port = process.env.PORT || 3000;
// client 폴더 사용
app.use('/chat', express.static('./client/src/chat'))
server.listen(port, ()=>{
console.log(`${port} 서버 실행 중 . . .`)
})
process.env.PORT는 socket 서버의 포트를 3000번으로 하겠다. 하고 환경변수를 정의하는 것입니다.
다음으로, listen을 통해 서버를 실행시켜줍니다.

위와같이 index.js를 설정하면 localhost:3000에서 서버가 구동되는 것을 확인할 수 있습니다.
io.sockets.on('connection', function(socket){
// 새로운 유저가 접속했을 경우 다른 소켓에게도 알려줌
socket.on('newUser', function(name){
socket.name = name;
// 접속되어 있는 다른 유저들에게 알리기 위해 모든 소켓에게 이름 전송
io.sockets.emit('update', {type: 'connect', name: '📢', message: name + '님이 접속하였습니다.'})
})
connection 이벤트는 클라이언트와 서버가 연결되었을 때 실행되는 이벤트입니다.
클라이언트에서 connection 이벤트를 발생시키면, 서버에서 connection 이벤트를 수신하고 콜백함수를 실행합니다.
예를들면,
client : server야! 나 채팅할거니까 너랑 연결 좀 시켜줘. (connection 이벤트 발생)
server : 연결 됐어~ 다른 소켓에게 너가 온 걸 알려줄게. (connection 이벤트 수신, 콜백함수 실행)
message 이벤트는 클라이언트에서 전송한 메세지를 서버에서 받고 다시 클라이언트에게 보내주는 역할을 합니다.
socket.on('message', function(data){
// 받은 데이터에 누가 보냈는지 이름을 추가
data.name = socket.name
// client에게 update 이벤트 발생
socket.broadcast.emit('update', data);
})
socket.on('disconnect', function(){
// 나가는 사람을 제외한 나머지 유저에게 메시지 전송
socket.broadcast.emit('update', {type: 'disconnect', name: '❌', message: socket.name+'님이 나가셨습니다.'});
})
disconnection 이벤트는 클라이언트와 서버의 연결이 끊겼을 때 실행되는 이벤트입니다.
이벤트 실행 후, 콜백함수를 통해 client에게 update 이벤트와 "oo님이 나가셨습니다." 문구를 발생시킵니다.
+) 저는 SPA를 구현하고 있으므로, 라우터에 message.jsx의 경로를 추가했습니다.
const socket = io('localhost:3000');
const name= useSelector((state)=> state.user?.myInfo.username);
io('localhost:3000')을 통하여 socket 서버의 주소를 입력해주고, 이 주소로 통신할 것을 명시해주었습니다.
socket.on('connect', function() {
/* 이름이 빈칸인 경우 */
if(!name) {
name = '익명';
}
/* 서버에 새로운 유저가 왔다고 알림 */
socket.emit('newUser', name)
})
connect는 클라이언트와 서버가 연결되었을 때 실행되는 이벤트입니다.
이를 통해 서버의 connection 이벤트를 실행하여 채팅창 화면에 "oo님이 접속하였습니다." 문구를 볼 수 있습니다.
위에서 언급한 "oo님이 접속하였습니다."와 같이 서버에서는 서버와 클라이언트가 연결 되었을 때, 서버와 클라이언트의 연결이 끊겼을 때, 다른 유저가 메세지를 보냈을 때 세 가지 경우로 클라이언트에게 데이터를 넘깁니다.
이 때 클라이언트는 세 가지 경우를 각각 메세지 색깔이 다르게 보여주도록, className을 다르게 분기처리했습니다.
⚪ 서버와 클라이언트가 연결 되었을 때
🔵 서버와 클라이언트의 연결이 끊겼을 때
🔴 다른 유저가 메세지를 보냈을 때
socket.on('update', function(data) {
var chat = document.getElementById('chat')
var message = document.createElement('div')
var node = document.createTextNode(`${data.name}: ${data.message}`)
var className = ''
// 타입에 따라 적용할 클래스를 다르게 지정
switch(data.type) {
🔵 case 'message':
className = 'other'
break
⚪ case 'connect':
className = 'connect'
break
🔴 case 'disconnect':
className = 'disconnect'
break
}
message.classList.add(className)
message.appendChild(node)
chat.appendChild(message)
})
className을 분기처리 해준 결과입니다.

function send() {
// input에 입력되어있는 데이터 가져오기
var message = document.getElementById('test').value
// 가져왔으니 input을 빈칸으로 변경
document.getElementById('test').value = ''
// 내가 전송할 메시지 클라이언트에게 표시
var chat = document.getElementById('chat')
var msg = document.createElement('div')
var node = document.createTextNode(message)
msg.classList.add('me')
msg.appendChild(node)
chat.appendChild(msg)
// 서버로 message 이벤트 전달 + 데이터와 함께
socket.emit('message', {type: 'message', message: message})
}
내가 메세지를 보낸 경우 또한 me라는 className으로 따로 처리하여 말풍선 색깔이 노랑색으로 나타나도록 하였습니다.
그리고는, 서버로 message 이벤트를 발생시키도록 emit으로 전송해줍니다. 그럼 서버는 messge 이벤트를 발생하여 다시 클라이언트에서 update 이벤트가 실행되고 채팅창에 '내가 보낸 메세지'가 띄워집니다.
socket.io는 npm을 통해 추가적으로 설치해야하지만, webSocket은 new WebSocket으로 객체를 생성하여 바로 사용할 수 있습니다.
socket.io는 이벤트명과 데이터를 구분하여 모든 자료형을 주고받을 수 있지만, webSocket은 string으로만 데이터를 주고 받습니다. 이벤트명도 데이터에 포함해서 받으므로 JSON.parse 또는 JSON.stringify 를 사용하여 데이터를 파싱하는 과정이 필요합니다.
서버와 연결이 끊어졌을 때 socket.io는 주기적으로 연결을 시도합니다. webSocket은 연결이 끊어지면 복구되지 않습니다.

제 프로젝트의 폴더 구조는 이렇습니다. 하나의 폴더 안에 client와 server 폴더가 같이 있어 깃허브 페이지로는 배포가 어려웠습니다.
그래서 client 폴더는 GitHub Pages로, server 폴더는 koyeb을 사용하여 배포했는데 .. 시행착오가 많았습니다.
client > message.jsx에서 socket 서버와 연결하는 부분의 주소를 localhost:3000을 koyeb에서 서버 배포 후 생긴 url로 변경해주어야 합니다.
변경 후, client 다시 build 후 배포하니 해결!
const socket = io('localhost:3000');
const socket = io('https://project1008.koyeb.app/');
이외에도 잔잔한 문제가 많았지만 레퍼런스를 참조하여 해결하는데 도움이 많이 되었습니다. 저와 같은 문제를 가지신 분들께 도움을 드리고자, 제가 참초한 블로그 링크를 걸어두겠습니다.