From 6de3df4ae1329d6891825904b188ac0f7067e70a Mon Sep 17 00:00:00 2001 From: kwonyj1022 Date: Tue, 17 Oct 2023 17:00:53 +0900 Subject: [PATCH 01/12] =?UTF-8?q?chore:=20aws,=20s3,=20cloudfront=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/ddang/build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/ddang/build.gradle b/backend/ddang/build.gradle index ccbc8a1e9..ea2d027f9 100644 --- a/backend/ddang/build.gradle +++ b/backend/ddang/build.gradle @@ -67,6 +67,11 @@ dependencies { implementation 'ch.qos.logback.contrib:logback-json-classic:0.1.5' implementation 'net.logstash.logback:logstash-logback-encoder:6.1' + // aws + implementation platform('software.amazon.awssdk:bom:2.20.56') + implementation 'software.amazon.awssdk:s3' + implementation 'software.amazon.awssdk:cloudfront' + implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' From 05f6984d5ead69e237884b8508a367b125e29854 Mon Sep 17 00:00:00 2001 From: kwonyj1022 Date: Tue, 17 Oct 2023 17:01:05 +0900 Subject: [PATCH 02/12] =?UTF-8?q?feat:=20AWS=20S3=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configuration/AwsConfiguration.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 backend/ddang/src/main/java/com/ddang/ddang/auction/configuration/AwsConfiguration.java diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/configuration/AwsConfiguration.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/configuration/AwsConfiguration.java new file mode 100644 index 000000000..c2d89ace1 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/configuration/AwsConfiguration.java @@ -0,0 +1,25 @@ +package com.ddang.ddang.auction.configuration; + +import com.google.api.client.util.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +@Profile("!local && !test") +@Configuration +public class AwsConfiguration { + + @Value("${aws.s3.region}") + private String s3Region; + + @Bean + public S3Client s3Client() { + return S3Client.builder() + .region(Region.of(s3Region)) + .credentialsProvider(InstanceProfileCredentialsProvider.create()) + .build(); + } +} From d930f359ab1b3eadef06223d7bf551d8b9bf88cc Mon Sep 17 00:00:00 2001 From: kwonyj1022 Date: Tue, 17 Oct 2023 17:03:25 +0900 Subject: [PATCH 03/12] =?UTF-8?q?feat:=20StoreImageProcessor=20=EC=83=81?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../image/domain/StoreImageProcessor.java | 7 +++++-- .../local/LocalStoreImageProcessor.java | 21 +++++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/domain/StoreImageProcessor.java b/backend/ddang/src/main/java/com/ddang/ddang/image/domain/StoreImageProcessor.java index 19158bfd1..5485586f2 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/domain/StoreImageProcessor.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/domain/StoreImageProcessor.java @@ -7,7 +7,10 @@ public interface StoreImageProcessor { - StoreImageDto storeImageFile(MultipartFile imageFile); + List WHITE_IMAGE_EXTENSION = List.of("jpg", "jpeg", "png"); + String EXTENSION_FILE_CHARACTER = "."; - List storeImageFiles(List imageFiles); + StoreImageDto storeImageFile(final MultipartFile imageFile); + + List storeImageFiles(final List imageFiles); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java index 136b348a4..221cd71a2 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java @@ -5,21 +5,19 @@ import com.ddang.ddang.image.infrastructure.local.exception.EmptyImageException; import com.ddang.ddang.image.infrastructure.local.exception.StoreImageFailureException; import com.ddang.ddang.image.infrastructure.local.exception.UnsupportedImageFileExtensionException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.web.multipart.MultipartFile; @Component public class LocalStoreImageProcessor implements StoreImageProcessor { - private static final List WHITE_IMAGE_EXTENSION = List.of("jpg", "jpeg", "png"); - private static final String EXTENSION_FILE_CHARACTER = "."; - @Value("${image.store.dir}") private String imageStoreDir; @@ -38,7 +36,8 @@ public List storeImageFiles(final List imageFiles) return storeImageDtos; } - public StoreImageDto storeImageFile(MultipartFile imageFile) { + @Override + public StoreImageDto storeImageFile(final MultipartFile imageFile) { try { final String originalImageFileName = imageFile.getOriginalFilename(); final String storeImageFileName = createStoreImageFileName(originalImageFileName); @@ -47,16 +46,16 @@ public StoreImageDto storeImageFile(MultipartFile imageFile) { imageFile.transferTo(new File(fullPath)); return new StoreImageDto(originalImageFileName, storeImageFileName); - } catch (IOException ex) { + } catch (final IOException ex) { throw new StoreImageFailureException("이미지 저장에 실패했습니다.", ex); } } - private String findFullPath(String storeImageFileName) { + private String findFullPath(final String storeImageFileName) { return imageStoreDir + storeImageFileName; } - private String createStoreImageFileName(String originalFilename) { + private String createStoreImageFileName(final String originalFilename) { final String extension = extractExtension(originalFilename); validateImageFileExtension(extension); @@ -66,7 +65,7 @@ private String createStoreImageFileName(String originalFilename) { return uuid + EXTENSION_FILE_CHARACTER + extension; } - private String extractExtension(String originalFilename) { + private String extractExtension(final String originalFilename) { int position = originalFilename.lastIndexOf(EXTENSION_FILE_CHARACTER); return originalFilename.substring(position + 1); From ac9ccc992ee414b932af2375ed384f1fa5a8056b Mon Sep 17 00:00:00 2001 From: kwonyj1022 Date: Tue, 17 Oct 2023 17:07:42 +0900 Subject: [PATCH 04/12] =?UTF-8?q?feat:=20S3=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../local/LocalStoreImageProcessor.java | 2 + .../s3/S3StoreImageProcessor.java | 101 ++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessor.java diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java index 221cd71a2..5c1f0a716 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java @@ -6,6 +6,7 @@ import com.ddang.ddang.image.infrastructure.local.exception.StoreImageFailureException; import com.ddang.ddang.image.infrastructure.local.exception.UnsupportedImageFileExtensionException; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; @@ -16,6 +17,7 @@ import java.util.UUID; @Component +@Profile("local || test") public class LocalStoreImageProcessor implements StoreImageProcessor { @Value("${image.store.dir}") diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessor.java b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessor.java new file mode 100644 index 000000000..2250220a1 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessor.java @@ -0,0 +1,101 @@ +package com.ddang.ddang.image.infrastructure.s3; + +import com.ddang.ddang.image.domain.StoreImageProcessor; +import com.ddang.ddang.image.domain.dto.StoreImageDto; +import com.ddang.ddang.image.infrastructure.local.exception.EmptyImageException; +import com.ddang.ddang.image.infrastructure.local.exception.StoreImageFailureException; +import com.ddang.ddang.image.infrastructure.local.exception.UnsupportedImageFileExtensionException; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Component +@Profile("!local && !test") +@RequiredArgsConstructor +public class S3StoreImageProcessor implements StoreImageProcessor { + + @Value("${aws.s3.bucket-name}") + private String bucketName; + + @Value("${aws.s3.image-path}") + private String path; + + private final S3Client s3Client; + + @Override + public List storeImageFiles(final List imageFiles) { + final List storeImageDtos = new ArrayList<>(); + + for (final MultipartFile imageFile : imageFiles) { + if (imageFile.isEmpty()) { + throw new EmptyImageException("이미지 파일의 데이터가 비어 있습니다."); + } + + storeImageDtos.add(storeImageFile(imageFile)); + } + + return storeImageDtos; + } + + @Override + public StoreImageDto storeImageFile(final MultipartFile imageFile) { + try { + final String originalImageFileName = imageFile.getOriginalFilename(); + final String storeImageFileName = createStoreImageFileName(originalImageFileName); + final String fullPath = findFullPath(storeImageFileName); + final PutObjectRequest putObjectRequest = PutObjectRequest.builder() + .key(fullPath) + .bucket(bucketName) + .contentType(imageFile.getContentType()) + .build(); + + s3Client.putObject( + putObjectRequest, + RequestBody.fromInputStream(imageFile.getInputStream(), imageFile.getSize()) + ); + + return new StoreImageDto(originalImageFileName, storeImageFileName); + } catch (final IOException ex) { + throw new StoreImageFailureException("이미지 저장에 실패했습니다.", ex); + } catch (final SdkException ex) { + throw new StoreImageFailureException("AWS 이미지 저장에 실패했습니다.", ex); + } + } + + private String findFullPath(final String storeImageFileName) { + return path + storeImageFileName; + } + + private String createStoreImageFileName(final String originalFilename) { + final String extension = extractExtension(originalFilename); + + validateImageFileExtension(extension); + + final String uuid = UUID.randomUUID().toString(); + + return uuid + EXTENSION_FILE_CHARACTER + extension; + } + + private String extractExtension(final String originalFilename) { + int position = originalFilename.lastIndexOf(EXTENSION_FILE_CHARACTER); + + return originalFilename.substring(position + 1); + } + + private void validateImageFileExtension(final String extension) { + if (!WHITE_IMAGE_EXTENSION.contains(extension)) { + throw new UnsupportedImageFileExtensionException("지원하지 않는 확장자입니다. : " + extension); + } + } +} From a831c1f4ce04b12b4f598d2075e07d1bc6b4da76 Mon Sep 17 00:00:00 2001 From: kwonyj1022 Date: Tue, 17 Oct 2023 17:08:34 +0900 Subject: [PATCH 05/12] =?UTF-8?q?test:=20S3=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EA=B8=B0=EB=8A=A5=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ddang/configuration/AwsConfiguration.java | 17 +++ .../s3/S3StoreImageProcessorTest.java | 106 ++++++++++++++++++ .../fixture/S3StoreImageProcessorFixture.java | 14 +++ .../ddang/src/test/resources/application.yml | 5 + 4 files changed, 142 insertions(+) create mode 100644 backend/ddang/src/test/java/com/ddang/ddang/configuration/AwsConfiguration.java create mode 100644 backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessorTest.java create mode 100644 backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/s3/fixture/S3StoreImageProcessorFixture.java diff --git a/backend/ddang/src/test/java/com/ddang/ddang/configuration/AwsConfiguration.java b/backend/ddang/src/test/java/com/ddang/ddang/configuration/AwsConfiguration.java new file mode 100644 index 000000000..8982d9067 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/configuration/AwsConfiguration.java @@ -0,0 +1,17 @@ +package com.ddang.ddang.configuration; + +import org.mockito.Mockito; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import software.amazon.awssdk.services.s3.S3Client; + +@Configuration +@Profile("test") +public class AwsConfiguration { + + @Bean + public S3Client s3Client() { + return Mockito.mock(S3Client.class); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessorTest.java b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessorTest.java new file mode 100644 index 000000000..635ad022b --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessorTest.java @@ -0,0 +1,106 @@ +package com.ddang.ddang.image.infrastructure.s3; + +import com.ddang.ddang.image.domain.dto.StoreImageDto; +import com.ddang.ddang.image.infrastructure.local.exception.EmptyImageException; +import com.ddang.ddang.image.infrastructure.local.exception.StoreImageFailureException; +import com.ddang.ddang.image.infrastructure.local.exception.UnsupportedImageFileExtensionException; +import com.ddang.ddang.image.infrastructure.s3.fixture.S3StoreImageProcessorFixture; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import software.amazon.awssdk.core.exception.SdkException; +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.PutObjectResponse; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; + +@ExtendWith({MockitoExtension.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class S3StoreImageProcessorTest extends S3StoreImageProcessorFixture { + + @InjectMocks + S3StoreImageProcessor imageProcessor; + + @Mock + S3Client s3Client; + + @Test + void 이미지_파일이_비어_있는_경우_예외가_발생한다() { + // when & then + assertThatThrownBy(() -> imageProcessor.storeImageFiles(List.of(빈_이미지_파일))) + .isInstanceOf(EmptyImageException.class) + .hasMessage("이미지 파일의 데이터가 비어 있습니다."); + } + + @Test + void 허용되지_않은_확장자의_이미지_파일인_경우_예외가_발생한다() { + // given + given(이미지_파일.getOriginalFilename()).willReturn(지원하지_않는_확장자를_가진_이미지_파일명); + + // when & then + assertThatThrownBy(() -> imageProcessor.storeImageFiles(List.of(이미지_파일))) + .isInstanceOf(UnsupportedImageFileExtensionException.class) + .hasMessageContaining("지원하지 않는 확장자입니다."); + } + + @Test + void 이미지_저장에_실패한_경우_예외가_발생한다() throws IOException { + // given + given(이미지_파일.getOriginalFilename()).willReturn(기존_이미지_파일명); + given(이미지_파일.getInputStream()).willThrow(new IOException()); + + // when & then + assertThatThrownBy(() -> imageProcessor.storeImageFiles(List.of(이미지_파일))) + .isInstanceOf(StoreImageFailureException.class) + .hasMessage("이미지 저장에 실패했습니다."); + } + + @Test + void AWS_이미지_저장에_실패한_경우_예외가_발생한다() throws IOException { + // given + final ByteArrayInputStream fakeInputStream = new ByteArrayInputStream("가짜 이미지 데이터".getBytes()); + given(이미지_파일.getOriginalFilename()).willReturn(기존_이미지_파일명); + given(이미지_파일.getInputStream()).willReturn(fakeInputStream); + given(s3Client.putObject(any(PutObjectRequest.class), any(RequestBody.class))) + .willThrow(SdkException.class); + + // when & then + assertThatThrownBy(() -> imageProcessor.storeImageFiles(List.of(이미지_파일))) + .isInstanceOf(StoreImageFailureException.class) + .hasMessage("AWS 이미지 저장에 실패했습니다."); + } + + @Test + void 유효한_이미지_파일인_경우_이미지_파일을_저장한다() throws Exception { + // given + final ByteArrayInputStream fakeInputStream = new ByteArrayInputStream("가짜 이미지 데이터".getBytes()); + given(이미지_파일.getOriginalFilename()).willReturn(기존_이미지_파일명); + given(이미지_파일.getInputStream()).willReturn(fakeInputStream); + given(s3Client.putObject(any(PutObjectRequest.class), any(RequestBody.class))) + .willReturn(PutObjectResponse.builder().build()); + + // when + final List actual = imageProcessor.storeImageFiles(List.of(이미지_파일)); + + // then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).hasSize(1); + softAssertions.assertThat(actual.get(0).storeName()).isNotBlank(); + softAssertions.assertThat(actual.get(0).uploadName()).isEqualTo(기존_이미지_파일명); + }); + } +} diff --git a/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/s3/fixture/S3StoreImageProcessorFixture.java b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/s3/fixture/S3StoreImageProcessorFixture.java new file mode 100644 index 000000000..662463161 --- /dev/null +++ b/backend/ddang/src/test/java/com/ddang/ddang/image/infrastructure/s3/fixture/S3StoreImageProcessorFixture.java @@ -0,0 +1,14 @@ +package com.ddang.ddang.image.infrastructure.s3.fixture; + +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.web.multipart.MultipartFile; + +import static org.mockito.Mockito.mock; + +public class S3StoreImageProcessorFixture { + + protected MockMultipartFile 빈_이미지_파일 = new MockMultipartFile("image.png", new byte[0]); + protected MultipartFile 이미지_파일 = mock(MultipartFile.class); + protected String 기존_이미지_파일명 = "image.png"; + protected String 지원하지_않는_확장자를_가진_이미지_파일명 = "image.gif"; +} diff --git a/backend/ddang/src/test/resources/application.yml b/backend/ddang/src/test/resources/application.yml index 1b0aeb253..860462236 100644 --- a/backend/ddang/src/test/resources/application.yml +++ b/backend/ddang/src/test/resources/application.yml @@ -49,3 +49,8 @@ fcm: enabled: false key: path: firebase/private-key.json + +aws: + s3: + bucket-name: awsbucketname + image-path: image/path From 0b663d3a31d3e12844c47d175d68fc838be260a4 Mon Sep 17 00:00:00 2001 From: kwonyj1022 Date: Tue, 17 Oct 2023 17:09:54 +0900 Subject: [PATCH 06/12] =?UTF-8?q?feat:=20S3=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EA=B8=B0=EB=8A=A5=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9D=84=20=EC=A0=9C=EC=96=B4=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8A=94=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../image/infrastructure/local/LocalStoreImageProcessor.java | 4 ++-- .../ddang/image/infrastructure/s3/S3StoreImageProcessor.java | 2 ++ backend/ddang/src/test/resources/application.yml | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java index 5c1f0a716..0258e4ee8 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java @@ -6,7 +6,7 @@ import com.ddang.ddang.image.infrastructure.local.exception.StoreImageFailureException; import com.ddang.ddang.image.infrastructure.local.exception.UnsupportedImageFileExtensionException; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Profile; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; @@ -17,7 +17,7 @@ import java.util.UUID; @Component -@Profile("local || test") +@ConditionalOnProperty(name = "aws.s3.enabled", havingValue = "false") public class LocalStoreImageProcessor implements StoreImageProcessor { @Value("${image.store.dir}") diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessor.java b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessor.java index 2250220a1..5b0246998 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessor.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessor.java @@ -7,6 +7,7 @@ import com.ddang.ddang.image.infrastructure.local.exception.UnsupportedImageFileExtensionException; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; @@ -22,6 +23,7 @@ @Component @Profile("!local && !test") +@ConditionalOnProperty(name = "aws.s3.enabled", havingValue = "true") @RequiredArgsConstructor public class S3StoreImageProcessor implements StoreImageProcessor { diff --git a/backend/ddang/src/test/resources/application.yml b/backend/ddang/src/test/resources/application.yml index 860462236..974058081 100644 --- a/backend/ddang/src/test/resources/application.yml +++ b/backend/ddang/src/test/resources/application.yml @@ -52,5 +52,6 @@ fcm: aws: s3: + enabled: true bucket-name: awsbucketname image-path: image/path From c6f1ad12ed2d132bd3c8643738e15fc220ea25a1 Mon Sep 17 00:00:00 2001 From: kwonyj1022 Date: Tue, 17 Oct 2023 23:06:21 +0900 Subject: [PATCH 07/12] =?UTF-8?q?feat:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=99=98=EA=B2=BD=EC=97=90=EC=84=9C=20S3=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9D=84=20=EC=A0=9C=EC=96=B4=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8A=94=20=EC=84=A4=EC=A0=95=EC=9D=98=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=EA=B0=92=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/ddang/src/test/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/ddang/src/test/resources/application.yml b/backend/ddang/src/test/resources/application.yml index 974058081..e1f69ed2c 100644 --- a/backend/ddang/src/test/resources/application.yml +++ b/backend/ddang/src/test/resources/application.yml @@ -52,6 +52,6 @@ fcm: aws: s3: - enabled: true + enabled: false bucket-name: awsbucketname image-path: image/path From 3ce395bd0e4e91948de72b2073a99eddaae0b807 Mon Sep 17 00:00:00 2001 From: kwonyj1022 Date: Wed, 18 Oct 2023 23:32:42 +0900 Subject: [PATCH 08/12] =?UTF-8?q?remove:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=9A=A9=20AwsConfiguration=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ddang/configuration/AwsConfiguration.java | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 backend/ddang/src/test/java/com/ddang/ddang/configuration/AwsConfiguration.java diff --git a/backend/ddang/src/test/java/com/ddang/ddang/configuration/AwsConfiguration.java b/backend/ddang/src/test/java/com/ddang/ddang/configuration/AwsConfiguration.java deleted file mode 100644 index 8982d9067..000000000 --- a/backend/ddang/src/test/java/com/ddang/ddang/configuration/AwsConfiguration.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.ddang.ddang.configuration; - -import org.mockito.Mockito; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import software.amazon.awssdk.services.s3.S3Client; - -@Configuration -@Profile("test") -public class AwsConfiguration { - - @Bean - public S3Client s3Client() { - return Mockito.mock(S3Client.class); - } -} From 2c657704e3f982d0579298de687cb82de6b182d5 Mon Sep 17 00:00:00 2001 From: kwonyj1022 Date: Wed, 18 Oct 2023 23:38:35 +0900 Subject: [PATCH 09/12] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EB=8D=95?= =?UTF-8?q?=EC=85=98=20=ED=99=98=EA=B2=BD=EC=97=90=EC=84=9C=EB=A7=8C=20?= =?UTF-8?q?=EB=8F=99=EC=9E=91=ED=95=98=EB=8A=94=20Profile=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auction/configuration/AwsConfiguration.java | 4 ++-- .../ddang/ddang/configuration/ProductProfile.java | 14 ++++++++++++++ .../configuration/fcm/ProdFcmConfiguration.java | 4 ++-- .../infrastructure/s3/S3StoreImageProcessor.java | 4 ++-- 4 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 backend/ddang/src/main/java/com/ddang/ddang/configuration/ProductProfile.java diff --git a/backend/ddang/src/main/java/com/ddang/ddang/auction/configuration/AwsConfiguration.java b/backend/ddang/src/main/java/com/ddang/ddang/auction/configuration/AwsConfiguration.java index c2d89ace1..f2b829e96 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/auction/configuration/AwsConfiguration.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/auction/configuration/AwsConfiguration.java @@ -1,14 +1,14 @@ package com.ddang.ddang.auction.configuration; +import com.ddang.ddang.configuration.ProductProfile; import com.google.api.client.util.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; import software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; -@Profile("!local && !test") +@ProductProfile @Configuration public class AwsConfiguration { diff --git a/backend/ddang/src/main/java/com/ddang/ddang/configuration/ProductProfile.java b/backend/ddang/src/main/java/com/ddang/ddang/configuration/ProductProfile.java new file mode 100644 index 000000000..99ffc02a4 --- /dev/null +++ b/backend/ddang/src/main/java/com/ddang/ddang/configuration/ProductProfile.java @@ -0,0 +1,14 @@ +package com.ddang.ddang.configuration; + +import org.springframework.context.annotation.Profile; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Profile("!local && !test") +public @interface ProductProfile { +} diff --git a/backend/ddang/src/main/java/com/ddang/ddang/configuration/fcm/ProdFcmConfiguration.java b/backend/ddang/src/main/java/com/ddang/ddang/configuration/fcm/ProdFcmConfiguration.java index ac0848a4b..89e69f731 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/configuration/fcm/ProdFcmConfiguration.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/configuration/fcm/ProdFcmConfiguration.java @@ -1,5 +1,6 @@ package com.ddang.ddang.configuration.fcm; +import com.ddang.ddang.configuration.ProductProfile; import com.ddang.ddang.configuration.fcm.exception.FcmNotFoundException; import com.google.auth.oauth2.GoogleCredentials; import com.google.firebase.FirebaseApp; @@ -13,10 +14,9 @@ import java.io.IOException; import java.io.InputStream; import java.util.List; -import org.springframework.context.annotation.Profile; @Configuration -@Profile("!test && !local") +@ProductProfile public class ProdFcmConfiguration { @Value("${fcm.key.path}") diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessor.java b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessor.java index 5b0246998..a7e4eaffe 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessor.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/s3/S3StoreImageProcessor.java @@ -1,5 +1,6 @@ package com.ddang.ddang.image.infrastructure.s3; +import com.ddang.ddang.configuration.ProductProfile; import com.ddang.ddang.image.domain.StoreImageProcessor; import com.ddang.ddang.image.domain.dto.StoreImageDto; import com.ddang.ddang.image.infrastructure.local.exception.EmptyImageException; @@ -8,7 +9,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import software.amazon.awssdk.core.exception.SdkException; @@ -22,7 +22,7 @@ import java.util.UUID; @Component -@Profile("!local && !test") +@ProductProfile @ConditionalOnProperty(name = "aws.s3.enabled", havingValue = "true") @RequiredArgsConstructor public class S3StoreImageProcessor implements StoreImageProcessor { From c1e510f19629df7300945b92232a4fd81cefc797 Mon Sep 17 00:00:00 2001 From: kwonyj1022 Date: Fri, 20 Oct 2023 01:18:31 +0900 Subject: [PATCH 10/12] =?UTF-8?q?test:=20=EB=88=84=EB=9D=BD=EB=90=9C=20s3?= =?UTF-8?q?=20=EC=A7=80=EC=97=AD=20=EC=84=A4=EC=A0=95=EA=B0=92=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/ddang/src/test/resources/application.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/ddang/src/test/resources/application.yml b/backend/ddang/src/test/resources/application.yml index e1f69ed2c..875006e98 100644 --- a/backend/ddang/src/test/resources/application.yml +++ b/backend/ddang/src/test/resources/application.yml @@ -53,5 +53,6 @@ fcm: aws: s3: enabled: false + region: region bucket-name: awsbucketname image-path: image/path From eedf6e2accf87c2243ed327a95ff2e918991510b Mon Sep 17 00:00:00 2001 From: kwonyj1022 Date: Fri, 20 Oct 2023 01:20:08 +0900 Subject: [PATCH 11/12] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EB=8D=95?= =?UTF-8?q?=EC=85=98=20=ED=99=98=EA=B2=BD=EC=97=90=20s3=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20yml=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/ddang/src/main/resources/application-local.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backend/ddang/src/main/resources/application-local.yml b/backend/ddang/src/main/resources/application-local.yml index 2db9e094b..8cc4dbc78 100644 --- a/backend/ddang/src/main/resources/application-local.yml +++ b/backend/ddang/src/main/resources/application-local.yml @@ -57,3 +57,10 @@ fcm: enabled: false key: path: firebase/private-key.json + +aws: + s3: + enabled: false + region: region + bucket-name: awsbucketname + image-path: image/path From 33963e2c782a0375aacba8c3035efb5475813fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=EC=98=88=EC=A7=84?= <96688810+kwonyj1022@users.noreply.github.com> Date: Fri, 3 Nov 2023 11:01:59 +0900 Subject: [PATCH 12/12] =?UTF-8?q?refactor:=20=EB=88=84=EB=9D=BD=EB=90=9C?= =?UTF-8?q?=20final=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../image/infrastructure/local/LocalStoreImageProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java index 0258e4ee8..75a4de328 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/image/infrastructure/local/LocalStoreImageProcessor.java @@ -27,7 +27,7 @@ public class LocalStoreImageProcessor implements StoreImageProcessor { public List storeImageFiles(final List imageFiles) { final List storeImageDtos = new ArrayList<>(); - for (MultipartFile imageFile : imageFiles) { + for (final MultipartFile imageFile : imageFiles) { if (imageFile.isEmpty()) { throw new EmptyImageException("이미지 파일의 데이터가 비어 있습니다."); }