Skip to content

Commit cc88b11

Browse files
committed
⚡ MarkSphere v1.0.9
이미지 WebP 변환 로직 AWS Lambda로 이전
1 parent 3833df5 commit cc88b11

5 files changed

Lines changed: 64 additions & 48 deletions

File tree

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ dependencies {
7878
// image 리사이징 및 변환
7979
implementation "com.sksamuel.scrimage:scrimage-core:4.3.5"
8080
implementation "com.sksamuel.scrimage:scrimage-webp:4.3.5"
81+
82+
// AWS Lambda 호출
83+
implementation 'software.amazon.awssdk:lambda:2.40.7'
8184
}
8285

8386
// Q-Class를 생성할 디렉토리 경로

scripts/deploy.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export YOUTUBE_API_KEY=$(aws ssm get-parameter --name "/marksphere/youtube-api-k
1010
export AWS_ACCESS_KEY=$(aws ssm get-parameter --name "/marksphere/aws-access-key" --with-decryption --query "Parameter.Value" --output text)
1111
export AWS_SECRET_KEY=$(aws ssm get-parameter --name "/marksphere/aws-secret-key" --with-decryption --query "Parameter.Value" --output text)
1212
export REDIS_HOST=$(aws ssm get-parameter --name "/marksphere/redis-host" --with-decryption --query "Parameter.Value" --output text)
13+
export AWS_FUNCTION_NAME=$(aws ssm get-parameter --name "/marksphere/aws-function-name" --with-decryption --query "Parameter.Value" --output text)
1314

1415
JAR_NAME=$(ls $REPOSITORY/build/libs/ | grep '.jar' | tail -n 1)
1516
JAR_PATH=$REPOSITORY/build/libs/$JAR_NAME

src/main/java/com/sonkim/bookmarking/common/config/S3Config.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
88
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
99
import software.amazon.awssdk.regions.Region;
10+
import software.amazon.awssdk.services.lambda.LambdaClient;
1011
import software.amazon.awssdk.services.s3.S3Client;
1112
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
1213

@@ -40,4 +41,15 @@ public S3Presigner s3Presigner() {
4041
.credentialsProvider(provider)
4142
.build();
4243
}
44+
45+
@Bean
46+
public LambdaClient lambdaClient() {
47+
AwsBasicCredentials credentials = AwsBasicCredentials.create(accessKeyId, secretAccessKey);
48+
AwsCredentialsProvider provider = StaticCredentialsProvider.create(credentials);
49+
50+
return LambdaClient.builder()
51+
.region(Region.AP_NORTHEAST_2)
52+
.credentialsProvider(provider)
53+
.build();
54+
}
4355
}

src/main/java/com/sonkim/bookmarking/common/s3/service/S3Service.java

Lines changed: 45 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
package com.sonkim.bookmarking.common.s3.service;
22

3-
import com.sksamuel.scrimage.ImmutableImage;
4-
import com.sksamuel.scrimage.webp.WebpWriter;
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
55
import com.sonkim.bookmarking.common.s3.dto.PresignedUrlDto;
66
import lombok.extern.slf4j.Slf4j;
77
import org.springframework.beans.factory.annotation.Value;
88
import org.springframework.stereotype.Service;
9-
import software.amazon.awssdk.core.ResponseBytes;
9+
import software.amazon.awssdk.core.SdkBytes;
1010
import software.amazon.awssdk.core.sync.RequestBody;
11+
import software.amazon.awssdk.services.lambda.LambdaClient;
12+
import software.amazon.awssdk.services.lambda.model.InvokeRequest;
13+
import software.amazon.awssdk.services.lambda.model.InvokeResponse;
1114
import software.amazon.awssdk.services.s3.S3Client;
12-
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
1315
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
14-
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
1516
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
1617
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
1718
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
1819
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
1920
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
2021
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
2122

22-
import java.io.IOException;
2323
import java.net.URL;
2424
import java.time.Duration;
25+
import java.util.HashMap;
26+
import java.util.Map;
2527
import java.util.UUID;
2628

