diff --git a/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIErrorRes.java b/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIErrorRes.java index f0cf0198..127f10a7 100644 --- a/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIErrorRes.java +++ b/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIErrorRes.java @@ -3,6 +3,6 @@ public record AIErrorRes( - String error + String detail ) { } diff --git a/gotcha-socket/src/main/java/socket_server/domain/game/service/AIClientService.java b/gotcha-socket/src/main/java/socket_server/domain/game/service/AIClientService.java index 735c40d9..e8f78b05 100644 --- a/gotcha-socket/src/main/java/socket_server/domain/game/service/AIClientService.java +++ b/gotcha-socket/src/main/java/socket_server/domain/game/service/AIClientService.java @@ -27,147 +27,122 @@ public class AIClientService { @Value("${ai-server.url}") private String AI_SERVER_BASE_URL; - public String getGameStartMessage(String roomId, AIGameStartReq request){ - try { - return webClient.post() - .uri(AI_SERVER_BASE_URL + "myomyo/" + roomId + "/start") - .bodyValue(request) - .retrieve() - .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() - , clientResponse -> clientResponse.bodyToMono(AIErrorRes.class).flatMap(error -> Mono.error( - new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR)))) - .bodyToMono(String.class) - .timeout(Duration.ofSeconds(60)) - .block(); - } catch (Exception e){ - log.error("[AI Server Exception] error on GAME_START: {}", e.getClass().getName()); - throw new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR); - } + /** + * AI 서버로 POST 요청을 보내는 제네릭 메서드. + * 비동기 호출을 처리하며, 응답을 Mono 형태로 반환합니다. + * @param path AI 서버의 API 경로 + * @param requestBody 요청 본문 + * @param responseClass 응답 본문을 매핑할 클래스 타입 + * @param errorContext 로깅 및 예외 처리를 위한 컨텍스트 문자열 + * @param 요청 본문의 타입 + * @param 응답 본문의 타입 + * @return AI 서버로부터의 응답을 담은 Mono 객체 + */ + private Mono postToAiServer(String path, T requestBody, Class responseClass, String errorContext) { + // todo: 비동기? 동기? 비동기? 동기? 비동기? 동기? + // todo: 모노? 안모노? 모노? 안모노? 모노? 안모노? + return webClient.post() + .uri(AI_SERVER_BASE_URL + path) + .bodyValue(requestBody) + .retrieve() + .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError(), + clientResponse -> clientResponse.bodyToMono(AIErrorRes.class).flatMap(error -> { + log.error("AI Server Error on Path: \"{}\", StatusCode: {}, Content: {}", path, clientResponse.statusCode(), error.detail()); + return Mono.error( + new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR)); + })) + .bodyToMono(responseClass) + .timeout(Duration.ofSeconds(60)) + .doOnError(e -> log.error("[AI Server Exception] error on {}: {}", errorContext, e.getClass().getName())) + .onErrorMap(e -> { + if (e instanceof SocketCustomException) { + return e; + } + return new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR); + }); } - public String getRoundStartMessage(String roomId, AIRoundStartReq request){ - try { - return webClient.post() - .uri(AI_SERVER_BASE_URL + "myomyo/" + roomId + "/round/start") - .bodyValue(request) - .retrieve() - .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() - , clientResponse -> clientResponse.bodyToMono(AIErrorRes.class).flatMap(error -> Mono.error( - new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR)))) - .bodyToMono(String.class) - .timeout(Duration.ofSeconds(60)) - .block(); - }catch (Exception e){ - log.error("[AI Server Exception] error on ROUND_START: {}", e.getClass().getName()); - throw new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR); - } + /** + * 게임 시작 메시지를 AI 서버에 요청합니다. + * @param roomId 게임방 ID + * @param request 게임 시작 요청 DTO + * @return AI 서버로부터의 응답 메시지를 담은 Mono 객체 + */ + public String getGameStartMessage(String roomId, AIGameStartReq request) { + // todo: 비동기? 동기? 비동기? 동기? 비동기? 동기? + // todo: 모노? 안모노? 모노? 안모노? 모노? 안모노? + return postToAiServer("myomyo/" + roomId + "/start", request, String.class, "GAME_START").block(); } - public String getGuessStartMessage(String roomId, AIGuessStartReq request){ - try { - return webClient.post() - .uri(AI_SERVER_BASE_URL + "myomyo/" + roomId + "/guess/start") - .bodyValue(request) - .retrieve() - .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError(), - clientResponse -> clientResponse.bodyToMono(AIErrorRes.class).flatMap(error -> Mono.error( - new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR)))) - .bodyToMono(String.class) - .timeout(Duration.ofSeconds(60)) - .block(); - } catch (Exception e){ - log.error("[AI Server Exception] error on GUESS_START: {}", e.getClass().getName()); - throw new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR); - } + + /** + * 라운드 시작 메시지를 AI 서버에 요청합니다. + * @param roomId 게임방 ID + * @param request 라운드 시작 요청 DTO + * @return AI 서버로부터의 응답 메시지를 담은 Mono 객체 + */ + public String getRoundStartMessage(String roomId, AIRoundStartReq request) { + return postToAiServer("myomyo/" + roomId + "/round/start", request, String.class, "ROUND_START").block(); + } + + /** + * 추측 시작 메시지를 AI 서버에 요청합니다. + * @param roomId 게임방 ID + * @param request 추측 시작 요청 DTO + * @return AI 서버로부터의 응답 메시지를 담은 Mono 객체 + */ + public String getGuessStartMessage(String roomId, AIGuessStartReq request) { + return postToAiServer("myomyo/" + roomId + "/guess/start", request, String.class, "GUESS_START").block(); } - public AIGuessImageRes getGuessImage(AIGuessImageReq request){ - try { - return webClient.post() - .uri(AI_SERVER_BASE_URL + "classify") - .bodyValue(request) - .retrieve() - .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() - , clientResponse -> clientResponse.bodyToMono(AIErrorRes.class).flatMap(error -> Mono.error( - new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR)))) - .bodyToMono(AIGuessImageRes.class) - .block(); - } catch (Exception e) { - log.error("[AI Server Exception] error on GUESS IMAGE: {}", e.getClass().getName()); - throw new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR); - } + /** + * 이미지 추측 결과를 AI 서버에 요청합니다. + * @param request 이미지 추측 요청 DTO + * @return AI 서버로부터의 이미지 추측 결과를 담은 Mono 객체 + */ + public AIGuessImageRes getGuessImage(AIGuessImageReq request) { + return postToAiServer("classify", request, AIGuessImageRes.class, "GUESS_IMAGE").block(); } - public String getGuessMessage(String roomId, AIGuessMessageReq request){ - try { - return webClient.post() - .uri(AI_SERVER_BASE_URL + "myomyo/" + roomId + "/guess") - .bodyValue(request) - .retrieve() - .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() - , clientResponse -> clientResponse.bodyToMono(AIErrorRes.class).flatMap(error -> Mono.error( - new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR)))) - .bodyToMono(String.class) - .timeout(Duration.ofSeconds(60)) - .block(); - } catch (Exception e) { - log.error("[AI Server Exception] error on GUESS MESSAGE: {}", e.getClass().getName()); - throw new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR); - } + + + /** + * 추측 메시지를 AI 서버에 요청합니다. + * @param roomId 게임방 ID + * @param request 추측 메시지 요청 DTO + * @return AI 서버로부터의 응답 메시지를 담은 Mono 객체 + */ + public String getGuessMessage(String roomId, AIGuessMessageReq request) { + return postToAiServer("myomyo/" + roomId + "/guess", request, String.class, "GUESS_MESSAGE").block(); } - public String getGuessReactMessage(String roomId, AIGuessReactReq request){ - try { - return webClient.post() - .uri(AI_SERVER_BASE_URL + "myomyo/" + roomId + "/guess/react") - .bodyValue(request) - .retrieve() - .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() - , clientResponse -> clientResponse.bodyToMono(AIErrorRes.class).flatMap(error -> Mono.error( - new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR)))) - .bodyToMono(String.class) - .timeout(Duration.ofSeconds(60)) - .block(); - } catch (Exception e) { - log.error("[AI Server Exception] error on GUESS REACT: {}", e.getClass().getName()); - throw new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR); - } + /** + * 추측 반응 메시지를 AI 서버에 요청합니다. + * @param roomId 게임방 ID + * @param request 추측 반응 요청 DTO + * @return AI 서버로부터의 응답 메시지를 담은 Mono 객체 + */ + public String getGuessReactMessage(String roomId, AIGuessReactReq request) { + return postToAiServer("myomyo/" + roomId + "/guess/react", request, String.class, "GUESS_REACT").block(); } - public String getRoundEndMessage(String roomId, AIRoundEndReq request){ - try { - return webClient.post() - .uri(AI_SERVER_BASE_URL + "myomyo/" + roomId + "/round/end") - .bodyValue(request) - .retrieve() - .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() - , clientResponse -> clientResponse.bodyToMono(AIErrorRes.class).flatMap(error -> Mono.error( - new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR)))) - .bodyToMono(String.class) - .timeout(Duration.ofSeconds(60)) - .block(); - } catch (Exception e) { - log.error("[AI Server Exception] error on ROUND END: {}", e.getClass().getName()); - throw new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR); - } + /** + * 라운드 종료 메시지를 AI 서버에 요청합니다. + * @param roomId 게임방 ID + * @param request 라운드 종료 요청 DTO + * @return AI 서버로부터의 응답 메시지를 담은 Mono 객체 + */ + public Mono getRoundEndMessage(String roomId, AIRoundEndReq request) { + return postToAiServer("myomyo/" + roomId + "/round/end", request, String.class, "ROUND_END"); } + /** + * 게임 종료 메시지를 AI 서버에 요청합니다. + * @param roomId 게임방 ID + * @param request 게임 종료 요청 DTO + * @return AI 서버로부터의 응답 메시지를 담은 Mono 객체 + */ public String getGameEndMessage(String roomId, AIGameEndReq request) { - try { - return webClient.post() - .uri(AI_SERVER_BASE_URL + "myomyo/" + roomId + "/end") - .bodyValue(request) - .retrieve() - .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() - , clientResponse -> clientResponse.bodyToMono(AIErrorRes.class).flatMap(error -> Mono.error( - new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR)))) - .bodyToMono(String.class) - .timeout(Duration.ofSeconds(60)) - .block(); - } catch (Exception e) { - log.error("[AI Server Exception] error on GAME END: {}", e.getClass().getName()); - throw new SocketCustomException(GAME_ERROR, GameExceptionCode.AI_SERVER_ERROR); - } - + return postToAiServer("myomyo/" + roomId + "/end", request, String.class, "GAME_END").block(); } }