WebSocket이란 무엇일까? (채팅기능)
WebSocket?
서버와 클라이언트 간에 Socket Connection을 유지해 언제든 양방향 통신 또는 데이터 전송이 가능하도록 하는 기술. Real-time web application 구현을 위해 널리 사용되어지고 있다.
웹 어플리케이션에서 기존의 서버와 클라이언트 간의 통신은 대부분 HTTP를 통해 이루어 졌으며 HTTP는 Request/response 기반의 Stateless protocol이다.
다시 말해, 서버와 클라이언트 간의 Socket connection 같은 영구적인 연결이 아니라 클라이언트 쪽에서 필요에 의해 request를 할때만 서버가 response를 하는 방식으로 진행되는 한방향 통신이다. 이럴경우 서버쪽 데이터가 업데이트 되더라도 클라이언트 쪽에는 새로고침을 하지 않는한 변경된 데이터가 업데이트 되지 않는 문제가 발생한다.
어떻게 작동하는가?
서버와 클라이언트 간의 WebSocket연결은 HTTP프로토콜을 통해 이루어진다. 연결이 정상적으로 이루어 진다면 서버와 클라이언트 간에 WebSocket연결이 이루어지고 일정 시간이 지나면 HTTP연결은 자동으로 끊어지게 된다. WebSocket 프로토콜은 접속 확립에 HTTP를 사용하고 그 후 통신은 WebSocket 독자의 프로토콜로 이루어진다. 접속한 상태라면 클라이언트나 서버로부터 데이터 송신이 가능하다. 더불어 데이터의 송신과 수신에 각각 커넥션을 맺을 필요가 없어 하나의 커넥션으로 데이터를 송수신 할 수 있다.
WebSocket API
스프링 프레임워크는 WebSocket 메시지를 처리하는 클라이언트측 및 서버측 응용프로그램을 작성하는데 사용할 수 있는 WebSocket API를 제공한다.
WebSocketHandler
웹소켓 서버는 간단하게 WebSocketHandler를 구현하거나 TextWebSocketHandler나 BinaryWebSockteHandler를 확장함으로써 만들 수 있다.
Stomp
WebSocket은 단지 통신 프로토콜일 뿐이다. 특정 토픽의 subscriber에게만 / 특정 user에게만 메시지를 보내는 방법을 담당하는 프로토콜이 Stomp다.
Streaming Text Oriented Messaging Protocol로, 프로토콜을 지원하는 Message Broker와 stomp client 간 통신을 지원한다.
Spring은 default로 Support, 다른 messaging protocol - RabbitMQ / ActiveMQ 등도 사용 가능하다.
Stomp의 Message 구조
<<<MESSAGE
destination : /topic/roomId
content-type:application/json;charset=UTF-8
subscription:sub-0
message-id:2oy02l5d-71
content-length:43
{"senderName : "user1" , "sendMessage":"hi?"}
데이터의 타입은 JSON으로 전송된다. 메시지의 길이는 43이고 body 부분에 데이터가 정의되어 있다.
JSON 구조로 key, value로 구성되어있고, 다양한 데이터 타입을 가질 수 있다.
package com.sp.ex;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
@RequestMapping("/echo")
public class EchoHandler extends TextWebSocketHandler {
// 세션 리스트
private List<WebSocketSession> sessionList = new ArrayList<WebSocketSession>();
private static Logger logger = LoggerFactory.getLogger(EchoHandler.class);
// 클라이언트가 연결 되었을 때 실행
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessionList.add(session);
logger.info("{} 연결됨", session.getId());
}
// 클라이언트가 웹소켓 서버로 메시지를 전송했을 때 실행
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
System.out.println("하이");
System.out.println(session.getId());
System.out.println(message.getPayload());
logger.info("{}로 부터 {} 받음", session.getId(), message.getPayload());
// 모든 유저에게 메세지 출력
for (WebSocketSession sess : sessionList) {
sess.sendMessage(new TextMessage(message.getPayload()));
}
}
// 클라이언트 연결을 끊었을 때 실행
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessionList.remove(session);
logger.info("{} 연결 끊김.", session.getId());
}
}