💻문제점
A가 B에게 채팅을 보내기 위해 1:1 대화하기 버튼을 누른다. 이때 A와 B의 채팅방이 만들어지고, 서버에서 해당 채팅방의 UUID를 넘겨주면 A는 해당 채팅방을 구독한다. 이때 B가 A와 1:1대화하기 버튼을 직접 누르지 않는 이상, 해당 채팅방에 대해 구독되어 있지 않기 때문에 메세지를 받을 수 없다.
A가 1:1대화하기 버튼을 눌렀을 때 B도 구독할 수 있는 방법이 없을까?
이 문제로 이미 이틀을 고민하는 중이다...
생각한 방법은, 사용자가 로그인을 할 때 생성된 WebSocketSession을 sessionId와 함께 저장하는 것이다.
Map의 형태로 Key는 sessionId, value는 WebSocketSession 객체를 저장한다.
Map<String, WebSocketSession> sessionMap = new ConcurrentHashMap<>();
A가 1:1대화하기 버튼을 눌렀을 때, B를 구독시켜주어야 하는데, 이때 서버로 넘겨주는 값은 고유한 nickname이다.
nickname으로 sessionId와 WebSocketSession 객체를 찾아 구독시켜주는 방법이다.
그리고 다시 key가 고유한 사용자 Id 또는 닉네임이고, value는 sessionMap인 map을 생성한다.
Map<String, Map<String, WebSocketSession>> userSessionMap = new ConcurrentHashMap<>();
WebSocket 연결을 시도할 때, 해당 유저의 sessionId와 session을 저장해야하는데, WebSocketSession 객체를 가져올 방법을 찾아야 했다.
@Slf4j
@Configuration
public class MyChannelIntercepter implements ChannelInterceptor {
Map<String, WebSocketSession> sessionMap = new ConcurrentHashMap<>();
Map<String, Map<String, WebSocketSession>> userSessionMap = new ConcurrentHashMap<>();
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
String sessionId = accessor.getSessionId();
//웹 소켓 연결 시점
if (StompCommand.CONNECT == accessor.getCommand()) {
log.info("sessionId: " + sessionId);
} else
if (StompCommand.SUBSCRIBE == accessor.getCommand()) {
//다른 채팅방 구독할 때
accessor.getSessionAttributes();
}
return message;
}
}
📃시도
시도1.
afterConnectionEstablised() 메서드를 오버라이딩 받아 저장한다. 매개변수로 sessionid를 받는 메서드를 생성하여 세션 객체를 넘겨주도록 하였다.
@Component
public class WebSocketHandler extends TextWebSocketHandler {
Map<String, WebSocketSession> sessionMap = new ConcurrentHashMap<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessionMap.put(session.getId(), session);
super.afterConnectionEstablished(session);
}
public WebSocketSession getWebSocketSession(String sessionId) {
return sessionMap.get(sessionId);
}
}
하지만 WebSocketConfig에서 생성한 핸들러를 등록할 방법을 찾지 못했다.
TextWebSocketHandler는 WebSocketMessageBrokerConfigurer를 구현한 WebSocketConfig에서 사용할 수 없기 때문이다. TextWebSocketHandler는 WebSocketConfigurer 구현체에서 사용 가능한 것 같다.
@Configuration
@EnableWebSocketMessageBroker
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
private final MyChannelIntercepter myChannelIntercepter;
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/sub"); //받기
config.setApplicationDestinationPrefixes("/pub"); //보내기
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws-edit")
.setAllowedOriginPatterns("*")
.withSockJS();
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(myChannelIntercepter);
}
}
시도2.
StompSessionHandlerAdapter라는 걸 찾았다.
@Component
public class StompHandler extends StompSessionHandlerAdapter {
@Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
super.afterConnected(session, connectedHeaders);
}
}
이걸 사용하려면 StompClient 객체를 생성하고 connect 할 때 핸들러를 사용하도록 해야하는데, 연결은 클라이언트 측에서 시도한다.... 아무래도 못 쓸 것 같다.
그리고 일단 WebSocketSession을 알아야 하는게 아니라 StompSession을 알아야 subscribe 메서드를 통해서 구독하게 해줄 수 있다는 것을 알았다.
🤦♀️느낀점
하루 종일 이 문제를 어떻게 해결할 수 있을지 고민했다. 공식 문서도 읽어보고 다른 사람들 코드도 보았지만, 우리 프로젝트에 적용할 마땅한 방법이 없었다. 생각이 너무 많아지니 오히려 방법을 못 찾는 것 같다. 내일 또 다시 시도해보자.
'TIL' 카테고리의 다른 글
[TIL - 20230506] 회원/비회원 전체 게시글 조회 처리 (0) | 2023.05.06 |
---|---|
[TIL - 202030504] WebSocket+Stomp 채팅방에 직접 참여하지 않은 사용자 구독시키기2 + 자잘한 문제점 해결 (0) | 2023.05.04 |
[TIL - 20230501] 채팅 시스템 프론트와 연결 (0) | 2023.05.02 |
[WIL - 20230424~20230430] (0) | 2023.05.01 |
[TIL - 20230430] WebSocket+Stomp 사용자들이 있는 채팅방 찾기 (0) | 2023.04.30 |