2729
@Slf4j
@@ -31,11 +33,18 @@ public class S3Service {
3133
private final S3Client s3Client;
3234
private final String bucketName;
3335
private final S3Presigner s3Presigner;
36+
private final LambdaClient lambdaClient;
37+
private final ObjectMapper objectMapper;
3438

35-
public S3Service(S3Client s3Client, @Value("${aws.s3.bucket-name}") String bucketName, S3Presigner s3Presigner) {
39+
@Value("${aws.lambda.function-name}")
40+
private String lambdaFunctionName;
41+
42+
public S3Service(S3Client s3Client, @Value("${aws.s3.bucket-name}") String bucketName, S3Presigner s3Presigner, LambdaClient lambdaClient, ObjectMapper objectMapper) {
3643
this.s3Client = s3Client;
3744
this.bucketName = bucketName;
3845
this.s3Presigner = s3Presigner;
46+
this.lambdaClient = lambdaClient;
47+
this.objectMapper = objectMapper;
3948
}
4049

4150
public URL generatePresignedGetUrl(String prefix, String key) {
@@ -77,48 +86,43 @@ public PresignedUrlDto generatePresignedPutUrl(String fileName) {
7786
}
7887

7988
public String moveFileToPermanentStorage(String prefix, String fileName) {
80-
String sourceKey = "temp/" + fileName;
81-
8289
try {
83-
// S3 임시 폴더에서 이미지 다운로드
84-
GetObjectRequest getRequest = GetObjectRequest.builder()
85-
.bucket(bucketName)
86-
.key(sourceKey)
90+
// Lambda에 보낼 데이터 생성
91+
Map<String, String> payloadMap = new HashMap<>();
92+
payloadMap.put("prefix", prefix);
93+
payloadMap.put("fileName", fileName);
94+
String jsonPayload = objectMapper.writeValueAsString(payloadMap);
95+
96+
// Lambda 호출 요청 생성
97+
InvokeRequest invokeRequest = InvokeRequest.builder()
98+
.functionName(lambdaFunctionName)
99+
.payload(SdkBytes.fromUtf8String(jsonPayload))
87100
.build();
88-
ResponseBytes<GetObjectResponse> responseBytes = s3Client.getObjectAsBytes(getRequest);
89-
byte[] originalBytes = responseBytes.asByteArray();
90101

91-
// 리사이징 및 WebP로 변환
92-
ImmutableImage image = ImmutableImage.loader().fromBytes(originalBytes);
93-
if (image.width > 600) {
94-
image = image.scaleToWidth(600);
102+
log.info("AWS Lambda 호출 시작: Function={}, Payload={}", lambdaFunctionName, jsonPayload);
103+
104+
// 호출. 완료될 때까지 대기
105+
InvokeResponse invokeResponse = lambdaClient.invoke(invokeRequest);
106+
107+
// 완료 후 전달된 응답에 담긴 파일 이름 가져오기
108+
String responseString = invokeResponse.payload().asUtf8String();
109+
110+
// 에러 체크 (Lambda 실행 에러)
111+
if (invokeResponse.functionError() != null) {
112+
log.error("Lambda 실행 오류: {}", responseString);
113+
throw new RuntimeException("이미지 처리 Lambda 실행 중 오류 발생: " + responseString);
95114
}
96-
byte[] convertedBytes = image.bytes(WebpWriter.DEFAULT.withQ(80));
97-
98-
// 새로운 파일 이름 생성 (확장자를 .webp로 변경)
99-
String newFileName = getFileNameWithoutExtension(fileName) + ".webp";
100-
String destKey = prefix + newFileName;
101-
102-
// 영구 저장소에 이미지 업로드
103-
PutObjectRequest putRequest = PutObjectRequest.builder()
104-
.bucket(bucketName)
105-
.key(destKey)
106-
.contentType("image/webp")
107-
.contentDisposition("inline")
108-
.build();
109-
s3Client.putObject(putRequest, RequestBody.fromBytes(convertedBytes));
110115

111-
// 임시 저장소의 원본 파일 제거
112-
s3Client.deleteObject(DeleteObjectRequest.builder()
113-
.bucket(bucketName)
114-
.key(sourceKey)
115-
.build());
116+
// 앞뒤 쌍따옴표 제거
117+
String newFileName = responseString.replace("\"", "");
116118

117-
log.info("이미지 변환 및 이동 완료: {} -> {}", sourceKey, destKey);
119+
log.info("AWS Lambda 처리 완료. 변환된 파일명: {}", newFileName);
118120
return newFileName;
119121

120-
} catch (IOException e) {
121-
throw new RuntimeException("이미지 처리 및 이동 중 오류 발생: " + fileName, e);
122+
} catch (JsonProcessingException e) {
123+
throw new RuntimeException("Lambda 호출 Payload 생성 실패", e);
124+
} catch (Exception e) {
125+
throw new RuntimeException("이미지 처리 서버(Lambda) 호출 중 오류 발생", e);
122126
}
123127
}
124128

@@ -166,11 +170,4 @@ private String getContentType(String fileName) {
166170
"application/octet-stream";
167171
};
168172
}
169-
170-
private String getFileNameWithoutExtension(String fileName) {
171-
if (fileName.contains(".")) {
172-
return fileName.substring(0, fileName.lastIndexOf('.'));
173-
}
174-
return fileName;
175-
}
176173
}

src/main/resources/application.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ aws.s3.accessKeyId=${AWS_ACCESS_KEY}
2424
aws.s3.secretAccessKey=${AWS_SECRET_KEY}
2525
aws.s3.bucket-name=socialbookmarking-bucket
2626

27+
# AWS Lambda
28+
aws.lambda.function-name=${AWS_FUNCTION_NAME}
29+
2730
spring.data.redis.host=${REDIS_HOST}
2831
spring.data.redis.port=6379
2932

0 commit comments

Comments
 (0)