diff --git a/backend/src/main/java/io/f1/backend/domain/game/app/GameService.java b/backend/src/main/java/io/f1/backend/domain/game/app/GameService.java index 72fd186e..6e74acc4 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/app/GameService.java +++ b/backend/src/main/java/io/f1/backend/domain/game/app/GameService.java @@ -176,7 +176,13 @@ public void gameEnd(Room room) { room.initializePlayers(); List disconnectedPlayers = room.getDisconnectedPlayers(); - roomService.handleDisconnectedPlayers(room, disconnectedPlayers); + + if (!disconnectedPlayers.isEmpty()) { + roomService.handleDisconnectedPlayers(room, disconnectedPlayers); + } else { + messageSender.sendBroadcast( + destination, MessageType.PLAYER_LIST, toPlayerListResponse(room)); + } room.updateRoomState(RoomState.WAITING); @@ -213,16 +219,9 @@ public void changeGameSetting( if (!request.change(room, quizService)) { return; } - request.afterChange(room, messageSender); + request.afterChange(room, messageSender, eventPublisher, quizService); broadcastGameSetting(room); - - RoomUpdatedEvent roomUpdatedEvent = - new RoomUpdatedEvent( - room, - quizService.getQuizWithQuestionsById(room.getGameSetting().getQuizId())); - - eventPublisher.publishEvent(roomUpdatedEvent); } private void validateRoomStart(Room room, UserPrincipal principal) { 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 db2a8f96..9596159e 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 @@ -26,6 +26,7 @@ import io.f1.backend.domain.game.dto.response.RoomSettingResponse; import io.f1.backend.domain.game.dto.response.SystemNoticeResponse; import io.f1.backend.domain.game.event.RoomCreatedEvent; +import io.f1.backend.domain.game.event.RoomDeletedEvent; import io.f1.backend.domain.game.model.ConnectionState; import io.f1.backend.domain.game.model.GameSetting; import io.f1.backend.domain.game.model.Player; @@ -338,10 +339,10 @@ public void exitRoomForDisconnectedPlayer(Long roomId, Player player, String ses SystemNoticeResponse systemNoticeResponse = ofPlayerEvent(player.nickname, RoomEventType.EXIT); - messageSender.sendBroadcast( - destination, MessageType.PLAYER_LIST, toPlayerListResponse(room)); messageSender.sendBroadcast( destination, MessageType.SYSTEM_NOTICE, systemNoticeResponse); + messageSender.sendBroadcast( + destination, MessageType.PLAYER_LIST, toPlayerListResponse(room)); } } @@ -349,6 +350,8 @@ private void cleanRoom(Room room, String sessionId, Player player) { /* 방 삭제 */ if (room.isLastPlayer(sessionId)) { removeRoom(room); + Long roomId = room.getId(); + eventPublisher.publishEvent(new RoomDeletedEvent(roomId)); return; } diff --git a/backend/src/main/java/io/f1/backend/domain/game/dto/request/GameSettingChanger.java b/backend/src/main/java/io/f1/backend/domain/game/dto/request/GameSettingChanger.java index fa3a4981..e8b2c65f 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/dto/request/GameSettingChanger.java +++ b/backend/src/main/java/io/f1/backend/domain/game/dto/request/GameSettingChanger.java @@ -4,9 +4,15 @@ import io.f1.backend.domain.game.websocket.MessageSender; import io.f1.backend.domain.quiz.app.QuizService; +import org.springframework.context.ApplicationEventPublisher; + public interface GameSettingChanger { boolean change(Room room, QuizService quizService); - void afterChange(Room room, MessageSender messageSender); + default void afterChange( + Room room, + MessageSender messageSender, + ApplicationEventPublisher eventPublisher, + QuizService quizService) {} } diff --git a/backend/src/main/java/io/f1/backend/domain/game/dto/request/QuizChangeRequest.java b/backend/src/main/java/io/f1/backend/domain/game/dto/request/QuizChangeRequest.java index 1237a44c..cfb4e5f9 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/dto/request/QuizChangeRequest.java +++ b/backend/src/main/java/io/f1/backend/domain/game/dto/request/QuizChangeRequest.java @@ -5,12 +5,15 @@ import io.f1.backend.domain.game.dto.MessageType; import io.f1.backend.domain.game.dto.response.PlayerListResponse; +import io.f1.backend.domain.game.event.RoomUpdatedEvent; import io.f1.backend.domain.game.model.Room; import io.f1.backend.domain.game.websocket.MessageSender; import io.f1.backend.domain.quiz.app.QuizService; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; + @Slf4j public record QuizChangeRequest(long quizId) implements GameSettingChanger { @@ -21,17 +24,28 @@ public boolean change(Room room, QuizService quizService) { } Long questionsCount = quizService.getQuestionsCount(quizId); room.changeQuiz(quizId, questionsCount.intValue()); + room.resetAllPlayerReadyStates(); return true; } @Override - public void afterChange(Room room, MessageSender messageSender) { - room.resetAllPlayerReadyStates(); + public void afterChange( + Room room, + MessageSender messageSender, + ApplicationEventPublisher eventPublisher, + QuizService quizService) { String destination = getDestination(room.getId()); PlayerListResponse response = toPlayerListResponse(room); log.info(response.toString()); messageSender.sendBroadcast(destination, MessageType.PLAYER_LIST, response); + + RoomUpdatedEvent roomUpdatedEvent = + new RoomUpdatedEvent( + room, + quizService.getQuizWithQuestionsById(room.getGameSetting().getQuizId())); + + eventPublisher.publishEvent(roomUpdatedEvent); } } diff --git a/backend/src/main/java/io/f1/backend/domain/game/dto/request/RoundChangeRequest.java b/backend/src/main/java/io/f1/backend/domain/game/dto/request/RoundChangeRequest.java index b44cb72a..76244ffd 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/dto/request/RoundChangeRequest.java +++ b/backend/src/main/java/io/f1/backend/domain/game/dto/request/RoundChangeRequest.java @@ -1,7 +1,6 @@ package io.f1.backend.domain.game.dto.request; import io.f1.backend.domain.game.model.Room; -import io.f1.backend.domain.game.websocket.MessageSender; import io.f1.backend.domain.quiz.app.QuizService; public record RoundChangeRequest(int round) implements GameSettingChanger { @@ -17,9 +16,4 @@ public boolean change(Room room, QuizService quizService) { room.changeRound(round, questionsCount.intValue()); return true; } - - @Override - public void afterChange(Room room, MessageSender messageSender) { - // 고유한 후처리 동작 없음 - } } diff --git a/backend/src/main/java/io/f1/backend/domain/game/dto/request/TimeLimitChangeRequest.java b/backend/src/main/java/io/f1/backend/domain/game/dto/request/TimeLimitChangeRequest.java index aa088c7d..2e58f679 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/dto/request/TimeLimitChangeRequest.java +++ b/backend/src/main/java/io/f1/backend/domain/game/dto/request/TimeLimitChangeRequest.java @@ -1,7 +1,6 @@ package io.f1.backend.domain.game.dto.request; import io.f1.backend.domain.game.model.Room; -import io.f1.backend.domain.game.websocket.MessageSender; import io.f1.backend.domain.quiz.app.QuizService; public record TimeLimitChangeRequest(int timeLimit) implements GameSettingChanger { @@ -14,9 +13,4 @@ public boolean change(Room room, QuizService quizService) { room.changeTimeLimit(TimeLimit.from(timeLimit)); return true; } - - @Override - public void afterChange(Room room, MessageSender messageSender) { - // 고유한 후처리 동작 없음 - } } diff --git a/backend/src/main/java/io/f1/backend/domain/game/dto/response/PlayerListResponse.java b/backend/src/main/java/io/f1/backend/domain/game/dto/response/PlayerListResponse.java index df09741c..97889ab1 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/dto/response/PlayerListResponse.java +++ b/backend/src/main/java/io/f1/backend/domain/game/dto/response/PlayerListResponse.java @@ -2,4 +2,4 @@ import java.util.List; -public record PlayerListResponse(String host, List players) {} +public record PlayerListResponse(String host, List players, int currentUserCount) {} diff --git a/backend/src/main/java/io/f1/backend/domain/game/dto/response/RoomSettingResponse.java b/backend/src/main/java/io/f1/backend/domain/game/dto/response/RoomSettingResponse.java index 019aeff7..92cd7cbf 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/dto/response/RoomSettingResponse.java +++ b/backend/src/main/java/io/f1/backend/domain/game/dto/response/RoomSettingResponse.java @@ -1,4 +1,3 @@ package io.f1.backend.domain.game.dto.response; -public record RoomSettingResponse( - String roomName, int maxUserCount, int currentUserCount, boolean locked) {} +public record RoomSettingResponse(String roomName, int maxUserCount, boolean locked) {} diff --git a/backend/src/main/java/io/f1/backend/domain/game/mapper/RoomMapper.java b/backend/src/main/java/io/f1/backend/domain/game/mapper/RoomMapper.java index 1731385b..0880a4d8 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/mapper/RoomMapper.java +++ b/backend/src/main/java/io/f1/backend/domain/game/mapper/RoomMapper.java @@ -48,7 +48,6 @@ public static RoomSettingResponse toRoomSettingResponse(Room room) { return new RoomSettingResponse( room.getRoomSetting().roomName(), room.getRoomSetting().maxUserCount(), - room.getPlayerSessionMap().size(), room.getRoomSetting().locked()); } @@ -63,7 +62,8 @@ public static PlayerListResponse toPlayerListResponse(Room room) { .map(player -> new PlayerResponse(player.getNickname(), player.isReady())) .toList(); - return new PlayerListResponse(room.getHost().getNickname(), playerResponseList); + return new PlayerListResponse( + room.getHost().getNickname(), playerResponseList, playerResponseList.size()); } public static RoomResponse toRoomResponse(Room room, Quiz quiz) { @@ -71,7 +71,7 @@ public static RoomResponse toRoomResponse(Room room, Quiz quiz) { room.getId(), room.getRoomSetting().roomName(), room.getRoomSetting().maxUserCount(), - room.getPlayerSessionMap().size(), + room.getCurrentUserCnt(), room.getRoomSetting().locked(), room.getState().name(), quiz.getTitle(), @@ -111,9 +111,9 @@ public static QuestionStartResponse toQuestionStartResponse(Room room, int delay room.getCurrentQuestion().getId(), room.getCurrentRound(), Instant.now().plusSeconds(delay), - room.getGameSetting().getTimeLimit(), + room.getTimeLimit(), Instant.now(), - room.getGameSetting().getRound()); + room.getRound()); } public static GameResultResponse toGameResultResponse( diff --git a/backend/src/main/java/io/f1/backend/domain/game/model/Room.java b/backend/src/main/java/io/f1/backend/domain/game/model/Room.java index cd3ef88f..5c08d369 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/model/Room.java +++ b/backend/src/main/java/io/f1/backend/domain/game/model/Room.java @@ -138,8 +138,8 @@ public void initializePlayers() { .forEach( player -> { player.initializeCorrectCount(); - player.toggleReady(); }); + resetAllPlayerReadyStates(); } public String getSessionIdByUserId(Long userId) { diff --git a/backend/src/main/java/io/f1/backend/domain/game/sse/mapper/SseMapper.java b/backend/src/main/java/io/f1/backend/domain/game/sse/mapper/SseMapper.java index efdd94c8..869e0219 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/sse/mapper/SseMapper.java +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/mapper/SseMapper.java @@ -21,7 +21,7 @@ public static LobbySseEvent fromRoomCreated(RoomCreatedEvent room.getId(), room.getRoomSetting().roomName(), room.getRoomSetting().maxUserCount(), - room.getPlayerSessionMap().size(), + room.getCurrentUserCnt(), room.getRoomSetting().locked(), room.getState().name(), quiz.getTitle(), @@ -38,7 +38,7 @@ public static LobbySseEvent fromRoomUpdated(RoomUpdatedEvent RoomUpdatedPayload payload = new RoomUpdatedPayload( room.getId(), - room.getPlayerSessionMap().size(), + room.getCurrentUserCnt(), room.getState().name(), quiz.getTitle(), quiz.getDescription(), diff --git a/backend/src/main/java/io/f1/backend/global/config/CorsConfig.java b/backend/src/main/java/io/f1/backend/global/config/CorsConfig.java index 37606918..d29c8f2a 100644 --- a/backend/src/main/java/io/f1/backend/global/config/CorsConfig.java +++ b/backend/src/main/java/io/f1/backend/global/config/CorsConfig.java @@ -15,6 +15,7 @@ public CorsConfigurationSource corsConfigurationSource() { config.addAllowedOrigin("http://localhost:3000"); config.addAllowedOrigin("https://brainrace.duckdns.org"); + config.addAllowedOrigin("https://api-brainrace.duckdns.org"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 10cbe36e..c14341b2 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -65,6 +65,29 @@ server: http-only: true timeout: ${SESSION_TIMEOUT} --- +spring: + config: + activate: + on-profile: dev + + security: + user: + name: ${PROM_NAME} + password: ${PROM_PASSWORD} + roles: PROMETHEUS + +management: + server: + port: ${ACTUATOR_PORT} + endpoints: + web: + exposure: + include: "prometheus" + base-path: ${ACTUATOR_BASE_PATH} + endpoint: + prometheus: + access: read_only +--- spring: config: activate: @@ -76,6 +99,20 @@ spring: password: ${PROM_PASSWORD} roles: PROMETHEUS +# TODO: Flyway 추가와 함께 설정이 필요한 항목들 +# sql: +# init: +# mode: never +# +# jpa: +# hibernate: +# ddl-auto: none +# +# properties: +# hibernate: +# show_sql: false +# format_sql: false + management: server: port: ${ACTUATOR_PORT} diff --git a/backend/src/main/resources/data.sql b/backend/src/main/resources/data.sql index 27c3b9e7..35015899 100644 --- a/backend/src/main/resources/data.sql +++ b/backend/src/main/resources/data.sql @@ -1,5 +1,68 @@ -INSERT INTO user (id, nickname, provider, provider_id, last_login) -VALUES (1, 'test-user', 'kakao', 'kakao-1234', NOW()); +INSERT INTO user (nickname, provider, provider_id, last_login) +VALUES + ('blueFox17', 'kakao', 'kakao-1', NOW()), + ('sparkLion23', 'kakao', 'kakao-2', NOW()), + ('novaTiger88', 'kakao', 'kakao-3', NOW()), + ('mintWolf5', 'kakao', 'kakao-4', NOW()), + ('darkFalcon32', 'kakao', 'kakao-5', NOW()), + ('chocoBear91', 'kakao', 'kakao-6', NOW()), + ('swiftOtter3', 'kakao', 'kakao-7', NOW()), + ('crazyKoala47', 'kakao', 'kakao-8', NOW()), + ('luckyWhale19', 'kakao', 'kakao-9', NOW()), + ('tinyMoose7', 'kakao', 'kakao-10', NOW()), + ('sunnyLizard22', 'kakao', 'kakao-11', NOW()), + ('fuzzyCat12', 'kakao', 'kakao-12', NOW()), + ('stormEagle9', 'kakao', 'kakao-13', NOW()), + ('frostRabbit6', 'kakao', 'kakao-14', NOW()), + ('amberGoose99', 'kakao', 'kakao-15', NOW()), + ('wittyDog42', 'kakao', 'kakao-16', NOW()), + ('zebraKnight77', 'kakao', 'kakao-17', NOW()), + ('happyDeer13', 'kakao', 'kakao-18', NOW()), + ('ironTurtle55', 'kakao', 'kakao-19', NOW()), + ('lazyHawk31', 'kakao', 'kakao-20', NOW()), + ('pixelPanda8', 'kakao', 'kakao-21', NOW()), + ('grapeSeal66', 'kakao', 'kakao-22', NOW()), + ('redMole21', 'kakao', 'kakao-23', NOW()), + ('lunaShark29', 'kakao', 'kakao-24', NOW()), + ('noblePiglet45', 'kakao', 'kakao-25', NOW()), + ('quietYak17', 'kakao', 'kakao-26', NOW()), + ('jumboDuck33', 'kakao', 'kakao-27', NOW()), + ('rapidWorm11', 'kakao', 'kakao-28', NOW()), + ('boldSwan73', 'kakao', 'kakao-29', NOW()), + ('vividAnt9', 'kakao', 'kakao-30', NOW()); + +INSERT INTO stat (user_id, score, total_games, winning_games, created_at, updated_at) +VALUES + (1, 10, 1, 1, NOW(), NOW()), + (2, 20, 3, 2, NOW(), NOW()), + (3, 15, 2, 1, NOW(), NOW()), + (4, 5, 1, 0, NOW(), NOW()), + (5, 25, 4, 3, NOW(), NOW()), + (6, 12, 2, 1, NOW(), NOW()), + (7, 18, 3, 2, NOW(), NOW()), + (8, 30, 5, 4, NOW(), NOW()), + (9, 7, 1, 0, NOW(), NOW()), + (10, 22, 3, 2, NOW(), NOW()), + (11, 16, 2, 1, NOW(), NOW()), + (12, 9, 1, 0, NOW(), NOW()), + (13, 27, 4, 3, NOW(), NOW()), + (14, 14, 2, 1, NOW(), NOW()), + (15, 8, 1, 1, NOW(), NOW()), + (16, 23, 3, 2, NOW(), NOW()), + (17, 11, 2, 1, NOW(), NOW()), + (18, 19, 3, 2, NOW(), NOW()), + (19, 6, 1, 0, NOW(), NOW()), + (20, 28, 4, 3, NOW(), NOW()), + (21, 13, 2, 1, NOW(), NOW()), + (22, 10, 1, 1, NOW(), NOW()), + (23, 21, 3, 2, NOW(), NOW()), + (24, 17, 2, 1, NOW(), NOW()), + (25, 24, 4, 3, NOW(), NOW()), + (26, 29, 5, 4, NOW(), NOW()), + (27, 26, 4, 3, NOW(), NOW()), + (28, 31, 5, 4, NOW(), NOW()), + (29, 32, 5, 4, NOW(), NOW()), + (30, 33, 5, 5, NOW(), NOW()); INSERT INTO quiz (title, 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 67c26298..80faaf91 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 @@ -47,8 +47,6 @@ class RoomServiceTests { @Mock private RoomRepository roomRepository; @Mock private QuizService quizService; - @Mock private GameService gameService; - @Mock private TimerService timerService; @Mock private ApplicationEventPublisher eventPublisher; @Mock private MessageSender messageSender; @@ -56,9 +54,7 @@ class RoomServiceTests { void setUp() { MockitoAnnotations.openMocks(this); // @Mock 어노테이션이 붙은 필드들을 초기화합니다. - roomService = - new RoomService( - timerService, quizService, roomRepository, eventPublisher, messageSender); + roomService = new RoomService(quizService, roomRepository, eventPublisher, messageSender); SecurityContextHolder.clearContext(); }