스프링부트 소켓서버 만들기 예제

 

1) WebSocket 서버 예제 (Spring Boot + STOMP)

웹소켓을 직접 다루기보다, 메시지 라우팅이 편리한 STOMP 프로토콜을 함께 쓰면 채팅 등 브로드캐스트 구조를 쉽게 구현할 수 있습니다.

프로젝트 설정

  • Spring Initializr로 생성
    • Dependencies: Spring Web, WebSocket, Lombok(선택), Spring Boot DevTools(선택)
  • Gradle 기준 build.gradle 예시:

gradle

dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-websocket' implementation 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' }

WebSocket/STOMP 설정 클래스

java

// WebSocketConfig.java package com.example.websocketdemo.config; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.*; @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { // STOMP 엔드포인트 등록: 클라이언트가 연결하는 URL @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws") // 예: ws://localhost:8080/ws .setAllowedOriginPatterns("*") .withSockJS(); // 필요 시 SockJS 폴백 } // 메시지 브로커 설정 @Override public void configureMessageBroker(MessageBrokerRegistry registry) { // 서버 -> 클라이언트로 브로드캐스트할 목적지(prefix) registry.enableSimpleBroker("/topic", "/queue"); // 클라이언트 -> 서버로 보낼 메시지의 prefix (컨트롤러 @MessageMapping 과 연결) registry.setApplicationDestinationPrefixes("/app"); } }

메시지 DTO

java

// ChatMessage.java package com.example.websocketdemo.model; import lombok.*; @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class ChatMessage { private String sender; private String content; }

메시지 핸들러(Controller)

java

// ChatController.java package com.example.websocketdemo.controller; import com.example.websocketdemo.model.ChatMessage; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Controller; @Controller public class ChatController { // 클라이언트가 /app/chat 으로 보내면, /topic/messages 로 브로드캐스트 @MessageMapping("/chat") @SendTo("/topic/messages") public ChatMessage broadcast(ChatMessage message) { // 필요한 로직(검증, 필터링 등)을 이곳에서 수행 return message; // 그대로 반환하면 구독자들에게 전달 } }

테스트용 클라이언트 (HTML)

아래 파일을 src/main/resources/static/index.html로 두고 서버 실행 후 http://localhost:8080 접속하여 테스트할 수 있습니다.

html

<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8" /> <title>WebSocket STOMP 테스트</title> <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script> </head> <body> <h2>간단 채팅</h2> <input id="sender" placeholder="닉네임" /> <input id="content" placeholder="메시지" /> <button onclick="sendMessage()">전송</button> <div id="logs"></div> <script> let stompClient = null; function connect() { const socket = new SockJS('/ws'); stompClient = Stomp.over(socket); stompClient.connect({}, () => { // 서버 브로드캐스트 구독 stompClient.subscribe('/topic/messages', (msg) => { const body = JSON.parse(msg.body); const line = `[${body.sender}] ${body.content}`; document.getElementById('logs').innerHTML += `<div>${line}</div>`; }); }); } function sendMessage() { const sender = document.getElementById('sender').value || '익명'; const content = document.getElementById('content').value; const payload = { sender, content }; stompClient.send('/app/chat', {}, JSON.stringify(payload)); } window.onload = connect; </script> </body> </html>

실행 방법

  • 애플리케이션 실행: gradle bootRun 또는 IDE에서 Spring Boot 앱 실행
  • 브라우저에서 두 개의 탭을 열어 메시지를 전송하면 서로에게 브로드캐스트되는 것을 확인하실 수 있습니다.

2) 간단한 WebSocket(핸드셰이크만, STOMP 없이) 예제

STOMP 없이 순수 WebSocket을 쓰고 싶을 경우 아래처럼 Handler를 구현할 수 있습니다.

java

// SimpleWebSocketHandler.java package com.example.websocketdemo.ws; import org.springframework.web.socket.*; import org.springframework.web.socket.handler.TextWebSocketHandler; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class SimpleWebSocketHandler extends TextWebSocketHandler { private final Set<WebSocketSession> sessions = ConcurrentHashMap.newKeySet(); @Override public void afterConnectionEstablished(WebSocketSession session) { sessions.add(session); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // 받은 메시지를 모든 세션에 브로드캐스트 for (WebSocketSession s : sessions) { if (s.isOpen()) { s.sendMessage(message); } } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { sessions.remove(session); } }

java

// RawWebSocketConfig.java package com.example.websocketdemo.config; import com.example.websocketdemo.ws.SimpleWebSocketHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.*; @Configuration @EnableWebSocket public class RawWebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(simpleWebSocketHandler(), "/ws-raw") .setAllowedOriginPatterns("*"); } @Bean public SimpleWebSocketHandler simpleWebSocketHandler() { return new SimpleWebSocketHandler(); } }

클라이언트 예시:

html

<script> const ws = new WebSocket("ws://localhost:8080/ws-raw"); ws.onmessage = (e) => console.log("받음:", e.data); ws.onopen = () => ws.send("안녕하세요!"); </script>

3) 순수 TCP 소켓 서버 예제 (Netty 또는 Java Socket)

Spring Boot 내부에서 간단히 TCP 소켓을 열고 싶다면, CommandLineRunner로 서버 소켓을 띄울 수 있습니다. 다만 운영 환경에서는 Netty 같은 프레임워크 사용을 권장드립니다.

java

// TcpServerRunner.java package com.example.websocketdemo.tcp; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; import java.io.*; import java.net.ServerSocket; import java.net.Socket; @Component public class TcpServerRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { new Thread(this::startServer, "tcp-server-thread").start(); } private void startServer() { try (ServerSocket serverSocket = new ServerSocket(9090)) { System.out.println("TCP 서버 시작: 9090"); while (true) { Socket client = serverSocket.accept(); new Thread(() -> handleClient(client)).start(); } } catch (IOException e) { e.printStackTrace(); } } private void handleClient(Socket client) { try (BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()))) { String line; while ((line = in.readLine()) != null) { // 에코 반환 out.write("echo: " + line); out.newLine(); out.flush(); } } catch (IOException e) { e.printStackTrace(); } } }

클라이언트 테스트:

  • 터미널에서: telnet localhost 9090 후 문자열 입력
  • 또는 netcat: nc localhost 9090

댓글

이 블로그의 인기 게시물