Skip to content

Commit 0d5b209

Browse files
authored
[DDING-000] Spring Boot 3.4.9 업그레이드 및 매트릭 수집 최적화 (#326)
1 parent 627f8d4 commit 0d5b209

29 files changed

+335
-234
lines changed

.ebextensions/00-makeFiles.config

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ files:
1414
java \
1515
-Dfile.encoding=UTF-8 \
1616
-Xms512m -Xmx512m \
17-
-Xlog:gc*:file="$LOG_DIR/gc.log":time,uptime,level,tags::filecount=5,filesize=100M \
17+
-Xlog:gc*,gc+humongous=debug:file=$LOG_DIR/gc.log:time,uptime,level,tags:filecount=5,filesize=100M \
1818
-XX:+HeapDumpOnOutOfMemoryError \
1919
-XX:HeapDumpPath=$LOG_DIR/heapdump.hprof \
20-
-jar $JAR_PATH \
20+
-XX:ConcGCThreads=2 \
21+
-XX:G1HeapRegionSize=2m \
22+
-XX:MaxNewSize=128m \
23+
-jar $JAR_PATH

build.gradle

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
plugins {
22
id 'java'
3-
id 'org.springframework.boot' version '3.2.6'
3+
id 'org.springframework.boot' version '3.4.9'
44
id 'io.spring.dependency-management' version '1.1.3'
55
id 'jacoco'
66
id "org.sonarqube" version "5.1.0.4882"
@@ -30,7 +30,7 @@ dependencies {
3030
//develop
3131
implementation 'org.springframework.boot:spring-boot-starter-web'
3232
implementation 'org.springframework.boot:spring-boot-starter-validation'
33-
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
33+
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.12'
3434
implementation 'org.springframework.boot:spring-boot-configuration-processor'
3535
implementation 'org.springframework.boot:spring-boot-starter-cache'
3636

@@ -44,13 +44,14 @@ dependencies {
4444
implementation "org.flywaydb:flyway-mysql"
4545

4646
// queryDSL
47-
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
48-
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
47+
implementation 'com.querydsl:querydsl-jpa:5.1.0:jakarta'
48+
annotationProcessor "com.querydsl:querydsl-apt:5.1.0:jakarta"
4949
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
5050
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
5151

5252
//etc(기타)
53-
implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.4'
53+
implementation 'software.amazon.awssdk:s3:2.30.16'
54+
implementation 'software.amazon.awssdk:url-connection-client:2.30.16'
5455
implementation 'org.apache.poi:poi:5.2.0'
5556
implementation 'org.apache.poi:poi-ooxml:5.2.0'
5657
implementation 'io.sentry:sentry-logback:7.6.0'
@@ -79,11 +80,9 @@ dependencies {
7980
testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter:1.0.23")
8081

8182
// TestContainer
82-
testImplementation 'org.testcontainers:testcontainers:1.20.1'
83-
testImplementation 'org.testcontainers:junit-jupiter:1.20.1'
84-
85-
// mysql 컨테이너
86-
testImplementation 'org.testcontainers:mysql:1.20.1'
83+
testImplementation 'org.testcontainers:testcontainers'
84+
testImplementation 'org.testcontainers:junit-jupiter'
85+
testImplementation 'org.testcontainers:mysql'
8786
}
8887

8988
tasks.named('test') {

src/main/java/ddingdong/ddingdongBE/common/config/AmazonS3Config.java

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
package ddingdong.ddingdongBE.common.config;
22

3-
import com.amazonaws.auth.AWSStaticCredentialsProvider;
4-
import com.amazonaws.auth.BasicAWSCredentials;
5-
import com.amazonaws.services.s3.AmazonS3Client;
6-
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
7-
import org.springframework.beans.factory.annotation.Value;
8-
import org.springframework.context.annotation.Bean;
9-
import org.springframework.context.annotation.Configuration;
103
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
114
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
125
import software.amazon.awssdk.regions.Region;
6+
import software.amazon.awssdk.services.s3.S3Client;
7+
import org.springframework.beans.factory.annotation.Value;
8+
import org.springframework.context.annotation.Bean;
9+
import org.springframework.context.annotation.Configuration;
10+
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
1311
import software.amazon.awssdk.services.ses.SesClient;
1412

1513
@Configuration
@@ -25,12 +23,22 @@ public class AmazonS3Config {
2523
private String region;
2624

2725
@Bean
28-
public AmazonS3Client amazonS3Client() {
29-
BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(accessKey, secretKey);
30-
return (AmazonS3Client) AmazonS3ClientBuilder
31-
.standard()
32-
.withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials))
33-
.withRegion(region)
26+
public S3Client s3Client() {
27+
return S3Client.builder()
28+
.region(Region.of(region))
29+
.credentialsProvider(StaticCredentialsProvider.create(
30+
AwsBasicCredentials.create(accessKey, secretKey)
31+
))
32+
.build();
33+
}
34+
35+
@Bean
36+
public S3Presigner s3Presigner() {
37+
return S3Presigner.builder()
38+
.region(Region.of(region))
39+
.credentialsProvider(StaticCredentialsProvider.create(
40+
AwsBasicCredentials.create(accessKey, secretKey)
41+
))
3442
.build();
3543
}
3644

src/main/java/ddingdong/ddingdongBE/common/config/SecurityConfig.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
1414
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
1515
import org.springframework.security.config.http.SessionCreationPolicy;
16+
import org.springframework.security.config.observation.SecurityObservationSettings;
1617
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
1718
import org.springframework.security.web.SecurityFilterChain;
1819
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@@ -130,4 +131,8 @@ public CustomAccessDeniedHandler accessDeniedHandler() {
130131
return new CustomAccessDeniedHandler();
131132
}
132133

134+
@Bean
135+
public SecurityObservationSettings noSpringSecurityObservations() {
136+
return SecurityObservationSettings.noObservations();
137+
}
133138
}

src/main/java/ddingdong/ddingdongBE/file/AwsS3FileStore.java

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package ddingdong.ddingdongBE.file;
22

3-
import com.amazonaws.services.s3.AmazonS3Client;
4-
import com.amazonaws.services.s3.model.ObjectMetadata;
5-
import com.amazonaws.services.s3.model.PutObjectRequest;
3+
import software.amazon.awssdk.core.sync.RequestBody;
4+
import software.amazon.awssdk.services.s3.S3Client;
5+
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
6+
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
67

78
import ddingdong.ddingdongBE.file.service.dto.UploadFileDto;
89

@@ -24,7 +25,7 @@
2425
@RequiredArgsConstructor
2526
public class AwsS3FileStore implements FileStore {
2627

27-
private final AmazonS3Client amazonS3Client;
28+
private final S3Client s3Client;
2829

2930
@Value("${cloud.aws.region.static}")
3031
private String region;
@@ -40,13 +41,15 @@ public List<UploadFileDto> storeFile(List<MultipartFile> files, String fileType,
4041
String storeFileName = createStoreFileName(uploadFileName);
4142
String keyName = fileType + filePath + storeFileName;
4243

43-
ObjectMetadata objectMetadata = new ObjectMetadata();
44-
objectMetadata.setContentLength(file.getSize());
45-
objectMetadata.setContentType(file.getContentType());
46-
4744
try {
48-
amazonS3Client.putObject(
49-
new PutObjectRequest(bucketName, keyName, file.getInputStream(), objectMetadata));
45+
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
46+
.bucket(bucketName)
47+
.key(keyName)
48+
.contentType(file.getContentType())
49+
.contentLength(file.getSize())
50+
.build();
51+
52+
s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(file.getInputStream(), file.getSize()));
5053
} catch (IOException e) {
5154
throw new RuntimeException("파일 업로드 실패");
5255
}
@@ -67,15 +70,19 @@ public List<UploadFileDto> storeDownloadableFile(List<MultipartFile> files, Stri
6770
String storeFileName = createStoreFileName(uploadFileName);
6871
String keyName = fileType + filePath + storeFileName;
6972

70-
ObjectMetadata objectMetadata = new ObjectMetadata();
71-
objectMetadata.setContentLength(file.getSize());
72-
objectMetadata.setContentType(file.getContentType());
73-
assert uploadFileName != null;
74-
objectMetadata.setContentDisposition("attachment; filename=" + "\"" + URLEncoder.encode(uploadFileName,
75-
StandardCharsets.UTF_8) + "\"");
7673
try {
77-
amazonS3Client.putObject(
78-
new PutObjectRequest(bucketName, keyName, file.getInputStream(), objectMetadata));
74+
assert uploadFileName != null;
75+
String contentDisposition = "attachment; filename=" + "\"" + URLEncoder.encode(uploadFileName, StandardCharsets.UTF_8) + "\"";
76+
77+
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
78+
.bucket(bucketName)
79+
.key(keyName)
80+
.contentType(file.getContentType())
81+
.contentLength(file.getSize())
82+
.contentDisposition(contentDisposition)
83+
.build();
84+
85+
s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(file.getInputStream(), file.getSize()));
7986
} catch (IOException e) {
8087
throw new RuntimeException("파일 업로드 실패");
8188
}
@@ -90,7 +97,11 @@ public List<UploadFileDto> storeDownloadableFile(List<MultipartFile> files, Stri
9097
@Override
9198
public void deleteFile(String fileType, String filePath, String uploadFileName) {
9299
String keyName = fileType + filePath + uploadFileName;
93-
amazonS3Client.deleteObject(bucketName, keyName);
100+
DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
101+
.bucket(bucketName)
102+
.key(keyName)
103+
.build();
104+
s3Client.deleteObject(deleteObjectRequest);
94105
}
95106

96107
@Override

src/main/java/ddingdong/ddingdongBE/file/controller/S3FileController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public UploadUrlResponse getPreSignedUrl(PrincipalDetails principalDetails, Stri
2828
GeneratePreSignedUrlRequestQuery query =
2929
s3FileService.generatePresignedUrlRequest(
3030
new GeneratePreSignedUrlRequestCommand(now, user.getId(), decodedFileName));
31-
URL presingedUrl = s3FileService.getPresignedUrl(query.generatePresignedUrlRequest());
31+
URL presingedUrl = s3FileService.getPresignedUrl(query.putObjectRequest());
3232
return UploadUrlResponse.of(query, presingedUrl);
3333
}
3434

@@ -39,7 +39,7 @@ public UploadUrlResponse getFormApplicationPreSignedUrl(String fileName) {
3939
GeneratePreSignedUrlRequestQuery query =
4040
s3FileService.generateDownloadPresignedUrlRequest(
4141
new GeneratePreSignedUrlRequestCommand(now, 9999L, decodedFileName));
42-
URL presingedUrl = s3FileService.getPresignedUrl(query.generatePresignedUrlRequest());
42+
URL presingedUrl = s3FileService.getPresignedUrl(query.putObjectRequest());
4343
return UploadUrlResponse.of(query, presingedUrl);
4444
}
4545
}

src/main/java/ddingdong/ddingdongBE/file/service/S3FileService.java

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package ddingdong.ddingdongBE.file.service;
22

3-
import com.amazonaws.AmazonClientException;
4-
import com.amazonaws.AmazonServiceException;
5-
import com.amazonaws.HttpMethod;
6-
import com.amazonaws.services.s3.AmazonS3Client;
7-
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
3+
import software.amazon.awssdk.services.s3.S3Client;
4+
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
5+
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
6+
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
7+
import software.amazon.awssdk.core.exception.SdkClientException;
8+
import software.amazon.awssdk.core.exception.SdkServiceException;
9+
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
810
import com.github.f4b6a3.uuid.UuidCreator;
911
import ddingdong.ddingdongBE.common.exception.AwsException.AwsClient;
1012
import ddingdong.ddingdongBE.common.exception.AwsException.AwsService;
@@ -17,7 +19,6 @@
1719
import ddingdong.ddingdongBE.file.service.dto.query.UploadedVideoUrlQuery;
1820
import java.net.URL;
1921
import java.time.LocalDateTime;
20-
import java.util.Date;
2122
import java.util.UUID;
2223
import lombok.RequiredArgsConstructor;
2324
import lombok.extern.slf4j.Slf4j;
@@ -41,7 +42,8 @@ public class S3FileService {
4142
@Value("${spring.config.activate.on-profile}")
4243
private String serverProfile;
4344

44-
private final AmazonS3Client amazonS3Client;
45+
private final S3Client s3Client;
46+
private final S3Presigner s3Presigner;
4547
private final FileMetaDataService fileMetaDataService;
4648

4749
public GeneratePreSignedUrlRequestQuery generatePresignedUrlRequest(GeneratePreSignedUrlRequestCommand command) {
@@ -54,13 +56,19 @@ public GeneratePreSignedUrlRequestQuery generateDownloadPresignedUrlRequest(Gene
5456
return buildPresignedUrlRequest(command, contentType);
5557
}
5658

57-
public URL getPresignedUrl(GeneratePresignedUrlRequest generatePresignedUrlRequest) {
59+
public URL getPresignedUrl(PutObjectRequest putObjectRequest) {
5860
try {
59-
return amazonS3Client.generatePresignedUrl(generatePresignedUrlRequest);
60-
} catch (AmazonServiceException e) {
61+
PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
62+
.signatureDuration(java.time.Duration.ofMillis(PRE_SIGNED_URL_EXPIRATION_TIME))
63+
.putObjectRequest(putObjectRequest)
64+
.build();
65+
66+
PresignedPutObjectRequest presignedRequest = s3Presigner.presignPutObject(presignRequest);
67+
return presignedRequest.url();
68+
} catch (SdkServiceException e) {
6169
log.warn("AWS Service Error : {}", e.getMessage());
6270
throw new AwsService();
63-
} catch (AmazonClientException e) {
71+
} catch (SdkClientException e) {
6472
log.warn("AWS Client Error : {}", e.getMessage());
6573
throw new AwsClient();
6674
}
@@ -70,7 +78,7 @@ public UploadedFileUrlQuery getUploadedFileUrl(String key) {
7078
if (key == null) {
7179
return null;
7280
}
73-
String region = amazonS3Client.getRegionName();
81+
String region = s3Client.serviceClientConfiguration().region().id();
7482
String[] splitKey = key.split("/");
7583
String originUrl = String.format(S3_URL_FORMAT, inputBucket, region) + key;
7684
String cdnUrl = FILE_CDN_URL + key;
@@ -90,7 +98,7 @@ public UploadedFileUrlAndNameQuery getUploadedFileUrlAndName(String key, String
9098
//TODO: video 피드 조회 시 수정 필요
9199
public UploadedVideoUrlQuery getUploadedVideoUrl(String key) {
92100
String fileId = extractFileId(key);
93-
String region = amazonS3Client.getRegionName();
101+
String region = s3Client.serviceClientConfiguration().region().id();
94102

95103
String thumbnailOriginUrl = generateS3Url(outputBucket, region, "thumbnails/", fileId, ".0000000.jpg");
96104
String thumbnailCdnUrl = generateCdnUrl("thumbnails/", fileId, ".0000000.jpg");
@@ -103,27 +111,21 @@ public UploadedVideoUrlQuery getUploadedVideoUrl(String key) {
103111
private GeneratePreSignedUrlRequestQuery buildPresignedUrlRequest(GeneratePreSignedUrlRequestCommand command, ContentType contentType) {
104112
UUID id = UuidCreator.getTimeOrderedEpoch();
105113
String key = generateKey(contentType, command, id);
106-
Date expiration = getExpirationTime();
107114

108115
fileMetaDataService.create(FileMetaData.createPending(id, key, command.fileName()));
109116

110-
GeneratePresignedUrlRequest request = createPresignedUrlRequest(key, contentType, expiration);
117+
PutObjectRequest request = createPutObjectRequest(key, contentType);
111118
return new GeneratePreSignedUrlRequestQuery(request, id, contentType.getMimeType());
112119
}
113120

114-
private GeneratePresignedUrlRequest createPresignedUrlRequest(String key, ContentType contentType,
115-
Date expiration) {
116-
return new GeneratePresignedUrlRequest(inputBucket, key)
117-
.withMethod(HttpMethod.PUT)
118-
.withExpiration(expiration)
119-
.withContentType(contentType.getMimeType());
121+
private PutObjectRequest createPutObjectRequest(String key, ContentType contentType) {
122+
return PutObjectRequest.builder()
123+
.bucket(inputBucket)
124+
.key(key)
125+
.contentType(contentType.getMimeType())
126+
.build();
120127
}
121128

122-
private Date getExpirationTime() {
123-
Date expiration = new Date();
124-
expiration.setTime(expiration.getTime() + PRE_SIGNED_URL_EXPIRATION_TIME);
125-
return expiration;
126-
}
127129

128130
private String generateKey(ContentType contentType, GeneratePreSignedUrlRequestCommand command,
129131
UUID uploadFileName) {

src/main/java/ddingdong/ddingdongBE/file/service/dto/query/GeneratePreSignedUrlRequestQuery.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package ddingdong.ddingdongBE.file.service.dto.query;
22

3-
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
3+
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
44
import java.util.UUID;
55

66
public record GeneratePreSignedUrlRequestQuery(
7-
GeneratePresignedUrlRequest generatePresignedUrlRequest,
7+
PutObjectRequest putObjectRequest,
88
UUID id,
99
String contentType
1010
) {

src/main/resources/application-prod.yml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ server:
5353

5454
# Actuator 메트릭 설정
5555
management:
56+
observations:
57+
enable:
58+
spring:
59+
security: false
5660
endpoints:
5761
web:
5862
exposure:
@@ -75,15 +79,19 @@ management:
7579
process: true
7680
hikaricp: true # 데이터베이스 연결 풀
7781
jvm: true # JVM 메모리, GC, 스레드
78-
system: true # 시스템 메트릭 (일부)
7982
web: true # HTTP 요청 메트릭
83+
system: false # 시스템 메트릭 (일부)
84+
logback: false
85+
spring : false
86+
cache: false
87+
8088

8189
# HTTP 응답시간 히스토그램
8290
distribution:
8391
percentiles-histogram:
84-
http.server.requests: true
92+
http.server.requests: false
8593
percentiles:
86-
http.server.requests: 0.5, 0.9, 0.95, 0.99
94+
http.server.requests: 0.5, 0.95, 0.99
8795

8896
# 공통 라벨
8997
tags:

0 commit comments

Comments
 (0)