본문 바로가기
Spring

[웹소켓] 2.

by moca7 2024. 11. 4.

 

 

 

ㅁ 퇴장하기 버튼을 눌러서 웹소켓 연결 해제를 먼저 해본다.

 

 

 

ㅁ room.jsp

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
   
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

<style>
    .chat{width:400px; margin:auto; padding:10px; border:1px solid lightgray;}
    .chat-area{height:500px; overflow: auto;}

     .chat-message{margin:10px 0px;}
     .chat-message.mine{display: flex; justify-content:flex-end;}
     
    .chat-message .send-message{
       padding: 5px 7px;
       border-radius: 10px;
       max-width: 190px;
       font-size:0.9em;
       white-space: pre-line;
    }
    .chat-message.other .send-message{background: lightgray;}
    .chat-message.mine .send-message{background: #FFE08C;}
 
    .chat-user {
       text-align:center;
       border-radius:10px;  
       background: lightgray;
       opacity: 0.5;
       margin: 20px 0px;
       color: black;
       line-height: 30px;
    }
 </style>

</head>
<body>
<div class="container p-3">

    <!-- Header, Nav start -->
    <jsp:include page="/WEB-INF/views/common/header.jsp" />
    <!-- Header, Nav end -->

    <!-- Section start -->
    <section class="row m-3" style="min-height: 500px">

      <div class="container border p-5 m-4 rounded">
        <h2 class="m-4">채팅방</h2>
       
        <div class="chat">
   
          <div class="chat-area">
         
            <div class="chat-message mine">
              <div class="send-message">내가보낸 메세지내가보낸 메세지내가보낸 메세지내가보낸 메세지내가보낸 메세지</div>
            </div>  
           
            <div class="chat-message other">
              <span class="send-user">상대방</span>
              <div class="send-message">남이보낸 메세지남이보낸 메세지남이보낸 메세지남이보낸 메세지남이보낸 메세지남이보낸 메세지</div>
            </div>
           
            <div class="chat-user entry">
              xxx님이 들어왔습니다.
            </div>
           
            <div class="chat-user exit">
              xxx님이 나갔습니다.
            </div>        
           
          </div>
         
          <div class="input-area">
         
            <div class="form-group">
                <textarea class="form-control" rows="3" id="message" style="resize:none"></textarea>
            </div>
           
            <button type="button" class="btn btn-sm btn-secondary btn-block" onclick="sendMessage();">전송하기</button>
            <button type="button" class="btn btn-sm btn-danger btn-block" onclick="onClose();">퇴장하기</button>
           
          </div>  
         
        </div>
      </div>


      <script src="https://cdn.jsdelivr.net/sockjs/1/sockjs.min.js"></script>
      <script>
     
        const sock = new SockJS("${contextPath}/chat"); // 이 구문이 웹소켓 서버와 연결이 되는 구문이다. 이 페이지에 진입한 클라이언트가.
        // - 아까 servlet-context에 /chat url 요청시에 ChatEchoHandler가 동작되게 해놨다.
        // 웹소켓 서버와 연결되는 순간 ChatEchoHandler가 동작된다고 보면 된다.
        // after afterConnectionEstablished 메소드가 실행된다.
        // 웹소켓 서버와 연결됨(즉, ChatEchoHandler의 afterConnectionEstablished 메소드가 실행된다.)
       
        sock.onmessage = onMessage; // 웹소켓에서 해당 클라이언트로 메세지 발송시 자동으로 실행할 함수를 지정(매핑)하는 구문
        sock.onclose = onClose; // 웹소켓과 해당 클라이언트간의 연결이 끊겼을 경우 자동으로 실행할 함수를 지정(매핑)하는 구문
        // 이렇게 3줄이 기본세팅이다.
       
       
       
     
        // 메세지를 출력시키는 영역의 요소
        const $chatArea = $(".chat-area");
       
       
        // 메세지 전송시 실행될 함수
        function sendMessage() {
         
        }
     
       
        // 나에게 메세지가 왔을 때 실행될 함수
        function onMessage() {
         
        }
       
       
        // 퇴장시 실행될 함수
        function onClose() {
          location.href = "${contextPath}"; // 이 페이지를 빠져나가면
        }
       
       
       
      </script>


    </section>
    <!-- Section end -->

    <!-- Footer start -->
    <jsp:include page="/WEB-INF/views/common/footer.jsp" />
    <!-- Footer end -->

  </div>
</body>
</html>
 

 

 

- room.jsp에서 퇴장하기 버튼을 누르면 onClose 함수가 실행되게 했었다. (아래 주석말고 변경사항 없음)

onClose 함수는 메인페이지로 이동하게 한다.

이 페이지를 빠져나가면 ChatEchoHandler의 after 

 

 

 

 

 

 

 ChatEchoHandler

 

 
package com.br.spring.handler;

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import lombok.extern.slf4j.Slf4j;

@Slf4j // 로그를 출력해보기 위해 롬복의 @Slf4j 어노테이션 작성.
public class ChatEchoHandler extends TextWebSocketHandler {


  /**
   * 1) afterConnectionEstablished : 웹소켓에 클라이언트가 연결되었을 때 처리할 내용 정의
   *
   * @param session - 현재 웹소켓과 연결된 클라이언트 객체 (즉, 채팅방에 접속된 클라이언트)
   * // param이 매개변수에 대한 설명을 작성하는 키워드다.
   */
  @Override
  public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    // 이 session이 바로 이 웹소켓과 연결된 클라이언트 객체다.
    // 다수의 클라이언트들이 연결될거고 각각의 클라이언트들 마다 고유한 id 정보가 session에 담겨있을 것이다.
   
   
    log.debug("====== websocket 연결됨 =====");
    log.debug("WebSocketSession 객체: {}", session);
    log.debug("session id: {}", session.getId());
    log.debug("session Attributes 목록: {}", session.getAttributes()); // {sessionId=xxxx, loginUser=MemberDto객체}
    log.debug("현재 채팅방에 참가한 로그인한 회원: {}", session.getAttributes().get("loginUser")); // MemberDto 객체 뽑기
   
  }

 
  // 2) handleMessage : 웹소켓으로 데이터(텍스트, 파일 등)가 전송되었을 경우 처리할 내용 정의
  @Override
  public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {

  }
 
  // 3) afterConnectionClosed : 웹소켓에 클라이언트가 연결이 끊겼을 때 처리할 내용 정의
  @Override
  public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
   
    log.debug("====== websocket 연결됨 =====");
    log.debug("WebSocketSession 객체: {}", session);
    log.debug("session id: {}", session.getId());
    log.debug("현재 채팅방에서 나간 회원: {}", session.getAttributes().get("loginUser")); // MemberDto 객체 뽑기
  }
 
}
 

 

 

 

 

 

