이호영 이호영 2025-04-21
친구톡 진행중
@69717924d58c23192a5cf71c8bd3897d6658a638
src/main/java/itn/let/kakao/kakaoComm/KakaoSendUtil.java
--- src/main/java/itn/let/kakao/kakaoComm/KakaoSendUtil.java
+++ src/main/java/itn/let/kakao/kakaoComm/KakaoSendUtil.java
@@ -269,6 +269,215 @@
 		return kakaoSendAdvcListVO;
 	}
 	
+	/**
+	 * @methodName	: populateSendListsFT 
+	 * @author		: 이호영
+	 * @date		: 2025. 4. 18.
+	 * @description	: 
+	 * @return : List<KakaoSendAdvcVO>
+	 * @param kakaoVO
+	 * @param isNotified
+	 * @param statusResponse
+	 * @return
+	 * @throws Exception
+	 * 
+	 */
+	public List<KakaoSendAdvcVO> populateSendListsFT(KakaoVO kakaoVO, boolean isNotified, StatusResponse statusResponse) throws Exception {
+		
+		//사용자 현재 보유 금액 불러오기(문자 발송 금액 차감 이전 금액)
+//		String befCash = kakaoVO.getBefCash();
+		
+		
+		
+		List<KakaoSendAdvcVO> kakaoSendAdvcListVO = new ArrayList<>();
+		Calendar calendar = setupBaseDate(kakaoVO, isNotified);
+		
+		
+		
+		KakaoReturnVO templateDetail = kakaoApiTemplate.selectKakaoApiTemplateDetail(kakaoVO);
+		String templateContent = templateDetail.getTemplateContent(); // 알림톡 템플릿
+		kakaoVO.setTemplateContent(templateContent);
+		String templateTitle = templateDetail.getTemplateTitle();
+		
+		
+//		log.info(" + templateDetail :: [{}]", templateDetail);
+//		templateDetail.getButtonList().forEach(t->log.info(" + ButtonList :: [{}]", t.toString()));
+		
+		Boolean hasContentReplacement = this.replBooleanStrChecker(templateContent);
+		Boolean hasTitleReplacement = this.replBooleanStrChecker(templateTitle);
+		Boolean hasButtonReplacement = this.needsButtonReplacement(templateDetail.getButtonList());
+		
+		/** @jsonStr 필요유무 */
+		boolean hasTitleOrButtons = StringUtils.isNotEmpty(templateTitle)  
+				|| CollectionUtils.isNotEmpty(templateDetail.getButtonList());
+		
+		/** @jsonStr 반복유무 */
+		boolean needsJsonReplacement = hasTitleReplacement || hasButtonReplacement;
+		String sharedJsonStr = null;
+		
+		String subMsgTxt = kakaoVO.getSubMsgTxt(); // 실패 대체 문자
+		
+		// 시스템 기본 단가 정보 불러오기
+		JoinSettingVO sysJoinSetVO = mjonMsgDataService.selectJoinSettingInfo();
+		// 사용자 개인 단가 정보 불러오기
+		MberManageVO mberManageVO = mjonMsgDataService.selectMberManageInfo(kakaoVO.getUserId());
+		
+		
+		
+		
+		/** @MSGID KEY값 */
+		List<String> idList = mjonCommon.getNextCustomMsgCId(kakaoVO.getVarListMap().size());
+//		for (int i = 0; i < kakaoSendAdvcListVO.size(); i++) {
+//			kakaoSendAdvcListVO.get(i).setMsgId(idList.get(i));
+//			kakaoSendAdvcListVO.get(i).setBizJsonName(idList.get(i));
+//		}
+		
+		
+		
+		// 분할 건수 카운터
+		int counter = 0;  
+		/** @Map에 총 갯수가 수신자 갯수와 동일함 */
+		List<Map<String, String>> varList = kakaoVO.getVarListMap();
+		for (int i = 0; i < varList.size(); i++) {
+			//		for(Map<String, String> variables : kakaoVO.getVarListMap()) {
+			// 치환 데이터
+			Map<String, String> variables = varList.get(i);
+			log.info("");
+			
+			/** @공통 기본값 */		
+			KakaoSendAdvcVO sendVO = createSendVO(kakaoVO);
+			String msgId = idList.get(i);
+			sendVO.setMsgId(msgId);
+			
+			// step1
+			// Step 1-1: 값 치환 및 수신번호 셋팅
+			// Step 1-2: 수신자 정보 설정 (callToList는 항상 설정).
+			if (variables.containsKey("callToList")) {
+				sendVO.setCallTo(variables.get("callToList"));
+				variables.remove("callToList"); // 사용 후 제거.
+			}
+			
+			/** @Step1-3: 템플릿 치환데이터 설정 */		
+			String templateContentTemp = templateContent;
+			String templateTitleTemp = templateTitle;
+			if (hasContentReplacement) {
+				templateContentTemp = mjonCommon.ATReplaceTemplateVariables(templateContent, variables);
+				if(hasTitleReplacement) {
+					templateTitleTemp = mjonCommon.ATReplaceTemplateVariables(templateTitle, variables);
+				}
+			}
+			/** @버튼 치환 */			// 버튼 리스트가 있으면 치환 수행, 항상 sendVO에 설정
+			List<KakaoButtonVO> buttonList = templateDetail.getButtonList();
+			if(hasButtonReplacement) {
+				buttonList = replaceButtonLinks(buttonList, variables);
+			}
+			sendVO.setButtonList(buttonList);
+			
+			sendVO.setTemplateTitle(templateTitleTemp);
+			sendVO.setTemplateContent(templateContentTemp);
+			
+			
+			// Step 1-4: 실패 대체 문자 치환데이터 설정
+			if("Y".equals(kakaoVO.getSubMsgSendYn())) { // 대체문자가 있나?
+				if ("Y".equals(kakaoVO.getSubMsgTxtReplYn())) { // 치환데이터가 있나?
+					subMsgTxt = mjonCommon.ATReplaceTemplateVariables(subMsgTxt, variables);
+				}
+				sendVO.setSubMsgTxt(subMsgTxt);// 실패 
+			}
+			sendVO.setSubMsgSendYn(kakaoVO.getSubMsgSendYn()); 
+			
+			
+			/*
+						log.info("kakaoSendAdvcVO Details: [callTo={}\n, templateContent=\n{}\n, subMsgTxt=\n{}]\n\n\n\n",
+								kakaoSendAdvcVO.getCallTo(),
+								kakaoSendAdvcVO.getTemplateContent(),
+								kakaoSendAdvcVO.getSubMsgTxt()
+								);
+			 */
+			
+			// Step1 END
+			
+			
+// step3
+// 바이트 수 체크 및 금액설정
+			
+			
+			Float kakaoAtPrice = mberManageVO.getKakaoAtPrice();
+			// 유효한 단가 계산
+			float shortPrice = getValidPrice(mberManageVO.getShortPrice(), sysJoinSetVO.getShortPrice());
+			float longPrice = getValidPrice(mberManageVO.getLongPrice(), sysJoinSetVO.getLongPrice());
+			
+			
+			String shortPStr = Float.toString(shortPrice);
+			String mmsPStr = Float.toString(longPrice);
+			
+			// 공통 가격 설정
+			sendVO.setSmsPrice(shortPStr);
+			sendVO.setMmsPrice(mmsPStr);
+			
+			
+			if("Y".equals(kakaoVO.getSubMsgSendYn())) {
+				int smsTxtByte = mjonCommon.getSmsTxtBytes(sendVO.getSubMsgTxt());
+				String sendType = getMsgType(smsTxtByte);
+				sendVO.setSubMsgType(sendType);
+				
+				if ("INVALID".equals(sendType)) {
+					statusResponseSet(statusResponse, HttpStatus.BAD_REQUEST, "전송 문자 길이를 초과하였습니다.");return kakaoSendAdvcListVO;
+				}
+				
+				boolean isMms = "MMS".equals(sendType);
+				sendVO.setEachPrice(isMms ? mmsPStr : shortPStr);
+				
+				
+			} else {
+				kakaoAtPrice = getValidPrice(mberManageVO.getKakaoAtPrice(), sysJoinSetVO.getKakaoAtPrice());
+				sendVO.setEachPrice( Float.toString(kakaoAtPrice) );
+			}
+			
+			
+			
+			// step4
+			// 예약 시간 설정 및 분할 데이터 설정
+			if ("Y".equalsIgnoreCase(kakaoVO.getReserveYn())  
+					&& "Y".equalsIgnoreCase(kakaoVO.getDivideChk())  
+					&& counter == Integer.parseInt(kakaoVO.getDivideCnt())) 
+			{
+				counter = 0;
+				calendar.add(Calendar.MINUTE, Integer.parseInt(kakaoVO.getDivideTime()));
+			}
+			counter++;
+			// 즉시 발송인경우 현재 시간 
+			// 예약인 경우 위에 설정한 시간 입력
+			sendVO.setReqDate(DATE_FORMATTER.format(calendar.getTime())); 
+			
+			
+			
+			/** @step5 전송 메세지 설정 json파일 만들기*/
+			// 타이틀과 버튼이 있고
+			if(hasTitleOrButtons) {
+				// 버튼과 타이틀에 치환데이터가 있으면 json String을 계속 생성
+				if(needsJsonReplacement) {
+					sharedJsonStr = kakaoApiJsonSave.kakaoApiJsonSave_advc(sendVO, templateDetail);
+					sendVO.setBizJsonName(msgId);
+					sendVO.setJsonStr(sharedJsonStr);
+				} else if (StringUtils.isEmpty(sharedJsonStr)) {
+					// 치환 데이터가 없고 아직 생성되지 않았으면 한 번만 생성
+					sharedJsonStr = kakaoApiJsonSave.kakaoApiJsonSave_advc(sendVO, templateDetail);
+					sendVO.setBizJsonName(idList.get(0));
+					sendVO.setJsonStr(sharedJsonStr);
+				}else {
+					sendVO.setBizJsonName(idList.get(0));
+				}
+				
+			}
+			log.info(" sendVO :: [{}]", sendVO);
+			kakaoSendAdvcListVO.add(sendVO);
+		}
+		
+		
+		return kakaoSendAdvcListVO;
+	}
+	
 	private Calendar setupBaseDate(KakaoVO kakaoVO, boolean isNotified) throws ParseException {
 		// 예약 시간 기본값 설정
 		Date now = new Date();
src/main/java/itn/let/kakao/kakaoComm/KakaoVO.java
--- src/main/java/itn/let/kakao/kakaoComm/KakaoVO.java
+++ src/main/java/itn/let/kakao/kakaoComm/KakaoVO.java
@@ -331,5 +331,56 @@
 			"\n ]";
 	}
 	
