[Socket] 채팅 기능 구현(Thymeleaf + WebSocket)

John·2023년 8월 21일
0

개발 메모🌷

목록 보기
12/13
post-thumbnail

기술 블로그를 읽고 실습하여 작성한 게시글입니다.

WebSocket - WebSocket
Spring WebSocket 소개


Thymeleaf + WebSocket

의존성 추가

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

WebSocket Hanlder 추가

@Slf4j
@Component
public class ChatHandler extends TextWebSocketHandler {

    private static List<WebSocketSession> list = new ArrayList<>();

    // 연결(클라이언트 접속)
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        list.add(session);
        log.info("[{}] 클라이언트 접속", session);
    }

    // 연결 종료(클라이언트 접속 해제)
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
            throws Exception {
        log.info("[{}] 클라이언트 접속 해제", session);
        list.remove(session);
    }

    // 메시지 수신
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message)
            throws Exception {
        String payload = message.getPayload();
        log.info("payload : {}", payload);

        for (WebSocketSession webSocketSession : list) {
            webSocketSession.sendMessage(message);
        }
    }

    // 오류 처리
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception)
            throws Exception {
        log.error(exception.getMessage());
    }

}

WebSocketHandler의 구현체 ChatHandler 작성


WebSocketConfig 작성

@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketConfigurer {

    private final ChatHandler chatHandler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(chatHandler, "ws/chat")
                .setAllowedOriginPatterns("*");
    }
}

@EnableWebSocket을 통한 WebSocket 활성화

웹 소켓 프로토콜 요청 [ws://~]로 시작된다.
registry.addHandler(chatHandler, "ws/chat")를 통해 엔드포인트를 /chat로 설정
.setAllowedOriginPatterns("*")를 통해 도메인이 다른 서버에 접속가능하도록 설정


ChatController 작성

@Slf4j
@Controller
public class ChatController {

    @GetMapping("/chat")
    public String chatGet(Model model) {
        log.info("ChatController Test");

        model.addAttribute("name", UUID.randomUUID().toString());
        return "chat";
    }
}

UUID를 통해 구분


chat.html 및 JS 작성

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>hello</title>
</head>


<body>
<div class="container">

  <div class="col-6">
    <label><b>채팅방</b></label>
  </div>

  <div>
    <div id="msgArea" class="col"></div>

    <div class="col-6">
      <div class="input-group mb-3">
        <input type="text" id="msg" class="form-control" aria-label="Recipient's username"
               aria-describedby="button-addon2">

        <div class="input-group-append">
          <button class="btn btn-outline-secondary" type="button" id="button-send">전송</button>
        </div>

      </div>
    </div>
  </div>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script th:inline="javascript">
  $(document).ready(function () {

    const username = [[${name}]];

    $("#disconn").on("click", (e) => {
      disconnect();
    })

    $("#button-send").on("click", (e) => {
      send();
    });

    const websocket = new WebSocket("ws://localhost:8080/ws/chat");

    websocket.onmessage = onMessage;
    websocket.onopen = onOpen;
    websocket.onclose = onClose;

    function send() {

      let msg = document.getElementById("msg");

      console.log(username + ":" + msg.value);
      websocket.send(username + ":" + msg.value);
      msg.value = '';
    }

    //채팅창에서 나갔을 때
    function onClose(evt) {
      var str = username + ": 님이 방을 나가셨습니다.";
      websocket.send(str);
    }

    //채팅창에 들어왔을 때
    function onOpen(evt) {
      var str = username + ": 님이 입장하셨습니다.";
      websocket.send(str);
    }

    function onMessage(msg) {
      var data = msg.data;
      var sessionId = null;
      //데이터를 보낸 사람
      var message = null;
      var arr = data.split(":");

      for (var i = 0; i < arr.length; i++) {
        console.log('arr[' + i + ']: ' + arr[i]);
      }

      var cur_session = username;

      //현재 세션에 로그인 한 사람
      console.log("cur_session : " + cur_session);
      sessionId = arr[0];
      message = arr[1];

      console.log("sessionID : " + sessionId);
      console.log("cur_session : " + cur_session);

      //로그인 한 클라이언트와 타 클라이언트를 분류하기 위함
      if (sessionId == cur_session) {
        var str = "<div class='col-6'>";
        str += "<div class='alert alert-secondary'>";
        str += "<b>" + sessionId + " : " + message + "</b>";
        str += "</div></div>";
        $("#msgArea").append(str);
      } else {
        var str = "<div class='col-6'>";
        str += "<div class='alert alert-warning'>";
        str += "<b>" + sessionId + " : " + message + "</b>";
        str += "</div></div>";
        $("#msgArea").append(str);
      }
    }
  })
</script>

</body>

</html>


테스트

랜덤으로 생성된 UUID로 입장 및 채팅 기능 테스트 완료

profile
기록을 습관으로

0개의 댓글