diff --git a/backend/src/main/java/io/f1/backend/domain/game/app/RoomService.java b/backend/src/main/java/io/f1/backend/domain/game/app/RoomService.java index e96bdec9..a2770f76 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/app/RoomService.java +++ b/backend/src/main/java/io/f1/backend/domain/game/app/RoomService.java @@ -13,10 +13,8 @@ import static io.f1.backend.global.util.SecurityUtils.getCurrentUserNickname; import io.f1.backend.domain.game.dto.ChatMessage; +import io.f1.backend.domain.game.dto.MessageType; import io.f1.backend.domain.game.dto.RoomEventType; -import io.f1.backend.domain.game.dto.RoomExitData; -import io.f1.backend.domain.game.dto.RoomInitialData; -import io.f1.backend.domain.game.dto.RoundResult; import io.f1.backend.domain.game.dto.request.RoomCreateRequest; import io.f1.backend.domain.game.dto.request.RoomValidationRequest; import io.f1.backend.domain.game.dto.response.GameSettingResponse; @@ -32,6 +30,7 @@ import io.f1.backend.domain.game.model.RoomSetting; import io.f1.backend.domain.game.model.RoomState; import io.f1.backend.domain.game.store.RoomRepository; +import io.f1.backend.domain.game.websocket.MessageSender; import io.f1.backend.domain.question.entity.Question; import io.f1.backend.domain.quiz.app.QuizService; import io.f1.backend.domain.quiz.dto.QuizMinData; @@ -62,6 +61,7 @@ public class RoomService { private final AtomicLong roomIdGenerator = new AtomicLong(0); private final ApplicationEventPublisher eventPublisher; private final Map roomLocks = new ConcurrentHashMap<>(); + private final MessageSender messageSender; private static final String PENDING_SESSION_ID = "PENDING_SESSION_ID"; public RoomCreateResponse saveRoom(RoomCreateRequest request) { @@ -116,8 +116,7 @@ public void enterRoom(RoomValidationRequest request) { } } - public RoomInitialData initializeRoomSocket( - Long roomId, String sessionId, UserPrincipal principal) { + public void initializeRoomSocket(Long roomId, String sessionId, UserPrincipal principal) { Room room = findRoom(roomId); @@ -150,11 +149,15 @@ public RoomInitialData initializeRoomSocket( SystemNoticeResponse systemNoticeResponse = ofPlayerEvent(player.getNickname(), RoomEventType.ENTER); - return new RoomInitialData( - roomSettingResponse, gameSettingResponse, playerListResponse, systemNoticeResponse); + String destination = getDestination(roomId); + + messageSender.send(destination, MessageType.ROOM_SETTING, roomSettingResponse); + messageSender.send(destination, MessageType.GAME_SETTING, gameSettingResponse); + messageSender.send(destination, MessageType.PLAYER_LIST, playerListResponse); + messageSender.send(destination, MessageType.SYSTEM_NOTICE, systemNoticeResponse); } - public RoomExitData exitRoom(Long roomId, String sessionId, UserPrincipal principal) { + public void exitRoom(Long roomId, String sessionId, UserPrincipal principal) { Object lock = roomLocks.computeIfAbsent(roomId, k -> new Object()); @@ -165,7 +168,8 @@ public RoomExitData exitRoom(Long roomId, String sessionId, UserPrincipal princi /* 방 삭제 */ if (isLastPlayer(room, sessionId)) { - return removeRoom(room); + removeRoom(room); + return; } /* 방장 변경 */ @@ -181,11 +185,14 @@ public RoomExitData exitRoom(Long roomId, String sessionId, UserPrincipal princi PlayerListResponse playerListResponse = toPlayerListResponse(room); - return new RoomExitData(playerListResponse, systemNoticeResponse, false); + String destination = getDestination(roomId); + + messageSender.send(destination, MessageType.PLAYER_LIST, playerListResponse); + messageSender.send(destination, MessageType.SYSTEM_NOTICE, systemNoticeResponse); } } - public PlayerListResponse handlePlayerReady(Long roomId, String sessionId) { + public void handlePlayerReady(Long roomId, String sessionId) { Player player = roomRepository .findPlayerInRoomBySessionId(roomId, sessionId) @@ -195,7 +202,9 @@ public PlayerListResponse handlePlayerReady(Long roomId, String sessionId) { Room room = findRoom(roomId); - return toPlayerListResponse(room); + String destination = getDestination(roomId); + + messageSender.send(destination, MessageType.PLAYER_LIST, toPlayerListResponse(room)); } public RoomListResponse getAllRooms() { @@ -214,30 +223,34 @@ public RoomListResponse getAllRooms() { } // todo 동시성적용 - public RoundResult chat(Long roomId, String sessionId, ChatMessage chatMessage) { + public void chat(Long roomId, String sessionId, ChatMessage chatMessage) { Room room = findRoom(roomId); + String destination = getDestination(roomId); + + messageSender.send(destination, MessageType.CHAT, chatMessage); + if (!room.isPlaying()) { - return buildResultOnlyChat(chatMessage); + return; } Question currentQuestion = room.getCurrentQuestion(); String answer = currentQuestion.getAnswer(); - if (!answer.equals(chatMessage.message())) { - return buildResultOnlyChat(chatMessage); + if (answer.equals(chatMessage.message())) { + room.increasePlayerCorrectCount(sessionId); + + messageSender.send( + destination, + MessageType.QUESTION_RESULT, + toQuestionResultResponse(currentQuestion.getId(), chatMessage, answer)); + messageSender.send(destination, MessageType.RANK_UPDATE, toRankUpdateResponse(room)); + messageSender.send( + destination, + MessageType.SYSTEM_NOTICE, + ofPlayerEvent(chatMessage.nickname(), RoomEventType.ENTER)); } - - room.increasePlayerCorrectCount(sessionId); - - return RoundResult.builder() - .questionResult( - toQuestionResultResponse(currentQuestion.getId(), chatMessage, answer)) - .rankUpdate(toRankUpdateResponse(room)) - .systemNotice(ofPlayerEvent(chatMessage.nickname(), RoomEventType.ENTER)) - .chat(chatMessage) - .build(); } private Player getRemovePlayer(Room room, String sessionId, UserPrincipal principal) { @@ -268,12 +281,11 @@ private boolean isLastPlayer(Room room, String sessionId) { return playerSessionMap.size() == 1 && playerSessionMap.containsKey(sessionId); } - private RoomExitData removeRoom(Room room) { + private void removeRoom(Room room) { Long roomId = room.getId(); roomRepository.removeRoom(roomId); roomLocks.remove(roomId); log.info("{}번 방 삭제", roomId); - return RoomExitData.builder().removedRoom(true).build(); } private void changeHost(Room room, String hostSessionId) { @@ -298,7 +310,7 @@ private void removePlayer(Room room, String sessionId, Player removePlayer) { room.removeSessionId(sessionId); } - private RoundResult buildResultOnlyChat(ChatMessage chatMessage) { - return RoundResult.builder().chat(chatMessage).build(); + private String getDestination(Long roomId) { + return "/sub/room/" + roomId; } } diff --git a/backend/src/main/java/io/f1/backend/domain/game/dto/RoomExitData.java b/backend/src/main/java/io/f1/backend/domain/game/dto/RoomExitData.java deleted file mode 100644 index 76efee90..00000000 --- a/backend/src/main/java/io/f1/backend/domain/game/dto/RoomExitData.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.f1.backend.domain.game.dto; - -import io.f1.backend.domain.game.dto.response.PlayerListResponse; -import io.f1.backend.domain.game.dto.response.SystemNoticeResponse; - -import lombok.Builder; -import lombok.Getter; - -@Getter -public class RoomExitData { - - private final PlayerListResponse playerListResponses; - private final SystemNoticeResponse systemNoticeResponse; - private final boolean removedRoom; - - @Builder - public RoomExitData( - PlayerListResponse playerListResponses, - SystemNoticeResponse systemNoticeResponse, - boolean removedRoom) { - this.playerListResponses = playerListResponses; - this.systemNoticeResponse = systemNoticeResponse; - this.removedRoom = removedRoom; - } -} diff --git a/backend/src/main/java/io/f1/backend/domain/game/dto/RoomInitialData.java b/backend/src/main/java/io/f1/backend/domain/game/dto/RoomInitialData.java deleted file mode 100644 index 439daca8..00000000 --- a/backend/src/main/java/io/f1/backend/domain/game/dto/RoomInitialData.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.f1.backend.domain.game.dto; - -import io.f1.backend.domain.game.dto.response.GameSettingResponse; -import io.f1.backend.domain.game.dto.response.PlayerListResponse; -import io.f1.backend.domain.game.dto.response.RoomSettingResponse; -import io.f1.backend.domain.game.dto.response.SystemNoticeResponse; - -public record RoomInitialData( - RoomSettingResponse roomSettingResponse, - GameSettingResponse gameSettingResponse, - PlayerListResponse playerListResponse, - SystemNoticeResponse systemNoticeResponse) {} diff --git a/backend/src/main/java/io/f1/backend/domain/game/dto/RoundResult.java b/backend/src/main/java/io/f1/backend/domain/game/dto/RoundResult.java deleted file mode 100644 index 6a537b3d..00000000 --- a/backend/src/main/java/io/f1/backend/domain/game/dto/RoundResult.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.f1.backend.domain.game.dto; - -import io.f1.backend.domain.game.dto.response.QuestionResultResponse; -import io.f1.backend.domain.game.dto.response.RankUpdateResponse; -import io.f1.backend.domain.game.dto.response.SystemNoticeResponse; - -import lombok.Builder; -import lombok.Getter; - -@Getter -public class RoundResult { - QuestionResultResponse questionResult; - RankUpdateResponse rankUpdate; - SystemNoticeResponse systemNotice; - ChatMessage chat; - - @Builder - public RoundResult( - QuestionResultResponse questionResult, - RankUpdateResponse rankUpdate, - SystemNoticeResponse systemNotice, - ChatMessage chat) { - this.questionResult = questionResult; - this.rankUpdate = rankUpdate; - this.systemNotice = systemNotice; - this.chat = chat; - } - - public boolean hasChat() { - return chat != null; - } - - public boolean hasOnlyChat() { - return chat != null && questionResult == null && rankUpdate == null && systemNotice == null; - } -} diff --git a/backend/src/main/java/io/f1/backend/domain/game/websocket/GameSocketController.java b/backend/src/main/java/io/f1/backend/domain/game/websocket/GameSocketController.java index 976c1c98..57535c00 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/websocket/GameSocketController.java +++ b/backend/src/main/java/io/f1/backend/domain/game/websocket/GameSocketController.java @@ -6,13 +6,7 @@ import io.f1.backend.domain.game.app.GameService; import io.f1.backend.domain.game.app.RoomService; import io.f1.backend.domain.game.dto.ChatMessage; -import io.f1.backend.domain.game.dto.MessageType; -import io.f1.backend.domain.game.dto.RoomExitData; -import io.f1.backend.domain.game.dto.RoomInitialData; -import io.f1.backend.domain.game.dto.RoundResult; import io.f1.backend.domain.game.dto.request.DefaultWebSocketRequest; -import io.f1.backend.domain.game.dto.response.GameStartResponse; -import io.f1.backend.domain.game.dto.response.PlayerListResponse; import io.f1.backend.domain.user.dto.UserPrincipal; import lombok.RequiredArgsConstructor; @@ -26,6 +20,7 @@ @RequiredArgsConstructor public class GameSocketController { + // todo 삭제 private final MessageSender messageSender; private final RoomService roomService; private final GameService gameService; @@ -37,19 +32,7 @@ public void initializeRoomSocket(@DestinationVariable Long roomId, Message me UserPrincipal principal = getSessionUser(message); - RoomInitialData roomInitialData = - roomService.initializeRoomSocket(roomId, websocketSessionId, principal); - - String destination = getDestination(roomId); - - messageSender.send( - destination, MessageType.ROOM_SETTING, roomInitialData.roomSettingResponse()); - messageSender.send( - destination, MessageType.GAME_SETTING, roomInitialData.gameSettingResponse()); - messageSender.send( - destination, MessageType.PLAYER_LIST, roomInitialData.playerListResponse()); - messageSender.send( - destination, MessageType.SYSTEM_NOTICE, roomInitialData.systemNoticeResponse()); + roomService.initializeRoomSocket(roomId, websocketSessionId, principal); } @MessageMapping("/room/exit/{roomId}") @@ -58,16 +41,7 @@ public void exitRoom(@DestinationVariable Long roomId, Message message) { String websocketSessionId = getSessionId(message); UserPrincipal principal = getSessionUser(message); - RoomExitData roomExitData = roomService.exitRoom(roomId, websocketSessionId, principal); - - String destination = getDestination(roomId); - - if (!roomExitData.isRemovedRoom()) { - messageSender.send( - destination, MessageType.PLAYER_LIST, roomExitData.getPlayerListResponses()); - messageSender.send( - destination, MessageType.SYSTEM_NOTICE, roomExitData.getSystemNoticeResponse()); - } + roomService.exitRoom(roomId, websocketSessionId, principal); } @MessageMapping("/room/start/{roomId}") @@ -75,42 +49,24 @@ public void gameStart(@DestinationVariable Long roomId, Message message) { UserPrincipal principal = getSessionUser(message); - GameStartResponse gameStartResponse = gameService.gameStart(roomId, principal); - - String destination = getDestination(roomId); - - messageSender.send(destination, MessageType.GAME_START, gameStartResponse); + gameService.gameStart(roomId, principal); } @MessageMapping("room/chat/{roomId}") public void chat( @DestinationVariable Long roomId, Message> message) { - RoundResult roundResult = - roomService.chat(roomId, getSessionId(message), message.getPayload().getMessage()); - - String destination = getDestination(roomId); - messageSender.send(destination, MessageType.CHAT, roundResult.getChat()); - - if (!roundResult.hasOnlyChat()) { - messageSender.send( - destination, MessageType.QUESTION_RESULT, roundResult.getQuestionResult()); - messageSender.send(destination, MessageType.RANK_UPDATE, roundResult.getRankUpdate()); - messageSender.send( - destination, MessageType.SYSTEM_NOTICE, roundResult.getSystemNotice()); - } + roomService.chat(roomId, getSessionId(message), message.getPayload().getMessage()); } @MessageMapping("/room/ready/{roomId}") public void playerReady(@DestinationVariable Long roomId, Message message) { - PlayerListResponse playerListResponse = - roomService.handlePlayerReady(roomId, getSessionId(message)); - - messageSender.send(getDestination(roomId), MessageType.PLAYER_LIST, playerListResponse); + roomService.handlePlayerReady(roomId, getSessionId(message)); } + // todo 삭제 private String getDestination(Long roomId) { return "/sub/room/" + roomId; } diff --git a/backend/src/test/java/io/f1/backend/domain/game/app/RoomServiceTests.java b/backend/src/test/java/io/f1/backend/domain/game/app/RoomServiceTests.java index dbe01b8f..3d9b3126 100644 --- a/backend/src/test/java/io/f1/backend/domain/game/app/RoomServiceTests.java +++ b/backend/src/test/java/io/f1/backend/domain/game/app/RoomServiceTests.java @@ -1,7 +1,6 @@ package io.f1.backend.domain.game.app; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.when; import io.f1.backend.domain.game.dto.request.RoomValidationRequest; @@ -10,6 +9,7 @@ import io.f1.backend.domain.game.model.Room; import io.f1.backend.domain.game.model.RoomSetting; import io.f1.backend.domain.game.store.RoomRepository; +import io.f1.backend.domain.game.websocket.MessageSender; import io.f1.backend.domain.quiz.app.QuizService; import io.f1.backend.domain.user.dto.UserPrincipal; import io.f1.backend.domain.user.entity.User; @@ -46,11 +46,12 @@ class RoomServiceTests { @Mock private RoomRepository roomRepository; @Mock private QuizService quizService; @Mock private ApplicationEventPublisher eventPublisher; + @Mock private MessageSender messageSender; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); // @Mock 어노테이션이 붙은 필드들을 초기화합니다. - roomService = new RoomService(quizService, roomRepository, eventPublisher); + roomService = new RoomService(quizService, roomRepository, eventPublisher, messageSender); SecurityContextHolder.clearContext(); } @@ -133,7 +134,6 @@ void exitRoom_synchronized() throws Exception { log.info("room.getPlayerSessionMap().size() = {}", room.getPlayerSessionMap().size()); when(roomRepository.findRoom(roomId)).thenReturn(Optional.of(room)); - doNothing().when(roomRepository).removeRoom(roomId); ExecutorService executorService = Executors.newFixedThreadPool(threadCount); CountDownLatch countDownLatch = new CountDownLatch(threadCount);