background
React4분

Socket.io

2025년 5월 8일

“Socekt.IO는 무엇이며, 어떻게 동작하지?”

1. Socket.IO란?

  • Socket.IO는 WebSocket을 기반으로 한 실시간 양방향 통신 라이브러리
  • 서버와 클라이언트 간의 데이터 교환을 쉽게 할 수 있도록 도와줌
  • 웹소켓을 지원하지 않는 환경에서도 작동 가능

2. Socket.IO와 WebSocket의 차이

특징WebSocketSocket.IO
프로토콜WebSocketWebSocket + HTTP
자동 재연결
이벤트 기반 메시징
네임스페이스
방(Room) 기능
HTTP 폴백 지원

결론: WebSocket보다 확장성이 뛰어나고, 추가 기능(방, 네임스페이스, 미들웨어 등)이 많아 실시간 채팅, 게임, 주식 정보, IoT 등에 적합


3. Socket.IO 기본 사용법

3-1 서버(Node.js 기준) 설정

설치

bash
npm install socket.io express

서버 코드 (server.js)

javascript
const express = require("express");
const http = require("http");
const { Server } = require("socket.io");

const app = express();
const server = http.createServer(app);
const io = new Server(server, {
	cors : { origin: "*"} // CORS 설정
});

io.on("connection", (socket) => {
	console.log("클라이언트 연결:", socket.id);
	
	socket.on("message", (msg) => {
		console.log(`메시지 받음: ${msg}`);
		io.emit("message", msg);
	});
	
	socket.on("disconnect", () => {
		console.log("클라이언트 연결종료:", socket.id);
	});
});

server.listen(3000, () => console.log("서버 실행중..."));

3-2. 클라이언트(React)

설치

bash
npm install socket.io-client

React 클라이언트 코드(App.tsx)

typescript
import { useEffect, useState } from "react";
import { io } from "socket.io-client";

const socekt = io("http://localhost:3000");

const App = () => {
	const [messsages, setMessages] = useState<string[]>([]);
	const [input, setInput] = useState("");
	
	useEffect(() => {
		socket.on("message", (msg) => {
			setMessages((prev) => [...prev, msg]);
		});
		return () => {
			socket.off("message");
		};
}, []);

	const sendMessage = () => {
		socket.emit("message", input);
		setInput("");
	};
	
	return (
		<div>
      <ul>{messages.map((msg, i) => <li key={i}>{msg}</li>)}</ul>
      <input value={input} onChange={(e) => setInput(e.target.value)} />
      <button onClick={sendMessage}>전송</button>
    </div>
	);
};

export default App;

클라이언트에서 메시지를 보내면 서버에서 받아서 전체 클라이언트에게 전달됨


4. Socket.IO 핵심 기능

4-1 이벤트 기반 통신

typescript
socekt.on("customEvent", (data) => {
	console.log("받은 데이터", data);
	socket.emit("responseEvent", { success: true});
});
typescript
socket.emit("customEvent", { username: "Alice" });

socket.on("responseEvent", (data) => {
	console.log("서버 응답", data);
})

4-2 방(Room) 기능

같은 목적을 가진 사용자끼리 그룹을 만들어 메시지를 주고받을 수 있음

서버

javascript
socket.on("joinRoom", (room) => {
	socket.join(room);
	io.to(room).emit("message", `방 ${room}에 새 유저 입장`);
});

socket.on("leaveRoom", (room) => {
	socket.leave(room);
	io.to(room).emit("message", `방 ${room}에서 유저 퇴장`);
});

클라이언트

typescript
socekt.emit("joinRoom", "room1");

socekt.emit("leaveRoom", "room1");

4-3. 네임스페이스 (Namespace)

특정 기능별로 채널을 분리해서 사용 가능

서버

javascript
const chat = io.of("/chat");

chat.on("connection", (socket) => {
	console.log("채팅 네임스페이스 연결됨");
	
	socket.on("message", (msg) => {
		chat.emit("message", msg);
	});
});

클라이언트

