[Spring] WebSocket이란? 스프링 프로젝트 웹소켓 구현
by 우와한개발자
1. Web socket 이란?
- 클라이언트와 서버 간 양방향 통신을 지원하는 프로토콜
- HTTP와 달리 한 번 연결되면 연결을 유지 (Persistent Connection)
- 실시간 채팅, 알림, 주식 시세 등에 활용
1) HTTP vs WebSocket 비교
| 구분 | HTTP | WebSocket |
| 통신 방식 | 단방향 (요청-응답) | 양방향 |
| 연결 유지 | 요청마다 연결/종료 | 연결 유지 (Persistent) |
| 실시간성 | 낮음 | 높음 |
| 오버헤드 | 매 요청마다 헤더 포함 | 최초 연결 후 헤더 없음 |
| 사용 사례 | 일반 웹 요청 | 채팅, 알림, 실시간 데이터 |
2) 전체 흐름

3) WebSocketHandler 인터페이스 메서드
| 리턴 타입 | 메서드 | 설명 |
| void | afterConnectionEstablished(WebSocketSession session) | WebSocket 연결이 성공적으로 수립된 후 호출됨 |
| void | handleMessage(WebSocketSession session, WebSocketMessage<?> message) | 클라이언트로부터 메시지를 수신했을 때 호출됨 |
| void | handleTransportError(WebSocketSession session, Throwable exception) | 메시지 전송 중 오류 발생 시 호출됨 |
| void | afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) | WebSocket 연결이 종료될 때 호출됨 (정상 종료 또는 오류 포함) |
| boolean | supportsPartialMessages() | WebSocket 메시지를 부분적으로 처리할지 여부 |
4) WebSocketHandler 인터페이스 구현 클래스
| 클래스명 | 클래스 설명 |
| AbstractWebSocketHandler | 기본 메서드가 비어있는 WebSocketHandler 추상 클래스 |
| BinaryWebSocketHandler | 바이너리 메시지만 처리하는 WebSocketHandler (텍스트 메시지는 거부됨) |
| ExceptionWebSocketHandlerDecorator | 핸들러에서 발생한 예외를 처리하고 세션을 종료하는 데코레이터 |
| LoggingWebSocketHandlerDecorator | WebSocket 라이프사이클 이벤트에 대한 로그를 추가하는 데코레이터 |
| PerConnectionWebSocketHandler | 연결마다 새로운 WebSocketHandler 인스턴스를 생성하여 처리 |
| SockJsWebSocketHandler | SockJS 지원을 위한 핸들러 (fallback, heartbeat 처리 포함) |
| SubProtocolWebSocketHandler | STOMP 등 하위 프로토콜 메시지를 처리하는 핸들러 |
| TextWebSocketHandler | 텍스트 메시지만 처리하는 WebSocketHandler (바이너리 메시지는 거부) |
| WebSocketHandlerDecorator | 기존 WebSocketHandler를 감싸 기능을 확장하는 데코레이터 |
2. WebSocket 구현 : Spring Boot (Maven)
1) pom.xml
- Spring Boot에서는 spring-boot-starter-websocket 하나만 추가하면 WebSocket 관련 설정이 자동으로 처리됨
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2) WebSocketConfig
- WebSocket 엔드포인트를 등록하고 핸들러와 URL을 연결
// config/WebSocketConfig.java
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
private final ChatWebSocketHandler chatWebSocketHandler;
public WebSocketConfig(ChatWebSocketHandler chatWebSocketHandler) {
this.chatWebSocketHandler = chatWebSocketHandler;
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(chatWebSocketHandler, "/ws/chat")
.setAllowedOrigins("*"); // CORS 허용 (운영 환경에서는 도메인 지정 권장)
}
}
3) WebSocketHandler 구현
- TextWebSocketHandler를 상속해서 텍스트 메시지를 처리
- 연결된 모든 세션에 메시지를 브로드캐스트하는 채팅 구현 예시
// handler/ChatWebSocketHandler.java
@Component
public class ChatWebSocketHandler extends TextWebSocketHandler {
// 연결된 세션 목록 (멀티스레드 환경에서 안전한 CopyOnWriteArrayList 사용)
private final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
// 연결 수립 시
@Override
public void afterConnectionEstablished(WebSocketSession session) {
sessions.add(session);
System.out.println("연결됨 : " + session.getId());
}
// 메시지 수신 시 → 연결된 모든 클라이언트에게 브로드캐스트
@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);
System.out.println("연결 종료 : " + session.getId());
}
}
⚠️CopyOnWriteArrayList를 사용하는 이유
- 여러 클라이언트가 동시에 연결/해제될 때 ArrayList는 동시성 문제가 발생할 수 있어, 멀티스레드 환경에서 안전한 CopyOnWriteArrayList를 사용
4) 클라이언트 (JavaScript)
const socket = new WebSocket("ws://localhost:8080/ws/chat");
// 연결 수립 시
socket.onopen = () => {
console.log("WebSocket 연결 성공");
socket.send("안녕하세요!");
};
// 메시지 수신 시
socket.onmessage = (event) => {
console.log("받은 메시지 : " + event.data);
};
// 연결 종료 시
socket.onclose = () => {
console.log("WebSocket 연결 종료");
};
// 오류 발생 시
socket.onerror = (error) => {
console.error("WebSocket 오류 : ", error);
};
3. WebSocket 구현 - Spring Legacy (XML 설정)
1) pom.xml
- Spring Legacy에서는 spring-websocket과 spring-messaging을 별도로 추가
<!-- Spring WebSocket -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>5.3.30</version>
</dependency>
<!-- Spring Messaging (WebSocket 메시지 처리에 필요) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>5.3.30</version>
</dependency>
<!-- Jackson (JSON 직렬화) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
2) servlet-context.xml 또는 Java Config
- servlet-context.xml 방식
<!-- servlet-context.xml -->
<websocket:handlers>
<websocket:mapping handler="chatWebSocketHandler" path="/ws/chat"/>
<websocket:handshake-interceptors>
<bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers>
<bean id="chatWebSocketHandler"
class="com.example.handler.ChatWebSocketHandler"/>
- Java Config 방식
// config/WebSocketConfig.java
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ChatWebSocketHandler(), "/ws/chat")
.addInterceptors(new HttpSessionHandshakeInterceptor())
.setAllowedOrigins("*");
}
}
3) WebSocketHandler 구현
- Spring Boot와 동일하게 TextWebSocketHandler를 상속.
// handler/ChatWebSocketHandler.java
public class ChatWebSocketHandler extends TextWebSocketHandler {
private final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
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) throws Exception {
sessions.remove(session);
}
}
4. Spring Boot vs Spring Legacy 비교
| 구분 | Spring Boot | Spring Legacy |
| 의존성 | spring-boot-starter-websocket 1개 | spring-websocket + spring-messaging 별도 추가 |
| 설정 방식 | Java Config (@EnableWebSocket) | XML 또는 Java Config |
| 자동 설정 | 대부분 자동 처리 | 수동 Bean 등록 필요 |
| 핸들러 구현 | 동일 (TextWebSocketHandler 상속) | 동일 (TextWebSocketHandler 상속) |
'프레임워크 > Spring' 카테고리의 다른 글
블로그의 정보
우와한개발자 님의 블로그
우와한개발자