Skip to content

Commit deada6c

Browse files
committed
fix: 코드 리뷰 사항 반영
- BASE_URL을 S3_BASE_URL라는 더 명시적인 이름으로 변경 - ImageUploadUtils private 생성자의 예외처리 메세지를 ExceptionMessage에 등록하여 사용하도록 수정 - 이미지 업로드 메서드의 내부 기능을 메서드로 추출하여 가독성 향상 - ImageUploadUtils의 일부 기능의 접근 제어자를 private으로 수정 - 테스트 코드의 when, then을 더 명확하게 분
1 parent 38b09b2 commit deada6c

File tree

8 files changed

+102
-43
lines changed

8 files changed

+102
-43
lines changed

.github/workflows/CD.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
BACK_URL: ${{secrets.BACK_URL}}
3030
BUCKET_NAME: ${{secrets.BUCKET_NAME}}
3131
BUCKET_REGION: ${{secrets.BUCKET_REGION}}
32-
BASE_URL:https: ${{secrets.BASE_URL}}
32+
IMG_BASE_URL: ${{secrets.BASE_URL}}
3333
S3_ACCESS_KEY: ${{secrets.S3_ACCESS_KEY}}
3434
S3_SECRET_KEY: ${{secrets.S3_SECRET_KEY}}
3535

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
BACK_URL: ${{secrets.BACK_URL}}
3838
BUCKET_NAME: ${{secrets.BUCKET_NAME}}
3939
BUCKET_REGION: ${{secrets.BUCKET_REGION}}
40-
BASE_URL:https: ${{secrets.BASE_URL}}
40+
IMG_BASE_URL: ${{secrets.BASE_URL}}
4141
S3_ACCESS_KEY: ${{secrets.S3_ACCESS_KEY}}
4242
S3_SECRET_KEY: ${{secrets.S3_SECRET_KEY}}
4343

src/main/java/com/somemore/global/exception/ExceptionMessage.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ public enum ExceptionMessage {
1717
UPLOAD_FAILED("파일 업로드에 실패했습니다."),
1818
INVALID_FILE_TYPE("지원하지 않는 파일 형식입니다."),
1919
FILE_SIZE_EXCEEDED("파일 크기가 허용된 한도를 초과했습니다."),
20-
EMPTY_FILE("파일이 존재하지 않습니다.")
20+
EMPTY_FILE("파일이 존재하지 않습니다."),
21+
INSTANTIATION_NOT_ALLOWED("인스턴스화 할 수 없는 클래스 입니다.")
2122
;
2223

2324
private final String message;

src/main/java/com/somemore/imageupload/service/ImageUploadService.java

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import lombok.RequiredArgsConstructor;
99
import org.springframework.beans.factory.annotation.Value;
1010
import org.springframework.stereotype.Service;
11+
import org.springframework.web.multipart.MultipartFile;
1112
import software.amazon.awssdk.core.sync.RequestBody;
1213
import software.amazon.awssdk.services.s3.S3Client;
1314
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
@@ -34,24 +35,29 @@ public String uploadImage(ImageUploadRequestDto requestDto) {
3435
imageUploadValidator.validateFileSize(requestDto.imageFile());
3536
imageUploadValidator.validateFileType(requestDto.imageFile());
3637

37-
String fileName = ImageUploadUtils.generateUniqueFileName(requestDto.imageFile().getOriginalFilename());
38-
39-
PutObjectRequest request = PutObjectRequest.builder()
40-
.bucket(bucket)
41-
.key(fileName)
42-
.contentType(requestDto.imageFile().getContentType())
43-
.build();
44-
4538
try {
46-
s3Client.putObject(request, RequestBody.fromInputStream(
47-
requestDto.imageFile().getInputStream(),
48-
requestDto.imageFile().getSize()
49-
));
50-
51-
return ImageUploadUtils.generateS3Url(baseUrl, fileName);
39+
return uploadToS3(requestDto.imageFile());
5240
} catch (IOException e) {
5341
throw new ImageUploadException(UPLOAD_FAILED.getMessage());
5442
}
5543
}
5644

45+
private String uploadToS3(MultipartFile file) throws IOException {
46+
String fileName = ImageUploadUtils.generateUniqueFileName(file.getOriginalFilename());
47+
48+
PutObjectRequest request = createPutObjectRequest(file, fileName);
49+
50+
s3Client.putObject(request, RequestBody.fromInputStream(file.getInputStream(), file.getSize()));
51+
52+
return ImageUploadUtils.generateS3Url(baseUrl, fileName);
53+
}
54+
55+
private PutObjectRequest createPutObjectRequest(MultipartFile file, String fileName) {
56+
return PutObjectRequest.builder()
57+
.bucket(bucket)
58+
.key(fileName)
59+
.contentType(file.getContentType())
60+
.build();
61+
}
62+
5763
}

