실시간 채팅을 구현하기 위해 웹소켓을 활용해보겠습니다.

 

 

웹소켓을 구현하기 위해

라이브러리를 다운 받습니다

 

//socket
    implementation 'org.springframework.boot:spring-boot-starter-websocket'
    implementation 'org.webjars:webjars-locator-core'
    implementation 'org.webjars:sockjs-client:1.0.2'
    implementation 'org.webjars:stomp-websocket:2.3.3'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    implementation 'org.webjars:bootstrap:3.3.7'
    implementation 'org.webjars:jquery:3.1.1-1'

 

소켓의 설정 정보를 구현하는 WebSocketConfig

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

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

   @Override
   public void registerStompEndpoints(StompEndpointRegistry registry) {
      registry.addEndpoint("/chat").withSockJS();
   }

}

ConfigureMessageBroker 메소드는 메시지 브로커를 구성하는 역할을 합니다. 메시지 브로커는 클라이언트와 서버간의 메시지 전송을 중개하는 시스템으로 구독과 발행 기능을 제공합니다.

 

config.enableSimpleBroker("/topic")

코드는 /topic 주제로 구독 및 발행 기능을 설정합니다. 클라이언트가 /topic 주제를 구독하면, 해당 주제로 발행된 메시지를 수신할 수 있습니다.

 

config.setApplicationDestinationPrefixes("/app")

클라이언트가 메시지를 전송할 때 사용할 애플리케이션 전용 접두사를 설정합니다. 클라이언트는  /app 접두사를 사용하여 메시지를 전송하게 됩니다.

 

요약하면 클라이언트는 /app을 접두사로 사용하여 서버에게 메시지를 전송하고, 서버는 /topic으로 발행한 메시지를 클라이언트에게 전송합니다. 이를 통해 클라이언트는 /topic 주제를 구독하고, 해당 주제로 발행되는 메시지를 수신할 수 있습니다.

 

 

 

 

채팅창을 입력하는 chat.html

 

<div class="row">
            <form>
                <button id="connect" hidden="hidden" class="game-button" type="submit">연결</button>
                <button id="disconnect" hidden="hidden" class="game-button" type="submit" disabled="disabled">연결 끊기</button>
            </form>
            <form class="form-inline" style="display: flex; justify-content: center; flex-direction: column; align-items: center">
                <input type="text" id="message" class="form-control" placeholder="메세지 입력" style="width: 350px">
                <button id="send" class="game-button" type="submit" style="width: 80px">Push</button>
            </form>
        </div>

채팅 입력창과 연결되있고 소켓과 통신을 담당하는 코드가 적혀있는 app.js 

$(function () {
    $("form").on('submit', function (e) {
        e.preventDefault();
    });
    $("#connect").click(function () {
        connect();
    });
    $("#disconnect").click(function () {
        disconnect();
    });
    $("#send").click(function () {
        sendMessage();
    });

    // 웹소켓 연결
    connect();
});
$(function () {
    $("form").on('submit', function (e) {
        e.preventDefault();
    });

e.preventDefault() 메소드를 통해  submit이 이루어 질 때,

화면이 새로 로드되는 것을 막고 javaScript로 처리할 수 있도록 합니다.

 

웹소켓과 연결하기 위해 connect() 함수를 실행합니다.

function connect() {
    var socket = new SockJS('/chat');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function (frame) {
        setConnected(true);
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/greetings', function (greeting) {
            showGreeting(JSON.parse(greeting.body).content);
        });
    });
}

 

var socket = new SockJS('/chat');

SockJs 객체를 생성하여 서버와의 웹 소켓 연결을 수행합니다. /chat 경로를 통해 서버의 웹 소켓 엔드포인트에 접속합니다.

 

 

stompClient = Stomp.over(socket);

stomp 객체를 생성하고 해당 객체를 SockJS와 연결합니다. Stomp는 stomp 프로토콜을 사용하여 웹소켓 통신을 처리하는 클라이언트 라이브러리입니다.

 

 

stompClient.connect({}, function (frame) { ... });

stompClient 객체를 사용하여 서버와의 실제 웹소켓 연결을 수행합니다. 빈 객체를 전달하여 추가적인 헤더나 매개변수를 설정하지 않고 연결합니다. 연결이 성공하면 콜백 함수가 호출됩니다.

 

 

setConnected(true); - setConnected()

함수를 호출하여 클라이언트의 연결 상태를 true로 설정합니다. 이 함수는 연결 및 연결 끊기 버튼의 상태를 업데이트하고, 연결이 성공했을 때 채팅창을 표시합니다.

 

stompClient.subscribe('/topic/greetings', function (greeting) {
    showGreeting(JSON.parse(greeting.body).content);
});

/topic/greetings 주제를 구독하여 서버로부터 오는 메시지를 수신하는 구독 작업을 수행합니다. 서버가 해당 주제로 메시지를 발행하면 콜백함수가 호출되고 수신된 메시지를 처리합니다. 수신된 메시지의 내용은 JSON으로 파싱하여 showGreeting의 매개변수로 활용합니다.

 

메세지 전송 버튼을 누를 때

 

function sendMessage() {

    // 현재 시간 가져오기
    const now = new Date();
    const hours = now.getHours().toString().padStart(2, '0');
    const minutes = now.getMinutes().toString().padStart(2, '0');
    const seconds = now.getSeconds().toString().padStart(2, '0');
    const currentTime = `${hours}:${minutes}:${seconds}`;

    const messageData = {
        'nick': nick,
        'message': $("#message").val(),
        'time': currentTime  // 시간 값 추가
    };

    stompClient.send("/app/hello", {}, JSON.stringify(messageData));
}

시간, 메세지, 닉네임 매개변수를 채워서

stompClient를 활용하여 JSON 형태로 /app/hello 맵핑 값으로 보냅니다.

 

 

이 값은 Controller의 @MessageMapping 값으로 이어집니다.

이전에 config 설정에서

config.setApplicationDestinationPrefixes("/app");

프리픽스를 해두었기에 /hello 맵핑값과 이어질 수 있습니다.

 

@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
   String nickname = message.getNick(); //닉네임 가져오기
   String time = message.getTime();
   String greetingMessage = "<div class='message'><span class='nick'>" + nickname + "</span> : <span class='text'>"
         + HtmlUtils.htmlEscape(message.getMessage()) + "</span> <span class='time'>  " + time + "</span></div>";
   // Thread.sleep(500); // simulated delay
   return new Greeting(greetingMessage);
}

이 값을 html구조로 @SendTo("/topic/greeting) 이 주제를 구독한 모든 이에게 발행합니다.

 

이로서 최종 채팅화면을 보여주는 html에서 채팅을 실시간으로 보여줄 수 있습니다.

 

 

 

'Deep Dive' 카테고리의 다른 글

한 서비스를 처음부터 끝까지 1인 구현 후의 회고  (0) 2023.07.16
iam 계정으로 ec2 접속하기  (0) 2023.07.09
셔플링 알고리즘  (0) 2023.07.02
타이머 기능  (0) 2023.07.02
JPA Repository & QueryDSL  (0) 2023.06.23

+ Recent posts