Socket.IO 와 친해지기 위해 일단 먼저 코드부터 작성해보기로 했다.
1. 통신 흐름
[클라이언트]
사용자가 메시지 입력
↓
socket.emit('chatMessage', message)
↓
[서버]
socket.on('chatMessage', callback)
io.emit('chatMessage', { id, message })
↓
[다른 클라이언트]
socket.on('chatMessage', callback) → UI 업데이트
2. 파일 구성 및 역할
client.js
:- 클라이언트 측 코드로, 브라우저에서 실행된다.
- 사용자 입력 처리, 서버와의 통신, UI 업데이트를 담당한다.
server.js
:- Node.js 기반 서버 코드로, 클라이언트와의 연결 관리 및 이벤트 처리를 담당한다.
Socket.IO
서버를 초기화하고, 클라이언트 이벤트를 처리하는 핸들러(events.js
)를 호출한다.
events.js
:server.js
에서 호출되는 이벤트 핸들러로, 클라이언트와 서버 간의 이벤트 처리를 분리하여 관리한다.- 서버에서 발생하는 이벤트와 클라이언트 간의 통신 로직을 정의한다.
3. 통신 흐름
3.1 클라이언트 연결
- 클라이언트에서
socket = io()
를 호출하면 서버와 연결이 시도된다. (client.js)- WebSocket 을 우선적으로 사용하며, 지원되지 않는 경우 fallback 방식으로 전환
- 서버는
io.on ('connection', callback)
을 통해 클라이언트 연결을 감지하고 이벤트 핸들러 (events.js
) 를 호출한다. (server.js)io.on('connection', (socket) => { console.log('사용자 연결됨:', socket.id); eventHandlers(socket, io); // events.js 호출
- 클라이언트와 서버 간에 연결이 성립되면 각 클라이언트는 고유한
socket.id
를 부여 받는다.
3.2 메시지 전송 (클라이언트 -> 서버)
- 사용자가 HTML 폼에서 메시지를 입력하고 전송 버튼을 누른다. (client.js)
document.querySelector('#chatForm').addEventListener('submit', (e) => { e.preventDefault(); // 기본 폼 제출 동작 방지 const message = document.querySelector('#messageInput').value; // 입력된 메시지 가져오기 socket.emit('chatMessage', message); // 서버로 이벤트와 데이터 전송 document.querySelector('#messageInput').value = ''; // 입력 필드 초기화 });
- 클라이언트는
socket.emit('chatMessage', message)
를 사용하여 서버로chatMessage
이벤트와 메시지 데이터를 전송한다.
- 클라이언트는
- 서버는 events.js 에서 해당 이벤트를 처리한다.
socket.on('chatMessage', (msg) => { console.log('메시지 수신:', msg); io.emit('chatMessage', { id: socket.id, message: msg }); // 모든 클라이언트에게 메시지 브로드캐스트 });
- 서버는 클라이언트로부터 수신한 메시지를 모든 클라이언트에게 브로드캐스트 (
io.emit
) 한다.
- 서버는 클라이언트로부터 수신한 메시지를 모든 클라이언트에게 브로드캐스트 (
3.3 메시지 수신 (서버 -> 클라이언트)
- 서버에서 브로드캐스트된 메시지를 클라이언트가 수신한다. (clinet.js)
socket.on('chatMessage', (data) => { const chatBox = document.querySelector('#chatBox'); const messageElement = document.createElement('p'); messageElement.textContent = `${data.id}: ${data.message}`; chatBox.appendChild(messageElement); // 화면에 메시지 표시 });
- 클라이언트는 socket.on('chatMessage') 로 서버에서 전달된 메시지를 수신한다.
- 수신한 데이터를 DOM 요소에 추가하여 채팅창에 표시한다.
3.4 연결 해제
- 클라이언트가 브라우저를 닫거나 연결이 종료되면, 서버는 이를 감지한다. (events.js)
socket.on('disconnect', () => { console.log('사용자 연결 종료:', socket.id); io.emit('userDisconnect', { id: socket.id }); // 모든 클라이언트에게 알림 });
- 서버는 disconnect 이벤트를 처리하여, 해당 클라이언트가 연결을 종료했음을 다른 클라이언트에게 브로드캐스트한다.
- 다른 클라이언트는 해당 이벤트를 수신하고 UI를 업데이트한다. (client.js)
socket.on('userDisconnect', (data) => { const chatBox = document.querySelector('#chatBox'); const messageElement = document.createElement('p'); messageElement.textContent = `사용자 ${data.id} 연결 종료`; chatBox.appendChild(messageElement); });
4. 주요 통신 포인트
socket.emit()
- 클라이언트 -> 서버 -> 특정 클라이언트로 데이터 전송.
io.emit()
- 서버 -> 모든 클라이언트로 브로드캐스트.
- 이벤트 기반 통신
- 서버와 클라이언트 간 통신은 on 과 emit 이벤트를 기반으로 이루어짐.
5. 구성 요소 간 관계
client.js
:- 사용자 입력을 처리하고, 데이터를 서버에 전송하거나 수신된 데이터를 UI에 반영
server.js
:- 클라이언트와의 연결을 관리하며, 이벤트 핸들러
events.js
를 통해 메시지를 처리.
- 클라이언트와의 연결을 관리하며, 이벤트 핸들러
events.js
:- 실제 서버 - 클라이언트 간의 이벤트 처리 로직을 정의하여, 서버의 역할을 분리하고 코드의 가독성을 높임.
6. 전체 코드
// server.js
const express = require('express');
const path = require('path');
const http = require('http');
const { Server } = require('socket.io');
const eventHandlers = require('./events');
const app = express();
const server = http.createServer(app);
const io = new Server(server);
// 정적 파일 제공
app.use(express.static(path.join(__dirname, '../public')));
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, '../public/index.html'));
})
// Socket.io 이벤트 등록
io.on('connection', (socket) => {
console.log('사용자 연결됨:', socket.id);
eventHandlers(socket, io);
});
// 서버 시작
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`서버 실행 중: http://localhost:${PORT}`);
});
// client.js
const socket = io();
// 메시지 송신
document.querySelector('#chatForm').addEventListener('submit', (e) => {
e.preventDefault(); // 폼이 기본적으로 메이지를 새로고침하는 동작을 막는다.
const message = document.querySelector('#messageInput').value; // messageInput 에서 입력한 메시지를 가지고 옴
socket.emit('chatMessage', message); // 서버로 chatMessage 이벤트와 함께 입력한 message 를 전송
document.querySelector('#messageInput').value = ''; // 메시지 전송 후 입력 필드를 지움
});
// 메시지 수신
socket.on('chatMessage', (data) => {
const chatBox = document.querySelector('#chatBox');
const messageElement = document.createElement('p');
messageElement.textContent = `${data.id}: ${data.message}`;
chatBox.appendChild(messageElement);
});
// 사용자 연결 종료
socket.on('userDisconnect', (data) => {
const chatBox = document.querySelector('#chatBox');
const messageElement = document.createElement('p');
messageElement.textContent = `사용자 ${data.id} 연결 종료`;
chatBox.appendChild(messageElement);
});
// events.js
module.exports = (socket, io) => {
// 사용자 메시지 처리
socket.on('chatMessage', (msg) => {
console.log('메시지 수신:', msg);
io.emit('chatMessage', { id: socket.id, message: msg });
});
// 사용자 연결 해제 처리
socket.on('disconnect', () => {
console.log('사용자 연결 종료:', socket.id);
io.emit('userDisconnect', { id: socket.id });
});
};
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="main.css">
<title>채팅 서비스</title>
</head>
<body>
<div id="chatBox"></div>
<form id="chatForm">
<input type="text" id="messageInput" placeholder="메시지를 입력하세요..." required />
<button type="submit">전송</button>
</form>
<script src="/socke t.io/socket.io.js"></script>
<script src="client.js"></script>
</body>
</html>
'Project > ST00CK' 카테고리의 다른 글
RKE2 Rancher 설치 실패 및 GCP 무료 크레딧 만료 ... 추후 계획 (2) | 2024.12.16 |
---|---|
GCP VM에 SSH 로 접근하기 (1) | 2024.12.10 |
Socket.IO, Kafka, Kafka Connect, ScyllaDB 사용 이유 (0) | 2024.12.05 |
GCP VM 설정 및 리소스 설치하기 (0) | 2024.11.28 |
메신저 + 주식 프로젝트 시작 (1) | 2024.11.27 |