From d0a6292cc591d27e0465d8332a18b7345282574e Mon Sep 17 00:00:00 2001 From: "hehihoho3@gmail.com" Date: Thu, 5 Jun 2025 16:42:21 +0900 Subject: [PATCH] =?UTF-8?q?=EC=B9=9C=EA=B5=AC=ED=86=A1=20=EC=A7=84?= =?UTF-8?q?=ED=96=89=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kakaoApi/KakaoApiImageUpload.java | 122 ++++++++++++++- .../KakaoFriendsTalkTemplateController.java | 57 +++++++ .../itn/let/mail/service/StatusResponse.java | 1 + .../ft/KakaoFriendsTalkMsgDataView.jsp | 147 ++++++++++++------ src/main/webapp/publish/css/content.css | 24 +-- src/main/webapp/publish/js/content.js | 6 +- .../webapp/publish/kakao_friendtalk_send.html | 1 + 7 files changed, 296 insertions(+), 62 deletions(-) diff --git a/src/main/java/itn/let/kakao/kakaoComm/kakaoApi/KakaoApiImageUpload.java b/src/main/java/itn/let/kakao/kakaoComm/kakaoApi/KakaoApiImageUpload.java index 6b090627..1f119e31 100644 --- a/src/main/java/itn/let/kakao/kakaoComm/kakaoApi/KakaoApiImageUpload.java +++ b/src/main/java/itn/let/kakao/kakaoComm/kakaoApi/KakaoApiImageUpload.java @@ -5,6 +5,7 @@ import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -15,6 +16,7 @@ import javax.annotation.Resource; import javax.imageio.ImageIO; import javax.swing.ImageIcon; +import org.apache.commons.io.FilenameUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; @@ -30,6 +32,7 @@ import org.apache.http.util.EntityUtils; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; @@ -38,8 +41,11 @@ import itn.com.cmm.service.EgovFileMngUtil; import itn.com.cmm.service.FileVO; import itn.let.kakao.kakaoComm.KakaoReturnVO; import itn.let.kakao.kakaoComm.KakaoVO; +import itn.let.mail.service.StatusResponse; import itn.let.utl.fcc.service.EgovStringUtil; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Component("kakaoApiImageUpload") public class KakaoApiImageUpload { @@ -219,13 +225,13 @@ public class KakaoApiImageUpload { .addTextBody("bizId", mjonBizId) .addTextBody("apiKey", mjonBizKakaoApiKey) .addTextBody("imageType", kakaoVO.getImageType()) - .addTextBody("title", kakaoVO.getImgTitle()) - .addTextBody("link", kakaoVO.getImgLink()) + .addTextBody("title", "test") + .addTextBody("link", "https://maaa.com") .addTextBody("senderKey", kakaoVO.getSenderKey()) .addBinaryBody("image", new File(filePath),ContentType.MULTIPART_FORM_DATA,fullFileName) .build(); - httpPost.setEntity(httpEntity); + httpPost.setEntity(httpEntity); CloseableHttpResponse response = httpClient.execute(httpPost); @@ -245,6 +251,7 @@ public class KakaoApiImageUpload { String msg = object.get("message").toString(); String imgUrl = ""; + log.info(" : code :: [{}]", code); if(code.equals("200")) { imgUrl = object.get("image").toString(); } @@ -273,6 +280,113 @@ public class KakaoApiImageUpload { } + + + /** + * @methodName : kakaoApiImageUpload_advc + * @author : 이호영 + * @date : 2025. 6. 4. + * @description : kakaoApiImageUpload 수정 + * @return : StatusResponse + * @param kakaoVO + * @param files + * @param i + * @return + * + */ + public StatusResponse kakaoApiImageUpload_advc(KakaoVO kakaoVO, Map files, int fileKeyParam) { + try { + String storePathString = propertyService.getString("Globals.fileStorePath"); + File saveFolder = new File(storePathString); + if (!saveFolder.exists()) saveFolder.mkdirs(); + + for (MultipartFile file : files.values()) { + if (file.isEmpty()) continue; + + String originalName = file.getOriginalFilename(); + if (originalName == null || originalName.isEmpty()) continue; + + String ext = FilenameUtils.getExtension(originalName).toLowerCase(); + if (!ext.matches("jpg|jpeg|png")) { + return new StatusResponse(HttpStatus.BAD_REQUEST, "지원하지 않는 이미지 형식입니다."); + } + + long size = file.getSize(); + if (size > 5 * 1024 * 1024) { + return new StatusResponse(HttpStatus.BAD_REQUEST, "이미지 용량은 5MB 이내여야 합니다."); + } + + BufferedImage image = ImageIO.read(file.getInputStream()); + if (image == null) { + return new StatusResponse(HttpStatus.BAD_REQUEST, "이미지를 읽을 수 없습니다."); + } + + int width = image.getWidth(); + int height = image.getHeight(); + String type = kakaoVO.getImageType(); + + if ("W".equals(type)) { + if (width != 800 || height != 600) { + return new StatusResponse(HttpStatus.BAD_REQUEST, "와이드 이미지는 800x600 사이즈만 허용됩니다."); + } + } else { + float ratio = width / (float) height; + if (width < 500 || ratio < 0.5 || ratio > 0.75) { + return new StatusResponse(HttpStatus.BAD_REQUEST, "일반 이미지 조건 위반: 가로 500px 이상, 비율 2:1 미만 및 3:4 초과 불가"); + } + } + + String newName = EgovStringUtil.getTimeStamp() + fileKeyParam; + String filePath = storePathString + File.separator + newName + "." + ext; + file.transferTo(new File(filePath)); + + // 카카오 API 호출 + CloseableHttpClient httpClient = HttpClients.createDefault(); + String apiUrl = mjonBizUrl + "/v3/kakao/image/upload"; + + HttpPost httpPost = new HttpPost(apiUrl); + HttpEntity httpEntity = MultipartEntityBuilder.create() + .addTextBody("bizId", mjonBizId) + .addTextBody("apiKey", mjonBizKakaoApiKey) + .addTextBody("imageType", kakaoVO.getImageType()) + .addTextBody("title", kakaoVO.getImgTitle()) + .addTextBody("link", kakaoVO.getImgLink()) + .addTextBody("senderKey", kakaoVO.getSenderKey()) + .addBinaryBody("image", new File(filePath), ContentType.MULTIPART_FORM_DATA, newName + "." + ext) + .build(); + httpPost.setEntity(httpEntity); + + CloseableHttpResponse response = httpClient.execute(httpPost); + int statusCode = response.getStatusLine().getStatusCode(); + + if (statusCode == 200) { + String result = EntityUtils.toString(response.getEntity(), "UTF-8"); + JSONParser parser = new JSONParser(); + JSONObject object = (JSONObject) parser.parse(result); + + String code = object.get("code").toString(); + if ("200".equals(code)) { + Map returnMap = new HashMap<>(); + returnMap.put("imgUrl", object.get("image").toString()); + returnMap.put("fileName", originalName); + + return new StatusResponse(HttpStatus.OK, "이미지 등록이 완료 되었습니다.", returnMap); + } else { + return new StatusResponse(HttpStatus.BAD_REQUEST, object.get("message").toString(), LocalDateTime.now()); + } + } else { + return new StatusResponse(HttpStatus.BAD_REQUEST, "카카오 API 요청 실패", LocalDateTime.now()); + } + } + } catch (Exception e) { + log.error("kakaoApiImageUpload_advc API Error", e); + return new StatusResponse(HttpStatus.BAD_REQUEST, "친구톡 이미지 등록에 실패했습니다.", LocalDateTime.now()); + } + return new StatusResponse(HttpStatus.BAD_REQUEST, "유효한 이미지 파일이 없습니다.", LocalDateTime.now()); + } + + + /** * @Method Name : kakaoApiTemplateImageUpload * @작성일 : 2023. 2. 16. @@ -528,5 +642,7 @@ public class KakaoApiImageUpload { return kakaoReturnVO; } + + } diff --git a/src/main/java/itn/let/kakao/user/kakaoFt/web/KakaoFriendsTalkTemplateController.java b/src/main/java/itn/let/kakao/user/kakaoFt/web/KakaoFriendsTalkTemplateController.java index 9b9540bd..869c7120 100644 --- a/src/main/java/itn/let/kakao/user/kakaoFt/web/KakaoFriendsTalkTemplateController.java +++ b/src/main/java/itn/let/kakao/user/kakaoFt/web/KakaoFriendsTalkTemplateController.java @@ -1,5 +1,6 @@ package itn.let.kakao.user.kakaoFt.web; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -8,11 +9,15 @@ import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.servlet.ModelAndView; @@ -21,6 +26,7 @@ import egovframework.rte.fdl.security.userdetails.util.EgovUserDetailsHelper; import egovframework.rte.ptl.mvc.tags.ui.pagination.PaginationInfo; import itn.com.cmm.EgovMessageSource; import itn.com.cmm.LoginVO; +import itn.com.cmm.RestResponse; import itn.com.cmm.service.EgovCmmUseService; import itn.com.utl.fcc.service.EgovStringUtil; import itn.let.kakao.kakaoComm.KakaoReturnVO; @@ -32,6 +38,7 @@ import itn.let.kakao.kakaoComm.kakaoApi.KakaoApiTemplate; import itn.let.kakao.kakaoComm.kakaoApi.KakaoFTJsonSave; import itn.let.kakao.kakaoComm.kakaoApi.service.KakaoApiService; import itn.let.kakao.user.kakaoFt.service.KakaoFriendsTalkTemplateService; +import itn.let.mail.service.StatusResponse; import itn.let.mjo.symbol.service.MjonSymbolService; import itn.let.mjo.symbol.service.MjonSymbolVO; import itn.let.uss.umt.service.EgovUserManageService; @@ -170,6 +177,56 @@ public class KakaoFriendsTalkTemplateController { return modelAndView; } + + @ResponseBody + @RequestMapping(value= {"/web/mjon/kakao/template/sendKakaoFriendsTemplateImageUploadAjax_advc.do"}) + public ResponseEntity sendKakaoFriendsTemplateImageUploadAjax_advc( + @ModelAttribute("kakaoVO") KakaoVO kakaoVO + , final MultipartHttpServletRequest multiRequest + ) throws Exception { + ModelAndView modelAndView = new ModelAndView(); + modelAndView.setViewName("jsonView"); + + try { + LoginVO loginVO = EgovUserDetailsHelper.isAuthenticated()? (LoginVO)EgovUserDetailsHelper.getAuthenticatedUser():null; + String userId = loginVO == null ? "" : EgovStringUtil.isNullToString(loginVO.getId()); + + if(StringUtils.isEmpty(userId)) { //KISA 보안취약점 조치 (2018-12-10, 이정은) + return ResponseEntity.ok( + new StatusResponse(HttpStatus.UNAUTHORIZED + , "로그인을 하셔야 이용 가능합니다." + , LocalDateTime.now() + ) + ); + } + + + + final Map files = multiRequest.getFileMap(); + /* if (!files.isEmpty()){ + + Map resultMap = kakaoApiImageUpload.kakaoApiImageUpload(kakaoVO, files, 0); + + //테스트 용 + modelAndView.addObject("code", resultMap.get("code")); + modelAndView.addObject("msg", resultMap.get("msg")); + modelAndView.addObject("imgUrl", resultMap.get("imgUrl")); + } + + modelAndView.addObject("result", "success");*/ + + return ResponseEntity.ok().body(kakaoApiImageUpload.kakaoApiImageUpload_advc(kakaoVO, files, 0)); + } catch (Exception e) { + e.printStackTrace(); + // TODO: handle exception + return ResponseEntity.ok().body(new StatusResponse( + HttpStatus.BAD_REQUEST + , "오류가 발생하였습니다." + , "" + )); + } + + } // 카카오 친구톡 템플릿 등록요청 diff --git a/src/main/java/itn/let/mail/service/StatusResponse.java b/src/main/java/itn/let/mail/service/StatusResponse.java index 50649c95..91844fda 100644 --- a/src/main/java/itn/let/mail/service/StatusResponse.java +++ b/src/main/java/itn/let/mail/service/StatusResponse.java @@ -5,6 +5,7 @@ import java.time.LocalDateTime; import org.springframework.http.HttpStatus; import itn.let.mjo.pay.service.RefundVO; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/src/main/webapp/WEB-INF/jsp/web/kakao/msgdata/ft/KakaoFriendsTalkMsgDataView.jsp b/src/main/webapp/WEB-INF/jsp/web/kakao/msgdata/ft/KakaoFriendsTalkMsgDataView.jsp index 69310d90..7ec599e4 100644 --- a/src/main/webapp/WEB-INF/jsp/web/kakao/msgdata/ft/KakaoFriendsTalkMsgDataView.jsp +++ b/src/main/webapp/WEB-INF/jsp/web/kakao/msgdata/ft/KakaoFriendsTalkMsgDataView.jsp @@ -414,7 +414,7 @@ function upImgClick(){ } //이미지 선택시 링크 정보 및 url 패턴 검사 - if(link == ''){ + /* if(link == ''){ $("#imgNm").text(""); $("#imgFile").val(""); @@ -427,7 +427,7 @@ function upImgClick(){ $("#imgFile").val(""); alert("이미지 URL 주소에는 http:// 또는 https://를 포함하여 입력해야 합니다."); return false; - } + } */ //첨부파일 선택 팝업 호출해주기 $("#imgFile").click(); @@ -485,6 +485,7 @@ function getTemplateImagUrl(){ } +// var url = url = "/web/mjon/kakao/template/sendKakaoFriendsTemplateImageUploadAjax_advc.do"; var url = url = "/web/mjon/kakao/template/sendKakaoFriendsTemplateImageUploadAjax.do"; $.ajax({ @@ -500,7 +501,32 @@ function getTemplateImagUrl(){ , success : function(returnData){ console.log('returnData : ', returnData); - if(returnData.result == "success") { + + + if (returnData.status === 'OK' || returnData.status === 200) { + alert(returnData.message); + + $("#templateImageName").val(fileName); + $("#imageFileName").val(fileName); + + // imgUrl은 object에 포함된 경우만 처리 + if (returnData.object) { + $("#templateImageUrl").val(returnData.object); + $(".kakao_image").css("display", "block"); + $("#kakaoImg").attr("src", returnData.object); + } + + } else { + alert(returnData.message || "이미지 등록 중 오류가 발생하였습니다."); + + // 첨부파일 정보 초기화 + $("#imgFile").val(""); + $("#imgNm").text(""); + return false; + } + }, + + /* if(returnData.result == "success") { var code = returnData.code; var msg = returnData.msg; @@ -549,7 +575,7 @@ function getTemplateImagUrl(){ alert("이미지 등록 중 오류가 발생하였습니다.!!!"); return false; } - }, + }, */ beforeSend : function(xmlHttpRequest) { //로딩창 show $('.loading_layer').addClass('active'); @@ -1574,49 +1600,82 @@ function updateButtons(){ 이미지 첨부 -
- checked > - checked > - checked > -
-

* 이미지 첨부 안내

-
    -
  • - 권장사이즈 : 720px * 720px
  • + checked > + + checked > + + checked > + + +
    +

    * 이미지 첨부 안내

    +
      +
    • - 권장사이즈 : 800px * 400px
    • - 제한사이즈 : 가로 500px 미만, 가로:세로 비율이 2:1 미만 또는 3:4 초과시 업로드 불가
    • -
    • - 파일형식 : jpg, png (최대 500kb)
    • -
    • - 이미지 첨부시 메시지 내용은 최대 400자까지 입력할 수 있습니다.
    • -
    -
    -
    -

    * 와이드 이미지 첨부 안내

    -
      -
    • - 권장사이즈 : 800px * 600px
    • - -
    • - 파일형식 : jpg, png (최대 2mb)
    • -
    • - 와이드 이미지 첨부시 메시지 내용은 최대 76자, 버튼 1개까지 입력할 수 있습니다.
    • -
    -
    -
-
-
    -
  • -

    이미지 제목

    - -
  • -
  • -

    이미지 클릭시 이동할 URL

    - -
  • -
  • -

    첨부파일 이미지

    - -
  • +
  • - 파일형식 : jpg, png (최대 5MB)
  • +
  • - 이미지 첨부 시 메시지 내용은 최대 400자, 버튼 5개까지 입력할 수 있습니다.
- - + + + + + + + + + + + + + + +
+
+

* 와이드 이미지 첨부 안내

+
    +
  • - 권장사이즈 : 800px * 600px
  • +
  • - 파일형식 : jpg, png (최대 5MB)
  • +
  • - 와이드 이미지 첨부시 메시지 내용은 최대 76자, 버튼 1개까지 입력할 수 있습니다.
  • +
+ + +
+
    +
  • + + +
    +

    이미지 없음

    + +
    +
  • +
  • +

    이미지 클릭시 이동할 URL

    +
  • +
+ + + <%--
    +
  • +

    이미지 제목

    + +
  • +
  • +

    이미지 클릭시 이동할 URL

    + +
  • +
  • +

    첨부파일 이미지

    + +
  • +
+ + --%> diff --git a/src/main/webapp/publish/css/content.css b/src/main/webapp/publish/css/content.css index 7e4477d5..9fbd61ef 100644 --- a/src/main/webapp/publish/css/content.css +++ b/src/main/webapp/publish/css/content.css @@ -1649,18 +1649,18 @@ button.check_validity:hover {border: 1px solid #a3a3a3;box-shadow: 0px 0px 5px .kakao_wrap .img_add_info_wrap .info_text{font-size: 15px; color: #555;} .kakao_wrap .img_add_info_wrap .info_text li{margin: 3px 0 0 0;} .kakao_wrap .img_file_add_wrap .info_title_text{font-weight: 500;margin:0 0 5px 0;} -.kakao_wrap .img_file_add_wrap .btn_img_upload{width: 251px;} -.kakao_wrap .img_file_add_wrap .img_file_info_wrap{width: 100%; max-width: 623px; border: 1px solid #e5e5e5; border-radius: 5px;} -.kakao_wrap .img_file_add_wrap .img_file_info_wrap li{display: flex;min-height: 60px;padding: 10px 20px; border-bottom: 1px solid #e5e5e5;justify-content: space-between; align-items: center; box-sizing: border-box;} -.kakao_wrap .img_file_add_wrap .img_file_info_wrap li:last-child{border-bottom: 0;} -.kakao_wrap .img_file_add_wrap .file_name{font-size: 16px; font-weight: 500;} -.kakao_wrap .img_file_add_wrap .btn_del{width: 30px;height: 30px;border: 1px solid #002c9a;border-radius: 5px;} -.kakao_wrap .img_file_add_wrap .img_url{width: calc(100% - 190px);} -.kakao_wrap .img_file_add_wrap .info_text{margin:0 0 10px 0;} -.kakao_wrap .img_file_add_wrap .info_text li{list-style:1.4;} -.kakao_wrap .img_file_add_wrap .img_file_wrap{display:inline-flex;width:calc(100% - 200px);} -.kakao_wrap .img_file_add_wrap .img_file_wrap .file_name{width:calc(100% - 28px);} -.kakao_wrap .img_file_add_wrap .img_file_wrap .file_name.file_none{color:#666;font-weight:300;line-height:1.8;} +.kakao_wrap .img_file_info_wrap .btn_img_upload{width: 251px;} +.kakao_wrap .img_file_info_wrap{display:none;width: 100%; max-width: 623px; border: 1px solid #e5e5e5; border-radius: 5px;margin:15px 0 0 0;} +.kakao_wrap .img_file_info_wrap li{display: flex;min-height: 60px;padding: 10px 20px; border-bottom: 1px solid #e5e5e5;justify-content: space-between; align-items: center; box-sizing: border-box;} +.kakao_wrap .img_file_info_wrap li:last-child{border-bottom: 0;} +.kakao_wrap .img_file_info_wrap .file_name{font-size: 16px; font-weight: 500;} +.kakao_wrap .img_file_info_wrap .btn_del{width: 30px;height: 30px;border: 1px solid #002c9a;border-radius: 5px;} +.kakao_wrap .img_file_info_wrap .img_url{width: calc(100% - 190px);} +.kakao_wrap .img_file_info_wrap .info_text{margin:0 0 10px 0;} +.kakao_wrap .img_file_info_wrap .info_text li{list-style:1.4;} +.kakao_wrap .img_file_info_wrap .img_file_wrap{display:inline-flex;width:calc(100% - 200px);} +.kakao_wrap .img_file_info_wrap .img_file_wrap .file_name{width:calc(100% - 28px);} +.kakao_wrap .img_file_info_wrap .img_file_wrap .file_name.file_none{color:#666;font-weight:300;line-height:1.8;} .kakaotalkset_cont .kakao_wrap .template_category{width: 270px;} .kakaotalkset_cont .kakao_wrap .emphasis_select{width: 250px;} diff --git a/src/main/webapp/publish/js/content.js b/src/main/webapp/publish/js/content.js index 3bb20dad..3367a936 100644 --- a/src/main/webapp/publish/js/content.js +++ b/src/main/webapp/publish/js/content.js @@ -522,15 +522,15 @@ $(document).ready(function () { $("input[name='img_file_add']").change(function () { if ($("input[name='img_file_add']:checked").attr("id") == "img_file_1") { $(".basic_img_add_wrap").show().siblings(".wide_img_add_wrap").hide(); - //$(".img_file_add_wrap").show(); + $(".img_file_info_wrap").show(); $(".kakao_image img").show().attr("src", "/publish/images/content/kakao_img_basic.jpg"); } else if ($("input[name='img_file_add']:checked").attr("id") == "img_file_2") { $(".wide_img_add_wrap").show().siblings(".basic_img_add_wrap").hide(); $(".kakao_image img").show().attr("src", "/publish/images/content/kakao_img_wide.jpg"); - //$(".img_file_add_wrap").show(); + $(".img_file_info_wrap").show(); } else { $(".img_add_info_wrap, .img_file_add_wrap").hide(); - $(".kakao_image img").hide(); + $(".kakao_image img, .img_file_info_wrap").hide(); } }); diff --git a/src/main/webapp/publish/kakao_friendtalk_send.html b/src/main/webapp/publish/kakao_friendtalk_send.html index 020f8c32..26f467ec 100644 --- a/src/main/webapp/publish/kakao_friendtalk_send.html +++ b/src/main/webapp/publish/kakao_friendtalk_send.html @@ -101,6 +101,7 @@ +

* 이미지 첨부 안내