src/main/java/com/somemore/imageupload/util/ImageUploadUtils.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import java.util.UUID;
44

5-
public final class ImageUploadUtils {
5+
import static com.somemore.global.exception.ExceptionMessage.INSTANTIATION_NOT_ALLOWED;
6+
7+
public class ImageUploadUtils {
68

79
private ImageUploadUtils() {
8-
throw new UnsupportedOperationException("인스턴스화 할 수 없는 클래스 입니다.");
10+
throw new UnsupportedOperationException(INSTANTIATION_NOT_ALLOWED.getMessage());
911
}
1012

1113
public static String generateUniqueFileName(String originalFileName) {
@@ -14,7 +16,7 @@ public static String generateUniqueFileName(String originalFileName) {
1416
return uuid + fileExtension;
1517
}
1618

17-
public static String extractFileExtension(String fileName) {
19+
private static String extractFileExtension(String fileName) {
1820
return fileName.substring(fileName.lastIndexOf("."));
1921
}
2022

src/main/resources/application.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ cloud:
1212
static: ${BUCKET_REGION}
1313
s3:
1414
bucket: ${BUCKET_NAME}
15-
base-url: ${BASE_URL}
15+
base-url: ${IMG_BASE_URL}
1616
stack:
1717
auto: false
1818

src/test/java/com/somemore/imageupload/util/ImageUploadUtilsTest.java

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,26 @@
33
import org.junit.jupiter.api.DisplayName;
44
import org.junit.jupiter.api.Test;
55

6+
import java.lang.reflect.Constructor;
7+
import java.lang.reflect.InvocationTargetException;
8+
69
import static org.junit.jupiter.api.Assertions.*;
710

811
class ImageUploadUtilsTest {
912

13+
@Test
14+
void privateConstructorShouldThrowException() throws Exception {
15+
// given
16+
Constructor<ImageUploadUtils> constructor = ImageUploadUtils.class.getDeclaredConstructor();
17+
constructor.setAccessible(true);
18+
19+
// when
20+
InvocationTargetException exception = assertThrows(InvocationTargetException.class, constructor::newInstance);
21+
22+
// then
23+
assertThrows(UnsupportedOperationException.class, () -> { throw exception.getCause(); });
24+
}
25+
1026
@DisplayName("이미지 업로드시 유일한 이미지 이름을 만들어줄 수 있다.")
1127
@Test
1228
void testGenerateUniqueFileName() {
@@ -21,19 +37,22 @@ void testGenerateUniqueFileName() {
2137
assertNotEquals(fileName, uniqueName);
2238
}
2339

24-
@DisplayName("이미지의 확장자를 검증할 수 있다.")
40+
@DisplayName("유니크한 파일 이름을 생성할 때 UUID는 정상적으로 생성된다.")
2541
@Test
26-
void testExtractFileExtension() {
27-
//given
28-
String fileName = "example.jpg";
42+
void testGenerateUniqueFileName_uuid() {
43+
// given
44+
String fileName = "image.png";
2945

30-
//when
31-
String extension = ImageUploadUtils.extractFileExtension(fileName);
46+
// when
47+
String uniqueName = ImageUploadUtils.generateUniqueFileName(fileName);
3248

33-
//then
34-
assertEquals(".jpg", extension);
49+
// then
50+
assertNotNull(uniqueName);
51+
assertTrue(uniqueName.contains("-"));
52+
assertTrue(uniqueName.endsWith(".png"));
3553
}
3654

55+
3756
@DisplayName("이미지의 주소를 반환할 수 있다.")
3857
@Test
3958
void testGenerateS3Url() {
@@ -47,4 +66,19 @@ void testGenerateS3Url() {
4766
//then
4867
assertEquals("https://amazonaws.com/unique-image.png", url);
4968
}
69+
70+
@DisplayName("baseUrl이 빈 문자열일 경우 URL을 생성할 수 있다.")
71+
@Test
72+
void testGenerateS3Url_emptyBaseUrl() {
73+
// given
74+
String baseUrl = "";
75+
String fileName = "unique-image.png";
76+
77+
// when
78+
String url = ImageUploadUtils.generateS3Url(baseUrl, fileName);
79+
80+
// then
81+
assertEquals("/unique-image.png", url);
82+
}
83+
5084
}

src/test/java/com/somemore/imageupload/validator/DefaultImageUploadValidatorTest.java

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class DefaultImageUploadValidatorTest {
1515

1616
@BeforeEach
1717
void setUp() {
18+
// given
1819
imageUploadValidator = new DefaultImageUploadValidator();
1920
}
2021

@@ -24,47 +25,62 @@ void shouldThrowExceptionWhenFileIsEmpty() {
2425
//given
2526
MultipartFile emptyFile = new MockMultipartFile("file", new byte[0]);
2627

27-
//when&then
28-
assertThrows(ImageUploadException.class, () -> imageUploadValidator.validateFileSize(emptyFile));
28+
//when
29+
Throwable exception = assertThrows(ImageUploadException.class, () -> imageUploadValidator.validateFileSize(emptyFile));
30+
31+
//then
32+
assertEquals(ImageUploadException.class, exception.getClass());
2933
}
3034

3135
@Test
3236
@DisplayName("파일 크기가 최대 8MB를 초과하는 경우, 예외가 발생한다.")
3337
void shouldThrowExceptionWhenFileSizeExceeded() {
34-
//given
38+
// given
3539
MultipartFile largeFile = new MockMultipartFile("file", "largeImage.jpg", "image/jpeg", new byte[9 * 1024 * 1024]);
3640

37-
//when&then
38-
assertThrows(ImageUploadException.class, () -> imageUploadValidator.validateFileSize(largeFile));
41+
// when
42+
Throwable exception = assertThrows(ImageUploadException.class, () -> imageUploadValidator.validateFileSize(largeFile));
43+
44+
// then
45+
assertEquals(ImageUploadException.class, exception.getClass());
3946
}
4047

4148
@Test
42-
@DisplayName("유효한 이미지 타입(JPEG) 파일이 있을 경우, 검증에 통과한다.")
49+
@DisplayName("유효한 이미지 타입(JPEG) 파일은, 검증에 통과한다.")
4350
void shouldNotThrowExceptionWhenFileTypeIsValidJpeg() {
44-
//given
51+
// given
4552
MultipartFile validFile = new MockMultipartFile("file", "validImage.jpg", "image/jpeg", new byte[1024]);
4653

47-
//when&then
54+
// when
55+
imageUploadValidator.validateFileType(validFile);
56+
57+
// then
4858
assertDoesNotThrow(() -> imageUploadValidator.validateFileType(validFile));
4959
}
5060

5161
@Test
5262
@DisplayName("유효하지 않은 이미지 타입 파일이 있을 경우, 예외가 발생한다.")
5363
void shouldThrowExceptionWhenFileTypeIsInvalid() {
54-
//given
64+
// given
5565
MultipartFile invalidFile = new MockMultipartFile("file", "invalidFile.pdf", "application/pdf", new byte[1024]);
5666

57-
//when&then
58-
assertThrows(ImageUploadException.class, () -> imageUploadValidator.validateFileType(invalidFile));
67+
// when
68+
Throwable exception = assertThrows(ImageUploadException.class, () -> imageUploadValidator.validateFileType(invalidFile));
69+
70+
// then
71+
assertEquals(ImageUploadException.class, exception.getClass());
5972
}
6073

6174
@Test
6275
@DisplayName("파일 타입이 올바르지 않을 경우, 예외가 발생한다.")
6376
void shouldThrowExceptionWhenFileTypeIsNull() {
64-
//given
77+
// given
6578
MultipartFile nullContentTypeFile = new MockMultipartFile("file", "noContentTypeFile.jpg", null, new byte[1024]);
6679

67-
//when&then
68-
assertThrows(ImageUploadException.class, () -> imageUploadValidator.validateFileType(nullContentTypeFile));
80+
// when
81+
Throwable exception = assertThrows(ImageUploadException.class, () -> imageUploadValidator.validateFileType(nullContentTypeFile));
82+
83+
// then
84+
assertEquals(ImageUploadException.class, exception.getClass());
6985
}
7086
}

0 commit comments

Comments
 (0)