diff --git a/be/src/main/java/codesquad/gaemimarble/exception/CustomException.java b/be/src/main/java/codesquad/gaemimarble/exception/CustomException.java new file mode 100644 index 0000000..ed29430 --- /dev/null +++ b/be/src/main/java/codesquad/gaemimarble/exception/CustomException.java @@ -0,0 +1,15 @@ +package codesquad.gaemimarble.exception; + +import lombok.Getter; + +@Getter +public class CustomException extends RuntimeException { + private final String playerId; + private final Long gameId; + + public CustomException(String message, String playerId, Long gameId) { + super(message); + this.playerId = playerId; + this.gameId = gameId; + } +} diff --git a/be/src/main/java/codesquad/gaemimarble/exception/GlobalExceptionHandler.java b/be/src/main/java/codesquad/gaemimarble/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..8f1c846 --- /dev/null +++ b/be/src/main/java/codesquad/gaemimarble/exception/GlobalExceptionHandler.java @@ -0,0 +1,21 @@ +package codesquad.gaemimarble.exception; + +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import codesquad.gaemimarble.game.controller.SocketDataSender; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + private final SocketDataSender socketDataSender; + + public GlobalExceptionHandler(SocketDataSender socketDataSender) { + this.socketDataSender = socketDataSender; + } + + @ExceptionHandler(CustomException.class) + public void handleCustomException(CustomException ex) { + socketDataSender.sendErrorMessage(ex.getGameId(), ex.getPlayerId(), ex.getMessage()); + } +} diff --git a/be/src/main/java/codesquad/gaemimarble/game/controller/SocketDataSender.java b/be/src/main/java/codesquad/gaemimarble/game/controller/SocketDataSender.java index 1c80040..d401bc5 100644 --- a/be/src/main/java/codesquad/gaemimarble/game/controller/SocketDataSender.java +++ b/be/src/main/java/codesquad/gaemimarble/game/controller/SocketDataSender.java @@ -10,7 +10,6 @@ import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import codesquad.gaemimarble.game.dto.ResponseDTO; @@ -23,24 +22,24 @@ @RequiredArgsConstructor @Slf4j public class SocketDataSender { - private final ConcurrentMap> gameSocketMap = new ConcurrentHashMap<>(); + private final ConcurrentMap> gameSocketMap = new ConcurrentHashMap<>(); private final ObjectMapper objectMapper; public void createRoom(Long gameRoomId) { - gameSocketMap.put(gameRoomId, new HashSet<>()); + gameSocketMap.put(gameRoomId, new ConcurrentHashMap<>()); } public boolean saveSocket(Long gameId, String playerId, WebSocketSession session) { - Set sessions = gameSocketMap.computeIfAbsent(gameId, key -> ConcurrentHashMap.newKeySet()); + ConcurrentMap socketMap = gameSocketMap.get(gameId); try { - if (sessions.size() == 4) { + if (socketMap.values().size() == 4) { session.sendMessage(new TextMessage(objectMapper.writeValueAsString( - new ResponseDTO<>(TypeConstants.ERROR, new SocketErrorResponse("full", "인원이 가득 찼습니다."))))); + new ResponseDTO<>(TypeConstants.ERROR, new SocketErrorResponse("인원이 가득 찼습니다."))))); + session.close(); return false; } - boolean isDuplicate = sessions.stream() - .anyMatch(s -> s.getAttributes().get("playerId").equals(playerId)); + boolean isDuplicate = socketMap.containsKey(playerId); if (!isDuplicate) { session.getAttributes().put("playerId", playerId); @@ -48,7 +47,7 @@ public boolean saveSocket(Long gameId, String playerId, WebSocketSession session return true; } else { session.sendMessage(new TextMessage(objectMapper.writeValueAsString( - new ResponseDTO<>(TypeConstants.ERROR, new SocketErrorResponse("duplicate", "이미 접속한 플레이어입니다."))))); + new ResponseDTO<>(TypeConstants.ERROR, new SocketErrorResponse("이미 접속한 플레이어입니다."))))); return false; } } catch (IOException e) { @@ -58,7 +57,7 @@ public boolean saveSocket(Long gameId, String playerId, WebSocketSession session } public void send(Long gameId, T object) { - for (WebSocketSession session : gameSocketMap.get(gameId)) { + for (WebSocketSession session : gameSocketMap.get(gameId).values()) { try { session.sendMessage(new TextMessage(objectMapper.writeValueAsString(object))); } catch (IOException e) { @@ -67,4 +66,13 @@ public void send(Long gameId, T object) { } System.out.println("전송 완료"); } + + public void sendErrorMessage(Long gameId, String playerId, String message) { + try { + gameSocketMap.get(gameId).get(playerId).sendMessage(new TextMessage(objectMapper.writeValueAsString( + new ResponseDTO<>(TypeConstants.ERROR, new SocketErrorResponse(message))))); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } } diff --git a/be/src/main/java/codesquad/gaemimarble/game/dto/SocketErrorResponse.java b/be/src/main/java/codesquad/gaemimarble/game/dto/SocketErrorResponse.java index bafcc83..cfddab9 100644 --- a/be/src/main/java/codesquad/gaemimarble/game/dto/SocketErrorResponse.java +++ b/be/src/main/java/codesquad/gaemimarble/game/dto/SocketErrorResponse.java @@ -5,12 +5,10 @@ @Getter public class SocketErrorResponse { - private String errorType; // 나중에 에러 코드로 변경 private String message; @Builder - public SocketErrorResponse(String errorType, String message) { - this.errorType = errorType; + public SocketErrorResponse(String message) { this.message = message; } } diff --git a/be/src/main/java/codesquad/gaemimarble/game/service/GameService.java b/be/src/main/java/codesquad/gaemimarble/game/service/GameService.java index 134223d..fe18148 100644 --- a/be/src/main/java/codesquad/gaemimarble/game/service/GameService.java +++ b/be/src/main/java/codesquad/gaemimarble/game/service/GameService.java @@ -10,6 +10,7 @@ import org.springframework.stereotype.Service; +import codesquad.gaemimarble.exception.CustomException; import codesquad.gaemimarble.game.dto.GameMapper; import codesquad.gaemimarble.game.dto.request.GameEndTurnRequest; import codesquad.gaemimarble.game.dto.request.GameEventResultRequest; @@ -249,10 +250,12 @@ public GameUserBoardResponse buyStock(GameStockBuyRequest gameStockBuyRequest) { .stream() .filter(s -> s.getName().equals(gameStockBuyRequest.getStockName())) .findFirst() - .orElseThrow(() -> new RuntimeException("존재하지 않는 주식이름입니다")); + .orElseThrow(() -> new CustomException("존재하지 않는 주식이름입니다", gameStockBuyRequest.getPlayerId(), + gameStockBuyRequest.getGameId())); if (stock.getRemainingStock() < gameStockBuyRequest.getQuantity() | player.getCashAsset() < stock.getCurrentPrice() * gameStockBuyRequest.getQuantity()) { - throw new RuntimeException("구매할 수량이 부족하거나, 플레이어 보유 캐쉬가 부족합니다"); + throw new CustomException("구매할 수량이 부족하거나, 플레이어 보유 캐쉬가 부족합니다", gameStockBuyRequest.getPlayerId(), + gameStockBuyRequest.getGameId()); } player.buy(stock, gameStockBuyRequest.getQuantity()); stock.decrementQuantity(gameStockBuyRequest.getQuantity());