typescript
const chatSocket = io("http://localhost:3000/chat");
chatSocket.emit("message", 안녕하세요!");

4-4 미들웨어 (인증, 로깅)

javascript
io.use((socket, next) => {
	const token = socket.handshake.auth.token;
	if (token === "valid_token") {
		next();
	} else {
		next(new Error("인증실패"));
	}
});

5. 고급 기능

5-1. 메시지 전송 방법

javascript
socket.to(socketId).emit("message", "개인 메시지");

io.to("room1").emit("message", "방 메시지");

socket.broadcast.emit("message", "모두에게");

5-2. 서버 간 확장 (Redis Adapter)

여러 대의 서버에서 같은 WebSocekt 이벤트를 공유할 수 있도록 Redis 활용

bash
npm install socekt.io-redis
javascript
const redisAdapter = require("socekt.io-redis");
io.adapter(redisAdapter({ host: "localhost", port: 6379}));

서버가 여러 대라도 모든 클라이언트에 동일한 메시지를 보낼 수 있음


결론

  • Socekt.IO웹 소켓을 기반으로 한 실시간 통신 라이브러리로, 이벤트 기반 메시징, 방 기능, 네임스페이스, 미들웨어 등을 지원하여 채팅, 게임, IoT, 실시간 알림 시스템 등에 최적화
  • WebSocekt보다 유연한 폴백 기능을 제공하며, 서버 확장 시 Redis Adapter와 함께 사용 가능

Spring Boot에서 Socket.IO 구현하기

“Spring Boot에서 Socket.IO 기능을 어떻게 구현할까?”

1. WebSocket vs Socket.IO

Spring Boot의 경우 기본적으로 WebSocket 을 지원하지만, Socket.IO방(Room), 네임스페이스, 자동 재연결 기능은 제공 x 따라서 Socket.IO 스타일의 기능을 Spring Boot에서 구현 하려면 Spring WebSocket + STOP 또는 Netty Socket.IO 라이브러리를 사용할 수 있음

기능Spring WebSocketSocket.IO
자동 재연결
이벤트 기반 메시징
방(Room) 기능❌(직접 구현 필요)
네임스페이스❌(직접 구현 필요)
HTTP 폴백

결론: Spring Boot에서는 WebSocket과 STOMP를 사용해 Socket.IO와 유사한 기능을 구현할 수 있음


2. Spring Boot WebSocekt 설정

2-1. WebSocket 의존성 추가

xml
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-websocekt</artifactId>
</dependency>

2-2. WebSocekt 설정 (WebSocketConfig.java)

java
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new ChatWebSocketHandler(), "/ws/chat")
                .setAllowedOrigins("*"); // CORS 허용
    }
}

ChatWebSocketHandler에서 WebSocket 메시지를 처리


3. WebSocket 핸들러 구현

3-1 WebSocket 핸들러 (ChatWebSocketHandler.java)

java
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.concurrent.CopyOnWriteArrayList;

public class ChatWebSocketHandler extends TextWebSocketHandler {
    private static final CopyOnWriteArrayList<WebSocketSession> sessions = new CopyOnWriteArrayList<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        sessions.add(session);
        System.out.println("✅ 새로운 클라이언트 연결: " + session.getId());
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.out.println("📩 메시지 받음: " + message.getPayload());

        // 모든 클라이언트에게 메시지 전송
        for (WebSocketSession s : sessions) {
            s.sendMessage(new TextMessage(message.getPayload()));
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        sessions.remove(session);
        System.out.println("❌ 클라이언트 연결 종료: " + session.getId());
    }
}

클라이언트가 메시지를 보내면, 모든 클라이언트에게 전달됨


4. STOP와 SockJS 활용 (Socket.ID 유사 기능 구현)

4-1. STOMP란?

  • STOMP(Simple Text Oriented Messaging Protocol)는 메시지 브로커(RabbitMQ, ActiveMQ)와 함께 사용 가능
  • 클라이언트가 서버와 주제(topic) 기반으로 소통 (Socket.IO의 네임스페이스와 비슷함)

4-2. WebSocket 설정 (WebSocketConfig.java)

java
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic"); // 클라이언트 구독 경로
        config.setApplicationDestinationPrefixes("/app"); // 클라이언트 메시지 전송 경로
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws") // WebSocket 엔드포인트
                .setAllowedOrigins("*")
                .withSockJS(); // SockJS 지원
    }
}

4-3. 메시지 컨트롤러 (ChatController.java)

java
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;

@Controller
public class ChatController {

    @MessageMapping("/chat") // 클라이언트 메시지 전송 주소 (/app/chat)
    @SendTo("/topic/messages") // 모든 구독자에게 메시지 전송
    public String sendMessage(String message) {
        return message;
    }
}

4-4. 클라이언트 (React)

typescript
import { useEffect, useState } from "react";
import SockJS from "sockjs-client";
import { Stomp } from "@stomp/stompjs";

const socket = new SockJS("http://localhost:8080/ws");
const stompClient = Stomp.over(socket);

const App = () => {
  const [messages, setMessages] = useState<string[]>([]);
  const [input, setInput] = useState("");

  useEffect(() => {
    stompClient.connect({}, () => {
      stompClient.subscribe("/topic/messages", (msg) => {
        setMessages((prev) => [...prev, msg.body]);
      });
    });

    return () => stompClient.disconnect();
  }, []);

  const sendMessage = () => {
    stompClient.send("/app/chat", {}, input);
    setInput("");
  };

  return (
    <div>
      <ul>{messages.map((msg, i) => <li key={i}>{msg}</li>)}</ul>
      <input value={input} onChange={(e) => setInput(e.target.value)} />
      <button onClick={sendMessage}>전송</button>
    </div>
  );
};

export default App;

5. 방(Room) 기능 구현

java
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.messaging.handler.annotation.MessageMapping;

@Controller
public class ChatController {
    private final SimpMessagingTemplate template;

    public ChatController(SimpMessagingTemplate template) {
        this.template = template;
    }

    @MessageMapping("/room/{roomId}")
    public void sendMessage(String message, @DestinationVariable String roomId) {
        template.convertAndSend("/topic/room/" + roomId, message);
    }
}

/topic/room/{roomId} 구독 시 방 기능 구현 가능


결론

  • Spring Web Socket + STOMP를 사용하면 **Socket.IO의 기능(방, 네임스페이스, 자동 재연결)**을 구현 가능
  • SockJS 지원으로 WebSocekt 미지원 환경에서도 동작
  • RabbitMQ와 연결하면 확장성 증가

태그

#React#Socket