WebSocketEventsListener:
ApplicationListener를 사용하여 STOMP 세션의 주요 이벤트를 감지하는 중앙 리스너입니다.SessionSubscribeEvent: 사용자가 특정 STOMP 토픽(e.g., 채팅방)을 구독할 때 발생하는 이벤트를 가로챕니다.SessionDisconnectEvent: 사용자의 WebSocket 연결이 끊어질 때 발생하는 이벤트를 가로챕니다.시스템 메시지 브로드캐스팅:
WebSocketEventsListener는 위 이벤트를 감지한 후, 해당 채팅방을 구독 중인 모든 클라이언트에게 "OOO님이 입장하셨습니다." 또는 "OOO님이 퇴장하셨습니다."와 같은 시스템 메시지를 브로드캐스트합니다.UserSessionRegistry (세션 레지스트리):
WebSocketEventsListener는 입장/퇴장 메시지를 브로드캐스트하기 전에, 먼저 이 레지스트리를 확인합니다. 만약 사용자가 이미 해당 방에 등록되어 있다면, 중복된 입장 알림을 보내지 않습니다. 이를 통해 알림의 정확성을 보장합니다.PresenceService 및 상태 관리:
ONLINE, STUDYING, BREAK, OFFLINE)를 관리하는 서비스입니다.@MessageMapping)를 통해 PresenceController로 전달됩니다.PresenceController는 PresenceService를 호출하여 사용자의 상태를 변경하고, 이 변경 사항을 해당 스터디룸이나 관련 사용자들에게 실시간으로 브로드캐스트합니다.WebSocketEventsListener는 사용자가 채팅방에 입장(subscribe)하면 상태를 STUDYING으로, 연결이 끊어지면(disconnect) OFFLINE으로 자동으로 변경하는 로직을 포함하여 Presence 정보의 신뢰성을 높였습니다.ChatSocketService가 메시지를 DB에 저장하는 책임과 다른 사용자에게 브로드캐스트하는 책임을 모두 가지고 있어 비대해지는 문제를 해결하기 위해, 관심사 분리(Separation of Concerns) 원칙에 따라 서비스를 리팩토링했습니다.ChatMessageWriteService: 오직 채팅 메시지를 데이터베이스에 저장(Write)하는 책임만 가집니다.ChatBroadcastService: 오직 채팅 메시지를 WebSocket 브로커를 통해 브로드캐스트(Broadcast)하는 책임만 가집니다.ChatSocketService (Coordinator): 이제 이 서비스는 직접 로직을 수행하는 대신, WriteService와 BroadcastService를 순서대로 호출하는 조정자(Coordinator) 역할을 수행합니다. 이를 통해 각 서비스의 역할이 명확해지고, 코드의 테스트 용이성과 유지보수성이 크게 향상되었습니다.Zustand ChatStore:
MessageList 컴포넌트는 이 스토어를 구독하여 항상 최신 메시지 목록을 렌더링합니다.실시간 UI 피드백:
MessageList 컴포넌트는 메시지 타입을 구분하여, 시스템 메시지("입장/퇴장")는 일반 채팅 버블과 다른 스타일(e.g., 중앙 라벨)로 렌더링하는 MessageSystemItem을 사용합니다.Presence 관련 WebSocket 메시지를 구독하여, 사용자의 상태가 변경될 때마다 UI(e.g., 프로필 옆 상태 아이콘)를 실시간으로 업데이트합니다.