Socket.IO로 실시간 채팅이랑 시세, 접속자 수를 한 번에 처리했다
코인 사이트의 실시간 통신 구조
#SocketIO #실시간 #웹소켓 #백엔드
코인 사이트를 만들다 보니 실시간으로 처리할 게 꽤 많았다. 시세는 계속 변하고, 채팅은 바로바로 떠야 하고, 지금 몇 명이 보고 있는지도 실시간으로 떠야 했다. 앞에서 다룬 SSE는 서버→클라이언트 단방향인데, 채팅처럼 양방향이 필요한 곳 은 웹소켓이 맞다. 그래서 Socket.IO를 썼다. 이 글에선 한 번의 연결로 채팅, 시세 알림, 접속자 수를 어떻게 같이 처리했는지 정리해본다. 연결되면 과거 채팅부터 보내준다 Socket.IO는 클라이언트가 접속하면 connection 이벤트가 뜬다. 새 사용자가 들어왔는데 채팅창이 텅 비어 있으면 어색하니까, 연결되자마자 최근 메시지부터 DB에서 꺼내 보내준다 . io.on('connection', async (socket) = { // 최근 메시지 100개를 DB에서 꺼내서 const rows = await query( 'SELECT * FROM chat_messages ORDER BY id DESC LIMIT ?', [MAX_MESSAGES] ); // 이 사용자에게만 보내준다 (socket.emit = 나한테만) socket.emit('previousMessages', { messages: rows.reverse(), hasMore: /* 더 있는지 */, }); }); 여기서 socket.emit 은 방금 접속한 그 사람한테만 보낸다. 과거 기록은 새로 들어온 사람만 필요하니까. 이 구분이 Socket.IO의 핵심이다. emit 두 종류를 구분하는 게 핵심 Socket.IO에서 제일 중요한 건 "누구한테 보낼 거냐" 다. 두 가지를 쓴다. 1. socket.emit — 지금 이 연결(한 사람)에게만 2. io.emit — 접속한 전체에게 broadcast 과거 메시지는 socket.emit(나만), 새 채팅 메시지는 io.emit(전체)으로 보낸다. 누가 채팅을 치면 모두에게 동시에 떠야 하니까. 시세 알림도 같은 연결로 비트코인이 특정 가격을 돌파하면 채팅창에 봇 메시지를 띄우게 했는데, 이것도 같은 Socket.IO 연결을 그대로 쓴다. 15초마다 시세를 체크하다가, 돌파나 이탈이 감지되면 전체에게 broadcast 한다. // 가격 돌파 감지되면 모두에게 알림 io.emit('chatMessage', botMessage); io.emit('btcAlert', { message: msg, isBreakout }); 채팅이랑 시세 알림이 완전히 다른 기능 같지만, 결국 "서버에서 일이 생기면 모두에게 알린다" 는 같은 패턴이다. 연결 하나로 둘 다 처리하니까 구조가 단순해졌다. 접속자 수는 거의 공짜로 지금 몇 명이 보고 있는지는 따로 카운팅할 필요도 없었다. Socket.IO가 현재 연결 수를 io.engine.clientsCount 로 들고 있어서, 누가 들어오고 나갈 때마다 그 숫자를 broadcast 하면 끝이다. io.emit('userCount', io.engine.clientsCount); 정리하면 실시간 기능이 여러 개라고 통신 채널을 여러 개 만들 필요는 없었다. 연결 하나를 열어두고, 이벤트 이름으로 종류를 구분 하면 채팅이든 시세든 접속자 수든 다 같은 통로로 흐른다. SSE랑 비교하면, 단방향이면 SSE / 양방향(채팅처럼 클라이언트도 서버로 보내야 함)이면 Socket.IO. 이 기준 하나로 둘 중 뭘 쓸지가 거의 정해진다. 도구는 화려한 걸 고르는 게 아니라 통신 방향에 맞는 걸 고르는 거였다.