From 149021f7facbd8cd3cb84042c93a01f495ec57e5 Mon Sep 17 00:00:00 2001 From: brothergiven Date: Sun, 21 Sep 2025 23:03:45 +0900 Subject: [PATCH 1/5] =?UTF-8?q?refact:=20=EB=B0=94=EB=80=90=20AI=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20API=20=EC=A3=BC=EC=86=8C=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/game/service/AIClientService.java | 18 ++++++++++-------- .../lulu/service/LuLuAIClientService.java | 2 +- gotcha/src/main/resources/application.yml | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) 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 9d57180c..fc637de0 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,10 +27,12 @@ public class AIClientService { @Value("${ai-server.url}") private String AI_SERVER_BASE_URL; + private final String MYOMYO_BASE_URL = AI_SERVER_BASE_URL + "myomyo/"; + public String getGameStartMessage(String roomId, AIGameStartReq request){ try { return webClient.post() - .uri(AI_SERVER_BASE_URL + "chat/" + roomId + "/start") + .uri(MYOMYO_BASE_URL + roomId + "/start") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() @@ -48,7 +50,7 @@ public String getGameStartMessage(String roomId, AIGameStartReq request){ public String getRoundStartMessage(String roomId, AIRoundStartReq request){ try { return webClient.post() - .uri(AI_SERVER_BASE_URL + "chat/" + roomId + "/round/start") + .uri(MYOMYO_BASE_URL + roomId + "/round/start") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() @@ -66,7 +68,7 @@ public String getRoundStartMessage(String roomId, AIRoundStartReq request){ public String getGuessStartMessage(String roomId, AIGuessStartReq request){ try { return webClient.post() - .uri(AI_SERVER_BASE_URL + "chat/" + roomId + "/guess/start") + .uri(MYOMYO_BASE_URL + roomId + "/guess/start") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError(), @@ -84,7 +86,7 @@ public String getGuessStartMessage(String roomId, AIGuessStartReq request){ public AIGuessImageRes getGuessImage(AIGuessImageReq request){ try { return webClient.post() - .uri(AI_SERVER_BASE_URL + "image/classify") + .uri(AI_SERVER_BASE_URL + "classify") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() @@ -101,7 +103,7 @@ public AIGuessImageRes getGuessImage(AIGuessImageReq request){ public String getGuessMessage(String roomId, AIGuessMessageReq request){ try { return webClient.post() - .uri(AI_SERVER_BASE_URL + "chat/" + roomId + "/guess") + .uri(MYOMYO_BASE_URL + roomId + "/guess") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() @@ -119,7 +121,7 @@ public String getGuessMessage(String roomId, AIGuessMessageReq request){ public String getGuessReactMessage(String roomId, AIGuessReactReq request){ try { return webClient.post() - .uri(AI_SERVER_BASE_URL + "chat/" + roomId + "/guess/react") + .uri(MYOMYO_BASE_URL + roomId + "/guess/react") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() @@ -137,7 +139,7 @@ public String getGuessReactMessage(String roomId, AIGuessReactReq request){ public String getRoundEndMessage(String roomId, AIRoundEndReq request){ try { return webClient.post() - .uri(AI_SERVER_BASE_URL + "chat/" + roomId + "/round/end") + .uri(MYOMYO_BASE_URL + roomId + "/round/end") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() @@ -155,7 +157,7 @@ public String getRoundEndMessage(String roomId, AIRoundEndReq request){ public String getGameEndMessage(String roomId, AIGameEndReq request) { try { return webClient.post() - .uri(AI_SERVER_BASE_URL + "chat/" + roomId + "/end") + .uri(MYOMYO_BASE_URL + roomId + "/end") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() diff --git a/gotcha/src/main/java/Gotcha/domain/lulu/service/LuLuAIClientService.java b/gotcha/src/main/java/Gotcha/domain/lulu/service/LuLuAIClientService.java index f2a6d582..4a1495ee 100644 --- a/gotcha/src/main/java/Gotcha/domain/lulu/service/LuLuAIClientService.java +++ b/gotcha/src/main/java/Gotcha/domain/lulu/service/LuLuAIClientService.java @@ -116,7 +116,7 @@ private TaskEvalRes requestEvaluate(String gameId, String imageCaption){ public String getImageCaption(String imageUrl){ Map body = Map.of("imageURL", imageUrl); return webClient.post() - .uri(AI_SERVER_BASE_URL + "image/caption") + .uri(AI_SERVER_BASE_URL + "caption") .bodyValue(body) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError( diff --git a/gotcha/src/main/resources/application.yml b/gotcha/src/main/resources/application.yml index 3ab8b25a..0569ad65 100644 --- a/gotcha/src/main/resources/application.yml +++ b/gotcha/src/main/resources/application.yml @@ -68,4 +68,4 @@ server: address: 0.0.0.0 ai-server: - url: ${AI_SERVER_API} \ No newline at end of file + url: ${AI_SERVER_API:http://localhost:8000/api/v1/} \ No newline at end of file From 6487d844f555ed806293bc2262d7f6d597f33258 Mon Sep 17 00:00:00 2001 From: brothergiven Date: Sun, 16 Nov 2025 18:48:34 +0900 Subject: [PATCH 2/5] =?UTF-8?q?refact:=20=EB=B0=94=EB=80=90=20AI=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=20=EC=A3=BC=EC=86=8C=20=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/game/dto/AIGuessImageReq.java | 2 +- .../domain/game/dto/AIGuessMessageReq.java | 2 +- .../domain/game/dto/AIGuessReactReq.java | 2 +- .../domain/game/dto/AIGuessStartReq.java | 4 ++-- .../domain/game/dto/AIRoundStartReq.java | 4 ++-- .../domain/game/service/AIClientService.java | 16 +++++++--------- .../domain/game/service/GameEndService.java | 1 + 7 files changed, 15 insertions(+), 16 deletions(-) diff --git a/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessImageReq.java b/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessImageReq.java index 53735a60..949dc042 100644 --- a/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessImageReq.java +++ b/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessImageReq.java @@ -1,6 +1,6 @@ package socket_server.domain.game.dto; public record AIGuessImageReq( - String imageURL + String image_url ) { } diff --git a/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessMessageReq.java b/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessMessageReq.java index 5831a0bd..9360f48b 100644 --- a/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessMessageReq.java +++ b/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessMessageReq.java @@ -1,6 +1,6 @@ package socket_server.domain.game.dto; public record AIGuessMessageReq( - String imageDescription + String image_description ) { } diff --git a/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessReactReq.java b/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessReactReq.java index 5fd4d614..ead86a56 100644 --- a/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessReactReq.java +++ b/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessReactReq.java @@ -1,7 +1,7 @@ package socket_server.domain.game.dto; public record AIGuessReactReq( - boolean isCorrect, + boolean is_correct, String answer, String guesser ) { diff --git a/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessStartReq.java b/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessStartReq.java index b2a92719..27f91f95 100644 --- a/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessStartReq.java +++ b/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIGuessStartReq.java @@ -4,8 +4,8 @@ * 추측 시작 시(GUESS_REQUEST) AI서버로 보낼 DTO */ public record AIGuessStartReq( - int roundNum, - int totalRounds, + int round_num, + int total_rounds, String drawer, String guesser ) { diff --git a/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIRoundStartReq.java b/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIRoundStartReq.java index 46e4cf8f..2c8658fa 100644 --- a/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIRoundStartReq.java +++ b/gotcha-socket/src/main/java/socket_server/domain/game/dto/AIRoundStartReq.java @@ -4,7 +4,7 @@ * 라운드 시작 시 AI 서버에 보낼 DTO */ public record AIRoundStartReq( - int roundNum, - int totalRounds + int round_num, + int total_rounds ) { } 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 fc637de0..735c40d9 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,12 +27,10 @@ public class AIClientService { @Value("${ai-server.url}") private String AI_SERVER_BASE_URL; - private final String MYOMYO_BASE_URL = AI_SERVER_BASE_URL + "myomyo/"; - public String getGameStartMessage(String roomId, AIGameStartReq request){ try { return webClient.post() - .uri(MYOMYO_BASE_URL + roomId + "/start") + .uri(AI_SERVER_BASE_URL + "myomyo/" + roomId + "/start") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() @@ -50,7 +48,7 @@ public String getGameStartMessage(String roomId, AIGameStartReq request){ public String getRoundStartMessage(String roomId, AIRoundStartReq request){ try { return webClient.post() - .uri(MYOMYO_BASE_URL + roomId + "/round/start") + .uri(AI_SERVER_BASE_URL + "myomyo/" + roomId + "/round/start") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() @@ -68,7 +66,7 @@ public String getRoundStartMessage(String roomId, AIRoundStartReq request){ public String getGuessStartMessage(String roomId, AIGuessStartReq request){ try { return webClient.post() - .uri(MYOMYO_BASE_URL + roomId + "/guess/start") + .uri(AI_SERVER_BASE_URL + "myomyo/" + roomId + "/guess/start") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError(), @@ -103,7 +101,7 @@ public AIGuessImageRes getGuessImage(AIGuessImageReq request){ public String getGuessMessage(String roomId, AIGuessMessageReq request){ try { return webClient.post() - .uri(MYOMYO_BASE_URL + roomId + "/guess") + .uri(AI_SERVER_BASE_URL + "myomyo/" + roomId + "/guess") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() @@ -121,7 +119,7 @@ public String getGuessMessage(String roomId, AIGuessMessageReq request){ public String getGuessReactMessage(String roomId, AIGuessReactReq request){ try { return webClient.post() - .uri(MYOMYO_BASE_URL + roomId + "/guess/react") + .uri(AI_SERVER_BASE_URL + "myomyo/" + roomId + "/guess/react") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() @@ -139,7 +137,7 @@ public String getGuessReactMessage(String roomId, AIGuessReactReq request){ public String getRoundEndMessage(String roomId, AIRoundEndReq request){ try { return webClient.post() - .uri(MYOMYO_BASE_URL + roomId + "/round/end") + .uri(AI_SERVER_BASE_URL + "myomyo/" + roomId + "/round/end") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() @@ -157,7 +155,7 @@ public String getRoundEndMessage(String roomId, AIRoundEndReq request){ public String getGameEndMessage(String roomId, AIGameEndReq request) { try { return webClient.post() - .uri(MYOMYO_BASE_URL + roomId + "/end") + .uri(AI_SERVER_BASE_URL + "myomyo/" + roomId + "/end") .bodyValue(request) .retrieve() .onStatus(httpStatusCode -> httpStatusCode.is4xxClientError() || httpStatusCode.is5xxServerError() diff --git a/gotcha-socket/src/main/java/socket_server/domain/game/service/GameEndService.java b/gotcha-socket/src/main/java/socket_server/domain/game/service/GameEndService.java index 948ded4c..0dec9994 100644 --- a/gotcha-socket/src/main/java/socket_server/domain/game/service/GameEndService.java +++ b/gotcha-socket/src/main/java/socket_server/domain/game/service/GameEndService.java @@ -100,6 +100,7 @@ public void updateScore(Game game){ } public void flushGame(GameMeta gameMeta) { + log.info("Flushing game data for roomId: {}", gameMeta.getRoomId()); //1. roomId 기반 삭제 gameRepository.deleteGameMeta(gameMeta.getRoomId()); From 6f71510ea752ac768becde84c276d00e745c4778 Mon Sep 17 00:00:00 2001 From: brothergiven Date: Sun, 16 Nov 2025 20:05:13 +0900 Subject: [PATCH 3/5] =?UTF-8?q?refact:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Gotcha/domain/image/api/ImageApi.java | 45 ------------------ .../image/controller/ImageController.java | 32 ------------- .../image/exception/ImageExceptionCode.java | 30 ------------ .../domain/image/service/ImageService.java | 46 ------------------- gotcha/src/main/resources/application.yml | 9 ---- 5 files changed, 162 deletions(-) delete mode 100644 gotcha/src/main/java/Gotcha/domain/image/api/ImageApi.java delete mode 100644 gotcha/src/main/java/Gotcha/domain/image/controller/ImageController.java delete mode 100644 gotcha/src/main/java/Gotcha/domain/image/exception/ImageExceptionCode.java delete mode 100644 gotcha/src/main/java/Gotcha/domain/image/service/ImageService.java diff --git a/gotcha/src/main/java/Gotcha/domain/image/api/ImageApi.java b/gotcha/src/main/java/Gotcha/domain/image/api/ImageApi.java deleted file mode 100644 index 7b8c5a75..00000000 --- a/gotcha/src/main/java/Gotcha/domain/image/api/ImageApi.java +++ /dev/null @@ -1,45 +0,0 @@ -package Gotcha.domain.image.api; - - -import gotcha_domain.auth.SecurityUserDetails; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.ExampleObject; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -@Tag(name = "[이미지 업로드 API]", description = "이미지 업로드 API") -public interface ImageApi { - - @Operation(summary = "이미지 업로드", description = "이미지 업로드 API, png, jpg, jpeg 파일만 허용합니다.") - @ApiResponses({ - @ApiResponse(responseCode = "200", description = "이미지 업로드 성공", - content = @Content(mediaType = "application/json", examples = { - @ExampleObject(value = """ - { - "status": "OK", - "message": "https://gotchaai-image-bucket.s3.ap-northeast-2.amazonaws.com/useruuid/6a0f9e3d-1b2c-4d5e-8f2a-8b02f60d92e4.jpg - " - } - """) - }) - ), - @ApiResponse(responseCode = "400", description = "유효하지 않은 파일 확장자(png, jpg, jpeg만 허용)", - content = @Content(mediaType = "application/json", examples = { - @ExampleObject(value = """ - { - "status": "BAD_REQUEST", - "code" : "IMAGE-400-001", - "message": "이미지 타입이 유효하지 않습니다.(png, jpg, jpeg만 허용)" - } - """) - }) - ), - }) - ResponseEntity uploadImage(@RequestParam("file") MultipartFile file, SecurityUserDetails userDetails); -} diff --git a/gotcha/src/main/java/Gotcha/domain/image/controller/ImageController.java b/gotcha/src/main/java/Gotcha/domain/image/controller/ImageController.java deleted file mode 100644 index 4a41df05..00000000 --- a/gotcha/src/main/java/Gotcha/domain/image/controller/ImageController.java +++ /dev/null @@ -1,32 +0,0 @@ -package Gotcha.domain.image.controller; - -import Gotcha.domain.image.api.ImageApi; -import Gotcha.domain.image.service.ImageService; -import gotcha_common.dto.SuccessRes; -import gotcha_domain.auth.SecurityUserDetails; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; - -@RestController -@RequestMapping("/api/v1/image") -@RequiredArgsConstructor -@Slf4j -public class ImageController implements ImageApi { - - private final ImageService imageService; - - - @PostMapping("/upload") - public ResponseEntity uploadImage(@RequestParam("file") MultipartFile file, @AuthenticationPrincipal SecurityUserDetails userDetails) { - String filename = imageService.uploadImage(userDetails.getUuid(), file); - return ResponseEntity.ok(SuccessRes.from(filename)); - } - -} diff --git a/gotcha/src/main/java/Gotcha/domain/image/exception/ImageExceptionCode.java b/gotcha/src/main/java/Gotcha/domain/image/exception/ImageExceptionCode.java deleted file mode 100644 index 06eaadcb..00000000 --- a/gotcha/src/main/java/Gotcha/domain/image/exception/ImageExceptionCode.java +++ /dev/null @@ -1,30 +0,0 @@ -package Gotcha.domain.image.exception; - -import gotcha_common.exception.exceptionCode.ExceptionCode; -import lombok.AllArgsConstructor; -import org.springframework.http.HttpStatus; - -@AllArgsConstructor -public enum ImageExceptionCode implements ExceptionCode { - INVALID_IMAGE_TYPE(HttpStatus.BAD_REQUEST, "IMAGE-400-001", "이미지 타입이 유효하지 않습니다.(png, jpg, jpeg만 허용)"); - - private final HttpStatus status; - private final String code; - private final String message; - - - @Override - public String getCode() { - return code; - } - - @Override - public HttpStatus getStatus() { - return status; - } - - @Override - public String getMessage() { - return message; - } -} diff --git a/gotcha/src/main/java/Gotcha/domain/image/service/ImageService.java b/gotcha/src/main/java/Gotcha/domain/image/service/ImageService.java deleted file mode 100644 index 5e87f462..00000000 --- a/gotcha/src/main/java/Gotcha/domain/image/service/ImageService.java +++ /dev/null @@ -1,46 +0,0 @@ -package Gotcha.domain.image.service; - -import Gotcha.domain.image.exception.ImageExceptionCode; -import gotcha_common.exception.CustomException; -import gotcha_common.s3.S3ClientService; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -import java.util.UUID; - -@Service -@RequiredArgsConstructor -public class ImageService { - - private final S3ClientService s3ClientService; - - @Value("${spring.cloud.aws.region.static}") - private String region; - @Value("${spring.cloud.aws.s3.bucket-name}") - private String bucketName; - - public String uploadImage(String userUuid, MultipartFile file) { - String filename = generateUserFileName(userUuid, file); - s3ClientService.uploadFile(filename, file); - return String.format("https://%s.s3.%s.amazonaws.com/%s", bucketName, region, filename); - } - - private String generateUserFileName(String userUuid, MultipartFile file) { - String originalFilename = file.getOriginalFilename(); - String extension = ""; - - if (originalFilename != null && originalFilename.contains(".")) { - extension = originalFilename.substring(originalFilename.lastIndexOf(".")); - } - - if(extension.isEmpty() || (!extension.equals(".jpg") && !extension.equals(".png") && !extension.equals(".jpeg"))){ - throw new CustomException(ImageExceptionCode.INVALID_IMAGE_TYPE); - } - - String uuid = UUID.randomUUID().toString(); - return userUuid + "/" + uuid + extension; - } - -} diff --git a/gotcha/src/main/resources/application.yml b/gotcha/src/main/resources/application.yml index 0569ad65..10322a2e 100644 --- a/gotcha/src/main/resources/application.yml +++ b/gotcha/src/main/resources/application.yml @@ -25,16 +25,7 @@ spring: timeout: 5000 starttls: enable: true - cloud: - aws: - credentials: - access-key: ${AWS_S3_ACCESS_KEY} - secret-key: ${AWS_S3_SECRET_KEY} - s3: - bucket-name: ${AWS_S3_BUCKET_NAME} - region: - static: ${AWS_S3_REGION} data: redis: host: ${REDIS_HOST:localhost} From 2d4a26b27d4a46173bed8300e177212dee1cc341 Mon Sep 17 00:00:00 2001 From: brothergiven Date: Sun, 16 Nov 2025 21:48:37 +0900 Subject: [PATCH 4/5] =?UTF-8?q?refact:=20=EB=B6=88=ED=95=84=EC=9A=94=20S3?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gotcha_common/s3/S3ClientService.java | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 gotcha-common/src/main/java/gotcha_common/s3/S3ClientService.java diff --git a/gotcha-common/src/main/java/gotcha_common/s3/S3ClientService.java b/gotcha-common/src/main/java/gotcha_common/s3/S3ClientService.java deleted file mode 100644 index 3761ecda..00000000 --- a/gotcha-common/src/main/java/gotcha_common/s3/S3ClientService.java +++ /dev/null @@ -1,42 +0,0 @@ - - -package gotcha_common.s3; - - -import gotcha_common.exception.CustomException; -import gotcha_common.exception.exceptionCode.GlobalExceptionCode; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; -import software.amazon.awssdk.core.sync.RequestBody; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.PutObjectRequest; -import software.amazon.awssdk.services.s3.model.S3Exception; - -import java.io.IOException; - - -@Service -@RequiredArgsConstructor -public class S3ClientService { - private final S3Client s3Client; - - @Value("${spring.cloud.aws.s3.bucket-name}") - private String bucketName; - - // MultipartFile 이미지를 그대로 S3에 업로드 - public void uploadFile(String filename, MultipartFile file){ - try { - PutObjectRequest request = PutObjectRequest.builder() - .bucket(bucketName) - .key(filename) - .contentType(file.getContentType()) - .build(); - s3Client.putObject(request, RequestBody.fromInputStream(file.getInputStream(), file.getSize())); - } catch(IOException e){ - throw new CustomException(GlobalExceptionCode.FILE_PROCESS_ERROR); - } - } - -} From 5d54efcb5f9fe11921f960f14a86a4c82ee184af Mon Sep 17 00:00:00 2001 From: brothergiven Date: Tue, 18 Nov 2025 00:09:34 +0900 Subject: [PATCH 5/5] =?UTF-8?q?refact:=20AI=20WebClient=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81,=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/game/dto/AIErrorRes.java | 2 +- .../domain/game/service/AIClientService.java | 229 ++++++++---------- 2 files changed, 103 insertions(+), 128 deletions(-) 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(); } }