ㅁ 서버 start

 

- 두 개 퇴장 해봤다.

 

 

 

 

 

 

===============================================================================

 

 

 

 

ㅁ ChatEchoHandler

 

 
package com.br.spring.handler;

import java.util.ArrayList;
import java.util.List;

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import lombok.extern.slf4j.Slf4j;

@Slf4j // 로그를 출력해보기 위해 롬복의 @Slf4j 어노테이션 작성.
public class ChatEchoHandler extends TextWebSocketHandler {

  // 웹소켓 세션 객체(클라이언트)들을 저장하는 리스트
  private List<WebSocketSession> sessionList = new ArrayList<>();
 
 
  /**
   * 1) afterConnectionEstablished : 웹소켓에 클라이언트가 연결되었을 때 처리할 내용 정의
   *
   * @param session - 현재 웹소켓과 연결된 클라이언트 객체 (즉, 채팅방에 접속된 클라이언트)
   * // param이 매개변수에 대한 설명을 작성하는 키워드다.
   */
  @Override
  public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    // 이 session이 바로 이 웹소켓과 연결된 클라이언트 객체다.
    // 다수의 클라이언트들이 연결될거고 각각의 클라이언트들 마다 고유한 id 정보가 session에 담겨있을 것이다.
   
    /*
    log.debug("====== websocket 연결됨 =====");
    log.debug("WebSocketSession 객체: {}", session);
    log.debug("session id: {}", session.getId());
    log.debug("session Attributes 목록: {}", session.getAttributes()); // {sessionId=xxxx, loginUser=MemberDto객체}
    log.debug("현재 채팅방에 참가한 로그인한 회원: {}", session.getAttributes().get("loginUser")); // MemberDto 객체 뽑기
    */
   
    sessionList.add(session);
   
  }
 
 
 

  /**
   * 2) handleMessage : 웹소켓으로 데이터(텍스트, 파일 등)가 전송되었을 경우 처리할 내용 정의
   *
   * @param session - 현재 웹소켓으로 데이터를 전송한 클라이언트 객체
   * @param message - 전송된 데이터에 대한 정보를 가지고 있는 객체
   */
  @Override
  public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
    log.debug("====== 메세지 들어옴 =====");
    log.debug("WebSocketSession 객체: {}", session);
    log.debug("WebSocketMessage 객체: {}", message);
    log.debug("메세지 내용: {}", message.getPayload());
  }
 
 
 
 
   
  /**
   * 3) afterConnectionClosed : 웹소켓에 클라이언트가 연결이 끊겼을 때 처리할 내용 정의
   */
  @Override
  public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
   
    /*
    log.debug("====== websocket 연결됨 =====");
    log.debug("WebSocketSession 객체: {}", session);
    log.debug("session id: {}", session.getId());
    log.debug("현재 채팅방에서 나간 회원: {}", session.getAttributes().get("loginUser")); // MemberDto 객체 뽑기
    */
   
    sessionList.remove(session);
   
  }
 
}
 

 

 

- 전역 필드로 websocket 세션 객체를 여러개 보관시킬 수 있는 list형 필드를 둔다. Map으로 모아도 상관 없다.

- 이렇게 한곳에 모아놔야 메세지를 모두에게 재전송할 수 있다.

- 웹소켓에 연결된 클라이언트 들에게만 메세지를 전송한다. 그러려면 퇴장하면 리스트에서 제거해줘야 한다.

 

- 웹소켓 세션이 끊긴 클라이언트에게 sss.sendMessage(new TextMessage(msg));를 호출하면 예외가 발생한다.

구체적으로 IllegalStateException이 발생하며, 메시지는 보낼 수 없습니다.

 

 

 

 

ㅁ room.jsp

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
   
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

