일반적인 웹사이트는 보통 HTTP(Hypertext Transfer Protocol)라는 통신 방식을 사용한다.
이건 마치 택배 주문과 같다.
여러분의 브라우저(클라이언트)가 서버에 "이 페이지 주세요!"라고 요청하면, 서버가 페이지를 보내주는 식이다.
요청이 있을 때만 통신하고, 한 번 보내면 끝나는 ..
이를 단발성 통신이라고 한다.
하지만, 실시간으로 여러 명이 동시에 작업하는 경우에는 이런 방식이 적합하지 않다.
한 사이트에 여러명이 접속해서 실시간으로 페이지를 변경하는 경우,
내가 " 이 페이지 주세요 ! " 라고 요청하지 않아도
변경사항이 내 화면에 반영돼야하는 경우가 있다.
이때 필요한 것이 바로 소켓(Socket),
특히 웹소켓(WebSocket)이다.
웹소켓은 브라우저와 서버 사이에 지속적인 양방향 통신 채널을 열어준다.
마치 전화 통화처럼, 한 번 연결되면 서로 끊임없이 대화할 수 있는 길을 만들어주는 거다.
(우리 프로젝트에 적용해보자.)
웹소켓은 표준 기술이지만, 실제로 구현하려면 다소 복잡한 부분이 있다.
연결이 끊겼을 때 다시 연결하거나, 오래된 브라우저에서 웹소켓을 지원하지 않을 경우를 대비하는 등 고려할 점이 많다.
Socket.IO는 이런 복잡한 웹소켓 통신을 아주 쉽게 만들어주는 라이브러리이다.
Socket.IO는 다음과 같은 장점들을 제공한다.
전체적인 흐름은 다음과 같다.
우리 프로젝트처럼 다수의 사용자가 동시에 접속하는 서비스는 단일 서버로는 감당하기 어렵기 때문에,
운영 환경에서는 보통 여러 대의 서버 인스턴스를 띄우고,
그 위에 로드 밸런서를 두어 트래픽을 분산시킨다.
하지만 이런 구조에서는 문제가 생긴다.
예를 들어 A 사용자가 서버 1에 연결되어 있고,
B 사용자가 서버 2에 연결되어 있다고 하자.
A가 픽셀을 그렸을 때, 서버 1은 이 정보를 받지만,
서버 2에 연결된 B는 그 소식을 들을 방법이 없다!
서버끼리는 기본적으로 서로 대화를 하지 않기 때문이다.
이럴 때 필요한 것이 바로 Redis
기반의 Socket.IO
어댑터다.
모든 서버가 공통된 Redis 서버에 연결되면,
서버 간에도 이벤트 메시지를 주고받을 수 있어
마치 하나의 서버처럼 모든 사용자에게 실시간 브로드캐스트가 가능해진다.
Socket.IO는 Redis 어댑터를 아주 간단하게 적용할 수 있게 되어 있다.
"Socket.IO는 Redis 어댑터와의 연동이 매우 쉽다.
그리고 실시간 멀티 서버 환경에서 Socket.IO를 쓰려면 어댑터가 꼭 필요하다.
그래서 Redis를 선택한 결정적인 이유가 ‘Socket.IO와의 궁합’ 때문이기도 하다.
npm install redis @socket.io/redis-adapter
import { Server } from "socket.io";
import { createAdapter } from "@socket.io/redis-adapter";
import { createClient } from "redis";
const io = new Server(httpServer);
// Redis 연결 (ElastiCache 등 사용 가능)
const pub = createClient({ url: process.env.REDIS_URL });
const sub = pub.duplicate();
await Promise.all([pub.connect(), sub.connect()]);
io.adapter(createAdapter(pub, sub)); // 🔗 이 한 줄이 핵심
io.on("connection", (socket) => {
socket.on("drawPixel", (data) => {
socket.broadcast.emit("drawPixel", data); // 기존 로직 그대로 사용 가능!
});
});
새로운 인스턴스가 Auto Scaling으로 자동 생성되어도
이 Redis 어댑터 설정만 있으면 바로 참여하게 된다.
즉, 자동 확장되더라도 모든 서버가 실시간 통신을 공유할 수 있게 되는 것이다.