ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • WebSocket을 이용한 실시간 채팅 구현하기 3-1: 브라우저 화면을 닫을 때 퇴장 메시지 전송하기
    Today I Learned 2023. 1. 21. 20:49

     

    // 1. 채팅 li 컴포넌트 Key 중복 문제 해결
    // 1-1. key에 메시지 내용을 쓰고 있었는데, 동일한 사용자가 들어왔다 나갔다를 반복하면
    // 메시지 로그 상태에 동일한 형태의 '나갔습니다' '들어왔습니다'가 쌓여서 문제를 일으키고 있었음
    // 1-2. 따라서 메시지에 new Date().toLocaleTimeString()을 추가해서 해결을 시도
    // 근데 이러면 빠르게 같은 내용의 메시지를 보낼 때 key의 중복을 막지 못한다.
    // 1-3. 서버에서 메시지의 id를 부여하게 하기
    // 서버에 messageIdGenerator를 생성하고, id를 부여해 해결
    // 프로젝트에서는 message 객체가 id를 갖게 하는 식으로 해결될듯

    // 2. unsubscribe할 때 undefined 없애는 방법 찾기 (뭐가 undefined인건지 찾아보기)
    // 2-1. 문제는 connected to server undefined
    // CONNECTED frame에는 헤더가 포함되어 있는데, 없는 경우 undefined로 표시되고,
    // 무시할 수 있는? 문제라는 공식 디렉터?의 피드백이 있음

    // 3. 브라우저를 종료했을 때에도 접속 종료되게 하기
    // 3-1. 이론적으로는...
    // 클라이언트가 서버와 연결할 때 세션 정보를 보내고
    // 서버는 등록된 세션을 주기적으로 health check해서
    // 세션이 응답이 없으면 연결을 끊는다. 식으로 하면 될 것 같은데
    // 딱 정리된 글이 없네...
    // 3-2. utils에 WebSocketEventListener를 정의
    // 메서드로 handleWebSocketConnectListener와 handleWebSocketDisconnectListener를 정의
    // 인자로는 SessionConnectedEvent/SessionDisconnectedEvent를 각각 받고
    // StompHeaderAccesor.wrap(event.getMessage())로 세션 정보를 알 수 있음
    // 그리고 접속 종료는 연결을 직접 종료하거나, 브라우저를 직접 종료하는 것 둘 다
    // SessionDisconnectedEvent가 발생하는 것이 확인되었음
    // 3-3. 그러면 다시 Repository를 정의하고, 방 별로 Sessions를 관리해서
    // 접속이 종료되는 경우에 그 Sessions에 들어 있는 각 session 들에게 서버에서 메시지를 쏘면 될 것 같음?
    // SessionConnectedEvent에서 연결된 session을 식별할 수 있는 정보는 sessionId뿐인 것 같다.
    // 하지만 그것만으로는 어느 Room에 접속하게 할지 식별이 안 된다.
    // Authorization에 Username을 같이 담아서 전송하면 될까?
    // 강제 종료 시 메시지 보내겠다고 로직이 바뀌는게 맞나?
    // 3-4. 이 이슈에 너무 많은 시간을 쓰고 있다.
    // 정리하자면 두 가지 방법 중 하나로 해보고 싶었다.
    // - 서버에서 heartbeat를 보내서 클라이언트가 응답하면 ㅇㅋ, 응답 없으면 연결 끊고 메시징
    // - 이 방법에서 모르겠는 점은 heartbeat를 클라이언트에서 어떻게 받는지, 서버에는 다시 어떻게 보내는지,
    // 어떻게 연결을 끊을 것인지와, 끊었다는 메시지는 다른 session들에게 어떻게 보낼 것인지.
    // - 연결 이벤트, 종료 이벤트 때 session을 확인해서 같이 있는 session들한테 메시지 쏘기
    // - 이 방법에서 모르겠는 점은 header에서 받아온 session만으로는
    // 어떤 방의 Sessions에 넣어줘야 하는지 모르겠다는 점이랑
    // 종료 이벤트가 발생했을 때 어떤 방의 Sessions에서 찾아서 제거하고
    // 그 Sessions의 세션들에게 메시지를 보낼 것인지.
    // 3-5. window를 이용
    // window에 Event Listener를 주입
    // beforeunload 이름으로 window를 닫기 전에, 연결되어 있을 경우 disconnect를 수행
    // 이 방식으로 해결
    // 컴포넌트가 unmount될 때 실행시켜보자는 데에서 착안했으나,
    // 화면 자체를 종료하는 것이 unmount로 이어지지는 않아 useEffect를 사용하지는 못했음

    // cf. MessageController에서 사용할 수 있는 @MessageExceptionHandler라는 게 있다.
    // 어떤 상황에서 에러가 발생하는지, 클라이언트에서 에러를 어떻게 처리해야 할지 아직은 모르겠다.

    // 4. 사용자 입장, 메시지 전송, 사용자 퇴장을 모두 다른 요청으로 전송시키고,
    // 서버의 Controller에서 구분하게 하기
    // - 요청 주소를 모두 다르게 전송하고, Controller에서 응답을 받는 주소에 차이를 둠

     

     

    // utils/WebSocketEventListener.java
    // 방법 중의 하나로, 여기에서 메시지를 식별해서 roomId에 해당하는 room의 Session Set에 session을 add하거나 remove하려고 했음
    
    @Component
    public class WebSocketEventListener {
        @Autowired
        private ChattingRoomRepository chattingRoomRepository;
    
        @EventListener
        public void handleWebSocketConnectListener(SessionConnectedEvent event) {
            String sessionId = StompHeaderAccessor
                .wrap(event.getMessage())
                .getSessionId();
        }
    
        @EventListener
        public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
            String sessionId = StompHeaderAccessor
                .wrap(event.getMessage())
                .getSessionId();
        }
    }

    댓글

Designed by Tistory.