From 96cbd134725c90fc2a6efb20f2e9309a4ce0faf8 Mon Sep 17 00:00:00 2001 From: ht3064 <145987233+ht3064@users.noreply.github.com> Date: Mon, 1 Jul 2024 00:31:37 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20FastAPI=20=EC=84=9C=EB=B2=84=20api=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=EC=9D=84=20=ED=86=B5=ED=95=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EB=B6=84=EB=A5=98=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Feign 클라이언트 요청의 헤더를 확인하여 Content-Type 헤더를 설정 * feat: 이미지 분류 요청, 응답 dto 생성 * feat: FastAPI 서버 URL 및 엔드포인트 상수 정의 * feat: FastAPI 서버와의 통신을 위한 Feign 클라이언트 설정 * feat: 이미지 분류 기능 구현 * fix: fastapi 서버 민감한 정보 .env 파일에 관리 * fix: 이미지 API 상위 파라미터 RequestMapping으로 수정 * feat: task definition에 .env 파일에 작성한 환경 변수 추가 --- .../domain/image/api/ImageController.java | 12 +++++++- .../image/application/ImageService.java | 8 ++++++ .../request/ImageClassificationRequest.java | 24 ++++++++++++++++ .../dto/response/ClassifiedImageResponse.java | 15 ++++++++++ .../global/config/feign/FeignConfig.java | 28 +++++++++++++++++++ .../global/config/feign/KakaoFeignConfig.java | 15 ---------- .../config/fastapi/FastAPIProperties.java | 13 +++++++++ .../feign/ImageClassificationClient.java | 17 +++++++++++ .../infra/config/feign/KakaoLoginClient.java | 4 +-- .../config/properties/PropertiesConfig.java | 7 +++-- src/main/resources/application-fastapi.yml | 3 ++ src/main/resources/application.yml | 2 ++ task-definition.json | 8 ++++++ 13 files changed, 136 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/api/pickle/domain/image/dto/request/ImageClassificationRequest.java create mode 100644 src/main/java/com/api/pickle/domain/image/dto/response/ClassifiedImageResponse.java create mode 100644 src/main/java/com/api/pickle/global/config/feign/FeignConfig.java delete mode 100644 src/main/java/com/api/pickle/global/config/feign/KakaoFeignConfig.java create mode 100644 src/main/java/com/api/pickle/infra/config/fastapi/FastAPIProperties.java create mode 100644 src/main/java/com/api/pickle/infra/config/feign/ImageClassificationClient.java create mode 100644 src/main/resources/application-fastapi.yml diff --git a/src/main/java/com/api/pickle/domain/image/api/ImageController.java b/src/main/java/com/api/pickle/domain/image/api/ImageController.java index aff7603..ecea7fc 100644 --- a/src/main/java/com/api/pickle/domain/image/api/ImageController.java +++ b/src/main/java/com/api/pickle/domain/image/api/ImageController.java @@ -1,25 +1,35 @@ package com.api.pickle.domain.image.api; import com.api.pickle.domain.image.application.ImageService; +import com.api.pickle.domain.image.dto.request.ImageClassificationRequest; import com.api.pickle.domain.image.dto.request.PresignedUrlRequest; +import com.api.pickle.domain.image.dto.response.ClassifiedImageResponse; import com.api.pickle.domain.image.dto.response.PresignedUrlResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @Tag(name = "이미지 API", description = "이미지 API입니다.") @RestController @RequiredArgsConstructor +@RequestMapping("/images") public class ImageController { private final ImageService imageService; @Operation(summary = "이미지 Presigned URL 생성", description = "이미지 Presigned URL을 생성합니다.") - @PostMapping("/images/upload-url") + @PostMapping("/upload-url") public PresignedUrlResponse imagePresignedUrlCreate(@RequestBody PresignedUrlRequest request) { return imageService.createImagePresignedUrl(request); } + + @Operation(summary = "이미지 분류 결과", description = "이미지 분류 결과를 반환합니다.") + @PostMapping("/classify") + public ClassifiedImageResponse classifyImages(@RequestBody ImageClassificationRequest request) { + return imageService.classifyImages(request); + } } \ No newline at end of file diff --git a/src/main/java/com/api/pickle/domain/image/application/ImageService.java b/src/main/java/com/api/pickle/domain/image/application/ImageService.java index c43ebf1..a553a19 100644 --- a/src/main/java/com/api/pickle/domain/image/application/ImageService.java +++ b/src/main/java/com/api/pickle/domain/image/application/ImageService.java @@ -7,10 +7,13 @@ import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; import com.api.pickle.domain.album.dao.AlbumRepository; import com.api.pickle.domain.image.dao.ImageRepository; +import com.api.pickle.domain.image.dto.request.ImageClassificationRequest; import com.api.pickle.domain.image.dto.request.PresignedUrlRequest; +import com.api.pickle.domain.image.dto.response.ClassifiedImageResponse; import com.api.pickle.domain.image.dto.response.PresignedUrlResponse; import com.api.pickle.domain.member.domain.Member; import com.api.pickle.global.util.MemberUtil; +import com.api.pickle.infra.config.feign.ImageClassificationClient; import com.api.pickle.infra.config.s3.S3Properties; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -35,6 +38,7 @@ public class ImageService { private final AmazonS3 amazonS3; private final ImageRepository imageRepository; private final AlbumRepository albumRepository; + private final ImageClassificationClient imageClassificationClient; public PresignedUrlResponse createImagePresignedUrl(PresignedUrlRequest request) { final Member member = memberUtil.getCurrentMember(); @@ -92,4 +96,8 @@ private Date getPresignedUrlExpiration() { return expiration; } + + public ClassifiedImageResponse classifyImages(ImageClassificationRequest request) { + return imageClassificationClient.getClassifiedImages(request); + } } diff --git a/src/main/java/com/api/pickle/domain/image/dto/request/ImageClassificationRequest.java b/src/main/java/com/api/pickle/domain/image/dto/request/ImageClassificationRequest.java new file mode 100644 index 0000000..f96694b --- /dev/null +++ b/src/main/java/com/api/pickle/domain/image/dto/request/ImageClassificationRequest.java @@ -0,0 +1,24 @@ +package com.api.pickle.domain.image.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@AllArgsConstructor +@Getter +public class ImageClassificationRequest { + + @Schema(description = "업로드 한 이미지 URL 리스트") + private List imageUrls; + + @Schema(description = "그룹 간 유사도 설정 (true: 강하게, false: 약하게)") + private Boolean strongClustering; + + @Schema(description = "선명하지 않은 사진 제외 (true: 체크)") + private Boolean eyeClosing; + + @Schema(description = "눈 감은 사진 제외 (true: 체크)") + private Boolean blurred; +} diff --git a/src/main/java/com/api/pickle/domain/image/dto/response/ClassifiedImageResponse.java b/src/main/java/com/api/pickle/domain/image/dto/response/ClassifiedImageResponse.java new file mode 100644 index 0000000..f729015 --- /dev/null +++ b/src/main/java/com/api/pickle/domain/image/dto/response/ClassifiedImageResponse.java @@ -0,0 +1,15 @@ +package com.api.pickle.domain.image.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +public class ClassifiedImageResponse { + + private List> groupedImages; +} diff --git a/src/main/java/com/api/pickle/global/config/feign/FeignConfig.java b/src/main/java/com/api/pickle/global/config/feign/FeignConfig.java new file mode 100644 index 0000000..1bd30a4 --- /dev/null +++ b/src/main/java/com/api/pickle/global/config/feign/FeignConfig.java @@ -0,0 +1,28 @@ +package com.api.pickle.global.config.feign; + +import feign.RequestInterceptor; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Collections; + +@Configuration +@EnableFeignClients(basePackages = "com.api.pickle.infra.config.feign") +public class FeignConfig { + @Bean + public RequestInterceptor requestInterceptor() { + return template -> { + String contentType = template.headers() + .getOrDefault("Content-Type", Collections.emptyList()) + .stream() + .findFirst() + .orElse(""); + if (contentType.equals("application/json")) { + template.header("Content-Type", "application/json;charset=utf-8"); + } else { + template.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); + } + }; + } +} diff --git a/src/main/java/com/api/pickle/global/config/feign/KakaoFeignConfig.java b/src/main/java/com/api/pickle/global/config/feign/KakaoFeignConfig.java deleted file mode 100644 index c491338..0000000 --- a/src/main/java/com/api/pickle/global/config/feign/KakaoFeignConfig.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.api.pickle.global.config.feign; - -import feign.RequestInterceptor; -import org.springframework.cloud.openfeign.EnableFeignClients; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -@EnableFeignClients(basePackages = "com.api.pickle.infra.config.feign") -public class KakaoFeignConfig { - @Bean - public RequestInterceptor requestInterceptor() { - return template -> template.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); - } -} diff --git a/src/main/java/com/api/pickle/infra/config/fastapi/FastAPIProperties.java b/src/main/java/com/api/pickle/infra/config/fastapi/FastAPIProperties.java new file mode 100644 index 0000000..386ab92 --- /dev/null +++ b/src/main/java/com/api/pickle/infra/config/fastapi/FastAPIProperties.java @@ -0,0 +1,13 @@ +package com.api.pickle.infra.config.fastapi; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "fastapi") +@Getter +@Setter +public class FastAPIProperties { + private String serverUrl; + private String endpoint; +} diff --git a/src/main/java/com/api/pickle/infra/config/feign/ImageClassificationClient.java b/src/main/java/com/api/pickle/infra/config/feign/ImageClassificationClient.java new file mode 100644 index 0000000..536446a --- /dev/null +++ b/src/main/java/com/api/pickle/infra/config/feign/ImageClassificationClient.java @@ -0,0 +1,17 @@ +package com.api.pickle.infra.config.feign; + +import com.api.pickle.domain.image.dto.request.ImageClassificationRequest; +import com.api.pickle.domain.image.dto.response.ClassifiedImageResponse; +import com.api.pickle.global.config.feign.FeignConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@FeignClient( + name = "imageClassificationClient", + url = "${fastapi.serverUrl}", + configuration = FeignConfig.class) +public interface ImageClassificationClient { + @PostMapping(value = "${fastapi.endpoint}") + ClassifiedImageResponse getClassifiedImages(@RequestBody ImageClassificationRequest request); +} \ No newline at end of file diff --git a/src/main/java/com/api/pickle/infra/config/feign/KakaoLoginClient.java b/src/main/java/com/api/pickle/infra/config/feign/KakaoLoginClient.java index 9e13be4..52b9aeb 100644 --- a/src/main/java/com/api/pickle/infra/config/feign/KakaoLoginClient.java +++ b/src/main/java/com/api/pickle/infra/config/feign/KakaoLoginClient.java @@ -2,7 +2,7 @@ import com.api.pickle.domain.auth.dto.response.KakaoTokenResponse; import com.api.pickle.global.common.constants.SecurityConstants; -import com.api.pickle.global.config.feign.KakaoFeignConfig; +import com.api.pickle.global.config.feign.FeignConfig; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -10,7 +10,7 @@ @FeignClient( name = "kakaoLoginClient", url = SecurityConstants.KAKAO_LOGIN_URL, - configuration = KakaoFeignConfig.class) + configuration = FeignConfig.class) public interface KakaoLoginClient { @PostMapping(value = SecurityConstants.KAKAO_LOGIN_ENDPOINT) KakaoTokenResponse getToken(@RequestBody String KakaoTokenRequest); diff --git a/src/main/java/com/api/pickle/infra/config/properties/PropertiesConfig.java b/src/main/java/com/api/pickle/infra/config/properties/PropertiesConfig.java index 37bb655..e71c72f 100644 --- a/src/main/java/com/api/pickle/infra/config/properties/PropertiesConfig.java +++ b/src/main/java/com/api/pickle/infra/config/properties/PropertiesConfig.java @@ -1,5 +1,6 @@ package com.api.pickle.infra.config.properties; +import com.api.pickle.infra.config.fastapi.FastAPIProperties; import com.api.pickle.infra.config.jwt.JwtProperties; import com.api.pickle.infra.config.oauth.KakaoProperties; import com.api.pickle.infra.config.redis.RedisProperties; @@ -11,6 +12,8 @@ KakaoProperties.class, JwtProperties.class, S3Properties.class, - RedisProperties.class}) + RedisProperties.class, + FastAPIProperties.class}) @Configuration -public class PropertiesConfig {} +public class PropertiesConfig { +} diff --git a/src/main/resources/application-fastapi.yml b/src/main/resources/application-fastapi.yml new file mode 100644 index 0000000..7d72362 --- /dev/null +++ b/src/main/resources/application-fastapi.yml @@ -0,0 +1,3 @@ +fastapi: + serverUrl: ${FAST_API_SERVER_URL} + endpoint: ${FAST_API_SERVER_ENDPOINT} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e17d90d..04cca7a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -6,10 +6,12 @@ spring: - s3 - security - redis + - fastapi prod: - s3 - security - redis + - fastapi management: endpoints: diff --git a/task-definition.json b/task-definition.json index 21d1b20..0128743 100644 --- a/task-definition.json +++ b/task-definition.json @@ -86,6 +86,14 @@ { "name": "AWS_SECRET_KEY", "valueFrom": "arn:aws:secretsmanager:ap-northeast-2:122971469363:secret:playauto/gmp-2dXhTZ:AWS_SECRET_KEY::" + }, + { + "name": "FAST_API_SERVER_URL", + "valueFrom": "arn:aws:secretsmanager:ap-northeast-2:122971469363:secret:playauto/gmp-2dXhTZ:FAST_API_SERVER_URL::" + }, + { + "name": "FAST_API_SERVER_ENDPOINT", + "valueFrom": "arn:aws:secretsmanager:ap-northeast-2:122971469363:secret:playauto/gmp-2dXhTZ:FAST_API_SERVER_ENDPOINT::" } ], "ulimits": [],