해당 글은 Socket.IO 공식문서 중 다음 두 페이지의 내용을 담고 있습니다. Socket.IO의 정체와 역할(그래서 얘가 대체 뭐하는 앤데?)에 대한 감을 잡고 싶다면 해당 글이 도움이 될거라 생각합니다.
Socket.IO 정체 내가 한 줄 정리 해드림.
Socket.IO란... 웹소켓을 더 안전하고 편리하게 사용할 수 있게 해주는 라이브러리다.
Socket.IO는 웹소켓이라는 통신 프로토콜을 더 쉽게 사용할 수 있도록 사용하기 편리한 인터페이스
와 연결의 안정성을 높이는 부가 기능들
을 추가한 라이브러리라고 할 수 있다.
공식문서에서는 Socket.IO를 클라이언트와 서버 간의 저지연의(low-latency), 양방향의, 이벤트 기반의 커뮤니케이션을 제공하는 라이브러리라고 소개하고 있다.
결론부터 말하자면, 그렇지 않다. 어떤 차이점이 있는지 살펴보자.
Socket.IO는 브라우저, 네트워크의 상태에 따라 다음 세 가지 low-level transports 옵션 중 하나를 선택하여 커넥션을 생성한다.
웹소켓은 Socket.IO가 사용하는 여러 통신 프로토콜 중 하나이다. 즉, Socket.IO와 웹소켓은 완전히 다른 층위에 속한 개념이라고 볼 수 있다. 여러 블로그들에서 '웹소켓의 사용'과 'Socket.IO의 사용'을 동일시하고는 하는데, 이는 부정확한 이해라고 볼 수 있다. 웹소켓을 처음 사용해보는 사람 대부분이 Socket.IO 라이브러리를 사용하는 경우가 많기 때문에 이러한 오해가 발생하는 듯하다.(내 이야기다 😅.)
추가적으로 Socket.IO이 WebSocket을 사용하긴 하지만, 각 패킷에 추가적인 메타데이터를 붙이기 때문에 순수 WebSocket과는 호환이 불가능하다.
다음과 같은 기능들을 제공한다.
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) {
// the other side did not acknowledge the event in the given delay
} else {
console.log(response); // "got it"
}
});
Broadcasting: 연결되어 있는 모든 클라이언트에게 한 번에 이벤트를 전송할 수 있다.
// 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는 1️⃣ 데이터의 전송과 2️⃣ disconnection 감지를 담당하며, Socket.IO는 여기에 몇 가지 부가 기능을 추가한 것이라고 이해하면 된다.
{
"sid": "FSDjX-WRwSA4zTZMALqx",// 세션 ID. 이후의 모든 HTTP 요청들의 쿼리 파라미터에 포함된다.
"upgrades": ["websocket"], // 서버가 지원하는 "better" transports의 리스트
"pingInterval": 25000, // heartbeat mechanism 에 사용되는 값
"pingTimeout": 20000
}
네트워크의 연결이 끊기면 이를 빠르게 감지할 수 있어야 한다. 유저에게 현재 메시지의 송/수신이 불가능한 상태임을 인지시키거나, 재연결을 시도하는 등의 대응을 해야 하기 때문이다.
Engine.IO는 다음 네 가지 경우에 '연결이 끊겼다' 라고 간주한다. 즉, Engine.IO에서는 disconnection을 다음과 같이 정의한다.
pingInterval
마다 PING 패킷을 보낸다. 클라이언트는 pingTimeout
안에 PONG 패킷을 보내야 한다. pingTimeout 안에 PONG 패킷을 돌려받지 못하면, 서버는 연결이 끊어졌다고 간주한다. 반대로 클라이언트 입장에서 일정 시간 동안(pingInterval
+pingTimeout
) PING 패킷을 받지 못하면 연결이 끊어졌다고 간주한다채팅 기능을 기준으로 간략하게 사용법을 설명해보겠다.
우선, socket 인스턴스를 생성해야 한다.
import { io } from "socket.io-client";
const socket = io("https://server-domain.com");
나의 경우 전역에 파일을 하나 만들어 인스턴스를 생성한 다음, 컴포넌트에서 이를 import하여 사용했다.
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")) 리스너에 원하는 콜백 함수를 전달하면 된다.
참고로 socket 인스턴스는 다음과 같은 이벤트들을 발생 시킨다.
socket.disconnect()
) 이 경우에는 재연결을 자동으로 시도하지 않는다.socket 인스턴스의 라이프사이클 도식을 보면 좀 더 명확히 이해할 수 있다.
위에서 살펴본 이벤트는, Socket.IO가 알아서 발생시켜주는 이벤트라고 할 수 있다. 메시지를 주고 받기 위해선 커스텀 이벤트를 발생시켜야 한다.
서버와 메시지를 주고 받기 위한 이벤트를 정의해야 한다. 이벤트명은 자유이다. 가령 message
라는 이름의 이벤트를 정의할 수 있다. 그리고 이벤트 emit 시 넘겨줄 인자를 함께 정의해야 한다. 아래는 예시 코드다.
// 메시지 발송: 이벤트 emit
const sendMessage = () => {
socket.emit('message',{name, message})
}
// 메시지 수신: 이벤트 listen
useEffect(() => {
socket.on('message',({name,message})=>{
setChat([...chat,{name,message}]) // 화면에 렌더링하는 chat state에 새 메시지를 추가한다.
})
},[])
이번 글에서는 Socket.IO의 정체와 동작 방식, 기본적인 사용법에 대해 알아보았다. 다음 글에서는 나에게 큰 재미와 고통을 주었던😇 채팅에서의 에러 핸들링과 연결 상태 관리를 구현한 과정에 대해 기술해보도록 하겠다.
React Hook Form 글도 그렇고 이렇게 공식문서 직접 읽어보고 인사이트나 주의점 정리해 주시는 글들의 가치가 정말 크다고 생각합니다. 덕분에 저도 공부한 내용 어떤 식으로 정리하면 좋을지 힌트도 얻어 갑니다. 감사합니다!