<style>
    .chat{width:400px; margin:auto; padding:10px; border:1px solid lightgray;}
    .chat-area{height:500px; overflow: auto;}

     .chat-message{margin:10px 0px;}
     .chat-message.mine{display: flex; justify-content:flex-end;}
     
    .chat-message .send-message{
       padding: 5px 7px;
       border-radius: 10px;
       max-width: 190px;
       font-size:0.9em;
       white-space: pre-line;
    }
    .chat-message.other .send-message{background: lightgray;}
    .chat-message.mine .send-message{background: #FFE08C;}
 
    .chat-user {
       text-align:center;
       border-radius:10px;  
       background: lightgray;
       opacity: 0.5;
       margin: 20px 0px;
       color: black;
       line-height: 30px;
    }
 </style>

</head>
<body>
<div class="container p-3">

    <!-- Header, Nav start -->
    <jsp:include page="/WEB-INF/views/common/header.jsp" />
    <!-- Header, Nav end -->

    <!-- Section start -->
    <section class="row m-3" style="min-height: 500px">

      <div class="container border p-5 m-4 rounded">
        <h2 class="m-4">채팅방</h2>
       
        <div class="chat">
   
          <div class="chat-area">
         
            <div class="chat-message mine">
              <div class="send-message">내가보낸 메세지내가보낸 메세지내가보낸 메세지내가보낸 메세지내가보낸 메세지</div>
            </div>  
           
            <div class="chat-message other">
              <span class="send-user">상대방</span>
              <div class="send-message">남이보낸 메세지남이보낸 메세지남이보낸 메세지남이보낸 메세지남이보낸 메세지남이보낸 메세지</div>
            </div>
           
            <div class="chat-user entry">
              xxx님이 들어왔습니다.
            </div>
           
            <div class="chat-user exit">
              xxx님이 나갔습니다.
            </div>        
           
          </div>
         
          <div class="input-area">
         
            <div class="form-group">
                <textarea class="form-control" rows="3" id="message" style="resize:none"></textarea>
            </div>
           
            <button type="button" class="btn btn-sm btn-secondary btn-block" onclick="sendMessage();">전송하기</button>
            <button type="button" class="btn btn-sm btn-danger btn-block" onclick="onClose();">퇴장하기</button>
           
          </div>  
         
        </div>
      </div>


      <script src="https://cdn.jsdelivr.net/sockjs/1/sockjs.min.js"></script>
      <script>
     
        const sock = new SockJS("${contextPath}/chat"); // 이 구문이 웹소켓 서버와 연결이 되는 구문이다. 이 페이지에 진입한 클라이언트가.
        // - 아까 servlet-context에 /chat url 요청시에 ChatEchoHandler가 동작되게 해놨다.
        // 웹소켓 서버와 연결되는 순간 ChatEchoHandler가 동작된다고 보면 된다.
        // after afterConnectionEstablished 메소드가 실행된다.
        // 웹소켓 서버와 연결됨(즉, ChatEchoHandler의 afterConnectionEstablished 메소드가 실행된다.)
       
        sock.onmessage = onMessage; // 웹소켓에서 해당 클라이언트로 메세지 발송시 자동으로 실행할 함수를 지정(매핑)하는 구문
        sock.onclose = onClose; // 웹소켓과 해당 클라이언트간의 연결이 끊겼을 경우 자동으로 실행할 함수를 지정(매핑)하는 구문
        // 이렇게 3줄이 기본세팅이다.
       
       
       
     
        // 메세지를 출력시키는 영역의 요소
        const $chatArea = $(".chat-area");
       
       
        // 메세지 전송시 실행될 함수
        function sendMessage() {
          sock.send($("#message").val()); // 웹소켓 측으로 메세지를 전송 (ChatEchoHandler의 handleMessage 메소드 자동 실행)
          $("#message").val("");
        }
     
       
        // 나에게 메세지가 왔을 때 실행될 함수
        function onMessage() {
         
        }
       
       
        // 퇴장시 실행될 함수
        function onClose() {
          location.href = "${contextPath}"; // 이 페이지를 빠져나가면
        }
       
       
       
      </script>


    </section>
    <!-- Section end -->

    <!-- Footer start -->
    <jsp:include page="/WEB-INF/views/common/footer.jsp" />
    <!-- Footer end -->

  </div>
</body>
</html>
 

 

 

 

 

 

ㅁ 서버 start

 

- 브라우저 하나로만 메세지를 보내본다. 보내면 textarea는 비워진다.

 

 

 

 

 

 

 

 

 

 

 


===============================================================================

 

 

 

 

ㅁ 메세지 재발송시키기

- 웹소켓의 역할은 웹소켓에 메시지가 하나 들어오는 순간, 보낸 사람을 포함해 웹소켓에 연결되어있는 모든 클라이언트들에게 일괄적으로 그 메세지를 재전송한다.

 

 

 


ㅁ ChatEchoHandler

 

 
package com.br.spring.handler;

import java.util.ArrayList;
import java.util.List;

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import com.br.spring.dto.MemberDto;

import lombok.extern.slf4j.Slf4j;

@Slf4j // 로그를 출력해보기 위해 롬복의 @Slf4j 어노테이션 작성.
public class ChatEchoHandler extends TextWebSocketHandler {

  // 웹소켓 세션 객체(클라이언트)들을 저장하는 리스트
  private List<WebSocketSession> sessionList = new ArrayList<>();
 
 
  /**
   * 1) afterConnectionEstablished : 웹소켓에 클라이언트가 연결되었을 때 처리할 내용 정의
   *
   * @param session - 현재 웹소켓과 연결된 클라이언트 객체 (즉, 채팅방에 접속된 클라이언트)
   * // param이 매개변수에 대한 설명을 작성하는 키워드다.
   */
  @Override
  public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    // 이 session이 바로 이 웹소켓과 연결된 클라이언트 객체다.
    // 다수의 클라이언트들이 연결될거고 각각의 클라이언트들 마다 고유한 id 정보가 session에 담겨있을 것이다.
   
    /*
    log.debug("====== websocket 연결됨 =====");
    log.debug("WebSocketSession 객체: {}", session);
    log.debug("session id: {}", session.getId());
    log.debug("session Attributes 목록: {}", session.getAttributes()); // {sessionId=xxxx, loginUser=MemberDto객체}
    log.debug("현재 채팅방에 참가한 로그인한 회원: {}", session.getAttributes().get("loginUser")); // MemberDto 객체 뽑기
    */
   
    sessionList.add(session);
   
  }
 
 
 

  /**
   * 2) handleMessage : 웹소켓으로 데이터(텍스트, 파일 등)가 전송되었을 경우 처리할 내용 정의
   *
   * @param session - 현재 웹소켓으로 데이터를 전송한 클라이언트 객체
   * @param message - 전송된 데이터에 대한 정보를 가지고 있는 객체
   */
  @Override
  public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
   
    /*
    log.debug("====== 메세지 들어옴 =====");
    log.debug("WebSocketSession 객체: {}", session);
    log.debug("WebSocketMessage 객체: {}", message);
    log.debug("메세지 내용: {}", message.getPayload());
    */
   
    // 현재 해당 웹소켓에 연결되어있는 모든 클라이언트들(작성자본인포함)에게 현재 들어온 메세지 재발송
    for(WebSocketSession sss : sessionList) {
     
      // 메세지유형(chat/entry/exit) | 채팅방에띄워주고자하는메세지내용 | 발신자아이디 | ...(프로필이미지경로 등) <- 나중에 |으로 split.
      String msg = "chat|" + message.getPayload() + "|" + ((MemberDto)session.getAttributes().get("loginUser")).getUserId();
      sss.sendMessage(new TextMessage(msg)); // 웹소켓에서 클라이언트로 메세지를 보냄. 그냥 보내면 안되고 TextMessage 객체로 보내야 한다.
      // room.jsp에서 onMesage 함수가 자동 실행
     
    }
  }
 
 
 
 
   
  /**
   * 3) afterConnectionClosed : 웹소켓에 클라이언트가 연결이 끊겼을 때 처리할 내용 정의
   */
  @Override
  public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
   
    /*
    log.debug("====== websocket 연결됨 =====");
    log.debug("WebSocketSession 객체: {}", session);
    log.debug("session id: {}", session.getId());
    log.debug("현재 채팅방에서 나간 회원: {}", session.getAttributes().get("loginUser")); // MemberDto 객체 뽑기
    */
   
    sessionList.remove(session);
   
  }
 
}
 

 

 

 

 