+	public String ftToString() {
+		StringBuilder sb = new StringBuilder("KakaoFTSendVO[");
+		sb.append("\n senderKey=[").append(senderKey).append("]");
+		sb.append("\n , imageFileName=[").append(imageFileName).append("]");
+		sb.append("\n , imageType=[").append(imageType).append("]");
+		sb.append("\n , imgTitle=[").append(imgTitle).append("]");
+		sb.append("\n , imgLink=[").append(imgLink).append("]");
+		sb.append("\n , templateContent=[").append(templateContent).append("]");
+		sb.append("\n , templateImageUrl=[").append(templateImageUrl).append("]");
+		sb.append("\n , smsTxtArea=[").append(getSubMsgTxt()).append("]");
+		sb.append("\n , subMsgSendYn=[").append(subMsgSendYn).append("]");
+		sb.append("\n , subMsgTxtReplYn=[").append(subMsgTxtReplYn).append("]");
+		sb.append("\n , subMsgType=[").append(subMsgType).append("]");
+		sb.append("\n , reserveYn=[").append(getReserveYn()).append("]");
+		sb.append("\n , menuTopTab=[").append(menuTopTab).append("]");
+		sb.append("\n , bizJsonYn=[").append(bizJsonYn).append("]");
+		sb.append("\n , senderKey=[").append(senderKey).append("]");
+		sb.append("\n , callFrom=[").append(getCallFrom()).append("]");
+		sb.append("\n , kakaoFtPrice=[").append(getEachPrice()).append("]");
+		sb.append("\n , reqDate=[").append(getReqDate()).append("]");
+		sb.append("\n , spamStatus=[").append(getSpamStatus()).append("]");
+		sb.append("\n , txtReplYn=[").append(getTxtReplYn()).append("]");
+		sb.append("\n , atSmishingYn=[").append(getAtSmishingYn()).append("]");
+//		sb.append("\n , tmpBtnSelect=[").append(getTmpBtnSelect()).append("]");
+		StringBuilder btnListSb = new StringBuilder("[");
+		if (buttonVOList != null && !buttonVOList.isEmpty()) {
+			String prefix = "";
+			for (KakaoButtonVO btn : buttonVOList) {
+				btnListSb.append(prefix).append(btn == null ? "null" : btn.toString());
+				prefix = ", ";
+			}
+		}
+		btnListSb.append("]");
+		sb.append("\n , buttonVOList=").append(btnListSb);
+
+
+		// mjonFTSendVOList 내용
+		StringBuilder ftList = new StringBuilder("[");
+		if (mjonFTSendVOList != null && !mjonFTSendVOList.isEmpty()) {
+			String prefix = "";
+			for (MjonFTSendVO vo : mjonFTSendVOList) {
+				ftList.append(prefix).append(vo == null ? "null" : vo.toString());
+				prefix = ", ";
+			}
+		}
+		ftList.append("]");
+		sb.append("\n , mjonFTSendVOList=").append(ftList);
+
+		sb.append("\n]");
+		return sb.toString();
+	}
 	
 }
