From 58dfda02ed6386cb42edf73010945da88019b908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=B1=84=EB=AF=BC?= Date: Sun, 24 Aug 2025 00:31:25 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=94=A7=20Style:=20apply=20spotless=20?= =?UTF-8?q?formatting=20(feature/brandimage)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../likelion/picklbe/domain/brand/Brand.java | 1 - .../domain/brand/BrandImageResolver.java | 49 +++++------------- .../global/s3/controller/S3Controller.java | 3 +- .../picklbe/global/s3/service/S3Service.java | 50 ++++++++++--------- 4 files changed, 40 insertions(+), 63 deletions(-) diff --git a/src/main/java/com/likelion/picklbe/domain/brand/Brand.java b/src/main/java/com/likelion/picklbe/domain/brand/Brand.java index edd633b..40b9a89 100644 --- a/src/main/java/com/likelion/picklbe/domain/brand/Brand.java +++ b/src/main/java/com/likelion/picklbe/domain/brand/Brand.java @@ -122,4 +122,3 @@ public static Brand fromCodeSafe(String code) { return DEFAULT; } } - diff --git a/src/main/java/com/likelion/picklbe/domain/brand/BrandImageResolver.java b/src/main/java/com/likelion/picklbe/domain/brand/BrandImageResolver.java index 216797c..e54272a 100644 --- a/src/main/java/com/likelion/picklbe/domain/brand/BrandImageResolver.java +++ b/src/main/java/com/likelion/picklbe/domain/brand/BrandImageResolver.java @@ -17,9 +17,7 @@ public class BrandImageResolver { @Value("${app.cdn.default-file:mart_default.png}") private String defaultFile; - /** - * 원문(지점명/상호명/브랜드명 포함 가능)에서 Brand enum을 추정 - */ + /** 원문(지점명/상호명/브랜드명 포함 가능)에서 Brand enum을 추정 */ public Brand resolveBrand(String raw) { if (!StringUtils.hasText(raw)) { return Brand.DEFAULT; @@ -27,9 +25,7 @@ public Brand resolveBrand(String raw) { return Brand.fromStoreName(raw); } - /** - * explicitBrand(컬럼) 우선, 없으면 name(지점명)으로 추정 - */ + /** explicitBrand(컬럼) 우선, 없으면 name(지점명)으로 추정 */ public Brand resolveBrand(String explicitBrand, String name) { if (StringUtils.hasText(explicitBrand)) { // fromStoreName 가 브랜드명도 파싱한다는 전제 @@ -38,38 +34,28 @@ public Brand resolveBrand(String explicitBrand, String name) { return resolveBrand(name); } - /** - * Brand 코드만 필요할 때 - */ + /** Brand 코드만 필요할 때 */ public String resolveBrandCode(String raw) { return resolveBrand(raw).code(); } - /** - * Brand 코드만 이미 있는 경우(예: DB 조회) - */ + /** Brand 코드만 이미 있는 경우(예: DB 조회) */ public String resolveBrandCodeFromCode(String brandCode) { Brand b = Brand.fromCodeSafe(brandCode); // 없으면 DEFAULT 반환하도록 enum에 구현되어 있어야 함 return b.code(); } - /** - * 원문에서 바로 대표 이미지 URL - */ + /** 원문에서 바로 대표 이미지 URL */ public String resolveImageUrl(String raw) { return imageUrlFor(resolveBrand(raw)); } - /** - * explicitBrand + name 동시 고려 - */ + /** explicitBrand + name 동시 고려 */ public String resolveImageUrl(String explicitBrand, String name) { return imageUrlFor(resolveBrand(explicitBrand, name)); } - /** - * Brand가 이미 있는 경우 이미지 URL - */ + /** Brand가 이미 있는 경우 이미지 URL */ public String imageUrlFor(Brand brand) { // 1) brand가 null/DEFAULT 이거나 파일명이 비어있으면 기본 이미지 String filename = @@ -86,25 +72,19 @@ public String imageUrlFor(Brand brand) { return joinUrl(rtrimSlashes(baseUrl), rtrimSlashes(brandPath), filename); } - /** - * Brand 코드로 바로 이미지 URL (코드만 내려받는 API/쿼리 대응) - */ + /** Brand 코드로 바로 이미지 URL (코드만 내려받는 API/쿼리 대응) */ public String imageUrlForCode(String brandCode) { Brand b = Brand.fromCodeSafe(brandCode); return imageUrlFor(b); } - /** - * 원문에서 Brand와 이미지 URL/코드를 한 번에 - */ + /** 원문에서 Brand와 이미지 URL/코드를 한 번에 */ public ResolvedBrand resolve(String raw) { Brand b = resolveBrand(raw); return new ResolvedBrand(b, imageUrlFor(b), b.code()); } - /** - * explicitBrand + name 동시 입력 버전 - */ + /** explicitBrand + name 동시 입력 버전 */ public ResolvedBrand resolve(String explicitBrand, String name) { Brand b = resolveBrand(explicitBrand, name); return new ResolvedBrand(b, imageUrlFor(b), b.code()); @@ -142,11 +122,6 @@ private static String joinUrl(String... parts) { return sb.toString(); } - /** - * 편의 반환 DTO - */ - public record ResolvedBrand(Brand brand, String imageUrl, String code) { - - } + /** 편의 반환 DTO */ + public record ResolvedBrand(Brand brand, String imageUrl, String code) {} } - diff --git a/src/main/java/com/likelion/picklbe/global/s3/controller/S3Controller.java b/src/main/java/com/likelion/picklbe/global/s3/controller/S3Controller.java index ccc9196..e2703eb 100644 --- a/src/main/java/com/likelion/picklbe/global/s3/controller/S3Controller.java +++ b/src/main/java/com/likelion/picklbe/global/s3/controller/S3Controller.java @@ -33,8 +33,7 @@ public class S3Controller { @Operation(summary = "이미지 업로드 API", description = "이미지를 업로드하고 URL을 리턴받는 API") @PostMapping(value = "/image-upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity> uploadImage( - @RequestParam PathName pathName, MultipartFile file - ) { + @RequestParam PathName pathName, MultipartFile file) { S3Response s3Response = s3Service.uploadImage(pathName, file); return ResponseEntity.ok(BaseResponse.success("이미지 업로드에 성공했습니다.", s3Response)); diff --git a/src/main/java/com/likelion/picklbe/global/s3/service/S3Service.java b/src/main/java/com/likelion/picklbe/global/s3/service/S3Service.java index 6127491..718b5c7 100644 --- a/src/main/java/com/likelion/picklbe/global/s3/service/S3Service.java +++ b/src/main/java/com/likelion/picklbe/global/s3/service/S3Service.java @@ -1,25 +1,29 @@ package com.likelion.picklbe.global.s3.service; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.DeleteObjectRequest; -import com.amazonaws.services.s3.model.ListObjectsV2Request; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.PutObjectRequest; -import com.likelion.picklbe.global.config.S3Config; -import com.likelion.picklbe.global.exception.CustomException; -import com.likelion.picklbe.global.s3.dto.S3Response; -import com.likelion.picklbe.global.s3.entity.PathName; -import com.likelion.picklbe.global.s3.exception.S3ErrorCode; import java.io.ByteArrayInputStream; import java.util.Base64; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; + import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; +import com.likelion.picklbe.global.config.S3Config; +import com.likelion.picklbe.global.exception.CustomException; +import com.likelion.picklbe.global.s3.dto.S3Response; +import com.likelion.picklbe.global.s3.entity.PathName; +import com.likelion.picklbe.global.s3.exception.S3ErrorCode; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.DeleteObjectRequest; +import com.amazonaws.services.s3.model.ListObjectsV2Request; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + @Slf4j @Service @RequiredArgsConstructor @@ -95,17 +99,17 @@ public String base64UploadFile(PathName pathName, String base64Url) { public String createKeyName(PathName pathName) { return switch (pathName) { - case M01 -> s3Config.getM01Folder(); - case M02 -> s3Config.getM02Folder(); - case M03 -> s3Config.getM03Folder(); - case M04 -> s3Config.getM04Folder(); - case M05 -> s3Config.getM05Folder(); - case M06 -> s3Config.getM06Folder(); - case M07 -> s3Config.getM07Folder(); - case M08 -> s3Config.getM08Folder(); - case MARKET -> s3Config.getMARKETFolder(); - case BRAND -> s3Config.getBRANDFolder(); - } + case M01 -> s3Config.getM01Folder(); + case M02 -> s3Config.getM02Folder(); + case M03 -> s3Config.getM03Folder(); + case M04 -> s3Config.getM04Folder(); + case M05 -> s3Config.getM05Folder(); + case M06 -> s3Config.getM06Folder(); + case M07 -> s3Config.getM07Folder(); + case M08 -> s3Config.getM08Folder(); + case MARKET -> s3Config.getMARKETFolder(); + case BRAND -> s3Config.getBRANDFolder(); + } + '/' + UUID.randomUUID(); } From e9a24c3c92ffc77fe27915c4f9c671545899c63f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=B1=84=EB=AF=BC?= Date: Sun, 24 Aug 2025 02:22:14 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=90=9B=20Fix:=20martimage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../likelion/picklbe/domain/brand/Brand.java | 15 +++++++ .../service/BrandImagePromotionService.java | 40 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/main/java/com/likelion/picklbe/global/s3/service/BrandImagePromotionService.java diff --git a/src/main/java/com/likelion/picklbe/domain/brand/Brand.java b/src/main/java/com/likelion/picklbe/domain/brand/Brand.java index 40b9a89..331047b 100644 --- a/src/main/java/com/likelion/picklbe/domain/brand/Brand.java +++ b/src/main/java/com/likelion/picklbe/domain/brand/Brand.java @@ -67,6 +67,21 @@ public enum Brand { Pattern.compile("(농협|하나로)\\s*마트"), Pattern.compile("하나로클럽"), Pattern.compile("hanaro", Pattern.CASE_INSENSITIVE))), + GS_SUPER( + "gs-super", + "GS수퍼마켓", + "gs_super.png", + List.of( + Pattern.compile("지에스\\s*리테일"), + Pattern.compile("\\bGS\\s*수퍼", Pattern.CASE_INSENSITIVE), + Pattern.compile("\\bGS\\s*super", Pattern.CASE_INSENSITIVE))), + GS_THE_FRESH( + "gs-the-fresh", + "GS더프레시", + "gs_the_fresh.png", + List.of( + Pattern.compile("더\\s*프레시"), + Pattern.compile("\\bGS\\s*the\\s*fresh", Pattern.CASE_INSENSITIVE))), DEFAULT("default", "기타", null, List.of()); private final String code; diff --git a/src/main/java/com/likelion/picklbe/global/s3/service/BrandImagePromotionService.java b/src/main/java/com/likelion/picklbe/global/s3/service/BrandImagePromotionService.java new file mode 100644 index 0000000..e670840 --- /dev/null +++ b/src/main/java/com/likelion/picklbe/global/s3/service/BrandImagePromotionService.java @@ -0,0 +1,40 @@ +package com.likelion.picklbe.global.s3.service; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import com.likelion.picklbe.domain.brand.Brand; + +import com.amazonaws.services.s3.AmazonS3; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class BrandImagePromotionService { + + private final AmazonS3 s3; + + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + @Value("${cloud.aws.s3.path.brand:images/brand}") + private String brandPath; + + public String promote(String uuidKey, Brand brand) { + if (brand == null || brand.filename() == null) { + throw new IllegalArgumentException("고정 파일명이 없는 브랜드입니다: " + brand); + } + String srcKey = trimSlash(brandPath) + "/" + trimSlash(uuidKey); + String dstKey = trimSlash(brandPath) + "/" + brand.filename(); + + s3.copyObject(bucket, srcKey, bucket, dstKey); + s3.deleteObject(bucket, srcKey); + + return dstKey; + } + + private String trimSlash(String s) { + return s == null ? "" : s.replaceAll("^/+", "").replaceAll("/+$", ""); + } +}