ㅁ room.jsp

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
   
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

<style>
    .chat{width:400px; margin:auto; padding:10px; border:1px solid lightgray;}
    .chat-area{height:500px; overflow: auto;}

     .chat-message{margin:10px 0px;}
     .chat-message.mine{display: flex; justify-content:flex-end;}
     
    .chat-message .send-message{
       padding: 5px 7px;
       border-radius: 10px;
       max-width: 190px;
       font-size:0.9em;
       white-space: pre-line;
    }
    .chat-message.other .send-message{background: lightgray;}
    .chat-message.mine .send-message{background: #FFE08C;}
 
    .chat-user {
       text-align:center;
       border-radius:10px;  
       background: lightgray;
       opacity: 0.5;
       margin: 20px 0px;
       color: black;
       line-height: 30px;
    }
 </style>

</head>
<body>
<div class="container p-3">

    <!-- Header, Nav start -->
    <jsp:include page="/WEB-INF/views/common/header.jsp" />
    <!-- Header, Nav end -->

    <!-- Section start -->
    <section class="row m-3" style="min-height: 500px">

      <div class="container border p-5 m-4 rounded">
        <h2 class="m-4">채팅방</h2>
       
        <div class="chat">
   
          <div class="chat-area">
         
            <div class="chat-message mine">
              <div class="send-message">내가보낸 메세지내가보낸 메세지내가보낸 메세지내가보낸 메세지내가보낸 메세지</div>
            </div>  
           
            <div class="chat-message other">
              <span class="send-user">상대방</span>
              <div class="send-message">남이보낸 메세지남이보낸 메세지남이보낸 메세지남이보낸 메세지남이보낸 메세지남이보낸 메세지</div>
            </div>
           
            <div class="chat-user entry">
              xxx님이 들어왔습니다.
            </div>
           
            <div class="chat-user exit">
              xxx님이 나갔습니다.
            </div>        
           
          </div>
         
          <div class="input-area">
         
            <div class="form-group">
                <textarea class="form-control" rows="3" id="message" style="resize:none"></textarea>
            </div>
           
            <button type="button" class="btn btn-sm btn-secondary btn-block" onclick="sendMessage();">전송하기</button>
            <button type="button" class="btn btn-sm btn-danger btn-block" onclick="onClose();">퇴장하기</button>
           
          </div>  
         
        </div>
      </div>


      <script src="https://cdn.jsdelivr.net/sockjs/1/sockjs.min.js"></script>
      <script>
     
        const sock = new SockJS("${contextPath}/chat"); // 이 구문이 웹소켓 서버와 연결이 되는 구문이다. 이 페이지에 진입한 클라이언트가.
        // - 아까 servlet-context에 /chat url 요청시에 ChatEchoHandler가 동작되게 해놨다.
        // 웹소켓 서버와 연결되는 순간 ChatEchoHandler가 동작된다고 보면 된다.
        // after afterConnectionEstablished 메소드가 실행된다.
        // 웹소켓 서버와 연결됨(즉, ChatEchoHandler의 afterConnectionEstablished 메소드가 실행된다.)
       
        sock.onmessage = onMessage; // 웹소켓에서 해당 클라이언트로 메세지 발송시 자동으로 실행할 함수를 지정(매핑)하는 구문
        sock.onclose = onClose; // 웹소켓과 해당 클라이언트간의 연결이 끊겼을 경우 자동으로 실행할 함수를 지정(매핑)하는 구문
        // 이렇게 3줄이 기본세팅이다.
       
       
       
     
        // 메세지를 출력시키는 영역의 요소
        const $chatArea = $(".chat-area");
       
       
        // 메세지 전송시 실행될 함수
        function sendMessage() {
          sock.send($("#message").val()); // 웹소켓 측으로 메세지를 전송 (ChatEchoHandler의 handleMessage 메소드 자동 실행)
          $("#message").val("");
        }
     
       
        // 나에게 메세지가 왔을 때 실행될 함수
        function onMessage(evt) { // 웹소켓에서 클라이언트로 보내는 메세지를 받기 위해 매개변수를 둔다.
          console.log("evt", evt);
          console.log("evt.data", evt.data);
        }
       
       
        // 퇴장시 실행될 함수
        function onClose() {
          location.href = "${contextPath}"; // 이 페이지를 빠져나가면
        }
       
       
       
      </script>


    </section>
    <!-- Section end -->

    <!-- Footer start -->
    <jsp:include page="/WEB-INF/views/common/footer.jsp" />
    <!-- Footer end -->

  </div>
</body>
</html>
 

 

 

 

 

 

ㅁ 서버 start

 

- 브라우저 2개로 채팅방에 입장한다.

- admin01로 메세지 2개, user01로 메세지 2개를 전송한다.

 

 

user01의 콘솔

 

admin01의 콘솔

 

 

 

 

 

=====================================================================================

 

 

 

 

ㅁ room.jsp

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
   
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

<style>
    .chat{width:400px; margin:auto; padding:10px; border:1px solid lightgray;}
    .chat-area{height:500px; overflow: auto;}

     .chat-message{margin:10px 0px;}
     .chat-message.mine{display: flex; justify-content:flex-end;}
     
    .chat-message .send-message{
       padding: 5px 7px;
       border-radius: 10px;
       max-width: 190px;
       font-size:0.9em;
       white-space: pre-line;
    }
    .chat-message.other .send-message{background: lightgray;}
    .chat-message.mine .send-message{background: #FFE08C;}
 
    .chat-user {
       text-align:center;
       border-radius:10px;  
       background: lightgray;
       opacity: 0.5;
       margin: 20px 0px;
       color: black;
       line-height: 30px;
    }
 </style>

</head>
<body>
<div class="container p-3">

    <!-- Header, Nav start -->
    <jsp:include page="/WEB-INF/views/common/header.jsp" />
    <!-- Header, Nav end -->

    <!-- Section start -->
    <section class="row m-3" style="min-height: 500px">

      <div class="container border p-5 m-4 rounded">
        <h2 class="m-4">채팅방</h2>
       
        <div class="chat">
   
          <div class="chat-area">
         
          <!-- 다 지웠다. -->
           
          </div>
         
          <div class="input-area">
         
            <div class="form-group">
                <textarea class="form-control" rows="3" id="message" style="resize:none"></textarea>
            </div>
           
            <button type="button" class="btn btn-sm btn-secondary btn-block" onclick="sendMessage();">전송하기</button>
            <button type="button" class="btn btn-sm btn-danger btn-block" onclick="onClose();">퇴장하기</button>
           
          </div>  
         
        </div>
      </div>


      <script src="https://cdn.jsdelivr.net/sockjs/1/sockjs.min.js"></script>
      <script>
     
        const sock = new SockJS("${contextPath}/chat"); // 이 구문이 웹소켓 서버와 연결이 되는 구문이다. 이 페이지에 진입한 클라이언트가.
        // - 아까 servlet-context에 /chat url 요청시에 ChatEchoHandler가 동작되게 해놨다.
        // 웹소켓 서버와 연결되는 순간 ChatEchoHandler가 동작된다고 보면 된다.
        // after afterConnectionEstablished 메소드가 실행된다.
        // 웹소켓 서버와 연결됨(즉, ChatEchoHandler의 afterConnectionEstablished 메소드가 실행된다.)
       
        sock.onmessage = onMessage; // 웹소켓에서 해당 클라이언트로 메세지 발송시 자동으로 실행할 함수를 지정(매핑)하는 구문
        sock.onclose = onClose; // 웹소켓과 해당 클라이언트간의 연결이 끊겼을 경우 자동으로 실행할 함수를 지정(매핑)하는 구문
        // 이렇게 3줄이 기본세팅이다.
       
       
       
     
        // 메세지를 출력시키는 영역의 요소
        const $chatArea = $(".chat-area");
       
       
        // 메세지 전송시 실행될 함수
        function sendMessage() {
          sock.send($("#message").val()); // 웹소켓 측으로 메세지를 전송 (ChatEchoHandler의 handleMessage 메소드 자동 실행)
          $("#message").val("");
        }
 
 
     
       
        // 나에게 메세지가 왔을 때 실행될 함수
        function onMessage(evt) { // 웹소켓에서 클라이언트로 보내는 메세지를 받기 위해 매개변수를 둔다.
          // console.log("evt", evt);
          // console.log("evt.data", evt.data);
         
          let msgArr = evt.data.split("|"); // ["메세지유형(chat|entry|exit)", "출력시킬메세지내용", "발신자아이디"];
         
         
         
          let $chatDiv = $("<div>"); // 채팅창에 append시킬 요소 (메세지 유형별로 다르게 제작)
          // $("<div>")는 기본적으로 빈 <div></div> 요소를 만듭니다.
          // jQuery 대신 순수 JavaScript로 <div> 요소를 생성하고 설정하려면 let chatDiv = document.createElement("div");
         
          if(msgArr[0] == "chat"){ // 채팅메세지일 경우
            $chatDiv.addClass("chat-message")
                    .addClass(msgArr[2] == "${loginUser.userId}" ? "mine" : "other") // 제이쿼리가 좋은게 메소드체이닝이 가능
                    .append( $("<div>").addClass("send-message").text(msgArr[1]) ); // innerText로 추가하고 자손으로 추가
                    // 여기가진 내가 보낸 메세지든 남이 보낸 메세지든 같다.
                   
            if($chatDiv.hasClass("other")) { // class 관련해선 add remove has 3개가 있다.
              $chatDiv.prepend( $("<span>").addClass("send-user").text(msgArr[2]) );
            }
                   
            // short-circuit으로 단일 if문 대체도 가능. 앞이 true면 뒤가 실행.      
            // $chatDiv.hasClass("other") && $chatDiv.prepend( $("<span>").addClass("send-user").text(msgArr[2]) );
               
           
                   
          }else { // 입장 또는 퇴장 메세지일 경우
            $chatDiv.addClass("chat-user")
                    .addClass(msgArr[0])
                    .text(msgArr[1]);
          }
         
         
          // 근데 계속 메세지가 추가되어도 맨 위의 메세지만 보여진다. 스크롤바가 생기고 하단으로 고정되려면.
          $chatArea.append($chatDiv);
          $chatArea.scrollTop( $chatArea[0].scrollHeight ); // 스크롤을 항상 하단으로 유지시켜주는 코드.
         
         
        }
       
       
        // 퇴장시 실행될 함수
        function onClose() {
          location.href = "${contextPath}"; // 이 페이지를 빠져나가면
        }
       
       
       
      </script>


    </section>
    <!-- Section end -->

    <!-- Footer start -->
    <jsp:include page="/WEB-INF/views/common/footer.jsp" />
    <!-- Footer end -->

  </div>
</body>
</html>
 

 

 

 

 

- onMessage 함수에 내용 작성.

 

 

 

 

 


ㅁ ChatEchoHandler

 

 
package com.br.spring.handler;

import java.util.ArrayList;
import java.util.List;

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import com.br.spring.dto.MemberDto;

import lombok.extern.slf4j.Slf4j;

@Slf4j // 로그를 출력해보기 위해 롬복의 @Slf4j 어노테이션 작성.
public class ChatEchoHandler extends TextWebSocketHandler {

  // 웹소켓 세션 객체(클라이언트)들을 저장하는 리스트
  private List<WebSocketSession> sessionList = new ArrayList<>();
 
 
  /**
   * 1) afterConnectionEstablished : 웹소켓에 클라이언트가 연결되었을 때 처리할 내용 정의
   *
   * @param session - 현재 웹소켓과 연결된 클라이언트 객체 (즉, 채팅방에 접속된 클라이언트)
   * // param이 매개변수에 대한 설명을 작성하는 키워드다.
   */
  @Override
  public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    // 이 session이 바로 이 웹소켓과 연결된 클라이언트 객체다.
    // 다수의 클라이언트들이 연결될거고 각각의 클라이언트들 마다 고유한 id 정보가 session에 담겨있을 것이다.
   
    /*
    log.debug("====== websocket 연결됨 =====");
    log.debug("WebSocketSession 객체: {}", session);
    log.debug("session id: {}", session.getId());
    log.debug("session Attributes 목록: {}", session.getAttributes()); // {sessionId=xxxx, loginUser=MemberDto객체}
    log.debug("현재 채팅방에 참가한 로그인한 회원: {}", session.getAttributes().get("loginUser")); // MemberDto 객체 뽑기
    */
   
    sessionList.add(session);
   
    for(WebSocketSession sss : sessionList) {
      String msg = "entry|" + ((MemberDto)session.getAttributes().get("loginUser")).getUserId() + "님이 입장하였습니다.";
      sss.sendMessage(new TextMessage(msg));
    }
   
  }
 
 
 

  /**
   * 2) handleMessage : 웹소켓으로 데이터(텍스트, 파일 등)가 전송되었을 경우 처리할 내용 정의
   *
   * @param session - 현재 웹소켓으로 데이터를 전송한 클라이언트 객체
   * @param message - 전송된 데이터에 대한 정보를 가지고 있는 객체
   */
  @Override
  public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
   
    /*
    log.debug("====== 메세지 들어옴 =====");
    log.debug("WebSocketSession 객체: {}", session);
    log.debug("WebSocketMessage 객체: {}", message);
    log.debug("메세지 내용: {}", message.getPayload());
    */
   
    // 현재 해당 웹소켓에 연결되어있는 모든 클라이언트들(작성자본인포함)에게 현재 들어온 메세지 재발송
    for(WebSocketSession sss : sessionList) {
     
      // 메세지유형(chat/entry/exit) | 채팅방에띄워주고자하는메세지내용 | 발신자아이디 | ...(프로필이미지경로 등) <- 나중에 |으로 split.
      String msg = "chat|" + message.getPayload() + "|" + ((MemberDto)session.getAttributes().get("loginUser")).getUserId();
      sss.sendMessage(new TextMessage(msg)); // 웹소켓에서 클라이언트로 메세지를 보냄. 그냥 보내면 안되고 TextMessage 객체로 보내야 한다.
      // room.jsp에서 onMesage 함수가 자동 실행
     
    }
  }
 
 
 
 
   
  /**
   * 3) afterConnectionClosed : 웹소켓에 클라이언트가 연결이 끊겼을 때 처리할 내용 정의
   */
  @Override
  public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
   
    /*
    log.debug("====== websocket 연결됨 =====");
    log.debug("WebSocketSession 객체: {}", session);
    log.debug("session id: {}", session.getId());
    log.debug("현재 채팅방에서 나간 회원: {}", session.getAttributes().get("loginUser")); // MemberDto 객체 뽑기
    */
   
    sessionList.remove(session);
   
    for(WebSocketSession sss : sessionList) {
      String msg = "exit|" + ((MemberDto)session.getAttributes().get("loginUser")).getUserId() + "님이 퇴장하였습니다.";
      sss.sendMessage(new TextMessage(msg));
    }
  }
 
}
 

 

 