src/main/java/itn/let/kakao/kakaoComm/kakaoApi/KakaoApiJsonSave.java
--- src/main/java/itn/let/kakao/kakaoComm/kakaoApi/KakaoApiJsonSave.java
+++ src/main/java/itn/let/kakao/kakaoComm/kakaoApi/KakaoApiJsonSave.java
@@ -210,6 +210,74 @@
 	
 	/*
 	 * 친구톡 발송시 이미지, 버튼 추가에 따른 Json 파일 생성 
+	 * 2025.04.18
+	 * 우영두
+	 * 파일은 하나만 생성해서 동일하게 사용함.
+	 * 
+	 * */
+	public String kakaoApiFTJsonSave_advc(KakaoVO kakaoVO) {
+		// json파일 저장
+		
+		
+		// 버튼리스트 JSON 생성
+		JSONArray buttonList = new JSONArray();
+		for(KakaoButtonVO buttonInfoVO : kakaoVO.getButtonVOList()) {
+			JSONObject buttonInfo = new JSONObject();
+			
+			buttonInfo.put("name", buttonInfoVO.getName());
+			buttonInfo.put("type", buttonInfoVO.getLinkType());
+			
+			if(buttonInfoVO.getLinkType().equals("WL")) {
+				buttonInfo.put("url_mobile", buttonInfoVO.getLinkMo());
+				buttonInfo.put("url_pc", buttonInfoVO.getLinkPc());
+			}else if(buttonInfoVO.getLinkType().equals("AL")) {
+				buttonInfo.put("scheme_ios", buttonInfoVO.getLinkIos());
+				buttonInfo.put("scheme_android", buttonInfoVO.getLinkAnd());
+			}else if(buttonInfoVO.getLinkType().equals("BC")) {
+				// 상담톡 진행시 등록해야함
+			}else if(buttonInfoVO.getLinkType().equals("BT")) {
+				// 봇 전환 시 전달
+			}
+			buttonList.add(buttonInfo);
+		}
+		
+		// 강조유형 JSON 생성
+		JSONObject templateImageInfo = new JSONObject();
+		JSONObject templateImageExtInfo = new JSONObject();
+		String imageType = kakaoVO.getImageType();
+		
+		if(!imageType.equals("")) {
+			templateImageInfo.put("img_url", kakaoVO.getTemplateImageUrl());
+			templateImageInfo.put("img_link", kakaoVO.getImgLink());
+		}
+		
+		if(imageType.equals("W")) {
+			templateImageExtInfo.put("wide", "Y");
+		}
+		
+		
+		JSONObject jo = new JSONObject();
+		
+		if(buttonList.size() != 0) {
+			jo.put("button", buttonList);
+		}
+		
+		if(templateImageInfo.size() != 0) {
+			jo.put("image", templateImageInfo);
+		}
+		
+		if(templateImageExtInfo.size() != 0) {
+			jo.put("extra", templateImageExtInfo);
+		}
+		
+		// 입력 json 데이터를 파일로 변경
+		String jsonStr = jo.toString();
+		
+		return jsonStr;
+	}
+
+	/*
+	 * 친구톡 발송시 이미지, 버튼 추가에 따른 Json 파일 생성 
 	 * 2024.01.17
 	 * 우영두
 	 * 파일은 하나만 생성해서 동일하게 사용함.
@@ -298,7 +366,7 @@
 			// 입력 json 데이터를 파일로 변경
 			String jsonStr = jo.toString();
 			System.out.println("jsonFileName  : "+jsonFileName);
-		
+			
 			File outPut = new File(jsonFileName);
 			outPut.createNewFile();
 			
src/main/java/itn/let/kakao/kakaoComm/kakaoApi/KakaoFTJsonSave.java
--- src/main/java/itn/let/kakao/kakaoComm/kakaoApi/KakaoFTJsonSave.java
+++ src/main/java/itn/let/kakao/kakaoComm/kakaoApi/KakaoFTJsonSave.java
@@ -30,115 +30,6 @@
 	
 	static String json;
 	
-	@SuppressWarnings("unchecked")
-	public String kakaoApiJsonSave(KakaoVO kakaoVO, String[] varValInfo) {
-		// json파일 저장
-		
-		
-		Date nowDate = new Date();
-		SimpleDateFormat todayFrom = new SimpleDateFormat("yyyyMMdd");
-		SimpleDateFormat timeFrom = new SimpleDateFormat("HHmmss");
-		String jsonFileName = mjonBizJsonDir+"/"+kakaoVO.getUserId()+"/"+todayFrom.format(nowDate)+"/"+kakaoVO.getSendType(); // 아이디/날짜/타입
-		
-		String fileName = timeFrom.format(nowDate)+"_"+kakaoVO.getDestPhone()+".json";
-		
-		try {
-			
-			File userIdFile = new File(jsonFileName);
-			if(!userIdFile.exists()) {
-				userIdFile.mkdirs(); // 없으면 하위 디렉토리 까지 생성
-				jsonFileName = jsonFileName +"/"+fileName;
-			}else {
-				
-				jsonFileName = jsonFileName +"/"+fileName;
-				System.out.println("jsonFileName  : "+jsonFileName);
-				File file1 = new File(jsonFileName);
-				if (file1.isFile()) {
-					return jsonFileName;
-				}
-			}
-			
-			
-			
-			
-			KakaoReturnVO templateDetail = kakaoApiTemplate.selectKakaoApiTemplateDetail(kakaoVO);
-			
-			// 버튼리스트 JSON 생성
-			JSONArray buttonList = new JSONArray();
-			
-			// 버튼 수량 체크 후 진행
-			for(KakaoButtonVO buttonInfoVO : templateDetail.getButtonList()) {
-				JSONObject buttonInfo = new JSONObject();
-				
-				buttonInfo.put("name", buttonInfoVO.getName());
-				buttonInfo.put("type", buttonInfoVO.getLinkType());
-				
-				if(buttonInfoVO.getLinkType().equals("WL")) {
-					buttonInfo.put("url_mobile", buttonInfoVO.getLinkMo());
-					buttonInfo.put("url_pc", buttonInfoVO.getLinkPc());
-				}else if(buttonInfoVO.getLinkType().equals("AL")) {
-					buttonInfo.put("scheme_ios", buttonInfoVO.getLinkIos());
-					buttonInfo.put("scheme_android", buttonInfoVO.getLinkAnd());
-				}else if(buttonInfoVO.getLinkType().equals("BC")) {
-					// 상담톡 진행시 등록해야함
-				}else if(buttonInfoVO.getLinkType().equals("BT")) {
-					// 봇 전환 시 전달
-				}
-				buttonList.add(buttonInfo);
-			}
-			
-			
-			
-			
-			// Image JSON 생성
-			JSONObject imageJson = new JSONObject();
-			// img형과 wide 형인경우만 등록 (if문으로 제어)
-			imageJson.put("img_url", "등록된 이미지 URL");
-			imageJson.put("img_link", "이동 페이지 URL");
-			
-			
-			// Wide JSON 생성
-			JSONObject wideJson = new JSONObject();
-			// wide 형인경우만 등록(if문으로 제어)
-			wideJson.put("wide", "Y");
-						
-						
-						
-			
-			JSONObject jo = new JSONObject();
-			
-			if(imageJson.size() != 0) {
-				jo.put("image", imageJson);
-			}
-			if(wideJson.size() != 0) {
-				jo.put("extra", wideJson);
-			}
-			
-			if(buttonList.size() != 0) {
-				jo.put("button", buttonList);
-			}
-			
-			
-			
-			
-			// 입력 json 데이터를 파일로 변경
-			String jsonStr = jo.toString();
-			System.out.println("jsonFileName  : "+jsonFileName);
-		
-			File outPut = new File(jsonFileName);
-			outPut.createNewFile();
-			
-			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outPut), "euc-kr"));
-			bw.write(jsonStr);
-			bw.close();
-			
-		} catch (IOException e) {
-			System.out.println("json 생성 실패");
-			e.printStackTrace();
-		}
-		return jsonFileName;
-	}
-	
 	
 	@SuppressWarnings("unchecked")
 	public String kakaoApiJsonSave(KakaoVO kakaoVO) {
src/main/java/itn/let/kakao/user/kakaoFt/service/KakaoFriendsTalkService.java
--- src/main/java/itn/let/kakao/user/kakaoFt/service/KakaoFriendsTalkService.java
+++ src/main/java/itn/let/kakao/user/kakaoFt/service/KakaoFriendsTalkService.java
@@ -7,6 +7,6 @@
 
 public interface KakaoFriendsTalkService {
 
-	StatusResponse insertKakaoFtSandAjax_advc(KakaoVO kakaoVO, HttpServletRequest request);
+	StatusResponse insertKakaoFtSandAjax_advc(KakaoVO kakaoVO, HttpServletRequest request) throws Exception;
 
 }
src/main/java/itn/let/kakao/user/kakaoFt/service/impl/KakaoFriendsTalkServiceImpl.java
--- src/main/java/itn/let/kakao/user/kakaoFt/service/impl/KakaoFriendsTalkServiceImpl.java
+++ src/main/java/itn/let/kakao/user/kakaoFt/service/impl/KakaoFriendsTalkServiceImpl.java
@@ -1,16 +1,29 @@
 package itn.let.kakao.user.kakaoFt.service.impl;
 
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Service;
 
 import egovframework.rte.fdl.cmmn.EgovAbstractServiceImpl;
 import egovframework.rte.fdl.idgnr.EgovIdGnrService;
+import egovframework.rte.fdl.security.userdetails.util.EgovUserDetailsHelper;
+import itn.com.cmm.LoginVO;
+import itn.com.utl.fcc.service.EgovStringUtil;
+import itn.let.kakao.kakaoComm.KakaoSendAdvcVO;
+import itn.let.kakao.kakaoComm.KakaoSendUtil;
 import itn.let.kakao.kakaoComm.KakaoVO;
 import itn.let.kakao.user.kakaoFt.service.KakaoFriendsTalkService;
 import itn.let.mail.service.StatusResponse;
+import itn.let.mjo.mjocommon.MjonCommon;
+import itn.let.uss.umt.service.EgovUserManageService;
 import lombok.extern.slf4j.Slf4j;
 
 @Slf4j
@@ -23,11 +36,85 @@
 	@Resource(name="kakaoFriendsTalkTemplateDAO")
 	private KakaoFriendsTalkTemplateDAO kakaoFriendsTalkTemplateDAO;
 
+	/** userManageService */
+	@Resource(name = "userManageService")
+	private EgovUserManageService userManageService;
+
+	@Autowired
+	private MjonCommon mjonCommon;
+
+	
+	@Autowired
+	KakaoSendUtil kakaoSendUtil;
+	
 	@Override
