From 41864be624996d251c971046be6fb882fa549dae Mon Sep 17 00:00:00 2001 From: sehee Date: Tue, 15 Jul 2025 17:34:16 +0900 Subject: [PATCH 01/11] =?UTF-8?q?:recycle:=20=EC=86=8C=EC=BC=93=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=20=EC=A0=84=20validation=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=20+=20enter=20=EB=A1=9C=EC=A7=81=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/game/api/RoomController.java | 4 +- .../backend/domain/game/app/RoomService.java | 105 ++++++++++-------- .../io/f1/backend/domain/game/model/Room.java | 3 + .../game/websocket/GameSocketController.java | 6 +- 4 files changed, 68 insertions(+), 50 deletions(-) diff --git a/backend/src/main/java/io/f1/backend/domain/game/api/RoomController.java b/backend/src/main/java/io/f1/backend/domain/game/api/RoomController.java index 79a245de..3682c7fa 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/api/RoomController.java +++ b/backend/src/main/java/io/f1/backend/domain/game/api/RoomController.java @@ -31,10 +31,10 @@ public RoomCreateResponse saveRoom(@RequestBody @Valid RoomCreateRequest request return roomService.saveRoom(request); } - @PostMapping("/validation") + @PostMapping("/enterRoom") @ResponseStatus(HttpStatus.NO_CONTENT) public void validateRoom(@RequestBody @Valid RoomValidationRequest request) { - roomService.validateRoom(request); + roomService.enterRoom(request); } @GetMapping 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 723c8355..ace55962 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 @@ -34,6 +34,7 @@ import lombok.RequiredArgsConstructor; +import org.hibernate.boot.model.naming.IllegalIdentifierException; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; @@ -73,41 +74,39 @@ public RoomCreateResponse saveRoom(RoomCreateRequest request) { return new RoomCreateResponse(newId); } - public void validateRoom(RoomValidationRequest request) { + public void enterRoom(RoomValidationRequest request) { - Room room = - roomRepository - .findRoom(request.roomId()) - .orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다.-1")); + Room room = findRoom(request.roomId()); if (room.getState().equals(RoomState.PLAYING)) { throw new IllegalArgumentException("403 게임이 진행중입니다."); } int maxUserCnt = room.getRoomSetting().maxUserCount(); - int currentCnt = room.getPlayerSessionMap().size(); + int currentCnt = room.getUserIdSessionMap().size(); if (maxUserCnt == currentCnt) { throw new IllegalArgumentException("403 정원이 모두 찼습니다."); } if (room.getRoomSetting().locked() - && !room.getRoomSetting().password().equals(request.password())) { + && !room.getRoomSetting().password().equals(request.password())) { throw new IllegalArgumentException("401 비밀번호가 일치하지 않습니다."); } + + room.getUserIdSessionMap().put(getCurrentUserId(), null); } - public RoomInitialData enterRoom(Long roomId, String sessionId) { + public RoomInitialData initializeRoomSocket(Long roomId, String sessionId) { - Room room = - roomRepository - .findRoom(roomId) - .orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다.")); + Room room = findRoom(roomId); Player player = createPlayer(); Map playerSessionMap = room.getPlayerSessionMap(); + Map userIdSessionMap = room.getUserIdSessionMap(); playerSessionMap.put(sessionId, player); + userIdSessionMap.put(roomId, sessionId); RoomSettingResponse roomSettingResponse = toRoomSettingResponse(room); @@ -115,53 +114,61 @@ public RoomInitialData enterRoom(Long roomId, String sessionId) { Quiz quiz = quizService.getQuizById(quizId); GameSettingResponse gameSettingResponse = - toGameSettingResponse(room.getGameSetting(), quiz); + toGameSettingResponse(room.getGameSetting(), quiz); PlayerListResponse playerListResponse = toPlayerListResponse(room); SystemNoticeResponse systemNoticeResponse = ofPlayerEvent(player, RoomEventType.ENTER); return new RoomInitialData( - getDestination(roomId), - roomSettingResponse, - gameSettingResponse, - playerListResponse, - systemNoticeResponse); + getDestination(roomId), + roomSettingResponse, + gameSettingResponse, + playerListResponse, + systemNoticeResponse); } public RoomExitData exitRoom(Long roomId, String sessionId) { - Room room = - roomRepository - .findRoom(roomId) - .orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다.")); + Room room = findRoom(roomId); Map playerSessionMap = room.getPlayerSessionMap(); String destination = getDestination(roomId); - if (playerSessionMap.size() == 1 && playerSessionMap.get(sessionId) != null) { + Player removePlayer = playerSessionMap.get(sessionId); + +// if (removePlayer == null) { +// room.getUserIdSessionMap().remove(getCurrentUserId()); +// throw new IllegalIdentifierException("404 세션 없음 비정상적인 퇴장 요청"); +// } + + /* 방 삭제 */ + if (playerSessionMap.size() == 1 && playerSessionMap.containsKey(sessionId)) { roomRepository.removeRoom(roomId); return RoomExitData.builder().destination(destination).removedRoom(true).build(); } - Player removedPlayer = playerSessionMap.remove(sessionId); - if (removedPlayer == null) { - throw new IllegalArgumentException("퇴장 처리 불가 - 404 해당 세션 플레이어는 존재하지않습니다."); - } + /* 방장 변경 */ + if (room.getHost().getId().equals(removePlayer.getId())) { + + Optional nextHostSessionId = playerSessionMap.keySet().stream() + .filter(key -> !key.equals(sessionId)).findFirst(); - if (room.getHost().getId().equals(removedPlayer.getId())) { - Optional nextHostSessionId = playerSessionMap.keySet().stream().findFirst(); Player nextHost = - playerSessionMap.get( - nextHostSessionId.orElseThrow( - () -> - new IllegalArgumentException( - "방장 교체 불가 - 404 해당 세션 플레이어는 존재하지않습니다."))); + playerSessionMap.get( + nextHostSessionId.orElseThrow( + () -> + new IllegalArgumentException( + "방장 교체 불가 - 404 해당 세션 플레이어는 존재하지않습니다."))); + room.updateHost(nextHost); } + room.getUserIdSessionMap().remove(removePlayer.getId()); + playerSessionMap.remove(sessionId); + SystemNoticeResponse systemNoticeResponse = - ofPlayerEvent(removedPlayer, RoomEventType.EXIT); + ofPlayerEvent(removePlayer, RoomEventType.EXIT); PlayerListResponse playerListResponse = toPlayerListResponse(room); @@ -171,15 +178,15 @@ public RoomExitData exitRoom(Long roomId, String sessionId) { public RoomListResponse getAllRooms() { List rooms = roomRepository.findAll(); List roomResponses = - rooms.stream() - .map( - room -> { - Long quizId = room.getGameSetting().getQuizId(); - Quiz quiz = quizService.getQuizById(quizId); - - return toRoomResponse(room, quiz); - }) - .toList(); + rooms.stream() + .map( + room -> { + Long quizId = room.getGameSetting().getQuizId(); + Quiz quiz = quizService.getQuizById(quizId); + + return toRoomResponse(room, quiz); + }) + .toList(); return new RoomListResponse(roomResponses); } @@ -187,7 +194,15 @@ private static String getDestination(Long roomId) { return "/sub/room/" + roomId; } - private static Player createPlayer() { + private Player createPlayer() { return new Player(getCurrentUserId(), getCurrentUserNickname()); } + + private Room findRoom(Long roomId) { + return roomRepository + .findRoom(roomId) + .orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다.")); + } + + } 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 00b7e71b..20ed2e86 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 @@ -27,6 +27,8 @@ public class Room { private Map playerSessionMap = new ConcurrentHashMap<>(); + private Map userIdSessionMap = new ConcurrentHashMap<>(); + private final LocalDateTime createdAt = LocalDateTime.now(); public Room(Long id, RoomSetting roomSetting, GameSetting gameSetting, Player host) { @@ -34,6 +36,7 @@ public Room(Long id, RoomSetting roomSetting, GameSetting gameSetting, Player ho this.roomSetting = roomSetting; this.gameSetting = gameSetting; this.host = host; + } public void updateHost(Player nextHost) { 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 9bf2f17f..d4ef3c38 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 @@ -20,12 +20,12 @@ public class GameSocketController { private final MessageSender messageSender; private final RoomService roomService; - @MessageMapping("/room/enter/{roomId}") - public void roomEnter(@DestinationVariable Long roomId, Message message) { + @MessageMapping("/room/initializeRoomSocket/{roomId}") + public void initializeRoomSocket(@DestinationVariable Long roomId, Message message) { String websocketSessionId = getSessionId(message); - RoomInitialData roomInitialData = roomService.enterRoom(roomId, websocketSessionId); + RoomInitialData roomInitialData = roomService.initializeRoomSocket(roomId, websocketSessionId); String destination = roomInitialData.destination(); messageSender.send( From f1b1308808f70e0119468895022e0615f2db2151 Mon Sep 17 00:00:00 2001 From: sehee Date: Tue, 15 Jul 2025 17:59:04 +0900 Subject: [PATCH 02/11] =?UTF-8?q?:recycle:=20NPE=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/f1/backend/domain/game/app/RoomService.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) 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 ace55962..4f054326 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 @@ -31,17 +31,13 @@ import io.f1.backend.domain.game.store.RoomRepository; import io.f1.backend.domain.quiz.app.QuizService; import io.f1.backend.domain.quiz.entity.Quiz; - -import lombok.RequiredArgsConstructor; - -import org.hibernate.boot.model.naming.IllegalIdentifierException; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Service; - import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; +import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor @@ -93,7 +89,7 @@ public void enterRoom(RoomValidationRequest request) { throw new IllegalArgumentException("401 비밀번호가 일치하지 않습니다."); } - room.getUserIdSessionMap().put(getCurrentUserId(), null); + room.getUserIdSessionMap().put(getCurrentUserId(), ""); } public RoomInitialData initializeRoomSocket(Long roomId, String sessionId) { From 9157f7cdb09d339c1888447ae7b1608625bca869 Mon Sep 17 00:00:00 2001 From: sehee Date: Wed, 16 Jul 2025 01:01:36 +0900 Subject: [PATCH 03/11] =?UTF-8?q?:sparkles:=20=EB=8F=99=EC=8B=9C=EC=84=B1?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/domain/game/app/RoomService.java | 118 +++++++----- .../domain/game/app/RoomServiceTests.java | 181 ++++++++++++++++++ 2 files changed, 254 insertions(+), 45 deletions(-) create mode 100644 backend/src/test/java/io/f1/backend/domain/game/app/RoomServiceTests.java 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 4f054326..ea3acb77 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 @@ -34,11 +34,15 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.boot.model.naming.IllegalIdentifierException; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; +@Slf4j @Service @RequiredArgsConstructor public class RoomService { @@ -47,6 +51,8 @@ public class RoomService { private final RoomRepository roomRepository; private final AtomicLong roomIdGenerator = new AtomicLong(0); private final ApplicationEventPublisher eventPublisher; + private final Map roomLocks = new ConcurrentHashMap<>(); + private static final String PENDING_SESSION_ID = "PENDING_SESSION_ID"; public RoomCreateResponse saveRoom(RoomCreateRequest request) { @@ -63,6 +69,8 @@ public RoomCreateResponse saveRoom(RoomCreateRequest request) { Room room = new Room(newId, roomSetting, gameSetting, host); + room.getUserIdSessionMap().put(host.id,PENDING_SESSION_ID); + roomRepository.saveRoom(room); eventPublisher.publishEvent(new RoomCreatedEvent(room, quiz)); @@ -72,24 +80,31 @@ public RoomCreateResponse saveRoom(RoomCreateRequest request) { public void enterRoom(RoomValidationRequest request) { - Room room = findRoom(request.roomId()); + Long roomId = request.roomId(); - if (room.getState().equals(RoomState.PLAYING)) { - throw new IllegalArgumentException("403 게임이 진행중입니다."); - } + Object lock = roomLocks.computeIfAbsent(roomId, k -> new Object()); - int maxUserCnt = room.getRoomSetting().maxUserCount(); - int currentCnt = room.getUserIdSessionMap().size(); - if (maxUserCnt == currentCnt) { - throw new IllegalArgumentException("403 정원이 모두 찼습니다."); - } + synchronized (lock) { - if (room.getRoomSetting().locked() - && !room.getRoomSetting().password().equals(request.password())) { - throw new IllegalArgumentException("401 비밀번호가 일치하지 않습니다."); - } + Room room = findRoom(request.roomId()); + + if (room.getState().equals(RoomState.PLAYING)) { + throw new IllegalArgumentException("403 게임이 진행중입니다."); + } - room.getUserIdSessionMap().put(getCurrentUserId(), ""); + int maxUserCnt = room.getRoomSetting().maxUserCount(); + int currentCnt = room.getUserIdSessionMap().size(); + if (maxUserCnt == currentCnt) { + throw new IllegalArgumentException("403 정원이 모두 찼습니다."); + } + + if (room.getRoomSetting().locked() + && !room.getRoomSetting().password().equals(request.password())) { + throw new IllegalArgumentException("401 비밀번호가 일치하지 않습니다."); + } + + room.getUserIdSessionMap().put(getCurrentUserId(), PENDING_SESSION_ID); + } } public RoomInitialData initializeRoomSocket(Long roomId, String sessionId) { @@ -102,7 +117,11 @@ public RoomInitialData initializeRoomSocket(Long roomId, String sessionId) { Map userIdSessionMap = room.getUserIdSessionMap(); playerSessionMap.put(sessionId, player); - userIdSessionMap.put(roomId, sessionId); + String existingSession = userIdSessionMap.get(player.getId()); + /* 정상 흐름 or 재연결 */ + if (existingSession.equals(PENDING_SESSION_ID) || !existingSession.equals(sessionId)) { + userIdSessionMap.put(player.getId(), sessionId); + } RoomSettingResponse roomSettingResponse = toRoomSettingResponse(room); @@ -125,50 +144,59 @@ public RoomInitialData initializeRoomSocket(Long roomId, String sessionId) { } public RoomExitData exitRoom(Long roomId, String sessionId) { - Room room = findRoom(roomId); - Map playerSessionMap = room.getPlayerSessionMap(); + Object lock = roomLocks.computeIfAbsent(roomId, k -> new Object()); + + synchronized (lock) { + + Room room = findRoom(roomId); - String destination = getDestination(roomId); + Map playerSessionMap = room.getPlayerSessionMap(); - Player removePlayer = playerSessionMap.get(sessionId); + String destination = getDestination(roomId); -// if (removePlayer == null) { -// room.getUserIdSessionMap().remove(getCurrentUserId()); -// throw new IllegalIdentifierException("404 세션 없음 비정상적인 퇴장 요청"); -// } + Player removePlayer = playerSessionMap.get(sessionId); - /* 방 삭제 */ - if (playerSessionMap.size() == 1 && playerSessionMap.containsKey(sessionId)) { - roomRepository.removeRoom(roomId); - return RoomExitData.builder().destination(destination).removedRoom(true).build(); + if (removePlayer == null) { + room.getUserIdSessionMap().remove(getCurrentUserId()); + throw new IllegalIdentifierException("404 세션 없음 비정상적인 퇴장 요청"); } - /* 방장 변경 */ - if (room.getHost().getId().equals(removePlayer.getId())) { + /* 방 삭제 */ + if (playerSessionMap.size() == 1 && playerSessionMap.containsKey(sessionId)) { + roomRepository.removeRoom(roomId); + roomLocks.remove(roomId); + log.info("{}번 방 삭제", roomId); + return RoomExitData.builder().destination(destination).removedRoom(true).build(); + } - Optional nextHostSessionId = playerSessionMap.keySet().stream() - .filter(key -> !key.equals(sessionId)).findFirst(); + /* 방장 변경 */ + if (room.getHost().getId().equals(removePlayer.getId())) { - Player nextHost = - playerSessionMap.get( - nextHostSessionId.orElseThrow( - () -> - new IllegalArgumentException( - "방장 교체 불가 - 404 해당 세션 플레이어는 존재하지않습니다."))); + Optional nextHostSessionId = playerSessionMap.keySet().stream() + .filter(key -> !key.equals(sessionId)).findFirst(); - room.updateHost(nextHost); - } + Player nextHost = + playerSessionMap.get( + nextHostSessionId.orElseThrow( + () -> + new IllegalArgumentException( + "방장 교체 불가 - 404 해당 세션 플레이어는 존재하지않습니다."))); - room.getUserIdSessionMap().remove(removePlayer.getId()); - playerSessionMap.remove(sessionId); + room.updateHost(nextHost); + log.info("user_id:{} 방장 변경 완료 ", nextHost.getId()); + } - SystemNoticeResponse systemNoticeResponse = - ofPlayerEvent(removePlayer, RoomEventType.EXIT); + room.getUserIdSessionMap().remove(removePlayer.getId()); + playerSessionMap.remove(sessionId); - PlayerListResponse playerListResponse = toPlayerListResponse(room); + SystemNoticeResponse systemNoticeResponse = + ofPlayerEvent(removePlayer, RoomEventType.EXIT); - return new RoomExitData(destination, playerListResponse, systemNoticeResponse, false); + PlayerListResponse playerListResponse = toPlayerListResponse(room); + + return new RoomExitData(destination, playerListResponse, systemNoticeResponse, false); + } } public RoomListResponse getAllRooms() { 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 new file mode 100644 index 00000000..e67a24c8 --- /dev/null +++ b/backend/src/test/java/io/f1/backend/domain/game/app/RoomServiceTests.java @@ -0,0 +1,181 @@ +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; +import io.f1.backend.domain.game.model.GameSetting; +import io.f1.backend.domain.game.model.Player; +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.quiz.app.QuizService; +import io.f1.backend.domain.user.entity.User; +import io.f1.backend.global.util.SecurityUtils; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.security.core.context.SecurityContextHolder; + +@Slf4j +@ExtendWith(MockitoExtension.class) +class RoomServiceTests { + + private RoomService roomService; + + @Mock + private RoomRepository roomRepository; + @Mock + private QuizService quizService; + @Mock + private ApplicationEventPublisher eventPublisher; + + + @BeforeEach + void setUp(){ + MockitoAnnotations.openMocks(this); // @Mock 어노테이션이 붙은 필드들을 초기화합니다. + roomService = new RoomService(quizService, roomRepository, eventPublisher); + + SecurityContextHolder.clearContext(); + } + + @AfterEach + void afterEach() { + SecurityContextHolder.clearContext(); + } + + @Test + @DisplayName("enterRoom_동시성_테스트") + void enterRoom_synchronized() throws Exception { + Long roomId = 1L; + Long quizId = 1L; + Long playerId = 1L; + String password = "123"; + + RoomSetting roomSetting = new RoomSetting("방제목", 5, true, password); + GameSetting gameSetting = new GameSetting(quizId, 10, 60); + Player host = new Player(playerId, "닉네임"); + + Room room = new Room(roomId, roomSetting, gameSetting, host); + + when(roomRepository.findRoom(roomId)).thenReturn(Optional.of(room)); + + int threadCount = 10; + ExecutorService executorService = Executors.newFixedThreadPool(threadCount); + CountDownLatch countDownLatch = new CountDownLatch(threadCount); + RoomValidationRequest roomValidationRequest = new RoomValidationRequest(roomId, password); + for (int i = 1; i <= threadCount; i++) { + Long userId = i + 1L; + String provider = "provider +" + i; + String providerId = "providerId" + i; + LocalDateTime lastLogin = LocalDateTime.now(); + executorService.submit(() -> { + try { + User user = User.builder() + .provider(provider) + .provider(providerId).lastLogin(lastLogin).build(); + + user.setId(userId); + + SecurityUtils.setAuthentication(user); + + roomService.enterRoom(roomValidationRequest); + } catch (Exception e) { + //e.printStackTrace(); + } finally { + SecurityContextHolder.clearContext(); + countDownLatch.countDown(); + } + }); + } + countDownLatch.await(); + assertThat(room.getUserIdSessionMap()).hasSize(room.getRoomSetting().maxUserCount()); + } + + + @Test + @DisplayName("exitRoom_동시성_테스트") + void exitRoom_synchronized() throws Exception { + Long roomId = 1L; + Long quizId = 1L; + int threadCount = 10; + + String password = "123"; + + RoomSetting roomSetting = new RoomSetting("방제목", 5, true, password); + GameSetting gameSetting = new GameSetting(quizId, 10, 60); + + List players = new ArrayList<>(); + for (int i = 1; i <=threadCount; i++) { + Long id = i + 1L; + String nickname = "nickname " + i; + + Player player = new Player(id, nickname); + players.add(player); + + } + Player host = players.getFirst(); + Room room = new Room(roomId, roomSetting, gameSetting, host); + + for (int i = 1; i <=threadCount; i++) { + String sessionId = "sessionId" + i; + Player player = players.get(i - 1); + room.getPlayerSessionMap().put(sessionId,player); + room.getUserIdSessionMap().put(player.getId(),sessionId); + } + + 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); + + for (int i = 1; i <= threadCount; i++) { + Long userId = i + 1L; + String sessionId = "sessionId" + i; + String provider = "provider +" + i; + String providerId = "providerId" + i; + LocalDateTime lastLogin = LocalDateTime.now(); + executorService.submit(() -> { + try { + User user = User.builder() + .provider(provider) + .provider(providerId).lastLogin(lastLogin).build(); + user.setId(userId); + SecurityUtils.setAuthentication(user); + + log.info("userId = {}", userId); + log.info("room.getHost().getId() = {}", room.getHost().getId()); + roomService.exitRoom(roomId, sessionId); + } catch (Exception e) { + e.printStackTrace(); + } finally { + SecurityContextHolder.clearContext(); + countDownLatch.countDown(); + } + }); + } + countDownLatch.await(); + assertThat(room.getUserIdSessionMap()).hasSize(1); + } + + + +} \ No newline at end of file From 754a82b687e5bda2ddadadba88fd0954840524e0 Mon Sep 17 00:00:00 2001 From: github-actions <> Date: Tue, 15 Jul 2025 16:02:46 +0000 Subject: [PATCH 04/11] =?UTF-8?q?chore:=20Java=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/domain/game/app/RoomService.java | 81 ++++++------ .../io/f1/backend/domain/game/model/Room.java | 3 +- .../game/websocket/GameSocketController.java | 3 +- .../domain/game/app/RoomServiceTests.java | 124 +++++++++--------- 4 files changed, 107 insertions(+), 104 deletions(-) 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 ea3acb77..c3c47bf0 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 @@ -31,17 +31,20 @@ import io.f1.backend.domain.game.store.RoomRepository; import io.f1.backend.domain.quiz.app.QuizService; import io.f1.backend.domain.quiz.entity.Quiz; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicLong; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; + import org.hibernate.boot.model.naming.IllegalIdentifierException; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + @Slf4j @Service @RequiredArgsConstructor @@ -69,7 +72,7 @@ public RoomCreateResponse saveRoom(RoomCreateRequest request) { Room room = new Room(newId, roomSetting, gameSetting, host); - room.getUserIdSessionMap().put(host.id,PENDING_SESSION_ID); + room.getUserIdSessionMap().put(host.id, PENDING_SESSION_ID); roomRepository.saveRoom(room); @@ -85,7 +88,6 @@ public void enterRoom(RoomValidationRequest request) { Object lock = roomLocks.computeIfAbsent(roomId, k -> new Object()); synchronized (lock) { - Room room = findRoom(request.roomId()); if (room.getState().equals(RoomState.PLAYING)) { @@ -99,7 +101,7 @@ public void enterRoom(RoomValidationRequest request) { } if (room.getRoomSetting().locked() - && !room.getRoomSetting().password().equals(request.password())) { + && !room.getRoomSetting().password().equals(request.password())) { throw new IllegalArgumentException("401 비밀번호가 일치하지 않습니다."); } @@ -129,18 +131,18 @@ public RoomInitialData initializeRoomSocket(Long roomId, String sessionId) { Quiz quiz = quizService.getQuizById(quizId); GameSettingResponse gameSettingResponse = - toGameSettingResponse(room.getGameSetting(), quiz); + toGameSettingResponse(room.getGameSetting(), quiz); PlayerListResponse playerListResponse = toPlayerListResponse(room); SystemNoticeResponse systemNoticeResponse = ofPlayerEvent(player, RoomEventType.ENTER); return new RoomInitialData( - getDestination(roomId), - roomSettingResponse, - gameSettingResponse, - playerListResponse, - systemNoticeResponse); + getDestination(roomId), + roomSettingResponse, + gameSettingResponse, + playerListResponse, + systemNoticeResponse); } public RoomExitData exitRoom(Long roomId, String sessionId) { @@ -148,7 +150,6 @@ public RoomExitData exitRoom(Long roomId, String sessionId) { Object lock = roomLocks.computeIfAbsent(roomId, k -> new Object()); synchronized (lock) { - Room room = findRoom(roomId); Map playerSessionMap = room.getPlayerSessionMap(); @@ -157,10 +158,10 @@ public RoomExitData exitRoom(Long roomId, String sessionId) { Player removePlayer = playerSessionMap.get(sessionId); - if (removePlayer == null) { - room.getUserIdSessionMap().remove(getCurrentUserId()); - throw new IllegalIdentifierException("404 세션 없음 비정상적인 퇴장 요청"); - } + if (removePlayer == null) { + room.getUserIdSessionMap().remove(getCurrentUserId()); + throw new IllegalIdentifierException("404 세션 없음 비정상적인 퇴장 요청"); + } /* 방 삭제 */ if (playerSessionMap.size() == 1 && playerSessionMap.containsKey(sessionId)) { @@ -173,15 +174,17 @@ public RoomExitData exitRoom(Long roomId, String sessionId) { /* 방장 변경 */ if (room.getHost().getId().equals(removePlayer.getId())) { - Optional nextHostSessionId = playerSessionMap.keySet().stream() - .filter(key -> !key.equals(sessionId)).findFirst(); + Optional nextHostSessionId = + playerSessionMap.keySet().stream() + .filter(key -> !key.equals(sessionId)) + .findFirst(); Player nextHost = - playerSessionMap.get( - nextHostSessionId.orElseThrow( - () -> - new IllegalArgumentException( - "방장 교체 불가 - 404 해당 세션 플레이어는 존재하지않습니다."))); + playerSessionMap.get( + nextHostSessionId.orElseThrow( + () -> + new IllegalArgumentException( + "방장 교체 불가 - 404 해당 세션 플레이어는 존재하지않습니다."))); room.updateHost(nextHost); log.info("user_id:{} 방장 변경 완료 ", nextHost.getId()); @@ -191,7 +194,7 @@ public RoomExitData exitRoom(Long roomId, String sessionId) { playerSessionMap.remove(sessionId); SystemNoticeResponse systemNoticeResponse = - ofPlayerEvent(removePlayer, RoomEventType.EXIT); + ofPlayerEvent(removePlayer, RoomEventType.EXIT); PlayerListResponse playerListResponse = toPlayerListResponse(room); @@ -202,15 +205,15 @@ public RoomExitData exitRoom(Long roomId, String sessionId) { public RoomListResponse getAllRooms() { List rooms = roomRepository.findAll(); List roomResponses = - rooms.stream() - .map( - room -> { - Long quizId = room.getGameSetting().getQuizId(); - Quiz quiz = quizService.getQuizById(quizId); - - return toRoomResponse(room, quiz); - }) - .toList(); + rooms.stream() + .map( + room -> { + Long quizId = room.getGameSetting().getQuizId(); + Quiz quiz = quizService.getQuizById(quizId); + + return toRoomResponse(room, quiz); + }) + .toList(); return new RoomListResponse(roomResponses); } @@ -224,9 +227,7 @@ private Player createPlayer() { private Room findRoom(Long roomId) { return roomRepository - .findRoom(roomId) - .orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다.")); + .findRoom(roomId) + .orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다.")); } - - } 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 20ed2e86..a282b694 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 @@ -27,7 +27,7 @@ public class Room { private Map playerSessionMap = new ConcurrentHashMap<>(); - private Map userIdSessionMap = new ConcurrentHashMap<>(); + private Map userIdSessionMap = new ConcurrentHashMap<>(); private final LocalDateTime createdAt = LocalDateTime.now(); @@ -36,7 +36,6 @@ public Room(Long id, RoomSetting roomSetting, GameSetting gameSetting, Player ho this.roomSetting = roomSetting; this.gameSetting = gameSetting; this.host = host; - } public void updateHost(Player nextHost) { 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 d4ef3c38..4cebfc09 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 @@ -25,7 +25,8 @@ public void initializeRoomSocket(@DestinationVariable Long roomId, Message me String websocketSessionId = getSessionId(message); - RoomInitialData roomInitialData = roomService.initializeRoomSocket(roomId, websocketSessionId); + RoomInitialData roomInitialData = + roomService.initializeRoomSocket(roomId, websocketSessionId); String destination = roomInitialData.destination(); messageSender.send( 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 e67a24c8..a8de3741 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 @@ -13,14 +13,9 @@ import io.f1.backend.domain.quiz.app.QuizService; import io.f1.backend.domain.user.entity.User; import io.f1.backend.global.util.SecurityUtils; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; + import lombok.extern.slf4j.Slf4j; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -32,22 +27,26 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.security.core.context.SecurityContextHolder; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + @Slf4j @ExtendWith(MockitoExtension.class) class RoomServiceTests { private RoomService roomService; - @Mock - private RoomRepository roomRepository; - @Mock - private QuizService quizService; - @Mock - private ApplicationEventPublisher eventPublisher; - + @Mock private RoomRepository roomRepository; + @Mock private QuizService quizService; + @Mock private ApplicationEventPublisher eventPublisher; @BeforeEach - void setUp(){ + void setUp() { MockitoAnnotations.openMocks(this); // @Mock 어노테이션이 붙은 필드들을 초기화합니다. roomService = new RoomService(quizService, roomRepository, eventPublisher); @@ -84,30 +83,33 @@ void enterRoom_synchronized() throws Exception { String provider = "provider +" + i; String providerId = "providerId" + i; LocalDateTime lastLogin = LocalDateTime.now(); - executorService.submit(() -> { - try { - User user = User.builder() - .provider(provider) - .provider(providerId).lastLogin(lastLogin).build(); - - user.setId(userId); - - SecurityUtils.setAuthentication(user); - - roomService.enterRoom(roomValidationRequest); - } catch (Exception e) { - //e.printStackTrace(); - } finally { - SecurityContextHolder.clearContext(); - countDownLatch.countDown(); - } - }); + executorService.submit( + () -> { + try { + User user = + User.builder() + .provider(provider) + .provider(providerId) + .lastLogin(lastLogin) + .build(); + + user.setId(userId); + + SecurityUtils.setAuthentication(user); + + roomService.enterRoom(roomValidationRequest); + } catch (Exception e) { + // e.printStackTrace(); + } finally { + SecurityContextHolder.clearContext(); + countDownLatch.countDown(); + } + }); } countDownLatch.await(); assertThat(room.getUserIdSessionMap()).hasSize(room.getRoomSetting().maxUserCount()); } - @Test @DisplayName("exitRoom_동시성_테스트") void exitRoom_synchronized() throws Exception { @@ -121,22 +123,21 @@ void exitRoom_synchronized() throws Exception { GameSetting gameSetting = new GameSetting(quizId, 10, 60); List players = new ArrayList<>(); - for (int i = 1; i <=threadCount; i++) { + for (int i = 1; i <= threadCount; i++) { Long id = i + 1L; String nickname = "nickname " + i; Player player = new Player(id, nickname); players.add(player); - } Player host = players.getFirst(); Room room = new Room(roomId, roomSetting, gameSetting, host); - for (int i = 1; i <=threadCount; i++) { + for (int i = 1; i <= threadCount; i++) { String sessionId = "sessionId" + i; Player player = players.get(i - 1); - room.getPlayerSessionMap().put(sessionId,player); - room.getUserIdSessionMap().put(player.getId(),sessionId); + room.getPlayerSessionMap().put(sessionId, player); + room.getUserIdSessionMap().put(player.getId(), sessionId); } log.info("room.getPlayerSessionMap().size() = {}", room.getPlayerSessionMap().size()); @@ -153,29 +154,30 @@ void exitRoom_synchronized() throws Exception { String provider = "provider +" + i; String providerId = "providerId" + i; LocalDateTime lastLogin = LocalDateTime.now(); - executorService.submit(() -> { - try { - User user = User.builder() - .provider(provider) - .provider(providerId).lastLogin(lastLogin).build(); - user.setId(userId); - SecurityUtils.setAuthentication(user); - - log.info("userId = {}", userId); - log.info("room.getHost().getId() = {}", room.getHost().getId()); - roomService.exitRoom(roomId, sessionId); - } catch (Exception e) { - e.printStackTrace(); - } finally { - SecurityContextHolder.clearContext(); - countDownLatch.countDown(); - } - }); + executorService.submit( + () -> { + try { + User user = + User.builder() + .provider(provider) + .provider(providerId) + .lastLogin(lastLogin) + .build(); + user.setId(userId); + SecurityUtils.setAuthentication(user); + + log.info("userId = {}", userId); + log.info("room.getHost().getId() = {}", room.getHost().getId()); + roomService.exitRoom(roomId, sessionId); + } catch (Exception e) { + e.printStackTrace(); + } finally { + SecurityContextHolder.clearContext(); + countDownLatch.countDown(); + } + }); } countDownLatch.await(); assertThat(room.getUserIdSessionMap()).hasSize(1); } - - - -} \ No newline at end of file +} From 3fe0628d0387f7e015d91eca3a61b534bb9292bb Mon Sep 17 00:00:00 2001 From: sehee Date: Wed, 16 Jul 2025 11:05:21 +0900 Subject: [PATCH 05/11] =?UTF-8?q?:recycle:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81(=EB=A9=94=EC=86=8C=EB=93=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/domain/game/app/RoomService.java | 79 ++++++++++++------- .../io/f1/backend/domain/game/model/Room.java | 12 +++ 2 files changed, 62 insertions(+), 29 deletions(-) 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 ea3acb77..b3ebd14f 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 @@ -151,44 +151,22 @@ public RoomExitData exitRoom(Long roomId, String sessionId) { Room room = findRoom(roomId); - Map playerSessionMap = room.getPlayerSessionMap(); - String destination = getDestination(roomId); - Player removePlayer = playerSessionMap.get(sessionId); - - if (removePlayer == null) { - room.getUserIdSessionMap().remove(getCurrentUserId()); - throw new IllegalIdentifierException("404 세션 없음 비정상적인 퇴장 요청"); - } + Player removePlayer = getRemovePlayer(room,sessionId); /* 방 삭제 */ - if (playerSessionMap.size() == 1 && playerSessionMap.containsKey(sessionId)) { - roomRepository.removeRoom(roomId); - roomLocks.remove(roomId); - log.info("{}번 방 삭제", roomId); - return RoomExitData.builder().destination(destination).removedRoom(true).build(); + if (isLastPlayer(room,sessionId)) { + return removeRoom(room,destination); } /* 방장 변경 */ - if (room.getHost().getId().equals(removePlayer.getId())) { - - Optional nextHostSessionId = playerSessionMap.keySet().stream() - .filter(key -> !key.equals(sessionId)).findFirst(); - - Player nextHost = - playerSessionMap.get( - nextHostSessionId.orElseThrow( - () -> - new IllegalArgumentException( - "방장 교체 불가 - 404 해당 세션 플레이어는 존재하지않습니다."))); - - room.updateHost(nextHost); - log.info("user_id:{} 방장 변경 완료 ", nextHost.getId()); + if (room.isHost(removePlayer.getId())) { + changeHost(room,sessionId); } - room.getUserIdSessionMap().remove(removePlayer.getId()); - playerSessionMap.remove(sessionId); + /* 플레이어 삭제 */ + removePlayer(room,sessionId,removePlayer); SystemNoticeResponse systemNoticeResponse = ofPlayerEvent(removePlayer, RoomEventType.EXIT); @@ -214,6 +192,15 @@ public RoomListResponse getAllRooms() { return new RoomListResponse(roomResponses); } + private Player getRemovePlayer(Room room, String sessionId) { + Player removePlayer = room.getPlayerSessionMap().get(sessionId); + if (removePlayer == null) { + room.removeUserId(getCurrentUserId()); + throw new IllegalIdentifierException("404 세션 없음 비정상적인 퇴장 요청"); + } + return removePlayer; + } + private static String getDestination(Long roomId) { return "/sub/room/" + roomId; } @@ -228,5 +215,39 @@ private Room findRoom(Long roomId) { .orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다.")); } + private boolean isLastPlayer(Room room, String sessionId) { + Map playerSessionMap = room.getPlayerSessionMap(); + return playerSessionMap.size() == 1 && playerSessionMap.containsKey(sessionId); + } + + private RoomExitData removeRoom(Room room, String destination) { + Long roomId = room.getId(); + roomRepository.removeRoom(roomId); + roomLocks.remove(roomId); + log.info("{}번 방 삭제", roomId); + return RoomExitData.builder().destination(destination).removedRoom(true).build(); + } + + private void changeHost(Room room, String hostSessionId) { + Map playerSessionMap = room.getPlayerSessionMap(); + + Optional nextHostSessionId = playerSessionMap.keySet().stream() + .filter(key -> !key.equals(hostSessionId)).findFirst(); + + Player nextHost = + playerSessionMap.get( + nextHostSessionId.orElseThrow( + () -> + new IllegalArgumentException( + "방장 교체 불가 - 404 해당 세션 플레이어는 존재하지않습니다."))); + + room.updateHost(nextHost); + log.info("user_id:{} 방장 변경 완료 ", nextHost.getId()); + } + + private void removePlayer(Room room, String sessionId, Player removePlayer) { + room.removeUserId(removePlayer.getId()); + room.removeSessionId(sessionId); + } } 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 20ed2e86..3939ab30 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 @@ -39,7 +39,19 @@ public Room(Long id, RoomSetting roomSetting, GameSetting gameSetting, Player ho } + public boolean isHost(Long id){ + return this.host.getId().equals(id); + } + public void updateHost(Player nextHost) { this.host = nextHost; } + + public void removeUserId(Long id){ + this.userIdSessionMap.remove(id); + } + + public void removeSessionId(String sessionId){ + this.playerSessionMap.remove(sessionId); + } } From 39738fc5c3178537e5ad1dd3848c18bc7069fa3b Mon Sep 17 00:00:00 2001 From: sehee Date: Wed, 16 Jul 2025 11:20:50 +0900 Subject: [PATCH 06/11] =?UTF-8?q?:sparkles:=20=EB=B0=A9=EC=9E=A5=EC=9D=80?= =?UTF-8?q?=20=EC=9E=85=EC=9E=A5=20=EC=8B=9C=20ready=3Dtrue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/f1/backend/domain/game/app/RoomService.java | 4 ++++ .../src/main/java/io/f1/backend/domain/game/model/Player.java | 4 ++++ 2 files changed, 8 insertions(+) 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 b3ebd14f..76f7b0c0 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 @@ -116,6 +116,10 @@ public RoomInitialData initializeRoomSocket(Long roomId, String sessionId) { Map playerSessionMap = room.getPlayerSessionMap(); Map userIdSessionMap = room.getUserIdSessionMap(); + if(room.isHost(player.getId())) { + player.toggleReady(); + } + playerSessionMap.put(sessionId, player); String existingSession = userIdSessionMap.get(player.getId()); /* 정상 흐름 or 재연결 */ diff --git a/backend/src/main/java/io/f1/backend/domain/game/model/Player.java b/backend/src/main/java/io/f1/backend/domain/game/model/Player.java index 3d3230c1..a5b1241c 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/model/Player.java +++ b/backend/src/main/java/io/f1/backend/domain/game/model/Player.java @@ -19,4 +19,8 @@ public Player(Long id, String nickname) { this.id = id; this.nickname = nickname; } + + public void toggleReady() { + this.isReady = !this.isReady; + } } From cae51215ce28dd89ea9f21f696d6c1fcd486c585 Mon Sep 17 00:00:00 2001 From: sehee Date: Wed, 16 Jul 2025 11:33:02 +0900 Subject: [PATCH 07/11] =?UTF-8?q?:recycle:=20test=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/game/app/RoomServiceTests.java | 68 ++++++++++--------- 1 file changed, 35 insertions(+), 33 deletions(-) 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 e67a24c8..82b4b431 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 @@ -65,13 +65,11 @@ void enterRoom_synchronized() throws Exception { Long roomId = 1L; Long quizId = 1L; Long playerId = 1L; + int maxUserCount = 5; String password = "123"; + boolean locked = true; - RoomSetting roomSetting = new RoomSetting("방제목", 5, true, password); - GameSetting gameSetting = new GameSetting(quizId, 10, 60); - Player host = new Player(playerId, "닉네임"); - - Room room = new Room(roomId, roomSetting, gameSetting, host); + Room room = createRoom(roomId, playerId, quizId, password, maxUserCount, locked); when(roomRepository.findRoom(roomId)).thenReturn(Optional.of(room)); @@ -80,23 +78,14 @@ void enterRoom_synchronized() throws Exception { CountDownLatch countDownLatch = new CountDownLatch(threadCount); RoomValidationRequest roomValidationRequest = new RoomValidationRequest(roomId, password); for (int i = 1; i <= threadCount; i++) { - Long userId = i + 1L; - String provider = "provider +" + i; - String providerId = "providerId" + i; - LocalDateTime lastLogin = LocalDateTime.now(); + User user = createUser(i); + executorService.submit(() -> { try { - User user = User.builder() - .provider(provider) - .provider(providerId).lastLogin(lastLogin).build(); - - user.setId(userId); - SecurityUtils.setAuthentication(user); - roomService.enterRoom(roomValidationRequest); } catch (Exception e) { - //e.printStackTrace(); + e.printStackTrace(); } finally { SecurityContextHolder.clearContext(); countDownLatch.countDown(); @@ -113,12 +102,14 @@ void enterRoom_synchronized() throws Exception { void exitRoom_synchronized() throws Exception { Long roomId = 1L; Long quizId = 1L; - int threadCount = 10; - + Long playerId = 1L; + int maxUserCount = 5; String password = "123"; + boolean locked = true; - RoomSetting roomSetting = new RoomSetting("방제목", 5, true, password); - GameSetting gameSetting = new GameSetting(quizId, 10, 60); + Room room = createRoom(roomId, playerId, quizId, password, maxUserCount, locked); + + int threadCount = 10; List players = new ArrayList<>(); for (int i = 1; i <=threadCount; i++) { @@ -127,10 +118,10 @@ void exitRoom_synchronized() throws Exception { Player player = new Player(id, nickname); players.add(player); - } Player host = players.getFirst(); - Room room = new Room(roomId, roomSetting, gameSetting, host); + room.updateHost(host); + for (int i = 1; i <=threadCount; i++) { String sessionId = "sessionId" + i; @@ -148,20 +139,11 @@ void exitRoom_synchronized() throws Exception { CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 1; i <= threadCount; i++) { - Long userId = i + 1L; String sessionId = "sessionId" + i; - String provider = "provider +" + i; - String providerId = "providerId" + i; - LocalDateTime lastLogin = LocalDateTime.now(); + User user = createUser(i); executorService.submit(() -> { try { - User user = User.builder() - .provider(provider) - .provider(providerId).lastLogin(lastLogin).build(); - user.setId(userId); SecurityUtils.setAuthentication(user); - - log.info("userId = {}", userId); log.info("room.getHost().getId() = {}", room.getHost().getId()); roomService.exitRoom(roomId, sessionId); } catch (Exception e) { @@ -176,6 +158,26 @@ void exitRoom_synchronized() throws Exception { assertThat(room.getUserIdSessionMap()).hasSize(1); } + private Room createRoom(Long roomId, Long playerId, Long quizId, String password, int maxUserCount, boolean locked) { + RoomSetting roomSetting = new RoomSetting("방제목", maxUserCount, locked, password); + GameSetting gameSetting = new GameSetting(quizId, 10, 60); + Player host = new Player(playerId, "nickname"); + + return new Room(roomId, roomSetting, gameSetting, host); + } + + private User createUser(int i) { + Long userId = i + 1L; + String provider = "provider +" + i; + String providerId = "providerId" + i; + LocalDateTime lastLogin = LocalDateTime.now(); + User user = User.builder() + .provider(provider) + .provider(providerId).lastLogin(lastLogin).build(); + user.setId(userId); + + return user; + } } \ No newline at end of file From cfc104a91f0b9faff7fbd1b7bc6ec4dd9df25cc6 Mon Sep 17 00:00:00 2001 From: github-actions <> Date: Wed, 16 Jul 2025 02:35:22 +0000 Subject: [PATCH 08/11] =?UTF-8?q?chore:=20Java=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/domain/game/app/RoomService.java | 84 +++++++------- .../io/f1/backend/domain/game/model/Room.java | 6 +- .../domain/game/app/RoomServiceTests.java | 103 +++++++++--------- 3 files changed, 99 insertions(+), 94 deletions(-) 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 76f7b0c0..27c1522c 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 @@ -31,17 +31,20 @@ import io.f1.backend.domain.game.store.RoomRepository; import io.f1.backend.domain.quiz.app.QuizService; import io.f1.backend.domain.quiz.entity.Quiz; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicLong; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; + import org.hibernate.boot.model.naming.IllegalIdentifierException; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + @Slf4j @Service @RequiredArgsConstructor @@ -69,7 +72,7 @@ public RoomCreateResponse saveRoom(RoomCreateRequest request) { Room room = new Room(newId, roomSetting, gameSetting, host); - room.getUserIdSessionMap().put(host.id,PENDING_SESSION_ID); + room.getUserIdSessionMap().put(host.id, PENDING_SESSION_ID); roomRepository.saveRoom(room); @@ -85,7 +88,6 @@ public void enterRoom(RoomValidationRequest request) { Object lock = roomLocks.computeIfAbsent(roomId, k -> new Object()); synchronized (lock) { - Room room = findRoom(request.roomId()); if (room.getState().equals(RoomState.PLAYING)) { @@ -99,7 +101,7 @@ public void enterRoom(RoomValidationRequest request) { } if (room.getRoomSetting().locked() - && !room.getRoomSetting().password().equals(request.password())) { + && !room.getRoomSetting().password().equals(request.password())) { throw new IllegalArgumentException("401 비밀번호가 일치하지 않습니다."); } @@ -116,7 +118,7 @@ public RoomInitialData initializeRoomSocket(Long roomId, String sessionId) { Map playerSessionMap = room.getPlayerSessionMap(); Map userIdSessionMap = room.getUserIdSessionMap(); - if(room.isHost(player.getId())) { + if (room.isHost(player.getId())) { player.toggleReady(); } @@ -133,18 +135,18 @@ public RoomInitialData initializeRoomSocket(Long roomId, String sessionId) { Quiz quiz = quizService.getQuizById(quizId); GameSettingResponse gameSettingResponse = - toGameSettingResponse(room.getGameSetting(), quiz); + toGameSettingResponse(room.getGameSetting(), quiz); PlayerListResponse playerListResponse = toPlayerListResponse(room); SystemNoticeResponse systemNoticeResponse = ofPlayerEvent(player, RoomEventType.ENTER); return new RoomInitialData( - getDestination(roomId), - roomSettingResponse, - gameSettingResponse, - playerListResponse, - systemNoticeResponse); + getDestination(roomId), + roomSettingResponse, + gameSettingResponse, + playerListResponse, + systemNoticeResponse); } public RoomExitData exitRoom(Long roomId, String sessionId) { @@ -152,28 +154,27 @@ public RoomExitData exitRoom(Long roomId, String sessionId) { Object lock = roomLocks.computeIfAbsent(roomId, k -> new Object()); synchronized (lock) { - Room room = findRoom(roomId); String destination = getDestination(roomId); - Player removePlayer = getRemovePlayer(room,sessionId); + Player removePlayer = getRemovePlayer(room, sessionId); /* 방 삭제 */ - if (isLastPlayer(room,sessionId)) { - return removeRoom(room,destination); + if (isLastPlayer(room, sessionId)) { + return removeRoom(room, destination); } /* 방장 변경 */ if (room.isHost(removePlayer.getId())) { - changeHost(room,sessionId); + changeHost(room, sessionId); } /* 플레이어 삭제 */ - removePlayer(room,sessionId,removePlayer); + removePlayer(room, sessionId, removePlayer); SystemNoticeResponse systemNoticeResponse = - ofPlayerEvent(removePlayer, RoomEventType.EXIT); + ofPlayerEvent(removePlayer, RoomEventType.EXIT); PlayerListResponse playerListResponse = toPlayerListResponse(room); @@ -184,15 +185,15 @@ public RoomExitData exitRoom(Long roomId, String sessionId) { public RoomListResponse getAllRooms() { List rooms = roomRepository.findAll(); List roomResponses = - rooms.stream() - .map( - room -> { - Long quizId = room.getGameSetting().getQuizId(); - Quiz quiz = quizService.getQuizById(quizId); - - return toRoomResponse(room, quiz); - }) - .toList(); + rooms.stream() + .map( + room -> { + Long quizId = room.getGameSetting().getQuizId(); + Quiz quiz = quizService.getQuizById(quizId); + + return toRoomResponse(room, quiz); + }) + .toList(); return new RoomListResponse(roomResponses); } @@ -215,8 +216,8 @@ private Player createPlayer() { private Room findRoom(Long roomId) { return roomRepository - .findRoom(roomId) - .orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다.")); + .findRoom(roomId) + .orElseThrow(() -> new IllegalArgumentException("404 존재하지 않는 방입니다.")); } private boolean isLastPlayer(Room room, String sessionId) { @@ -235,15 +236,17 @@ private RoomExitData removeRoom(Room room, String destination) { private void changeHost(Room room, String hostSessionId) { Map playerSessionMap = room.getPlayerSessionMap(); - Optional nextHostSessionId = playerSessionMap.keySet().stream() - .filter(key -> !key.equals(hostSessionId)).findFirst(); + Optional nextHostSessionId = + playerSessionMap.keySet().stream() + .filter(key -> !key.equals(hostSessionId)) + .findFirst(); Player nextHost = - playerSessionMap.get( - nextHostSessionId.orElseThrow( - () -> - new IllegalArgumentException( - "방장 교체 불가 - 404 해당 세션 플레이어는 존재하지않습니다."))); + playerSessionMap.get( + nextHostSessionId.orElseThrow( + () -> + new IllegalArgumentException( + "방장 교체 불가 - 404 해당 세션 플레이어는 존재하지않습니다."))); room.updateHost(nextHost); log.info("user_id:{} 방장 변경 완료 ", nextHost.getId()); @@ -253,5 +256,4 @@ private void removePlayer(Room room, String sessionId, Player removePlayer) { room.removeUserId(removePlayer.getId()); room.removeSessionId(sessionId); } - } 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 90adbedc..64310c3f 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 @@ -38,7 +38,7 @@ public Room(Long id, RoomSetting roomSetting, GameSetting gameSetting, Player ho this.host = host; } - public boolean isHost(Long id){ + public boolean isHost(Long id) { return this.host.getId().equals(id); } @@ -46,11 +46,11 @@ public void updateHost(Player nextHost) { this.host = nextHost; } - public void removeUserId(Long id){ + public void removeUserId(Long id) { this.userIdSessionMap.remove(id); } - public void removeSessionId(String sessionId){ + public void removeSessionId(String sessionId) { this.playerSessionMap.remove(sessionId); } } 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 82b4b431..8f15de47 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 @@ -13,14 +13,9 @@ import io.f1.backend.domain.quiz.app.QuizService; import io.f1.backend.domain.user.entity.User; import io.f1.backend.global.util.SecurityUtils; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; + import lombok.extern.slf4j.Slf4j; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -32,22 +27,26 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.security.core.context.SecurityContextHolder; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + @Slf4j @ExtendWith(MockitoExtension.class) class RoomServiceTests { private RoomService roomService; - @Mock - private RoomRepository roomRepository; - @Mock - private QuizService quizService; - @Mock - private ApplicationEventPublisher eventPublisher; - + @Mock private RoomRepository roomRepository; + @Mock private QuizService quizService; + @Mock private ApplicationEventPublisher eventPublisher; @BeforeEach - void setUp(){ + void setUp() { MockitoAnnotations.openMocks(this); // @Mock 어노테이션이 붙은 필드들을 초기화합니다. roomService = new RoomService(quizService, roomRepository, eventPublisher); @@ -80,23 +79,23 @@ void enterRoom_synchronized() throws Exception { for (int i = 1; i <= threadCount; i++) { User user = createUser(i); - executorService.submit(() -> { - try { - SecurityUtils.setAuthentication(user); - roomService.enterRoom(roomValidationRequest); - } catch (Exception e) { - e.printStackTrace(); - } finally { - SecurityContextHolder.clearContext(); - countDownLatch.countDown(); - } - }); + executorService.submit( + () -> { + try { + SecurityUtils.setAuthentication(user); + roomService.enterRoom(roomValidationRequest); + } catch (Exception e) { + e.printStackTrace(); + } finally { + SecurityContextHolder.clearContext(); + countDownLatch.countDown(); + } + }); } countDownLatch.await(); assertThat(room.getUserIdSessionMap()).hasSize(room.getRoomSetting().maxUserCount()); } - @Test @DisplayName("exitRoom_동시성_테스트") void exitRoom_synchronized() throws Exception { @@ -112,7 +111,7 @@ void exitRoom_synchronized() throws Exception { int threadCount = 10; List players = new ArrayList<>(); - for (int i = 1; i <=threadCount; i++) { + for (int i = 1; i <= threadCount; i++) { Long id = i + 1L; String nickname = "nickname " + i; @@ -122,12 +121,11 @@ void exitRoom_synchronized() throws Exception { Player host = players.getFirst(); room.updateHost(host); - - for (int i = 1; i <=threadCount; i++) { + for (int i = 1; i <= threadCount; i++) { String sessionId = "sessionId" + i; Player player = players.get(i - 1); - room.getPlayerSessionMap().put(sessionId,player); - room.getUserIdSessionMap().put(player.getId(),sessionId); + room.getPlayerSessionMap().put(sessionId, player); + room.getUserIdSessionMap().put(player.getId(), sessionId); } log.info("room.getPlayerSessionMap().size() = {}", room.getPlayerSessionMap().size()); @@ -141,24 +139,31 @@ void exitRoom_synchronized() throws Exception { for (int i = 1; i <= threadCount; i++) { String sessionId = "sessionId" + i; User user = createUser(i); - executorService.submit(() -> { - try { - SecurityUtils.setAuthentication(user); - log.info("room.getHost().getId() = {}", room.getHost().getId()); - roomService.exitRoom(roomId, sessionId); - } catch (Exception e) { - e.printStackTrace(); - } finally { - SecurityContextHolder.clearContext(); - countDownLatch.countDown(); - } - }); + executorService.submit( + () -> { + try { + SecurityUtils.setAuthentication(user); + log.info("room.getHost().getId() = {}", room.getHost().getId()); + roomService.exitRoom(roomId, sessionId); + } catch (Exception e) { + e.printStackTrace(); + } finally { + SecurityContextHolder.clearContext(); + countDownLatch.countDown(); + } + }); } countDownLatch.await(); assertThat(room.getUserIdSessionMap()).hasSize(1); } - private Room createRoom(Long roomId, Long playerId, Long quizId, String password, int maxUserCount, boolean locked) { + private Room createRoom( + Long roomId, + Long playerId, + Long quizId, + String password, + int maxUserCount, + boolean locked) { RoomSetting roomSetting = new RoomSetting("방제목", maxUserCount, locked, password); GameSetting gameSetting = new GameSetting(quizId, 10, 60); Player host = new Player(playerId, "nickname"); @@ -172,12 +177,10 @@ private User createUser(int i) { String providerId = "providerId" + i; LocalDateTime lastLogin = LocalDateTime.now(); - User user = User.builder() - .provider(provider) - .provider(providerId).lastLogin(lastLogin).build(); + User user = + User.builder().provider(provider).provider(providerId).lastLogin(lastLogin).build(); user.setId(userId); return user; } - -} \ No newline at end of file +} From ed66c3816ec96c031071d2c45112376e90c41dc4 Mon Sep 17 00:00:00 2001 From: sehee Date: Wed, 16 Jul 2025 11:46:57 +0900 Subject: [PATCH 09/11] =?UTF-8?q?:recycle:=20test=20code=20=EB=B9=8C?= =?UTF-8?q?=EB=8D=94=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/f1/backend/domain/game/app/RoomServiceTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 82b4b431..f3dce882 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 @@ -174,7 +174,7 @@ private User createUser(int i) { User user = User.builder() .provider(provider) - .provider(providerId).lastLogin(lastLogin).build(); + .providerId(providerId).lastLogin(lastLogin).build(); user.setId(userId); return user; From 6624b79137b1d64675323b72179870b8b5f4838b Mon Sep 17 00:00:00 2001 From: github-actions <> Date: Wed, 16 Jul 2025 02:48:22 +0000 Subject: [PATCH 10/11] =?UTF-8?q?chore:=20Java=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/game/app/RoomServiceTests.java | 107 ++++++++++-------- 1 file changed, 57 insertions(+), 50 deletions(-) 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 f3dce882..2c9df079 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 @@ -13,14 +13,9 @@ import io.f1.backend.domain.quiz.app.QuizService; import io.f1.backend.domain.user.entity.User; import io.f1.backend.global.util.SecurityUtils; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; + import lombok.extern.slf4j.Slf4j; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -32,22 +27,26 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.security.core.context.SecurityContextHolder; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + @Slf4j @ExtendWith(MockitoExtension.class) class RoomServiceTests { private RoomService roomService; - @Mock - private RoomRepository roomRepository; - @Mock - private QuizService quizService; - @Mock - private ApplicationEventPublisher eventPublisher; - + @Mock private RoomRepository roomRepository; + @Mock private QuizService quizService; + @Mock private ApplicationEventPublisher eventPublisher; @BeforeEach - void setUp(){ + void setUp() { MockitoAnnotations.openMocks(this); // @Mock 어노테이션이 붙은 필드들을 초기화합니다. roomService = new RoomService(quizService, roomRepository, eventPublisher); @@ -80,23 +79,23 @@ void enterRoom_synchronized() throws Exception { for (int i = 1; i <= threadCount; i++) { User user = createUser(i); - executorService.submit(() -> { - try { - SecurityUtils.setAuthentication(user); - roomService.enterRoom(roomValidationRequest); - } catch (Exception e) { - e.printStackTrace(); - } finally { - SecurityContextHolder.clearContext(); - countDownLatch.countDown(); - } - }); + executorService.submit( + () -> { + try { + SecurityUtils.setAuthentication(user); + roomService.enterRoom(roomValidationRequest); + } catch (Exception e) { + e.printStackTrace(); + } finally { + SecurityContextHolder.clearContext(); + countDownLatch.countDown(); + } + }); } countDownLatch.await(); assertThat(room.getUserIdSessionMap()).hasSize(room.getRoomSetting().maxUserCount()); } - @Test @DisplayName("exitRoom_동시성_테스트") void exitRoom_synchronized() throws Exception { @@ -112,7 +111,7 @@ void exitRoom_synchronized() throws Exception { int threadCount = 10; List players = new ArrayList<>(); - for (int i = 1; i <=threadCount; i++) { + for (int i = 1; i <= threadCount; i++) { Long id = i + 1L; String nickname = "nickname " + i; @@ -122,12 +121,11 @@ void exitRoom_synchronized() throws Exception { Player host = players.getFirst(); room.updateHost(host); - - for (int i = 1; i <=threadCount; i++) { + for (int i = 1; i <= threadCount; i++) { String sessionId = "sessionId" + i; Player player = players.get(i - 1); - room.getPlayerSessionMap().put(sessionId,player); - room.getUserIdSessionMap().put(player.getId(),sessionId); + room.getPlayerSessionMap().put(sessionId, player); + room.getUserIdSessionMap().put(player.getId(), sessionId); } log.info("room.getPlayerSessionMap().size() = {}", room.getPlayerSessionMap().size()); @@ -141,24 +139,31 @@ void exitRoom_synchronized() throws Exception { for (int i = 1; i <= threadCount; i++) { String sessionId = "sessionId" + i; User user = createUser(i); - executorService.submit(() -> { - try { - SecurityUtils.setAuthentication(user); - log.info("room.getHost().getId() = {}", room.getHost().getId()); - roomService.exitRoom(roomId, sessionId); - } catch (Exception e) { - e.printStackTrace(); - } finally { - SecurityContextHolder.clearContext(); - countDownLatch.countDown(); - } - }); + executorService.submit( + () -> { + try { + SecurityUtils.setAuthentication(user); + log.info("room.getHost().getId() = {}", room.getHost().getId()); + roomService.exitRoom(roomId, sessionId); + } catch (Exception e) { + e.printStackTrace(); + } finally { + SecurityContextHolder.clearContext(); + countDownLatch.countDown(); + } + }); } countDownLatch.await(); assertThat(room.getUserIdSessionMap()).hasSize(1); } - private Room createRoom(Long roomId, Long playerId, Long quizId, String password, int maxUserCount, boolean locked) { + private Room createRoom( + Long roomId, + Long playerId, + Long quizId, + String password, + int maxUserCount, + boolean locked) { RoomSetting roomSetting = new RoomSetting("방제목", maxUserCount, locked, password); GameSetting gameSetting = new GameSetting(quizId, 10, 60); Player host = new Player(playerId, "nickname"); @@ -172,12 +177,14 @@ private User createUser(int i) { String providerId = "providerId" + i; LocalDateTime lastLogin = LocalDateTime.now(); - User user = User.builder() - .provider(provider) - .providerId(providerId).lastLogin(lastLogin).build(); + User user = + User.builder() + .provider(provider) + .providerId(providerId) + .lastLogin(lastLogin) + .build(); user.setId(userId); return user; } - -} \ No newline at end of file +} From 2e8cbbcc101f6cc72e6abf8a5ce8aacb66c33ed4 Mon Sep 17 00:00:00 2001 From: sehee Date: Wed, 16 Jul 2025 11:51:38 +0900 Subject: [PATCH 11/11] =?UTF-8?q?:recycle:=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=EC=9D=B4=EB=A6=84=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/f1/backend/domain/game/api/RoomController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/io/f1/backend/domain/game/api/RoomController.java b/backend/src/main/java/io/f1/backend/domain/game/api/RoomController.java index 3682c7fa..e492d754 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/api/RoomController.java +++ b/backend/src/main/java/io/f1/backend/domain/game/api/RoomController.java @@ -33,7 +33,7 @@ public RoomCreateResponse saveRoom(@RequestBody @Valid RoomCreateRequest request @PostMapping("/enterRoom") @ResponseStatus(HttpStatus.NO_CONTENT) - public void validateRoom(@RequestBody @Valid RoomValidationRequest request) { + public void enterRoom(@RequestBody @Valid RoomValidationRequest request) { roomService.enterRoom(request); }