스프링부트 소켓서버 만들기 예제
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.*;
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
// STOMP 엔드포인트 등록: 클라이언트가 연결하는 URL
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws") // 예: ws://localhost:8080/ws
.setAllowedOriginPatterns("*")
.withSockJS(); // 필요 시 SockJS 폴백
}
// 메시지 브로커 설정
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.*;
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;
public class ChatController {
// 클라이언트가 /app/chat 으로 보내면, /topic/messages 로 브로드캐스트
public ChatMessage broadcast(ChatMessage message) {
// 필요한 로직(검증, 필터링 등)을 이곳에서 수행
return message; // 그대로 반환하면 구독자들에게 전달
}
}테스트용 클라이언트 (HTML)
아래 파일을 src/main/resources/static/index.html로 두고 서버 실행 후 http://localhost:8080 접속하여 테스트할 수 있습니다.
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();
public void afterConnectionEstablished(WebSocketSession session) {
sessions.add(session);
}
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// 받은 메시지를 모든 세션에 브로드캐스트
for (WebSocketSession s : sessions) {
if (s.isOpen()) {
s.sendMessage(message);
}
}
}
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.*;
public class RawWebSocketConfig implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(simpleWebSocketHandler(), "/ws-raw")
.setAllowedOriginPatterns("*");
}
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;
public class TcpServerRunner implements CommandLineRunner {
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
댓글
댓글 쓰기