-	public StatusResponse insertKakaoFtSandAjax_advc(KakaoVO kakaoVO, HttpServletRequest request) {
+	public StatusResponse insertKakaoFtSandAjax_advc(KakaoVO kakaoVO, HttpServletRequest request) throws Exception {
 		StatusResponse statusResponse = new StatusResponse();
 		
-		log.info(" + kakaoVO.toString() :: [{}]", kakaoVO.toString()); 
+//		log.info(" + kakaoVO.toString() :: [{}]", kakaoVO.toString()); 
+		
+		log.info(" + kakaoVO.toString() :: [{}]", kakaoVO.ftToString()); 
+		
+
+		
+		// 측정할 메소드 호출 전 시간 기록
+		Instant start = Instant.now();
+//		KakaoSendAdvcVO
+		
+		Map<String, Object> returnMap = new HashMap<>();
+
+		LoginVO loginVO = EgovUserDetailsHelper.isAuthenticated()
+				? (LoginVO) EgovUserDetailsHelper.getAuthenticatedUser()
+				: null;
+		String userId = loginVO == null ? "" : EgovStringUtil.isNullToString(loginVO.getId());
+
+		if (userId.equals("")) {
+			return new StatusResponse(HttpStatus.BAD_REQUEST, "로그인 후 이용이 가능합니다.");
+		}
+		
+		kakaoVO.setUserId(userId);
+
+		/**
+		 * 회원 정지된 상태이면 문자 발송이 안되도록 처리함 현재 로그인 세션도 만료 처리함
+		 */
+		boolean mberSttus = userManageService.selectUserStatusInfo(userId);
+		if (!mberSttus) {
+			request.getSession().invalidate();
+			// UNAUTHORIZED : 인증되지 않은 사용자가 접근하려고 할 때
+			return new StatusResponse(HttpStatus.UNAUTHORIZED,
+					"현재 고객님께서는 문자온 서비스 이용이 정지된 상태로 알림톡을 발송하실 수 없습니다. 이용정지 해제를 원하시면 고객센터로 연락주시기 바랍니다.");
+		}
+		
+
+
+/** @isHolidayNotified 
+ * @false : 알림 X
+ * @true  : 알림 O */
+		boolean isNotified = mjonCommon.processUserAndCheckAT(kakaoVO);
+		
+		
+		
+/** @카카오톡 전송 list 셋팅 -------------------------------------------*/
+		List<KakaoSendAdvcVO> kakaoSendAdvcListVO = kakaoSendUtil.populateSendListsFT(kakaoVO, isNotified, statusResponse);
+		if (statusResponse.getStatus() != null && !statusResponse.getStatus().equals(HttpStatus.OK)) {
+			log.error(" + populateSendLists 처리 중 오류 발생: {}", statusResponse.getMessage());
+			return statusResponse;
+		}
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
 		
 		
 		
src/main/webapp/WEB-INF/jsp/web/kakao/msgdata/ft/KakaoFriendsTalkMsgDataView.jsp
--- src/main/webapp/WEB-INF/jsp/web/kakao/msgdata/ft/KakaoFriendsTalkMsgDataView.jsp
+++ src/main/webapp/WEB-INF/jsp/web/kakao/msgdata/ft/KakaoFriendsTalkMsgDataView.jsp
@@ -234,7 +234,7 @@
 	setContentsLengForFriends(tmpContents);
 	
 	//초기 광고포함 여부 처리
-	var adFlagVal = $("input[name=ad_flag]:checked").val();
+	var adFlagVal = $("input[name=adFlag]:checked").val();
 	advTextChange(adFlagVal);
 	
 	var reserYn = $("input[name=reserYn]:checked").val();
@@ -373,18 +373,16 @@
 
    	}
 	
-	var url = "";
 	
-	if(fileExt == "jpg" || fileExt == "jpeg" || fileExt == "png"){
-		
-		url = "/web/mjon/kakao/template/sendKakaoFriendsTemplateImageUploadAjax.do";
-		
-	}else{
-		
-		alert('jpg, jpeg 파일만 업로드 할수 있습니다.');
-		return;
-		
+// 	if(fileExt == "jpg" || fileExt == "jpeg" || fileExt == "png"){
+	console.log('fileExt : ', fileExt);
+	if(fileExt != null && fileExt !== "jpg" && fileExt !== "jpeg" && fileExt !== "png"){
+	    alert("jpg, jpeg, png 파일만 업로드 할 수 있습니다.");
+	    return;
 	}
+
+	
+    var url = url = "/web/mjon/kakao/template/sendKakaoFriendsTemplateImageUploadAjax.do";
 	
 	$.ajax({
 		type : 'POST'
@@ -396,7 +394,9 @@
 		, processData: false
 		, contentType: false
 		, cache : false
-		, success : function(returnData, status){
+		, success : function(returnData){
+			console.log('returnData : ', returnData);
+			
 			if(returnData.result == "success") {
 				
 				var code = returnData.code;
@@ -602,7 +602,7 @@
 	var imageType = $("input[name=img_file_add]:checked").val();		// 첨부 이미지 종류(없음, 일반, 와이드 이미지)
 	var imageTitle = $("#imgTitle").val();								//첨부이미지 제목
 	var imageLink = $("#imgLink").val();								//첨부이미지 클릭시 이동 링크 주소
-	var inputTemplateAd = $("input[name=ad_flag]:checked").val();	// 광고성메시지 선택 여부
+	var inputTemplateAd = $("input[name=adFlag]:checked").val();	// 광고성메시지 선택 여부
 	var inputTemplateContent = $("#inputTemplateContent").val();		// 템플릿 내용
 	
 	var inputTemplateImageName = $("#templateImageName").val();	// 템플릿 이미지 파일명
@@ -842,236 +842,14 @@
 	}
 	
 
-	//수신번호 리스트 체크하기
-	var numCnt = 0;
-	var nameList = [];		//치환문자 이름
-	var phoneNum = [];	//받는사람
-	var rep1List = [];		//치환문자1
-	var rep2List = [];		//치환문자2
-	var rep3List = [];		//치환문자3
-	var rep4List = [];		//치환문자4
-	
-	var varValList = [];		//치환문자 연결시킬 변수 셋팅
 	
 	var selectedData = tableL.getRows();
-	
-	var varValStatus = true; //치환분자 데이터 체크용
 	
 	if(selectedData == "" || selectedData == null){
 		
 		alert("받는사람 주소를 한 건 이상 입력해주세요.");
 		return false;
 	
-	}else{ // 선택한 Row '-' 문자 삭제하기
-		
-		var txtReplYn = $("#txtReplYn").val();
-		
-		if(txtReplYn == 'Y'){//치환문자가 있는 경우 변수 치환 처리
-		
-			for(var i=0; i < selectedData.length; i++){
-				
-				var nmStatus = false;
-				var rep1Status = false;
-				var rep2Status = false;
-				var rep3Status = false;
-				var rep4Status = false;
-				
-				
-				if(tmpContents.indexOf("\#{이름}")  > -1){
-					
-					nmStatus = true;
-				}
-				
-				if(tmpContents.indexOf("\#{1}")  > -1){
-					
-					rep1Status = true;
-				}
-				
-				if(tmpContents.indexOf("\#{2}")  > -1){
-					
-					rep2Status = true;
-				}
-				
-				if(tmpContents.indexOf("\#{3}")  > -1){
-					
-					rep3Status = true;
-				}
-				
-				if(tmpContents.indexOf("\#{4}")  > -1){
-					
-					rep4Status = true;
-				}
-				
-				//일괄변환 문자에 콤마(,)가 들어가있으면 배열로 넘길때 문제가 발생하여 특수문자(§)로 치환하여 넘겨주도록 한다.
-				var name = tableL.getRows()[i].getData().name;
-				var phone = removeDash(tableL.getRows()[i].getData().phone);
-				var rep1 = tableL.getRows()[i].getData().rep1;
-				var rep2 = tableL.getRows()[i].getData().rep2;
-				var rep3 = tableL.getRows()[i].getData().rep3;
-				var rep4 = tableL.getRows()[i].getData().rep4;
-				var varValStr = "";	//¶ 구분자
-				
-				if(phone == ""){
-					
-					alert("수신 목록에 핸드폰 번호가 없는 항목이 있습니다.");
-					return false;
-					
-				}else if(!checkHpNum(phone)){
-					
-					alert("수신 목록에 잘 못된 핸드폰 번호가 있습니다. 핸드폰 번호 : " + phone + " 입니다.");
-					return false;
-					
-				}else{
-					
-					if(typeof(name) != 'undefined' && name != null && name !=""){
-						if(!emojiCheck(name)){//이모지 체크 해주기
-							return false;
-						}
-						
-						//이름 치환변수가 있으면 저장
-						if(nmStatus){
-							//nameList[i] = name.replaceAll(",","§");
-							if(varValStr == ''){
-								
-								varValStr = name.replaceAll(",","§");
-							}else{
-								
-								varValStr = varValStr + "¶" +  name.replaceAll(",","§");
-								
-							}
-						}
-					}else{
-						if(nmStatus){
-							varValStatus = false;
-						}
-					}
-
-					
-					if(phone != '' && phone != null){
-						//연락처 변수 저장하기
-						phoneNum[i] = phone;
-						if(varValStr == ''){
-							varValStr = phone;	
-						}else{
-							varValStr = varValStr + "¶" +  phone;
-						}
-					}else{
-						varValStatus = false;
-					}
-					
-					if(typeof(rep1) != 'undefined' && rep1 != null && rep1 !=""){
-						if(!emojiCheck(rep1)){//이모지 체크 해주기
-							return false;
-						}
-						
-						if(rep1Status){
-							
-							if(varValStr == ''){
-								//rep1List[i] = rep1.replaceAll(",","§");
-								varValStr = rep1.replaceAll(",","§");
-							}else{
-								//rep1List[i] = rep1.replaceAll(",","§");
-								varValStr = varValStr + "¶" +  rep1.replaceAll(",","§");
-							}
-						}
-					}else{
-						if(rep1Status){
-							varValStatus = false;
-						}
-					}
-					
-					if(typeof(rep2) != 'undefined' && rep2 != null && rep2 !=""){
-						if(!emojiCheck(rep2)){//이모지 체크 해주기
-							return false;
-						}
-						
-						if(rep2Status){
-							
-							if(varValStr == ''){
-								varValStr = rep2.replaceAll(",","§");
-							}else{
-								//rep2List[i] = rep2.replaceAll(",","§");
-								varValStr = varValStr + "¶" +  rep2.replaceAll(",","§");
-							}
-						}
-					}else{
-						if(rep2Status){
-							varValStatus = false;
-						}
-					}
-					
-					
-					if(typeof(rep3) != 'undefined' && rep3 != null && rep3 !=""){
-						if(!emojiCheck(rep3)){//이모지 체크 해주기
-							return false;
-						}
-						
-						if(rep3Status){
-							
-							if(varValStr == ''){
-								varValStr = rep3.replaceAll(",","§");
-							}else{
-								//rep3List[i] = rep3.replaceAll(",","§");
-								varValStr = varValStr + "¶" +  rep3.replaceAll(",","§");
-							}
-						}
-					}else{
-						if(rep3Status){
-							varValStatus = false;
-						}
-					}
-					
-					
-					if(typeof(rep4) != 'undefined' && rep4 != null && rep4 !=""){
-						if(!emojiCheck(rep4)){//이모지 체크 해주기
-							return false;
-						}
-						
-						if(rep4Status){
-							
-							if(varValStr == ''){
-								varValStr = rep4.replaceAll(",","§");
-							}else{
-								//rep4List[i] = rep4.replaceAll(",","§");
-								varValStr = varValStr + "¶" +  rep4.replaceAll(",","§");
-							}
-						}
-					}else{
-						if(rep4Status){
-							varValStatus = false;
-						}
-					}
-					
-				}
-				
-				varValList[i] = varValStr;
-				
-			}
-		
-		}else{//치환문자가 없는 경우 휴대폰 번호만 입력
-			
-			for(var i=0; i < selectedData.length; i++){
-				
-				//일괄변환 문자에 콤마(,)가 들어가있으면 배열로 넘길때 문제가 발생하여 특수문자(§)로 치환하여 넘겨주도록 한다.
-				var phone = removeDash(tableL.getRows()[i].getData().phone);
-				
-				if(phone == ""){
-					
-					alert("수신 목록에 핸드폰 번호가 없는 항목이 있습니다.");
-					return false;
-					
-				}else if(!checkHpNum(phone)){
-					
-					alert("수신 목록에 잘 못된 핸드폰 번호가 있습니다. 핸드폰 번호 : " + phone + " 입니다.");
-					return false;
-					
-				}else{
-					
-					phoneNum[i] = phone;
-					
-				}
-			}
-		}
 	}
 	
 	//예약문자 시간 체크
@@ -1111,51 +889,22 @@
 	
 	//대체문자 선택 및 내용 체크
 	var subMsgSendYn = "N";
-	
 	if($("#send_fail_check").is(":checked")){
-		
-		subMsgSendYn = "Y";
-		$("#subMsgSendYn").val("Y");
-		
-		//대체문자 체크사항 함수 호출
-		if(!fn_subMsgCheck()){ 
-			
-			/* $("#send_fail_check").prop("checked", false);
-			$(".replace_send_wrap").slideUp(400);
-			// 초기화 버튼 클릭
-			$('#failCheckInit').click();
-			$('.send_top .send_right .phone').css({'top': '0','transition': 'top .4s linear'}); */
-			return false;
-		
-		}
-		
-	}else{
-		
-		subMsgSendYn = "N";
-		$("#subMsgSendYn").val("N");
-		
+		subMsgSendYn = 'Y'
 	}
+	$("#subMsgSendYn").val(subMsgSendYn);
+		
 	
-	//수신전화번호 목록
-	$("#callToList").val(phoneNum);
 	
-	//치환변수 데이터 정보 목록(수신번호 포함, tabulator 정보 모두 )
-	$("#varValList").val(varValList);
-
 	//템플릿 내용 입력
 	$("#templateContent").val(tmpContents);
 	
 	//광고포함 여부
-	var adFlag = $("input[name=ad_flag]:checked").val();
+	var adFlag = $("input[name=adFlag]:checked").val();
 	$("#adFlag").val(adFlag);
+/* 	var adFlag = $("input[name=adFlag]:checked").val();
+	$("#adFlag").val(adFlag); */
 	
-	//치환문자에 대한 데이터 누락 체크
-	if(!varValStatus){
-		
-		alert("특정문구 일괄변환에 대한 일부 데이터가 누락된 부분이 있습니다. 데이터를 확인해 주세요.");
-		return false;
-		
-	}
 	
 	if(!confirm("친구톡을 발송하시겠습니까?")){
 		
@@ -1241,12 +990,18 @@
 		// 3. formData에 배열로 추가
 		formData["buttonVOList"] = buttonList;
 
-		// ✅ 4. 기존의 buttonVOList[0].xxx 형태 제거
+		// 4. 기존의 buttonVOList[0].xxx 형태 제거
 		Object.keys(formData).forEach(function(key) {
-		    if (/^buttonVOList\[\d+\]\./.test(key)) {
-		        delete formData[key];
-		    }
+			if (/^buttonVOList\[\d+\]\./.test(key)) {
+			    delete formData[key];
+			}
 		});
+		
+		// VO에 정의되어있지 않는 필요없는 값은 제거
+		["adFlag", "img_file_add", "userMoney", "callToList"].forEach(function(key) {
+			  delete formData[key];
+		});
+
 		
 		// 빈 값 제거
 		removeEmptyValues(formData);
@@ -1363,74 +1118,6 @@
 	$('#errorChk').val(val);
 }
 
-/*
- *대체문자 필수 항목 체크 
- *
- **/
- 
-function fn_subMsgCheck(){
-
-	if($('#callFromList').val() === ''){
-		
-		if(confirm('대체문자 전송을 위한 발신번호가 등록되지 않았습니다. \n대체문자 발신번호를 지금 등록하시겠습니까?')){
-			window.location="<c:out value='/web/user/sendNumberManage.do' />";
-		}
-		$("#send_fail_check").prop("checked", false);
-		
-		return false;
-		
-	}else{
-	
-		//발신번호 입력 처리
-		$("#callFrom").val(removeDash($('#callFromList').val()));
-		
-	}
-	
-	if($("#smsTxtArea").val() === ''){
-		
-		alert("대체문자 내용을 입력해 주세요.");
-		return false;
-		
-	}else{//대체문자에 치환문자 여부 체크
-		
-		var smsTxtArea = $("#smsTxtArea").val();
-		var replStatus = false;
-	
-		if(smsTxtArea.indexOf("\#{이름}")  > -1){
-			replStatus = true;
-		}
-		
-		if(smsTxtArea.indexOf("\#{1}")  > -1){
-			replStatus = true;
-		}
-		
-		if(smsTxtArea.indexOf("\#{2}")  > -1){
-			replStatus = true;
-		}
-		
-		if(smsTxtArea.indexOf("\#{3}")  > -1){
-			replStatus = true;
-		}
-		
-		if(smsTxtArea.indexOf("\#{4}")  > -1){
-			replStatus = true;
-		}
-		
-		if(replStatus){
-			$("#subMsgTxtReplYn").val("Y");
-		}else{
-			$("#subMsgTxtReplYn").val("N");
-		}
-		
-		$("#subMsgTxt").val(smsTxtArea);
-		
-	}
-	
-	
-	
-	return true;
-	
-}
 
 //문자 바이트수 계산하기 함수
 function thisFnByteString(contents){
@@ -1924,8 +1611,8 @@
 								<tr>
 									<th>광고포함 여부</th>
 									<td>
-<%-- 										<input type="radio" class="inputAdFlag" name="ad_flag" id="ad_Y" value="Y" <c:if test="${resultTemplateVO.adFlag eq 'Y'}">checked</c:if> ><label for="ad_Y">광고성 정보 포함</label> --%>
-										<input type="radio" class="inputAdFlag" id="ad_Y" value="Y" <c:if test="${resultTemplateVO.adFlag eq 'Y'}">checked</c:if> ><label for="ad_Y">광고성 정보 포함</label>
+										<input type="radio" class="inputAdFlag" name="adFlag" id="ad_Y" value="Y" <c:if test="${resultTemplateVO.adFlag eq 'Y'}">checked</c:if> ><label for="ad_Y">광고성 정보 포함</label>
+<%-- 										<input type="radio" class="inputAdFlag" id="ad_Y" value="Y" <c:if test="${resultTemplateVO.adFlag eq 'Y'}">checked</c:if> ><label for="ad_Y">광고성 정보 포함</label> --%>
 									</td>
 								</tr>
 								<tr>
Add a comment
List