Socket.IO란 간단히 말하면 웹 소켓 연결을 통해 클라이언트와 서버간에 실시간 양방향 통신을 가능하게하는 JavaScript 라이브러리이다.
Socket: 소켓은 클라이언트와 서버 간의 연결을 나타낸다. 소켓을 사용하여 실시간으로 데이터를 보내고 받을 수 있다.
Server: 서버는 클라이언트와의 소켓 연결을 설정하고 관리한다. 서버는 클라이언트로 이벤트를 방출하거나 클라이언트로부터 이벤트를 수신 할 수 있다.
Client: 클라이언트는 서버와의 소켓 연결을 설정하고 관리한다. 클라이언트는 서버와 동일하게, 서버로 이벤트를 방출하거나 서버에서 이벤트를 수신 할 수 있다.
Event: 이벤트는 소켓 연결을 통해 클라이언트와 서버간에 전송 된 메시지. 이벤트 이름은 서버와 클라이언트에서 동일하게 선언해야 한다. (JSON, 문자열, 이진 등 모든 형식의 데이터 포함 가능)
Namespace: 네임 스페이스는 클라이언트와 서버 간의 별도의 통신 채널. 단일 서버에서 여러 네임 스페이스를 작성할 수 있으며 클라이언트는 특정 네임 스페이스에 연결하여 해당 네임 스페이스의 다른 클라이언트에게만 이벤트를 수신하거나 방출 할 수 있다.
Room: 룸은 함께 결합 된 소켓 그룹입니다. 소켓은 여러 룸에 합류하거나 떠날 수 있다. 룸은 연결된 모든 고객에게 방송하는 대신 특정 클라이언트 그룹에 메시지를 보내는 데 유용.
Socket.IO는 브라우저, 네트워크의 상태에 따라 아래 세 가지 low-level transports 옵션 중 하나를 선택하여 커넥션을 생성한다.
- HTTP long-polling
- WebSocket
- WebTransport: WebSocket의 modern update를 제공하는 API
HTTP long-polling fallback: 웹소켓 연결이 불가능할 때 HTTP long-polling을 사용한다. 예컨대 유저의 브라우저가 웹소켓을 지원하지 않을 때
Automatic reconnection: 주기적으로 연결의 상태를 체크한다. 클라이언트의 연결이 갑자기 끊어지면 자동으로 재연결을 시도한다.
Packet buffering: 클라이언트 연결이 끊어지면 패킷들은 자동으로 버퍼에 쌓이고, 재연결 시에 전송된다. Socket이 연결되어 있지 않을 때에 전달된 이벤트는 버퍼에 쌓인다.
Acknowledgements: 이벤트를 전송하고 이벤트 전송에 대한 응답을 받는 편리한 방식을 제공한다.
// 이벤트를 발송한다.
socket.emit("hello", "world", (response) => {
console.log(response); // "got it"
});
// 이벤트 발생을 수신하고 핸들링 한다.
socket.on("hello", (arg, callback) => {
console.log(arg); // "world"
callback("got it");
});
socket.timeout(5000).emit("hello", "world", (err, response) => {
if (err) {
} else {
console.log(response); // "got it"
}
});
// to all connected clients
io.emit("hello");
// to all connected clients in the "news" room
io.to("news").emit("hello");
공식 문서에 뜬금 없이 Engine.IO라는 표현이 등장하는데, 이는 Socket.IO의 내부 엔진이다. Engine.IO는 데이터의 전송과 disconnection 감지를 담당하며, Socket.IO는 여기에 몇 가지 부가 기능을 추가한 것이라고 이해하면 된다.
import { io } from "socket.io-client";
const socket = io("https://server-domain.com");
Socket.IO는 이벤트 기반의 라이브러리다. 실제 사용 코드를 보면 쉽게 이해할 수 있다. 네트워크가 연결되었을 때 화면에 초록불을 표시하고 싶다고 해보자.
import socket from './socket.ts'
export default function App() {
const [isConnected, setIsConnected] = useState(false)
useEffect(() => {
socket.on("connect"), () => {
setIsConnected(true)
}
}, [])
return (
<div>{isConnected ? <div>connected</div> : null} </div>
)
}
즉 네트워크가 연결되었을 때 socket 인스턴스는 "connect" 라는 이름의 이벤트를 발생, 이 이벤트에 대한 리스너를 등록하고, (socket.on("connect")) 리스너에 원하는 콜백 함수를 전달.
서버와 메시지를 주고 받기 위한 이벤트를 정의해야 한다. 이벤트명은 자유이다. 가령 message 라는 이름의 이벤트를 정의할 수 있다. 그리고 이벤트 emit 시 넘겨줄 인자를 함께 정의해야 한다. 아래는 예시 코드다.
// 메시지 발송: 이벤트 emit
const sendMessage = () => {
socket.emit('message',{name, message})
}
// 메시지 수신: 이벤트 listen
useEffect(() => {
socket.on('message',({name,message})=>{
setChat([...chat,{name,message}]) // 화면에 렌더링하는 chat state에 새 메시지를 추가한다.
})
},[])