- afterConnectionEstablished 메소드에서도 메세지를 보낼 수 있다.

afterConnectionClosed 메소드에서도 메세지를 보낼 수 있다.

 

 

 

 

 

 

scrollHeight와 scrollTop은 JavaScript에서 스크롤 위치와 스크롤 가능한 전체 높이를 조작하는 데 사용되는 속성입니다. 채팅창과 같은 요소에서 스크롤을 맨 아래로 자동으로 유지할 때 자주 사용됩니다.

  1. scrollHeight
    • 요소 내부에 스크롤이 가능한 전체 콘텐츠의 높이를 의미합니다.
    • 예를 들어, 채팅창에 많은 메시지가 쌓이면 scrollHeight는 메시지를 모두 포함한 높이가 됩니다.
    • 이 값은 스크롤바가 포함된 전체 콘텐츠 높이로, 채팅 내용이 많아질수록 scrollHeight 값도 커집니다.
  2. scrollTop
    • 스크롤바가 현재 위치한 곳을 의미하며, 스크롤을 조작하기 위해 값을 설정할 수도 있습니다.
    • scrollTop 값을 scrollHeight 값으로 설정하면, 스크롤바가 요소의 맨 아래로 이동합니다.
    • 예를 들어 chatArea.scrollTop = chatArea.scrollHeight;라고 설정하면 스크롤이 항상 맨 아래를 가리키게 됩니다.

 

 

 

 

