From 590171bb511b3fea92b632ef49abda9acbaace1d Mon Sep 17 00:00:00 2001 From: choidongkuen Date: Mon, 5 Feb 2024 17:45:39 +0900 Subject: [PATCH 1/3] =?UTF-8?q?build=20:=20aws=20s3=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EB=B3=80=EA=B2=BD=20(#163)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 8780468..cfe19c1 100644 --- a/build.gradle +++ b/build.gradle @@ -60,9 +60,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-mail' // s3 - implementation platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.1.0") - implementation "io.awspring.cloud:spring-cloud-aws-starter-s3" - + implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.4' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' From 797ac9ee598918db1c781c1ba1e476995f0c5836 Mon Sep 17 00:00:00 2001 From: choidongkuen Date: Mon, 5 Feb 2024 17:46:12 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat=20:=20=EB=A9=94=EC=9D=B8=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20aws=20s3=20=EA=B4=80=EB=A0=A8=20=EC=A0=9C?= =?UTF-8?q?=EC=99=B8=20=EC=B6=94=EA=B0=80=20(#163)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/betteriter/BetterIterApplication.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/betteriter/BetterIterApplication.java b/src/main/java/com/example/betteriter/BetterIterApplication.java index 56fb759..5d069ac 100644 --- a/src/main/java/com/example/betteriter/BetterIterApplication.java +++ b/src/main/java/com/example/betteriter/BetterIterApplication.java @@ -3,7 +3,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -@SpringBootApplication +@SpringBootApplication( + exclude = { + io.awspring.cloud.autoconfigure.context.ContextInstanceDataAutoConfiguration.class, + io.awspring.cloud.autoconfigure.context.ContextStackAutoConfiguration.class, + io.awspring.cloud.autoconfigure.context.ContextRegionProviderAutoConfiguration.class + } +) public class BetterIterApplication { public static void main(String[] args) { From d915dcb7d34e014faa6694ce72a1cde285bd7950 Mon Sep 17 00:00:00 2001 From: choidongkuen Date: Mon, 5 Feb 2024 17:46:35 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat=20:=20aws=20s3=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?+=20=EB=B3=80=EA=B2=BD=20(#163)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/code/status/ErrorStatus.java | 2 +- .../config/properties/JwtProperties.java | 3 +- .../config/test/TestSecurityConfig.java | 14 ----- .../global/config/web/WebConfig.java | 5 +- .../example/betteriter/infra/s3/S3Config.java | 34 +++++++++++++ .../betteriter/infra/s3/S3Service.java | 26 ++++++---- .../example/betteriter/global/ConfigTest.java | 51 +++++++++++++++++++ 7 files changed, 108 insertions(+), 27 deletions(-) delete mode 100644 src/main/java/com/example/betteriter/global/config/test/TestSecurityConfig.java create mode 100644 src/main/java/com/example/betteriter/infra/s3/S3Config.java create mode 100644 src/test/java/com/example/betteriter/global/ConfigTest.java diff --git a/src/main/java/com/example/betteriter/global/common/code/status/ErrorStatus.java b/src/main/java/com/example/betteriter/global/common/code/status/ErrorStatus.java index 4c6267f..ab8d8da 100644 --- a/src/main/java/com/example/betteriter/global/common/code/status/ErrorStatus.java +++ b/src/main/java/com/example/betteriter/global/common/code/status/ErrorStatus.java @@ -70,6 +70,7 @@ public enum ErrorStatus implements BaseErrorCode { _IMAGE_FILE_UPLOAD_FAILED(HttpStatus.BAD_REQUEST, "IMAGE_FILE_UPLOAD_FAILED", "이미지 파일 업로드에 실패했습니다."), _IMAGE_FILE_UPLOAD_REQUEST_IS_NOT_VALID(HttpStatus.BAD_REQUEST, "IMAGE_FILE_UPLOAD_IS_NOT_VALID", "이미지 파일 업로드 요청 형식이 올바르지 않습니다."), + _IMAGE_FILE_IS_NOT_EXIST(HttpStatus.BAD_REQUEST, "IMAGE_FILE_IS_NOT_EXISTED", "이미지 파일이 존재하지 않습니다."), // REVIEW_LIKE _REVIEW_LIKE_NOT_FOUND(HttpStatus.BAD_REQUEST, "REVIEW_LIKE_NOT_FOUND_400", "일치하는 리뷰 좋아요 정보를 찾을 수 없습니다."), @@ -93,7 +94,6 @@ public enum ErrorStatus implements BaseErrorCode { _FOLLOW_ALREADY(HttpStatus.BAD_REQUEST, "FOLLOW_ALREADY_400", "이미 팔로우한 유저입니다."), _FOLLOW_NOT_MATCH(HttpStatus.BAD_REQUEST, "FOLLOW_NOT_MATCH_400", "팔로우한 유저가 아닙니다."), - ; diff --git a/src/main/java/com/example/betteriter/global/config/properties/JwtProperties.java b/src/main/java/com/example/betteriter/global/config/properties/JwtProperties.java index 2fe93c6..9cbaaa9 100644 --- a/src/main/java/com/example/betteriter/global/config/properties/JwtProperties.java +++ b/src/main/java/com/example/betteriter/global/config/properties/JwtProperties.java @@ -5,11 +5,12 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConstructorBinding; -@ConfigurationProperties(prefix = "jwt") @Getter @ConstructorBinding @AllArgsConstructor +@ConfigurationProperties(prefix = "jwt") public class JwtProperties { + private String bearer; private String secret; private String accessHeader; diff --git a/src/main/java/com/example/betteriter/global/config/test/TestSecurityConfig.java b/src/main/java/com/example/betteriter/global/config/test/TestSecurityConfig.java deleted file mode 100644 index c6b188a..0000000 --- a/src/main/java/com/example/betteriter/global/config/test/TestSecurityConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.example.betteriter.global.config.test; - -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - -public class TestSecurityConfig extends WebSecurityConfigurerAdapter { - @Override - protected void configure(HttpSecurity httpSecurity) throws Exception { - httpSecurity - .csrf().disable() - .authorizeRequests() - .anyRequest().permitAll(); - } -} diff --git a/src/main/java/com/example/betteriter/global/config/web/WebConfig.java b/src/main/java/com/example/betteriter/global/config/web/WebConfig.java index 1d41dc0..a4acd6e 100644 --- a/src/main/java/com/example/betteriter/global/config/web/WebConfig.java +++ b/src/main/java/com/example/betteriter/global/config/web/WebConfig.java @@ -7,6 +7,7 @@ /* config.properties 패키지 내부에 모든 설정 클래스 위치 */ @Configuration @EnableJpaAuditing -@ConfigurationPropertiesScan("com.example.betteriter.global.config.properties") +@ConfigurationPropertiesScan({"com.example.betteriter.global.config.properties"}) public class WebConfig { -} \ No newline at end of file + +} diff --git a/src/main/java/com/example/betteriter/infra/s3/S3Config.java b/src/main/java/com/example/betteriter/infra/s3/S3Config.java new file mode 100644 index 0000000..678c540 --- /dev/null +++ b/src/main/java/com/example/betteriter/infra/s3/S3Config.java @@ -0,0 +1,34 @@ +package com.example.betteriter.infra.s3; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Getter +@Configuration +public class S3Config { + + @Value("${spring.cloud.aws.credentials.access-key}") + private String accessKey; + + @Value("${spring.cloud.aws.credentials.secret-key}") + private String accessSecret; + + @Value("${spring.cloud.aws.region.static}") + private String region; + + @Bean + public AmazonS3 s3Client() { + AWSCredentials credentials = new BasicAWSCredentials(accessKey, accessSecret); + return AmazonS3ClientBuilder.standard() + .withCredentials(new AWSStaticCredentialsProvider(credentials)) + .withRegion(region).build(); + } + +} diff --git a/src/main/java/com/example/betteriter/infra/s3/S3Service.java b/src/main/java/com/example/betteriter/infra/s3/S3Service.java index 70eef82..8a6320e 100644 --- a/src/main/java/com/example/betteriter/infra/s3/S3Service.java +++ b/src/main/java/com/example/betteriter/infra/s3/S3Service.java @@ -1,8 +1,12 @@ package com.example.betteriter.infra.s3; +import static com.example.betteriter.global.common.code.status.ErrorStatus._IMAGE_FILE_IS_NOT_EXIST; import static com.example.betteriter.global.common.code.status.ErrorStatus._IMAGE_FILE_NAME_IS_NOT_EXIST; import static com.example.betteriter.global.common.code.status.ErrorStatus._IMAGE_FILE_UPLOAD_FAILED; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; import com.example.betteriter.fo_domain.review.domain.Review; import com.example.betteriter.fo_domain.review.domain.ReviewImage; import com.example.betteriter.fo_domain.review.exception.ReviewHandler; @@ -14,9 +18,7 @@ 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; + @Slf4j @Service @@ -25,7 +27,7 @@ public class S3Service implements ImageUploadService { private static final String FOLDER = "iter"; - private final S3Client s3Client; + private final AmazonS3 s3Client; @Value("${spring.cloud.aws.s3.bucket}") private String bucketName; @@ -39,13 +41,13 @@ public ReviewImage uploadImage(MultipartFile image, Review review, int orderNum) String fileName = UUID.randomUUID().toString(); String key = FOLDER + "/" + review.getId().toString() + "/" + fileName + fileExtension; - PutObjectRequest request = PutObjectRequest.builder() - .bucket(bucketName) - .key(key) - .build(); + ObjectMetadata objectMetaData = new ObjectMetadata(); + objectMetaData.setContentType(image.getContentType()); try (InputStream inputStream = image.getInputStream()) { - s3Client.putObject(request, RequestBody.fromInputStream(inputStream, image.getSize())); + + s3Client.putObject(new PutObjectRequest(bucketName, key, inputStream, objectMetaData)); + } catch (Exception e) { throw new ReviewHandler(_IMAGE_FILE_UPLOAD_FAILED); } @@ -57,6 +59,12 @@ public ReviewImage uploadImage(MultipartFile image, Review review, int orderNum) .build(); } + private void validateImageFileExists(MultipartFile multipartFile) { + if (multipartFile.isEmpty()) { + throw new ReviewHandler(_IMAGE_FILE_IS_NOT_EXIST); + } + } + private String getImageUrl(String key) { return "https://" + bucketName + ".s3.amazonaws.com/" + key; diff --git a/src/test/java/com/example/betteriter/global/ConfigTest.java b/src/test/java/com/example/betteriter/global/ConfigTest.java new file mode 100644 index 0000000..e47a3de --- /dev/null +++ b/src/test/java/com/example/betteriter/global/ConfigTest.java @@ -0,0 +1,51 @@ +package com.example.betteriter.global; + +import com.example.betteriter.infra.s3.S3Config; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.test.context.ContextConfiguration; + +@DisplayName("S3Config 설정 클래스는") +@ContextConfiguration(classes = {S3Config.class, ConfigTest.PropertyPlaceholderConfig.class}) +public class ConfigTest { + + @Autowired + private S3Config s3Config; + + @TestConfiguration + static class PropertyPlaceholderConfig { + + @Bean + public static PropertySourcesPlaceholderConfigurer propertiesResolver() { + return new PropertySourcesPlaceholderConfigurer(); + } + } + + @Nested + @DisplayName("각 필드 값을") + class Get_s3_config_values { + + private String accessKey; + private String accessSecret; + + @BeforeEach + void setUp() { + accessKey = s3Config.getAccessKey(); + accessSecret = s3Config.getAccessSecret(); + } + + @Test + @DisplayName("정상적으로 가져온다.") + void With_successful() { + // then + System.out.println("accessKey = " + accessKey); + System.out.println("accessSecret = " + accessSecret); + } + } +}