스피너 프로젝트는 데이터베이스와 코드가 들어가 있는 서버 A와 레디스만 배포하고 채팅 pub/sub 인 B 서버를 따로 이용해서 최대한 부하가 없이 진행하려고 한다.
지난번에 구현했던 채팅은 단순히 로컬 환경에서 채팅이였다면 이번에는 redis의 메시지 큐를 이용해서 분산 환경에서 구현하려고 한다.
redis는 pub/sub으로 발행/구독 모델이다. 발행자는 특정 채널에 메시지를 전달하고, 채널을 구독하는 모든 구독자에게 메시지를 전달하는 것이다. 또 인메모리 디비로 속도가 빠르고, 가장 중요한건 메시지의 순서를 알 수 있다. 또 토픽(채널)을 미리 생성하지 않고, sub가 특정 토픽을 구독하면 그 채널이 레디스 내부에서 동적으로 생성된다. 단점은 pub의 메시지를 저장하지 않아서 수신 확인이 불가능하다는 점이다.
구현 전 네트워크 필요한 내용 정리 ( 꼭 알고 있어야함)
HTML5에 등장했고, 실시간 웹애플리케이션을 위해 설계된 통신 프로토콜이며 TCP기반이다. 신뢰성 있는 데이터 전송을 보장하고, 순서가 보장된 양방향 통신을 제공한다. 이때 데이터는 패킷 형태로 전달되며 전송은 연결중단과 추가 HTTP 요청 없이 양방향으로 이뤄진다. 여기서 패킷이란 네트워크를 통해 데이터를 주고받을 때 사용되는 데이터 조각이다.

헤더는 수신처를 나타내는 주소같은 제어정보가 있고, 데이터에는 실제로 운반되는 내용이 들어가 있다.
클라이언트는 서버에 웹소켓과의 연결 요청을 보내는데 이때 HTTP 요청 헤더에 upgrade, connection, sec-websocket-key, sec-websocket-version등 필드를 포함해서 요청을 보낸다.
그 때 서버는 요청을 처리하고 HTTP 응답으로 101 Switching protocols 를 반환하고, 핸드쉐이크가 완료되어 HTTP 연결이 종료되고 Websocket연결로 전환된다.
이 때 핸드쉐이크는 클라이언트가 서버로 요청을 보내고, 응답을 받고, 다시 websocket연결로 전환되어 핸드쉐이크라고 불린다.
웹소켓이 연결된 이후에는 클라이언트와 서버간의 연결 상태를 나타내는 websocket session이 생성된다. 그래서 연결이 살아있는 동안에는 서버간의 상태유지, 메시지 송수신을 위한 통로 제공, 클라이언트에 대한 정보를 저장하는 역할을 한다.
websocketConfig 클래스를 만들고 아래 코드를 작성하면 된다. 하나하나 살펴보겠다.
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
/**
* 메시지 브로커
* [note] 클라이언트가 메시지를 주고받는 경로
* @param registry
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/room");
registry.setApplicationDestinationPrefixes("/app");
}
/**
* STOMP 엔드포인드 등록
* [note] 클라이언와 서버를 연결하는 webSocket 경로
* @param registry
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat")
.setAllowedOrigins("*");
}
}
@EnableWebSocketMessageBroker
웹소켓 메시지 브로커를 활성화하는 어노테이션이다. STOMP 프로토콜을 기반으로 웹소켓 통신을 처리할 수 있게 구성해준다.
이 인터페이스를 통해서 우리가 웹소켓과 STOMP 메시징을 커스터 마이징 할 수 있다. 내부 메서드 중 사용한 configureMessageBroker 과 registerStompEndpoints 를 자세히 살펴봤다.

registry.enableSimpleBroker("/room");
이 메서드는 메시지 브로커의 경로를 설정한다. 즉 클라이언트가 구독하거나 서버로 메시지를 전송할 때 사용할 경로이다.
예를 들어 클라이언트가 /room/chat 을 구독하면 해당 경로로 오는 메시지를 받을 수 있다.
이후에 레디스로 분산처리할 때에는 경로를 동적으로 따로 설정해줘야한다.
registry.setApplicationDestinationPrefixes("/app");
클라이언트에서 서버로 메시지를 보낼 때 사용하는 경로이다.
registry.addEndpoint("/chat")
/chat은 웹소켓 연결을 맺기 위한 STOMP 엔드포인트이다.
포스트맨에서 new -> websocket 을 선택한다.

주소에 endpoint로 저장한 url을 적고 연결을 누른다.

연결이 잘 되면 200이 뜬다.

포스트맨은 표준 websocket 프로토콜만 지원하고 sockJS가 사용하는 폴백 프로토콜을 처리하지 못한다. 그래서 sockJS를 활성화하면 표준 websock경로가 아닌 sockJS경로가 추가된다. 또 최근에는 거의 웹소켓을 지원하기 때문에 굳이 sockJS를 쓰지 않아도 된다고 한다(확실치 않음)
이제 웹소켓이 연결되었으니 다음은 레디스를 배포하고 나서 웹소켓이 레디스를 구독하는 코드를 구현해보겠다. 끝