ㅁ 서버 start

- 3개로

 

 

 

 

 

 

 

 

- db에 기록을 해둬야 나중에 다시 왔을 때 대화내용이 보여진다.

어딘가에 기록해둬야 한다. db에 insert해두면 나중에 이 방에 접속했을 때 db로부터 그때 나눴던 채팅 메시지 내역을 조회해와서 뿌릴 수 있다.

db에 기록하느 ㄴ시점은 handleMessage 누군가가 메세지를 전송하ㅐㅆ을때실행되는 메소드. 여기서 db에 기록하면 된다.

 

 

 

- cmd에서 ipconfig 하고 다른사람이 서버키면 들어갈 수 있따.

 

   연결별 DNS 접미사. . . . :
   IPv6 주소 . . . . . . . . . : fdc4:74d5:3e94:947e:158e:ecb7:74ed:68eb
   임시 IPv6 주소. . . . . . . : fdc4:74d5:3e94:947e:f190:d811:233a:fba3
   링크-로컬 IPv6 주소 . . . . : fe80::299e:7730:401a:9ce6%18
   IPv4 주소 . . . . . . . . . : 192.168.10.33
   서브넷 마스크 . . . . . . . : 255.255.255.0
   기본 게이트웨이 . . . . . . : 192.168.10.1

 

http://localhost:8888/spring/chat/room.do 대신

http://192.168.10.33:8888/spring/chat/room.do

 

 

 

========================================================================================

 

 

 

ㅁ room.jsp

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
   
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

