1. Lambda ๋์ ์ด์
๋ง์ฝ 50X50 ์ฌ์ด์ฆ์ ์ด๋ฏธ์ง๋ฅผ ๋ก๋ฉํ๋๋ฐ 10MB ์ง๋ฆฌ 400X400 ์ฌ์ด์ฆ์ ์ด๋ฏธ์ง๋ฅผ ์ด์ฉํ๋ค๋ฉด ๋ฆฌ์์ค ๋ญ๋น๊ฐ ์ฌํ๋ค.
์์ ๊ฐ์ ๊ฒฝ์ฐ๊ฐ ํ ๊ฐ๊ฐ ์๋๋ผ ์ฌ๋ฌ ๊ฐ์ผ ๊ฒฝ์ฐ ์ฌ์ฉ์๊ฐ ์ฒด๊ฐํ๋ ์๋ต ์๊ฐ์ ๊ฝค ํด ๊ฒ์ด๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฆฌ์ฌ์ด์ง(RTesizing)๊ธฐ์ ์ ์ฌ์ฉํ์ฌ ์ด๋ฏธ์ง ์ฉ๋์ ์ค์์ผ๋ก์จ ์๋๋ฅผ ๋น ๋ฅด๊ฒํ๋ ๋ฐฉ๋ฒ๋ก ์ ์ด๋ฏธ ๋ง์ ์ฌ๋๋ค์ด ์ฌ์ฉํ๊ณ ์๋ค.
ํ์ง๋ง ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ง ์์ ๊ฐ์ ๊ฒฝ์ฐ CPU์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ง์ด ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋ก์ปฌ ์๋ฒ์์ ์์ ์ ๋๋ ค๋ฒ๋ฆฌ๋ฉด ๋ค๋ฅธ ์ฌ์ฉ์์ ์์ฒญ์ ๋ชป๋ฐ๋ ํ์์ด ์๊ธธ์ ์๋ค.
๊ทธ๋ ๋ค๊ณ ๋ฐ๋ก ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ง ์ ์ฉ ๋ก์ปฌ ์๋ฒ๋ฅผ ๋ง๋ค์ด ์ฌ๋ฆฐ๋ค ํด๋, ๋ชจ๋ ์ฌ์ฉ์๊ฐ ํญ์ ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ง ์์ ๊ธฐ๋ฅ์ ์ด์ฉํ๋ ๊ฒ๋ ์๋๊ธฐ์ ์๋ฒ๋ฅผ ๊ณ์ ๋๋ฆฐ๋ค๋ ๊ฒ์ ์์ ๋ญ๋น์ผ์ ์๋ค.
์ด๋ฐ ์ํฉ์์ AWS Lambda์ Server Less ์๋น์ค๋ ํด๊ฒฐ์ฑ ์ด ๋ ์ ์๋ค.
๋๋ค๋ ํน์ ํ ๋์์ ์ํํ๋ ๋ก์ง์ ์ ์ฅํ๊ณ ์์ฒญ์ด ๋ค์ด์ฌ ๋ ๋ก์ง์ ์คํํ๋ ์๋น์ค๋ค. ๋ง์น ํจ์์ฒ๋ผ ํธ์ถํ ๋ ์คํํ์ฌ FaaS๋ผ๊ณ ๋ถ๋ฆฐ๋ค. ์ด์ฒ๋ผ ๋๋ค๋ฅผ ์ด์ฉํด ๋ ๋ฆฝ์ ์ธ ์๋ฒ์์ ํจ์๋ฅผ ์คํ ์์ผ, ๋ก์ปฌ ์๋ฒ์ ๋ถ๋ด์ ์ค์ผ์ ์์ ๋ฟ๋ง ์๋๋ผ ์จ๋๋ฉ๋(on-demand) ํ์์ด๋ผ ์ด๋งํผ ์๊ธ์ ์ง๋ถํ๋ฉด ๋์ ํญ์ ์ปดํจํ ์ ์คํ์ํค๋ ์์ ๋ญ๋น๋ฅผ ์ค์ผ์ ์๋ค.
๋๋ค์ ์๊ธ์ ๋ค์๊ณผ ๊ฐ๋ค.
2. S3 ๋ฒํท ์์ฑ
๋จผ์ ๊ธฐ์กด ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํ๋ S3๊ฐ ์๋ค๋ฉด ์๋ก์ด S3 ๋ฒํท์ ์์ฑํด์ค๋ค.
ํ๋ ๋ฒํท์์ ๊ฒฝ๋ก๋ฅผ ๋๋ ํด๋ ๊ฐ๋ฅ์ ํ์ง๋ง, ์ฌ๊ท ํธ์ถ์ด ๋ ์ ์๊ธฐ ๋๋ฌธ์ ๋ฒํท์ ๋๋๋ ๊ฒ์ AWS์์๋ ๊ถ์ฅํ๊ณ ์๋ค.
๋์ ๊ฒฝ์ฐ ์ด๋ ๊ฒ ์ด 3๊ฐ์ ๋ฒํท์ ์ด์ฉํ๊ณ ์๋ค.
1. jikgong-bucket: ๋์ค์ ๋ค์์ Lambda๋ฅผ ์์ฑํ ๋ ์์ค ์ฝ๋๊ฐ 10MB ์ด์์ผ ๊ฒฝ์ฐ S3์ ์ฌ๋ ค์ ์ฌ์ฉํด์ผํ๋๋ฐ, ๊ทธ๋ ์ฌ์ฉ
2. jikgong-image: ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํ ๋ฒํท
3. jikgong-resize-bucket: jikgong-image์ ์ฌ๋ผ๊ฐ ์ด๋ฏธ์ง๋ฅผ ๋ฆฌ์ฌ์ด์ง ํด ์ ์ฅํ ๋ฒํท
S3 ์์ฑ์ ๋ค๋ฅธ ๋ธ๋ก๊ทธ์ ์ ๋์์์ผ๋ ๋ฐ๋ก ๋ค๋ฃจ์ง ์๊ฒ ๋ค. (๊ฐ์ธ์ ์ผ๋ก ์ธํ๋ ๋ธ๋ก๊ทธ๊ฐ ์์ธํ๊ฒ ์ ์๋ ค์ฃผ์๋๋ฏ)
3. IAM ์ ์ฑ & ์ญํ ์์ฑ
AWS IAM ์ ์ฑ ์ ์์ฑํด์ค์ผํ๋ค.
์ ์ฑ ์์ฑ
์ง์ JSON์ ์ ๋ ฅํด์ค๋ค.
jikgong-image ๋ฒํท์ ๋ํ ๊ถํ์ s3:GetObject, jikgong-resize-bucket ์ ๋ํ ๊ถํ์ s3:PutObject, s3:PutObjectAcl ์ ๋ถ์ฌํด์ค๋ค.
๋์ ๊ฒฝ์ฐ์ PutObjectAcl ๊ถํ๊น์ง ๋ถ์ฌํด์ค์ผ Lambda ํจ์๊ฐ ์ ์์ ์ผ๋ก ๋์ํ๋ค. ์์ผ๋ฉด Access Denied ์๋ฌ๊ฐ ๋ด์๋ค.
AWS Lambda ์ฌ์ฉ ์ S3 Access Denied ์๋ฌ ํด๊ฒฐ
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:PutLogEvents",
"logs:CreateLogGroup",
"logs:CreateLogStream"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": "arn:aws:s3:::jikgong-image/*"
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl" // ์ด๊ฑฐ ํ์
],
"Resource": "arn:aws:s3:::jikgong-resize-bucket/*"
}
]
}
์ญํ ์์ฑ
๋ฐ๋ก ์์์ ๋ง๋ ์ ์ฑ ์ ์ฌ์ฉํ ์ญํ ์ ์์ฑํด์ค๋ค.
4. Lambda ํจ์์ ์ฌ์ฉํ ํจ์ ์์ฑ
์ด์ origin S3 ๋ฒํท์ ์ด๋ฏธ์ง๊ฐ ์ ๋ก๋ ๋์์๋ ์๋์ผ๋ก resized ๋ฒํท์ ๋ฆฌ์ฌ์ด์ง๋ ์ด๋ฏธ์ง๊ฐ ์ ์ฅ๋๋๋ก ํจ์๋ฅผ ๋ง๋ค์ด์ผํ๋ค. ์ฌ๋ฌ๊ฐ์ง ์ธ์ด๋ฅผ ์ง์ํ์ง๋ง ๋๋ Java17๋ก ์งํํ์๋ค.
Node.js๋ก ์งํํ ๊ฒฝ์ฐ ์ฝ์์ ์ฝ๋ ํธ์ง๊ธฐ๋ก ๋ฐ๋ก ์ฝ๋ ์์ฑ์ด ๊ฐ๋ฅํ์ง๋ง java17์ ํด๋น ๊ธฐ๋ฅ์ ์ง์ํ์ง ์๊ธฐ ๋๋ฌธ์ ์ธํ ๋ฆฌ์ ์ด ๋๋ ์ดํด๋ฆฝ์ค๋ฅผ ์ด์ฉํด ํจ์(๋ฉ์๋)๋ฅผ ์์ฑ ํ zipํ์ผ์ ๋น๋ํ๋ ๊ณผ์ ์ ๊ฑฐ์ณ์ผ ํ๋ค.
- build.gradle ์์กด์ฑ ์ถ๊ฐ
implementation group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.1'
implementation group: 'com.amazonaws', name: 'aws-lambda-java-events', version: '3.7.0'
implementation group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.11.969'
- lambda ํจ์ ์์ฑ
์๋ ํจ์๋ AWS์์ ์ ๊ณตํ๋ ์์ ์ฝ๋๋ฅผ ๋์๊ฒ ๋ง๊ฒ ์์ ํ ์ฝ๋์ด๋ค.
package jikgong;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3EventNotificationRecord;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.PutObjectRequest;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.S3Event;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
public class ResizeHandler implements RequestHandler<S3Event, String> {
private static final float MAX_HEIGHT = 60;
private final String JPG_TYPE = (String) "jpg";
private final String JPG_MIME = (String) "image/jpeg";
private final String JPEG_TYPE = (String) "jpeg";
private final String JPEG_MIME = (String) "image/jpeg";
private final String PNG_TYPE = (String) "png";
private final String PNG_MIME = (String) "image/png";
private final String GIF_TYPE = (String) "gif";
private final String GIF_MIME = (String) "image/gif";
public String handleRequest(S3Event s3event, Context context) {
LambdaLogger logger = context.getLogger();
try {
S3EventNotificationRecord record = s3event.getRecords().get(0);
String srcBucket = record.getS3().getBucket().getName(); // ์๋ณธ ๋ฒํท ์ด๋ฆ
String key = record.getS3().getObject().getUrlDecodedKey(); // ๊ฐ์ฒด์ ํค (ํ์ผ ๊ฒฝ๋ก ๋ฐ ์ด๋ฆ)
String dstBucket = "jikgong-resize-bucket"; // ์์ ๋ ์ ์ฅ๋ ๋ฒํท ์ด๋ฆ
// ํ์ผ ํ์ฅ์ ์ถ์ถ
Matcher matcher = Pattern.compile(".*\\.([^\\.]*)").matcher(key);
if (!matcher.matches()) {
logger.log("Unable to infer image type for key " + key);
return "";
}
String imageType = matcher.group(1);
// ์ง์ํ์ง ์๋ ์ด๋ฏธ์ง ํ์์ธ ๊ฒฝ์ฐ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๊ณ ๋ฆฌํด
if (!(JPG_TYPE.equals(imageType)) && !(JPEG_TYPE.equals(imageType)) && !(PNG_TYPE.equals(imageType)) && !(GIF_TYPE.equals(imageType))) {
logger.log("Skipping non-image " + key);
return "";
}
// S3์์ ์ด๋ฏธ์ง ๊ฐ์ ธ์ค๊ธฐ
AmazonS3 s3Client = AmazonS3ClientBuilder.defaultClient();
S3Object s3Object = s3Client.getObject(new GetObjectRequest(srcBucket, key));
InputStream objectData = s3Object.getObjectContent();
// ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ง ์ฒ๋ฆฌ
BufferedImage srcImage = ImageIO.read(objectData);
int srcHeight = srcImage.getHeight();
int srcWidth = srcImage.getWidth();
int width = (int) (srcWidth * (MAX_HEIGHT / srcHeight)); // ๋น์จ์ ๋ง์ถฐ ๋๋น ๊ณ์ฐ
int height = (int) MAX_HEIGHT;
// ์ ์ด๋ฏธ์ง ๋ฒํผ ์์ฑ ๋ฐ ๊ทธ๋ํฝ ์ค์
BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = resizedImage.createGraphics();
g.setPaint(Color.white);
g.fillRect(0, 0, width, height);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(srcImage, 0, 0, width, height, null);
g.dispose();
// ๋ฐ์ดํธ ๋ฐฐ์ด๋ก ๋ณํ
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(resizedImage, imageType, os);
InputStream is = new ByteArrayInputStream(os.toByteArray());
ObjectMetadata meta = new ObjectMetadata();
meta.setContentLength(os.size()); // ๋ฉํ๋ฐ์ดํฐ ์ค์
meta.setContentType(getMimeType(imageType)); // MIME ํ์
์ค์
// ๋ฆฌ์ฌ์ด์ฆ๋ ์ด๋ฏธ์ง๋ฅผ S3์ ์ ์ฅ
logger.log("Writing to: " + dstBucket + "/" + key);
try {
s3Client.putObject(new PutObjectRequest(dstBucket, key, is, meta).withCannedAcl(CannedAccessControlList.PublicRead));
} catch (AmazonServiceException e) {
logger.log(e.getErrorMessage());
System.exit(1);
}
logger.log("Successfully resized " + srcBucket + "/" + key + " and uploaded to " + dstBucket + "/" + key);
return "Ok";
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// MIME ํ์
์ ๋ฐํํ๋ ๋ณด์กฐ ๋ฉ์๋
private String getMimeType(String imageType) {
switch(imageType) {
case JPG_TYPE:
case JPEG_TYPE:
return JPG_MIME;
case PNG_TYPE:
return PNG_MIME;
case GIF_TYPE:
return GIF_MIME;
default:
return "";
}
}
}
build.gradle์ ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ฅผ ์ถ๊ฐํ๊ณ `gradle build`๋ฅผ ์งํํ์.
task buildZip(type: Zip) {
from compileJava
from processResources
into('lib') {
from configurations.runtimeClasspath
}
}
๋๋ ์๋์ ๋ช ๋ น์ด๋ฅผ ํตํด zip ํ์ผ์ ์์ฑํด์ฃผ์๋ค.
./gradlew buildZip
์์ฑ์ด ์๋ฃ๋๋ฉด ์๋ ๊ฒฝ๋ก์ zip ํ์ผ์ด ์์ฑ๋๋ค.
์ด๋ ๊ฒ ์์ฑํ zip ํ์ผ์ S3์ ์ ๋ก๋ํด์ค๋ค.
ํ์ผ ์ฌ์ด์ฆ๊ฐ ์ปค S3๋ฅผ URL์ ํตํด AWS Lambda์ ์ ์ฉํ ์์ ์ด๋ค.
5. Lambda ํจ์ ์์ฑ
ํจ์ ์ด๋ฆ์ ์ ๋ ฅํ๊ณ ,
์ฌ์ฉํ ์ธ์ด(์์ฑํ ํจ์ ์ธ์ด)๋ฅผ ์ ํํด์ค๋ค. ์์์ Java17์ ์ด์ฉํ๊ธฐ ๋๋ฌธ์ ๋๋ Java17 ์ ํํ๋ค.
๊ทธ๋ฆฌ๊ณ ๊ธฐ์กด ์คํ ์ญํ ๋ณ๊ฒฝ์์, ์์์ ์์ฑํ๋ ์ญํ ์ ์ ํํด์ค๋ค.
์ฝ๋ ์์ค๋ฅผ ์ ๋ก๋ ํด์ค์ผํ๋ค. ์๊น ์์์ zipํ์ผ์ S3์ ์ ๋ก๋ํ๋๋ฐ, ๊ทธ URL์ ์ฌ๊ธฐ ์ ๋ ฅํด์ฃผ๋ฉด ๋๋ค.
๋ฐ๋ก ๋ฐ์ ๋ฐํ์ ์ค์ ์ ํธ์งํด์ค๋ค.
์ฌ์ฉํ ์ธ์ด์ ํจํค์ง, ํด๋์ค, ๋ฉ์๋ ๋ช ์ ์ ๋ ฅํด์ค๋ค. ํจํค์ง๋ช ์ ์์์ ์์ฑํ ํจ์ ๋งจ ์์ ๋ช ์๋์ด์์ ๊ฒ์ด๋ค.
๋ง์ง๋ง์ผ๋ก ํธ๋ฆฌ๊ฑฐ๋ฅผ ์ถ๊ฐํด์ค๋ค.
์ด๋ ๊ฒ ๋ฆฌ์ฌ์ด์ง์ ์ํํ๋ AWS Lambda ์ค์ ์ด ๋๋ฌ๋ค.
์ถ๊ฐ๋ก ๋๋ค์์ ์์ ํ ๋ก๊ทธ๋ค์ ๋ชจ๋ํฐ๋ง -> Cloud Watch Logs ์์ ํ์ธํ ์ ์๋ค.
6. ๊ฒฐ๊ณผ
S3์ ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํ์ฌ AWS Lambda์์ ๋ฆฌ์ฌ์ด์ง์ ์ ์ํํ๋์ง ํ ์คํธํด๋ณด์๋ค.
์ผ์ชฝ์ด ์๋ณธ ์ด๋ฏธ์ง์ ํฌ๊ธฐ์ด๊ณ , ์ค๋ฅธ์ชฝ์ ๋ฆฌ์ฌ์ด์ง ์์ ์ ์๋ฃํ ์ด๋ฏธ์ง์ด๋ค.
์๋นํ ํฌ๊ธฐ ์ฐจ์ด๋ฅผ ๋ณด์ฌ์ค๋ค.
7. ํ๋ก์ ํธ์์ ๋ฆฌ์ฌ์ด์ง๋ ์ด๋ฏธ์ง ํ์ฉ
์์์ jikgong-image ๋ฒํท์ ๋ค์ด๊ฐ๋ ๋ชจ๋ ์ด๋ฏธ์ง์ ์ ๋ถ ํธ๋ฆฌ๊ฑฐ๊ฐ ๊ฑธ๋ ค resize๋ฅผ ์งํํ๊ฒ ๋๋๋ฐ ์ค์ ๋ก ์ธ๋ค์ผ ์ด๋ฏธ์ง์ ๋ํด์๋ง resize๋ฅผ ์งํํด์ผํ๋ค.
๋ฐ๋ผ์ ์๋์ ๊ฐ์ด Prefix ์กฐ๊ฑด์ ๋์ด thumbnail๋ง ์ฒ๋ฆฌํ๋๋ก ๋ณ๊ฒฝํ์๋ค.
์ด์ ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํ ๋ ์ธ๋ค์ผ์ ๋ํด์ storeName ์์ thumbnail prefix๋ฅผ ๋ถ์ฌ์ฃผ๋ฉด ๊ทธ ์ด๋ฏธ์ง์ ๋ํด์๋ง resize๋ฅผ ์งํํ๊ฒ ๋ ๊ฒ์ด๋ค.
- ์ ์ฅํ ๋ prefix ์ค์
// unique ์ด๋ฆ ์์ฑ
String storeImageName = createStoreImageName(extension);
// ์ธ๋ค์ผ ์ด๋ฏธ์ง๋ผ๋ฉด Prefix๋ฅผ ๋ฑ๋กํด AWS Lambda๊ฐ ์คํ๋๋๋ก ์ธํ
String prefix = isFirst ? "thumbnail_" : "";
storeImageName = "jobPost/" + prefix + storeImageName;
- thumbnail url ์กฐํ
// ๋ฒํท์์ ์ด๋ฏธ์ง ์กฐํ
public String getImgPath(String fileName) {
if (amazonS3Client.doesObjectExist(bucket, fileName)) {
return amazonS3Client.getUrl(bucket, fileName).toString();
} else {
throw new CustomException(ErrorCode.S3_NOT_FOUND_FILE_NAME);
}
}
// resize๋ฒํท์์ ์ด๋ฏธ์ง ์กฐํ
public String getThumbnailImgPath(String fileName) {
if (amazonS3Client.doesObjectExist(resize_bucket, fileName)) {
return amazonS3Client.getUrl(resize_bucket, fileName).toString();
} else {
// resize_bucket์ ํ์ผ์ด ์์ ๊ฒฝ์ฐ, ๋ฉ์ธ bucket์์ ๊ฒ์
if (amazonS3Client.doesObjectExist(bucket, fileName)) {
log.warn("resize_bucket ์์ ์กฐํ๋ฅผ ์๋ํ์ง๋ง, bucket์๋ง ์กด์ฌ");
return amazonS3Client.getUrl(bucket, fileName).toString();
} else {
// ๋ ๋ฒํท ๋ชจ๋์ ํ์ผ์ด ์์ ๊ฒฝ์ฐ ์์ธ ๋ฐ์
throw new CustomException(ErrorCode.S3_NOT_FOUND_FILE_NAME);
}
}
}
๋์ ํ๋ก์ ํธ์์ ๋ฆฌ์ฌ์ด์ง๋ ์ด๋ฏธ์ง๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ๋ชจ์ง ๊ณต๊ณ ๋ชฉ๋ก์ ์กฐํํ ๋์ด๋ค. ์ด๋ ๋ง์ ์ธ๋ค์ผ ์ด๋ฏธ์ง๋ค์ ์์ ํฌ๊ธฐ๋ก ๋ณด์ฌ์ฃผ๊ฒ ๋๋ค. ๋ง์ฝ ์ฌ๊ธฐ ์ฌ์ฉ๋ ์ด๋ฏธ์ง๋ก ์๋ณธ ์ด๋ฏธ์ง๋ฅผ ์ฌ์ฉํ๊ฒ ๋ ๊ฒฝ์ฐ ๋ก๋ฉ ์๊ฐ์ด ๊ธธ์ด์ง๊ฒ๋๋ค.
๋ฐ๋ผ์ ์ผ์๋ฆฌ ๋ชจ์ง๊ณต๊ณ ๋ชฉ๋ก ์กฐํ ์ thumbnail ์ด๋ฏธ์ง๋ฅผ ์ฐพ์ resize๋ url์ ๋ฐํํ ์ ์๋๋ก ์ฝ๋๋ฅผ ์งฐ๋ค.
์๋๋ ์ผ๋ถ ์ฝ๋์ด๋ค.
// querydsl
Page<JobPost> jobPostPage = jobPostRepository.getMainPage(worker.getId(), techList, workDateList, scrap, meal, park, location, sortType, pageable);
List<JobPostListResponse> jobPostListResponseList = jobPostPage.getContent().stream()
.map(jobPost -> {
// Thumbnail ์ด๋ฏธ์ง ์ถ์ถ
Optional<JobPostImage> thumbnailImage = jobPost.getJobPostImageList().stream()
.filter(JobPostImage::isThumbnail)
.findFirst();
// Thumbnail URL์ ์ถ์ถํ๊ณ , ์ธ๋ค์ผ ์ด๋ฏธ์ง๊ฐ ์์ ๊ฒฝ์ฐ null ์ฒ๋ฆฌ
String thumbnailS3Url = thumbnailImage
.map(JobPostImage::getStoreImgName) // Optional์ด ๋น์ด์์ง ์๋ค๋ฉด getStoreImgName ์คํ
.map(s3Handler::getThumbnailImgPath) // s3Handler๋ฅผ ์ฌ์ฉํด url ์กฐํ
.orElse(null); // Thumbnail ์ด๋ฏธ์ง๊ฐ ์์ผ๋ฉด null ๋ฐํ
return JobPostListResponse.from(jobPost, location, thumbnailS3Url);
})
.collect(Collectors.toList());
8. ์ฑ๋ฅ ๋น๊ต
๋์ผํ ์ฌ์ง์ resize๋ ์ธ๋ค์ผ ์ด๋ฏธ์ง url์ ๋๋๋ง ํ์ ๋ Latency์, ์๋ณธ ์ด๋ฏธ์ง๋ฅผ ๋๋๋ง ํ์ ๋ Latency์ด๋ค.
์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ค๋๋ฐ๋ง ๊ณ์ฐํ๋ฉด
๋ฆฌ์ฌ์ด์ง๋ ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ค๋๋ฐ ์ด 926ms๊ฐ ๊ฑธ๋ ธ๊ณ , ์๋ณธ ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ค๋๋ด 19090ms๊ฐ ๊ฑธ๋ ธ๋ค.
๋๋ต 95.15%์ ์ฑ๋ฅ์ ํฅ์์ํฌ ์ ์์๋ค.