Socket.io


웹소켓

HTTP 프로토콜은 Client가 요청을 보내면 Server가 이에 응답해주는 방식으로 진행된다. 그러나 이러한 방식은 요청이 있을 때만 1회성으로 데이터를 주고받는 것이기에 실시간으로 주식가격을 보여준다거나 실시간 채팅을 하는 등의 기능을 구현하기에는 부적합하다.

이러한 배경에서 등장한 것이 웹소켓 통신이다. 웹소켓 통신은 ws 라는 프로토콜을 이용한 통신으로, 이를 사용하면 서버와 브라우저 간 연결을 유지한 상태로 데이터를 교환할 수 있다. http와 https의 관계처럼 wss도 존재한다. 이러한 웹소켓 통신을 구현하는데 도움을 주는 라이브러리는 여러가지가 있지만, 그 중 하나가 Socket.io이다. 아래는 Socket.io를 이용하여 웹소켓 통신을 구현한 예제이다.

서버 측 설정

const express = require('express');
const cors = require('cors');
const http = require('http');
const { Server } = require('socket.io');
require('dotenv').config();
const server = express();
const { PORT } = process.env;
const httpServer = http.createServer(server);
// httpServer를 이용, 웹소켓 생성 후 cors 설정
const io = new Server(httpServer, {
cors: {
origin: 'http://localhost:3000',
credentials: true,
optionsSuccessStatus: 200,
},
});
io.on('connection', (socket) => {
// 연결 성공 시 콘솔로그
console.log('소켓 연결 성공');
socket.on('join', (data) => {
// 방에 참가도록 하는 코드
socket.join(data.roomId);
// 특정 방에 참가하였을 경우 콘솔로그
console.log(`${data.userName}님이 ${data.roomId}방에 참가하였습니다.`);
});
socket.on('send_message', (data) => {
console.log(
// 메시지를 보냈을 때 콘솔로그
`${data.roomId}방에서 ${data.userName}님이 메시지를 보냈습니다: ${data.message}`,
);
// 받은 메시지를 해당 방에 전송
socket.to(data.roomId).emit('receive_message', data);
});
});
httpServer.listen(PORT, () => {
// 서버 구동 시 콘솔로그
console.log(`${PORT}번에서 서버가 작동 중입니다!`);
});

클라이언트 측 설정

export default function ChatRoom() {
const [userName, setUserName] = useState(null);
const [loading, setLoading] = useState(true);
const chatInput = useRef('');
const socket = io(process.env.REACT_APP_API_URL);
const messageWindow = useRef(null);
const roomId = useSelector((state) => state.chat.roomId);
const sendMessage = async (e) => {
e.preventDefault();
if (chatInput.current.value === '') return;
const message = chatInput.current.value;
const data = {
roomId,
userName,
message,
};
const newChat = {
userName,
message,
};
// 소켓으로 메시지 전송
await socket.emit('send_message', data);
// 채팅 로그 갱신
const newChatLog = [...chatLog, newChat];
dispatch(setChatLog(newChatLog));
// 입력창 비우기
chatInput.current.value = '';
};
const getChatLog = async () => {
const res = await getChat(roomId);
dispatch(setChatLog(res.chatLog));
setLoading(false);
};
useEffect(() => {
socket.emit('join', { roomId, userName });
// 서버에서 메시지를 받으면 채팅 로그 갱신
socket.on('receive_message', (data) => {
if (data.userName === userInfo.userId) return;
const date =
new Date().getHours().toString().padStart(2, '0') +
':' +
new Date().getMinutes().toString().padStart(2, '0');
const newChat = {
userName: data.userName,
message: data.message,
date,
};
dispatch(setChatLog([...chatLog, newChat]));
});
}, [socket, roomId, userName, chatLog]);