티스토리 뷰

 

 

동아리 홈페이지 투표 개발 2 - WebSocket과 STOMP

동아리 홈페이지 투표 개발 1 - 개요 및 설계 개요 동아리 홈페이지 백엔드 개발을 진행해오고 있었고 홈페이지라면 기본적으로 갖춰야 할 기본적인 기능들이 거의 구현이 된 상태에서 기존 홈

woo-chang.tistory.com

홈페이지 투표 개발의 마지막 글입니다. 앞서 설명해 드렸던 내용을 바탕으로 실제 어떻게 개발을 진행하였는지 알려드리고자 합니다. 저희 홈페이지 백엔드 서버는 Spring Boot 기반으로 동작하고 있기에 Spring에서 WebSocket을 설정하고 클라이언트와 연결하고 요청과 응답이 실시간으로 이루어지도록 코드를 작성해야 합니다.

 

Spring에서 WebSocket을 사용하고 있는 예시를 우선 확인하고 비슷한 방법으로 개발을 진행하려고 했습니다. WebSocket을 사용한 가장 대표적인 예시가 실시간 채팅을 구현하는 것이었고 투표 현황 페이지에 들어와 있는 클라이언트는 채팅방에 접속한 클라이언트와 비슷한 상황이라 판단하여 이를 참고하고자 하였습니다.

 

그러던 도중 SockJS라는 개념을 알게 되었고 이를 잠시 설명해 드리고자 합니다.

SockJS

개념 및 사용 이유

SockJS는 애플리케이션이 WebSocket API를 사용하도록 하지만 애플리케이션 코드를 변경할 필요 없이 런타임에 WebSocket이 아닌 대안으로 대체하는 것을 의미합니다. 이를 조금 더 쉽게 설명해 드리자면 WebSocket을 지원하지 않는 브라우저라 할지라도 다른 기술을 사용하여 지속성 있는 연결을 지원할 수 있도록 해줍니다.

 

이를 WebSocket Emulation을 이용한다고 합니다. 먼저 WebSocket 연결을 시도하지만 실패하면 HTTP Streaming, Long-Polling과 같은 HTTP 기반의 다른 기술로 전환해 연결을 시도합니다.

 

위의 장점과 Spring은 Servlet 위에서 Client/Server 용도의 SockJS 프로토콜을 모두 지원하기에 사용하기로 하였습니다. 또한 홈페이지의 프론트는 React를 이용해 구현했는데 React에서도 SockJS Client 사용이 쉬웠다는 점도 선택에 영향을 주었습니다.

Develop

의존성 추가

implementation 'org.springframework.boot:spring-boot-starter-websocket'

WebSocket을 사용하기 위해 해당하는 의존성을 build.gradle에 추가합니다.

WebSocket Config

다음으로 WebSocket 설정 정보를 등록해야 합니다. 실제 작성한 설정 정보는 아래의 코드와 같습니다.

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/v1/websocket")
            .setAllowedOrigins("https://keeper.or.kr")
            .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic");
    }

}

@EnableWebSocketMessageBroker 어노테이션은 @Configuration 클래스에 추가하여 WebSocket을 통한 브로커 메시징을 활성화하도록 합니다. WebSocketMessageBrokerConfigurer 인터페이스를 구현하여 WebSocket 설정을 사용자가 커스텀할 수 있습니다.

 

registerStompEndpoints 메서드에서는 StompEndPointRegistry를 매개변수로 받는데 해당 객체를 통해 WebSocket 연결을 위한 EndPoint 정보를 설정할 수 있습니다. 코드를 보시면 엔드 포인트, 연결을 허용하는 주소, SockJS를 활성화하고 있음을 알 수 있습니다.

 

configureMessageBroker 메서드에서는 MessageBrokerRegistry 객체를 통해 메시지 브로커로 라우팅할 접두어를 설정하고 있습니다. 해당 접두어를 가진 요청이 들어오게 되면 메시지 브로커로 라우팅 되고 매칭된 모든 subscriber에게 메시지를 전송하게 됩니다.

메시지 전송

@Service
@RequiredArgsConstructor
public class WebSocketService {

    private final SimpMessagingTemplate webSocket;

    public void sendVoteStatusMessage(String payload, ElectionVoteStatus status) {
        webSocket.convertAndSend(payload, status);
    }

}

subscriber에게 메시지 전송을 위해서 SimpMessagingTemplate를 사용했습니다. 스프링 자체적으로 제공하는 객체로 클라이언트에게 메시지를 전송하는 방법을 제공하고 있습니다.

 

convertAndSend 메서드를 사용하였는데 목적지인 payload와 전송할 Object를 매개변수로 받습니다. 전달받은 Object를 직렬화된 형식으로 변환하고 지정된 목적지로 보내게 됩니다.

public void sendVoteStatus(Long electionId) {
    ElectionVoteStatus status = electionUtilService.getVoteStatus(electionId);
    webSocketService.sendVoteStatusMessage("/topics/votes/result", status);
}

선거 서비스 계층에서는 WebSocketService를 주입받아 사용하였습니다. 선거가 정상적으로 이루어진 후 위의 메서드가 호출되고 subscriber에게 현재 선거 현황이 전송되게 됩니다.

결과

SockJS에 의해 연결된 클라이언트는 별도의 요청 없이 투표자가 투표가 진행했을 때 실시간으로 변환되는 투표율을 확인할 수 있었습니다. 실제 투표를 참여한 인원들도 완성도 높은 결과물에 많이 감탄하는 것을 보고 뿌듯함을 느끼게 되었습니다. 열심히 같이 개발을 진행해준 프론트 팀에게도 감사드립니다.

 

React에서 사용한 코드는 해당 블로그에서 참고하여 개발했습니다. 제가 연습으로 작성한 코드는 아래에서 확인할 수 있습니다.

 

GitHub - woo-chang/spring-lab: Spring 실험 및 학습 Repo

Spring 실험 및 학습 Repo. Contribute to woo-chang/spring-lab development by creating an account on GitHub.

github.com

 

댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Total
Today
Yesterday