💻문제점 1
어제의 문제가 오늘까지 이어진다. 문제점은 다음과 같았다.
A가 B에게 채팅을 보내기 위해 1:1 대화하기 버튼을 누른다. 이때 A와 B의 채팅방이 만들어지고, 서버에서 해당 채팅방의 UUID를 넘겨주면 A는 해당 채팅방을 구독한다. 이때 B가 A와 1:1대화하기 버튼을 직접 누르지 않는 이상, 해당 채팅방에 대해 구독되어 있지 않기 때문에 메세지를 받을 수 없다.
A가 1:1대화하기 버튼을 눌렀을 때 B도 구독할 수 있는 방법이 없을까?
📃문제점1 시도
시도3.
새로운 방법을 고안했다. 이 방법은 StompSession 객체를 찾을 필요가 없다!
우선, 모든 사용자는 웹 소켓을 연결할 때 자기 자신의 개인 채팅방도 구독한다. ex) /sub/chat/queue유저1
유저2가 유저1에 대해 처음으로 1:1대화 버튼을 누르면 유저1과 유저2의 채팅방이 생성되고, 유저1은 해당 채팅방을 구독하게 된다. ex) /sub/chat/room방1
이제 어떻게 유저2가 그 채팅방을 구독하게 할 것이냐!
SubScribeMapping 으로 경로가 /chat/queue 인 경우에, 유저2가 채팅방에 들어갈 수 있도록 ChatDto를 만들어서 enterChatRoom() 메서드를 수행하도록 했다.
@SubscribeMapping("/chat/queue")
public void subscribeAlarm(ChatRoomIdResponseDto roomIdResponseDto, @AuthenticationPrincipal UserDetailsImpl userDetails, SimpMessageHeaderAccessor accessor) throws Exception {
log.info("구독 발생");
Thread.sleep(500);
ChatDto chatDto = ChatDto.builder()
.type(ChatDto.MessageType.ENTER)
.roomId(roomIdResponseDto.getRoomId())
.sender(userDetails.getNickname()).build();
enterChatRoom(chatDto, accessor);
}
될거라고 생각했지만... SubscribMapping 자체가 구독을 감지한다는 느낌은 아니었다. 또 pub을 시켜주지는 않기 때문에 구독 문제는 여전히 해결 할 수 없었다.
🔍문제점1 해결
바로 유저1이 채팅방을 구독할 때 유저2의 개인방에 구독해야할 roomId를 보내주는 것이다.
ex) pub/chat/queue유저1 {"roomId": "23910sdasfw", "type": "SUB"}
유저2는 개인방에 온 메시지를 보고, type이 SUB이면 해당 roomId를 구독하도록 한다.
message를 보내지 않으므로 클라이언트 측에서는 message가 비지 않은 경우만 채팅방에 출력하도록 한다.
서버
@MessageMapping("/chat/subscribe")
@SendTo("/sub/chat/queue")
public void subscribeUser(ChatRoomIdRequestDto chatRoomIdRequestDto) throws Exception {
Thread.sleep(200); // simulated delay
msgTemplate.convertAndSend("/sub/chat/queue" + chatRoomIdRequestDto.getReceiver(), new ChatRoomIdResponseDto(chatRoomIdRequestDto));
}
@Getter
@AllArgsConstructor
public class ChatRoomIdResponseDto {
private String roomId;
private String type;
private String sender;
public ChatRoomIdResponseDto(ChatRoomIdRequestDto chatRoomIdRequestDto) {
this.roomId = chatRoomIdRequestDto.getRoomId();
this.type = "SUB";
this.sender = chatRoomIdRequestDto.getSender();
}
}
클라이언트
const onPrivateMessage = (payload) => {
let payloadData = JSON.parse(payload.body);
console.log("PAYLOAD DATA", payloadData);
if (privateChats.has(payloadData.roomId) && payloadData.message) {
privateChats.get(payloadData.roomId).messages.push(payloadData);
setPrivateChats(new Map(privateChats));
console.log("new:", privateChats.get(payloadData.roomId));
} else {
if (payloadData.type == "SUB") {
if (stompClient) {
stompClient.subscribe("/sub/chat/room" + payloadData.roomId, onPrivateMessage);
}
}
if (payloadData.message) {
let list = [];
list.push(payloadData);
privateChats.set(payloadData.roomId, { targetName: payloadData.sender, messages: list });
setPrivateChats(new Map(privateChats));
}
console.log("add:", privateChats.get(payloadData.roomId));
}
};
이렇게 짱멋짐과 짱귀엽이 대화하는 중이다. 메시지를 한 번 보냈는데, 두 번 오는건 잠시후 해결하자..
여기서 짱예쁨이 짱멋짐과의 채팅을 시작한다. 짱예쁨은 입장 메시지를 보내면서 짱멋짐에게 구독 요청을 보냈고, 짱멋짐이 받은 roomId를 토대로 해당 채팅방을 구독하여 채팅을 정상적으로 수신한 것을 볼 수 있다...!!!
드디어 예쁨이가 멋짐이에게 채팅을 보낼 수 있게 되었다...!!! o(TヘTo)
💻문제점2
유저1이 유저2에 대해 1:1대화를 신청한다. 이때 유저1은 생성된 채팅방과 개인 채팅방을 구독한다.
유저2는 방에 참여했을 때 입장 메시지와 채팅방에 대한 구독요청을 보내기 때문에 유저1은 같은 채팅방에 대해 2번 구독하게 된다.
때문에 유저2가 메시지를 보내면 유저1이 같은 메시지를 두 번 받게 되는 문제가 발생했다.
const userJoin = (room) => {
let roomIdMessage = {
sender: userData.username,
receiver: userData.receivername,
roomId: room.roomId,
};
let chatMessage = {
type: "ENTER",
sender: userData.username,
roomId: room.roomId,
roomName: room.roomName,
message: "",
};
stompClient.send("/pub/chat/enter", {}, JSON.stringify(chatMessage));
stompClient.send("/pub/chat/subscribe", {}, JSON.stringify(roomIdMessage));
};
🔍문제점2 해결
채팅을 시작할 때, 채팅방이 새로 생성된 방인지, 아니면 이미 생성되어 있는 방인지 판단하여 구독 요청을 보낼 필요가 있었다. 이미 생성되어 있는 방이라면 유저2가 유저1에게 구독 요청을 보내지 않고 입장만 하면 되기 때문이다.
때문에 다음과 같이 서버 측에서 응답을 보내줄 때 이미 있는 방이었는지, 아니면 없어서 새로 생성한 방인지 알려주도록 했다.
서버
public ResponseDto createChatRoom(ChatRoomRequestDto requestDto, Member sender) {
Member receiver = memberRepository.findByNickname(requestDto.getReceiver()).get();
//이미 reciever와 sender로 생성된 채팅방이 있는지 확인
JoinChatRoom findChatRoom = findExistChatRoom(receiver, sender);
//채팅방 있으면 ChatRoom의 roomId 반환
if (findChatRoom != null){
return ResponseDto.setSuccess("already has room and find Chat Room Success!", new ChatRoomResponseDto(findChatRoom, "EXIST"));
}
//채팅방 없으면 receiver와 sender의 방을 생성해주고 roomId 반환
ChatRoom newChatRoom = new ChatRoom();
JoinChatRoom newJoinChatRoom = new JoinChatRoom(sender, newChatRoom, receiver.getNickname());
newChatRoom.addJoinChatRoom(newJoinChatRoom);
newChatRoom.addJoinChatRoom(new JoinChatRoom(receiver, newChatRoom, sender.getNickname()));
chatRoomRepository.save(newChatRoom);
return ResponseDto.setSuccess("create ChatRoom success", new ChatRoomResponseDto(newJoinChatRoom, "NEW"));
}
@Getter
@AllArgsConstructor
public class ChatRoomResponseDto {
String roomId;
String roomName;
String type;
public ChatRoomResponseDto(JoinChatRoom joinChatRoom, String type) {
this.roomId = joinChatRoom.getChatRoom().getRoomId();
this.roomName = joinChatRoom.getRoomName();
this.type = type;
}
}
클라이언트
클라이언트 측에서는 서버로 부터 받은 응답에서 type이 EXIST이면 유저가 입장만 하도록 하고, NEW이면 구독 요청을 보내도록 했다.
const userJoin = (room) => {
if (room.type == "EXIST") {
let chatMessage = {
type: "ENTER",
sender: userData.username,
roomId: room.roomId,
roomName: room.roomName,
message: "",
};
stompClient.send("/pub/chat/enter", {}, JSON.stringify(chatMessage));
} else {
let roomIdMessage = {
sender: userData.username,
receiver: userData.receivername,
roomId: room.roomId,
};
stompClient.send("/pub/chat/subscribe", {}, JSON.stringify(roomIdMessage));
}
};
메시지가 두 번 찍히지 않고 잘 나온다.
💻문제점3
문제점2를 해결하고 난 뒤,
짱예쁨이 짱멋짐에게 채팅을 보낸다.
짱멋짐의 채팅방 목록에 짱예쁨이 추가가 된 것을 볼 수있다.
짱예쁨이 채팅을 보낼 때 짱멋짐과의 채팅방이 없었으므로 type은 NEW이다. 때문에 짱예쁨은 입장 메시지는 보내지 않고 구독 요청만 보내게 된다. 짱멋짐도 단순히 요청에 의해 구독만 하기 때문에 입장 메시지가 뜨지 않는다.
📃문제점3 시도
@Getter
@AllArgsConstructor
public class ChatRoomIdResponseDto {
private String roomId;
private String type;
private String sender;
private String message;
public ChatRoomIdResponseDto(ChatRoomIdRequestDto chatRoomIdRequestDto) {
this.roomId = chatRoomIdRequestDto.getRoomId();
this.type = "SUB";
this.sender = chatRoomIdRequestDto.getSender();
this.message = chatRoomIdRequestDto.getReceiver() + "님이 입장함! ο(=•ω<=)ρ⌒☆";
}
}
이렇게 하면 메시지가 뜨긴 하지만, 짱예쁨이 짱멋짐님이 입장함!이라는 메세지를 출력한다. 짱멋짐이 출력하게 해야하는데...
프론트를 수정이 필요했는데, 그것보다는 입장 메시지가 없는게 나을 것 같다는 의견이 모여서 없애기로 결정했다.
입장 메시지의 귀여운 이모티콘 때문에 살리고 싶었지만, 빠이다...!
🔍문제점3 해결
'TIL' 카테고리의 다른 글
[TIL - 20230510] Service 유효성 검사 메서드 분리 (0) | 2023.05.11 |
---|---|
[TIL - 20230506] 회원/비회원 전체 게시글 조회 처리 (0) | 2023.05.06 |
[TIL - 20230503] WebSocket+Stomp 채팅방에 직접 참여하지 않은 사용자 구독시키기1 (0) | 2023.05.04 |
[TIL - 20230501] 채팅 시스템 프론트와 연결 (0) | 2023.05.02 |
[WIL - 20230424~20230430] (0) | 2023.05.01 |