<style>
    .chat{width:400px; margin:auto; padding:10px; border:1px solid lightgray;}
    .chat-area{height:500px; overflow: auto;}

     .chat-message{margin:10px 0px;}
     .chat-message.mine{display: flex; justify-content:flex-end;}
     
    .chat-message .send-message{
       padding: 5px 7px;
       border-radius: 10px;
       max-width: 190px;
       font-size:0.9em;
       white-space: pre-line;
    }
    .chat-message.other .send-message{background: lightgray;}
    .chat-message.mine .send-message{background: #FFE08C;}
 
    .chat-user {
       text-align:center;
       border-radius:10px;  
       background: lightgray;
       opacity: 0.5;
       margin: 20px 0px;
       color: black;
       line-height: 30px;
    }
 </style>

</head>
<body>
<div class="container p-3">

    <!-- Header, Nav start -->
    <jsp:include page="/WEB-INF/views/common/header.jsp" />
    <!-- Header, Nav end -->

    <!-- Section start -->
    <section class="row m-3" style="min-height: 500px">

      <div class="container border p-5 m-4 rounded">
        <h2 class="m-4">채팅방</h2>
       
        <div class="chat">
   
          <div class="chat-area">
         
            <!--  
            <div class="chat-message mine">
              <div class="send-message">내가보낸 메세지내가보낸 메세지내가보낸 메세지내가보낸 메세지내가보낸 메세지</div>
            </div>  
           
            <div class="chat-message other">
              <span class="send-user">상대방</span>
              <div class="send-message">남이보낸 메세지남이보낸 메세지남이보낸 메세지남이보낸 메세지남이보낸 메세지남이보낸 메세지</div>
            </div>
            -->
           
          </div>
         
          <div class="input-area">
         
            <div class="form-group">
                <textarea class="form-control" rows="3" id="message" style="resize:none"></textarea>
            </div>
           
            <button type="button" class="btn btn-sm btn-secondary btn-block" onclick="sendMessage();">전송하기</button>
            <button type="button" class="btn btn-sm btn-danger btn-block" onclick="onClose();">퇴장하기</button>
           
          </div>  
         
        </div>
      </div>


      <script src="https://cdn.jsdelivr.net/sockjs/1/sockjs.min.js"></script>
      <script>
     
        const sock = new SockJS("${contextPath}/chat"); // 이 구문이 웹소켓 서버와 연결이 되는 구문이다. 이 페이지에 진입한 클라이언트가.
        // - 아까 servlet-context에 /chat url 요청시에 ChatEchoHandler가 동작되게 해놨다.
        // 웹소켓 서버와 연결되는 순간 ChatEchoHandler가 동작된다고 보면 된다.
        // after afterConnectionEstablished 메소드가 실행된다.
        // 웹소켓 서버와 연결됨(즉, ChatEchoHandler의 afterConnectionEstablished 메소드가 실행된다.)
       
        sock.onmessage = onMessage; // 웹소켓에서 해당 클라이언트로 메세지 발송시 자동으로 실행할 함수를 지정(매핑)하는 구문
        sock.onclose = onClose; // 웹소켓과 해당 클라이언트간의 연결이 끊겼을 경우 자동으로 실행할 함수를 지정(매핑)하는 구문
        // 이렇게 3줄이 기본세팅이다.
       
       
       
     
        // 메세지를 출력시키는 영역의 요소
        const $chatArea = $(".chat-area");
       
       
        // 메세지 전송시 실행될 함수
        function sendMessage() {
          sock.send($("#message").val()); // 웹소켓 측으로 메세지를 전송 (ChatEchoHandler의 handleMessage 메소드 자동 실행)
          $("#message").val("");
        }
     
       
        // 나에게 메세지가 왔을 때 실행될 함수
        function onMessage(evt) { // 매개볁수를 하나 둬야한다. 웹소켓에서 클라이언트로 보내는 메세지를 받기 위해
          // console.log("evt", evt);
          // console.log("evt.data", evt.data);
         
          let msgArr = evt.data.split("|"); // ["메세지유형(chat|entry|exit)", "출력시킬메세지내용", "발신자아이디"];
         
         
          let $chatDiv = $("<div>"); // 채팅창에 append시킬 요소 (메세지 유형별로 다르게 제작)
          // $("<div>")는 기본적으로 빈 <div></div> 요소를 만듭니다.
          // jQuery 대신 순수 JavaScript로 <div> 요소를 생성하고 설정하려면 let chatDiv = document.createElement("div");
         
          if(msgArr[0] == "chat"){ // 채팅메세지일 경우
            $chatDiv.addClass("chat-message")
                    .addClass(msgArr[2] == "${loginUser.userId}" ? "mine" : "other") // 제이쿼리가 좋은게 메소드체이닝이 가능
                    .append( $("<div>").addClass("send-message").text(msgArr[1]) ); // innerText로 추가하고 자손으로 추가
                    // 여기가진 내가 보낸 메세지든 남이 보낸 메세지든 같다.
                   
            if($chatDiv.hasClass("other")) { // class 관련해선 add remove has 3개가 있다.
              $chatDiv.prepend( $("<span>").addClass("send-user").text(msgArr[2]) );
            }
                   
            // short-circuit으로 단일 if문 대체도 가능. 앞이 true면 뒤가 실행.      
            // $chatDiv.hasClass("other") && $chatDiv.prepend( $("<span>").addClass("send-user").text(msgArr[2]) );
               
           
                   
          }else { // 입장 또는 퇴장 메세지일 경우
            $chatDiv.addClass("chat-user")
                    .addClass(msgArr[0])
                    .text(msgArr[1]);
          }
         
         
          // 근데 계속 메세지가 추가되어도 맨 위의 메세지만 보여진다. 스크롤바가 생기고 하단으로 고정되려면.
          $chatArea.append($chatDiv);
          $chatArea.scrollTop( $chatArea[0].scrollHeight ); // 스크롤을 항상 하단으로 유지시켜주는 코드.
         
         
        }
       
       
        // 퇴장시 실행될 함수
        function onClose() {
          location.href = "${contextPath}"; // 이 페이지를 빠져나가면
        }
       
       
        $(document).ready(function(){
         
          // enter 눌렀을 때 메세지 전송, shift+enter 눌렀을 때 줄바꿈 적용
          $("#message").on("keydown", function(evt){
            if(evt.keyCode == 13){
              if(!evt.shiftKey){
                evt.preventDefault();
                sendMessage(); // 메세지 전송시키는 함수
              }
            }
          })
         
        })
       
       
      </script>


    </section>
    <!-- Section end -->

    <!-- Footer start -->
    <jsp:include page="/WEB-INF/views/common/footer.jsp" />
    <!-- Footer end -->

  </div>
</body>
</html>
 

 

- textarea에서 엔터치면 메세지 가야하고 줄바꿈하면 쉬프트 하고 엔터.

그러려면 이 코드 추가.

 

 

 

 

 

 

 

ㅁ ChatEchoHandler

 

 
package com.br.spring.handler;

import java.util.ArrayList;
import java.util.List;

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import com.br.spring.dto.MemberDto;

import lombok.extern.slf4j.Slf4j;

@Slf4j // 로그를 출력해보기 위해 롬복의 @Slf4j 어노테이션 작성.
public class ChatEchoHandler extends TextWebSocketHandler {

  // 웹소켓 세션 객체(클라이언트)들을 저장하는 리스트
  private List<WebSocketSession> sessionList = new ArrayList<>();
 
 
  /**
   * 1) afterConnectionEstablished : 웹소켓에 클라이언트가 연결되었을 때 처리할 내용 정의
   *
   * @param session - 현재 웹소켓과 연결된 클라이언트 객체 (즉, 채팅방에 접속된 클라이언트)
   * // param이 매개변수에 대한 설명을 작성하는 키워드다.
   */
  @Override
  public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    // 이 session이 바로 이 웹소켓과 연결된 클라이언트 객체다.
    // 다수의 클라이언트들이 연결될거고 각각의 클라이언트들 마다 고유한 id 정보가 session에 담겨있을 것이다.
   
    /*
    log.debug("====== websocket 연결됨 =====");
    log.debug("WebSocketSession 객체: {}", session);
    log.debug("session id: {}", session.getId());
    log.debug("session Attributes 목록: {}", session.getAttributes()); // {sessionId=xxxx, loginUser=MemberDto객체}
    log.debug("현재 채팅방에 참가한 로그인한 회원: {}", session.getAttributes().get("loginUser")); // MemberDto 객체 뽑기
    */
   
    sessionList.add(session);
   
    for(WebSocketSession sss : sessionList) {
      String msg = "entry|" + ((MemberDto)session.getAttributes().get("loginUser")).getUserId() + "님이 입장하였습니다.";
      sss.sendMessage(new TextMessage(msg));
    }
   
  }
 
 
 

  /**
   * 2) handleMessage : 웹소켓으로 데이터(텍스트, 파일 등)가 전송되었을 경우 처리할 내용 정의
   *
   * @param session - 현재 웹소켓으로 데이터를 전송한 클라이언트 객체
   * @param message - 전송된 데이터에 대한 정보를 가지고 있는 객체
   */
  @Override
  public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
   
    /*
    log.debug("====== 메세지 들어옴 =====");
    log.debug("WebSocketSession 객체: {}", session);
    log.debug("WebSocketMessage 객체: {}", message);
    log.debug("메세지 내용: {}", message.getPayload());
    */
   
    // 현재 해당 웹소켓에 연결되어있는 모든 클라이언트들(작성자본인포함)에게 현재 들어온 메세지 재발송
    for(WebSocketSession sss : sessionList) {
     
      // 메세지유형(chat/entry/exit) | 채팅방에띄워주고자하는메세지내용 | 발신자아이디 | ...(프로필이미지경로 등) <- 나중에 |으로 split.
      String msg = "chat|" + message.getPayload() + "|" + ((MemberDto)session.getAttributes().get("loginUser")).getUserId();
      sss.sendMessage(new TextMessage(msg)); // 웹소켓에서 클라이언트로 메세지를 보냄. 그냥 보내면 안되고 TextMessage 객체로 보내야 한다.
      // room.jsp에서 onMesage 함수가 자동 실행
     
    }
   
    // 특정 회원과 채팅방을 만든다거나 해당 회원과 나눈 채팅 내역을 보존하려면 메세지를 발송할 때 마다 db에 기록해야 한다.
    // 그때 실행되는 메소드가 handleMessage 메소드다.
    // insert 해주는 서비스측 메소드를 여기서 실행시키면 된다.
    // 해당 클래스에 Service 클래스를 DI(의존성주입)해서 채팅메세지를 insert하는 메소드를 여기서 실행하면 됨.
 
  }
 
 
 
 
   
  /**
   * 3) afterConnectionClosed : 웹소켓에 클라이언트가 연결이 끊겼을 때 처리할 내용 정의
   */
  @Override
  public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
   
    /*
    log.debug("====== websocket 연결됨 =====");
    log.debug("WebSocketSession 객체: {}", session);
    log.debug("session id: {}", session.getId());
    log.debug("현재 채팅방에서 나간 회원: {}", session.getAttributes().get("loginUser")); // MemberDto 객체 뽑기
    */
   
    sessionList.remove(session);
   
    for(WebSocketSession sss : sessionList) {
      String msg = "exit|" + ((MemberDto)session.getAttributes().get("loginUser")).getUserId() + "님이 퇴장하였습니다.";
      sss.sendMessage(new TextMessage(msg));
    }
  }
 
}
 

 

- db에 기록하려면 handleMessage 메소드에 추가하면 된다.

 

 

- 메세지를 DB에 저장하는 실제 로직을 수행하는 ChatService라는 클래스를 @Service 어노테이션과 함께 별도로 작성합니다.

- ChatEchoHandler 클래스에서 이 ChatService를 필드로 선언하고, DI를 위해 @Autowired 어노테이션을 붙입니다.

- ChatService를 @Service로 등록하고, ChatEchoHandler에서 @Autowired를 통해 chatService 필드를 주입받아 사용하면 됩니다.

 

'Spring' 카테고리의 다른 글

[스케줄러]  (2) 2024.11.05
[인터셉터]  (0) 2024.11.05
[웹소켓] 1.  (0) 2024.11.04
[웹프로젝트] 20. 게시글 수정  (1) 2024.10.31
[웹프로젝트] 19. 게시글 삭제  (1) 2024.10.31