이호영 이호영 2025-03-26
알림톡 엑셀 불러오기 수정
@9de4dbc620009a9687b223f490f7e9560e17188f
src/main/webapp/WEB-INF/jsp/web/kakao/msgdata/at/KakaoAlimtalkMsgDataView.jsp
--- src/main/webapp/WEB-INF/jsp/web/kakao/msgdata/at/KakaoAlimtalkMsgDataView.jsp
+++ src/main/webapp/WEB-INF/jsp/web/kakao/msgdata/at/KakaoAlimtalkMsgDataView.jsp
@@ -15,6 +15,8 @@
 <script type="text/javascript" src="<c:out value='/js/kakao/at/alimtalkExcel.js' />"></script>
 <script type="text/javascript" src="<c:out value='/js/kakao/at/priceClclt.js' />"></script>
 <script type="text/javascript" src="<c:out value='/js/common/popup.js' />"></script>
+<!-- 주소록 유효성 체크 공통유틸로 인해 추가 -->
+<script type="text/javascript" src="<c:url value='/js/web/addr/cmn.js?date=202409021440'/>"></script>
 <script type="text/javascript">
 var loginVO = '${loginVO}';
 // 체크박스 동적 바인딩
@@ -1540,7 +1542,7 @@
 															<button type="button" data-tooltip="popup06" class="btnType btnType7 popupAddr">주소록 불러오기</button>
 														</div>
 														<div class="btn_popup_wrap">
-															<button type="button" data-tooltip="popup02" class="btnType btnType7">엑셀 불러오기</button>
+															<button type="button" data-tooltip="popup07" class="btnType btnType7">엑셀 불러오기</button>
 														</div>
 														<div class="btn_popup_wrap">
 															<button type="button" data-tooltip="popup03" class="btnType btnType7 tab1">최근 전송내역</button>
@@ -1548,13 +1550,13 @@
 														<div class="btn_popup_wrap">
 															<button type="button" data-tooltip="popup03" class="btnType btnType7 tab2">자주보내는 번호</button>
 														</div>
-														<div class="btn_popup_wrap check_validity_wrap">
+														<!-- <div class="btn_popup_wrap check_validity_wrap">
 															<button type="button" class="btnType btnType7" id="check_validity">오류검사 <i class="qmMark"></i></button>
 															<div class="error_hover_cont send_hover_cont">
 																<p>휴대폰 번호 입력 시 해당 휴대폰 번호에 대한 형식이 어긋나거나 휴대폰 번호에 오류가 있는지 등을 검사하는 기능</p>
 																<span>(예시) 010-1234-0001(O) / 010-123-0001(X)</span>
 															</div>
-														</div>
+														</div> -->
 													</div>
 												</div>
 												<div class="list_bottom clearfix">
@@ -1794,7 +1796,7 @@
 		
 <!-- 주소록 불러오기 -->
 <div class="tooltip-wrap">
-	<div class="popup-com import_layer popup06" tabindex="0" data-tooltip-con="popup06" data-focus="popup06" data-focus-prev="popup06-close" style="width: 1000px">
+	<div class="popup-com import_layer popup06 adr_call_popup" tabindex="0" data-tooltip-con="popup06" data-focus="popup06" data-focus-prev="popup06-close" style="width: 1000px">
 		<div class="popup_heading">
 			<p><span>주소록 불러오기</p>
 			<button type="button" onClick="javascript:addrClose(); return false;">
@@ -1936,45 +1938,58 @@
 
 
 
+<!--         <div class="popup-com import_layer popup07" tabindex="0" data-tooltip-con="popup07" data-focus="popup07" data-focus-prev="popup07-close"> -->
+<%--             <%@include file="/WEB-INF/jsp/web/kakao/msgdata/include/atDataIncludeExcel.jsp" %> --%>
+<!--         </div> -->
 
 <!-- 엑셀 불러오기 -->
+
 <form id="excelToolTipForm" name="excelToolTipForm" method="post">
-	<div class="tooltip-wrap">
-		<div class="popup-com import_layer popup02" tabindex="0" data-tooltip-con="popup02" data-focus="popup02" data-focus-prev="popup02-close">
-			<div class="popup_heading">
-				<p><span>엑셀</span> 불러오기</p>
-				<button type="button" class="tooltip-close" data-focus="popup02-close"><img src="/publish/images/content/layerPopup_close.png" alt="팝업 닫기"></button>
-			</div>
-			<div class="layer_in">
-				<!-- 엑셀파일 불러오기 -->
-				<div class="hascont">
-					<div class="titBox">
-						<p>- 최대 2만 건까지 등록할 수 있습니다.</p>
-						<p>- [엑셀 불러오기]시 문서의 A, B열을 불러옵니다.(지원하는 파일 형식 : xls, xlsx)</p>
-						<p>- 휴대폰 항목은 숫자, 하이픈(-)만 인식하며, 번호 앞에 0이 생략되어도 정상 등록됩니다.
-						</p>
-						<!-- <button type="button" class="excel_btn" onclick="location.href='/cmm/fms/FileDown.do?atchFileId=FILE_000000000011651&fileSn=1'"><i></i>샘플파일 다운로드</button> -->
-						<button type="button" class="excel_btn" onclick="location.href='/download/msg/알림톡_엑셀주소록_등록양식.xlsx'"><i class="downroad"></i>샘플파일 다운로드</button>
-					</div>
-					<div class="attachedFile">
-						<label for="excelNm01" class="attachedFile_label">첨부파일</label>
-						<input type="text" id="excelNm01" value="" readonly>
-						<input type="file" id="excelFile01" accept=".xls, .xlsx" onchange="excelExportAjax(event); return false;" style="display:none"/>
-						<!-- <input type="file" id="excelFile01" accept=".xls, .xlsx" onchange="excelExport01(event); return false;" style="display:none"/> -->
-						<button type="button" class="btnType btnType6 c1">찾아보기</button>
-<!-- 						<p><span class="vMiddle">*</span> 첨부된 파일은 <span class="c_e40000">[추가]버튼을 클릭</span>하셔야 받는 사람에 등록됩니다.</p> -->
-						<p><span class="vMiddle">*</span> 첨부된 파일은 <span class="c_e40000">[추가]버튼을 클릭</span>하셔야 받는 사람에 등록됩니다.</p>
-					</div>
-				</div><!--// 엑셀파일 불러오기 -->
-				<div class="popup_btn_wrap2">
-					<button type="button" class="tooltip-close" data-focus="popup02-close"  data-focus-next="popup02" onclick="excelAddAjax()">추가</button>
-					<!-- <button type="button" class="tooltip-close" data-focus="popup02-close"  data-focus-next="popup02" onclick="excelAdd()">추가</button> -->
-					<button type="button" class="tooltip-close" data-focus="popup02-close"  data-focus-next="popup02">닫기</button>                      
-				</div>
-			</div>
-		</div>
-	</div><!--// 엑셀 불러오기 -->
+    <div class="tooltip-wrap">
+        <div class="popup-com import_layer popup07" tabindex="0" data-tooltip-con="popup07" data-focus="popup07" data-focus-prev="popup07-close">
+        
+            <%@include file="/WEB-INF/jsp/web/kakao/msgdata/include/atDataIncludeExcel.jsp" %>
+        </div>
+    </div>
 </form>
+<!-- 엑셀 불러오기 -->
+<!-- <form id="excelToolTipForm" name="excelToolTipForm" method="post">
+    <div class="tooltip-wrap">
+        <div class="popup-com import_layer popup07" tabindex="0" data-tooltip-con="popup07" data-focus="popup07" data-focus-prev="popup07-close">
+        
+        
+            <div class="popup_heading">
+                <p><span>엑셀</span> 불러오기</p>
+                <button type="button" class="tooltip-close" data-focus="popup07-close"><img src="/publish/images/content/layerPopup_close.png" alt="팝업 닫기"></button>
+            </div>
+            <div class="layer_in">
+                엑셀파일 불러오기
+                <div class="hascont">
+                    <div class="titBox">
+                        <p>- 최대 2만 건까지 등록할 수 있습니다.</p>
+                        <p>- [엑셀 불러오기]시 문서의 A, B열을 불러옵니다.(지원하는 파일 형식 : xls, xlsx)</p>
+                        <p>- 휴대폰 항목은 숫자, 하이픈(-)만 인식하며, 번호 앞에 0이 생략되어도 정상 등록됩니다.
+                        </p>
+                        <button type="button" class="excel_btn" onclick="location.href='/cmm/fms/FileDown.do?atchFileId=FILE_000000000011651&fileSn=1'"><i></i>샘플파일 다운로드</button>
+                        <button type="button" class="excel_btn" onclick="location.href='/download/msg/알림톡_엑셀주소록_등록양식.xlsx'"><i class="downroad"></i>샘플파일 다운로드</button>
+                    </div>
+                    <div class="attachedFile">
+                        <label for="excelNm01" class="attachedFile_label">첨부파일</label>
+                        <input type="text" id="excelNm01" value="" readonly>
+                        <input type="file" id="excelFile01" accept=".xls, .xlsx" onchange="excelExportAjax(event); return false;" style="display:none"/>
+                        <button type="button" class="btnType btnType6 c1">찾아보기</button>
+                        <p><span class="vMiddle">*</span> 첨부된 파일은 <span class="c_e40000">[추가]버튼을 클릭</span>하셔야 받는 사람에 등록됩니다.</p>
+                    </div>
+                </div>// 엑셀파일 불러오기
+                <div class="popup_btn_wrap2">
+                    <button type="button" class="tooltip-close" data-focus="popup07-close"  data-focus-next="popup07" onclick="excelAddAjax()">추가</button>
+                    <button type="button" class="tooltip-close" data-focus="popup07-close"  data-focus-next="popup07" onclick="excelAdd()">추가</button>
+                    <button type="button" class="tooltip-close" data-focus="popup07-close"  data-focus-next="popup07">닫기</button>                      
+                </div>
+            </div>
+        </div>
+    </div>// 엑셀 불러오기
+</form> -->
 <form id="excelVarFileForm" name="excelVarFileForm" method="post">
 	<input type="hidden" id="excelVarCnt" name="excelVarCnt" value="0"/>
 	<input type="hidden" id="excelVarList" name="excelVarList" value=""/>
 
src/main/webapp/WEB-INF/jsp/web/kakao/msgdata/include/atDataIncludeExcel.jsp (added)
+++ src/main/webapp/WEB-INF/jsp/web/kakao/msgdata/include/atDataIncludeExcel.jsp
@@ -0,0 +1,916 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@ taglib prefix="ui" uri="http://egovframework.gov/ctl/ui"%>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
+
+<script type="text/javascript" src="<c:url value='/publish/js/content.js'/>"></script>
+
+<script type="text/javascript">
+
+var $tableExcel = null; //엑셀입력 탭
+var $tableError = null; //엑셀입력 탭
+$(document).ready(function(){
+	
+	//Tabulator AJAX Data Loading
+	$tableError = new Tabulator("#tabulator_error", {
+		height:"255px",
+		width:"100%",
+	    layout:"fitColumns",
+	    autoColumns:false,
+	    headerHozAlign:"center", 
+	    validationMode:"highlight",
+	    clipboard:false,
+	    clipboardCopySelector:"table",
+	    clipboardPasteAction:"insert", // insert, update, replace
+	    placeholder:"등록 팝업에서 휴대폰을 선택 후 확인해주세요.", //fit columns to width of table (optional)
+	 	columns:[ //Define Table Columns
+// 		 	{title:"이름", field:"name", hozAlign:"center", headerHozAlign: "center", width:125},
+		 	{title:"휴대폰", field:"phone", hozAlign:"center", headerHozAlign: "center"},
+		 	{title:"미등록 결과", field:"result", hozAlign:"center", headerHozAlign: "center"}
+	 	]
+	});
+	
+
+	//Tabulator AJAX Data Loading
+	$tableExcel = new Tabulator("#tabulator_excel", {
+		height:"255px",
+		width:"100%",
+	    layout:"fitColumns",
+	    autoColumns:false,
+	    headerHozAlign:"center", 
+	    validationMode:"highlight",
+	    clipboard:false,
+	    clipboardCopySelector:"table",
+	    clipboardPasteAction:"insert", // insert, update, replace
+	    placeholder:"Excel 파일을 업로드 해주세요.", //fit columns to width of table (optional)
+	 	columns:[ //Define Table Columns
+	 		{formatter:"rowSelection", titleFormatter:"rowSelection",clipboard:false, headerHozAlign:"center", hozAlign:"center", headerSort:false, cellClick:function(e, cell){
+	 	        cell.getRow().toggleSelect();
+		 		}
+		 	}, 
+			{formatter:"rownum", align:"center" ,title:"No", hozAlign:"center", headerHozAlign:"center", width:50},
+		 	{title:"휴대폰", field:"addrPhoneNo", hozAlign:"center", headerHozAlign: "center", width:600, validator:["maxLength:100", "string"]}
+	 	],
+	 	validationFailed:function(cell, value, parameters){ // 유효성 체크 함수 
+	        var valid = cell.isValid();
+	 		if(!valid){
+	 			alert("양식에 맞지 않는 정보가 입력되었습니다.");
+	 			
+	 			//해당 셀 데이터 삭제
+	 			cell.setValue("");
+	 		}
+	 		return value % parameters.phone;
+	    },
+	});
+	
+	
+    
+	
+	// 타뷸레이터 width값 변경 시 위에 select width 값 변경
+	var titleArray = ["No","A","B","C","D","E","F"];
+
+	$tableExcel.on("columnWidth",function(column){
+		var titleIndex = titleArray.indexOf(column._column.definition.title);
+		titleIndex += 1;
+		
+		if(titleIndex != 0){
+			$('.select_adr_hd>div').eq(titleIndex).css('width', column._column.width);
+		}else{
+			$('.select_adr_hd>div').eq(0).css('width', column._column.width);
+			
+		}
+	});
+
+	$tableExcel.on("scrollHorizontal",function(left){
+	      $(".adr_excel").scrollLeft(left);
+	})
+
+
+	$(".adr_excel").on("scroll",function(){
+		$(".tabulator-tableholder").scrollLeft($(this).scrollLeft());
+	});
+
+	
+	
+
+    $("#excelFileC4").on("change", function(event) {
+    	var fileInfo =  event.target.files;
+        if(fileInfo.length > 0){
+            excelFileChange(fileInfo[0]);
+        } else {
+            fn_loadRemoveActive(); // 파일이 선택되지 않은 경우 로딩 상태 제거
+            setTimeout(() => { $(this).val(''); }, 0);  // 파일 선택 초기화
+        }
+    });
+	
+
+
+    $(document).on('click', '#btnAddrMassClose', function() {
+
+    	$('.field-selector').each(function() { $(this).val(''); });
+    	setAddrMassClose();
+    });
+    
+
+    $(document).on('click', '#closeBtn', function() {
+    	// 대량등록 닫기
+    	setAddrMassClose();
+    });
+    
+
+    
+
+ // 엑셀등록 닫기
+	function setAddrMassClose() {
+		$tableExcel.clearData();
+		$("#excelRowTotCnt").text(0); //총건수 수정
+		$("#excelRowDupCnt").text(0); //중복건수 수정
+		$("#excelRowErrorCnt").text(0); //중복건수 수정
+// 		dupliPhoneDataRealList.length = 0;	// 중복 휴대폰번호 초기화
+		
+		
+		// 중복 카운트
+		$("#errorPopDupCnt").text(0);
+		// 에러 카운트
+		$("#errorPopErrorCnt").text(0);
+		// 
+		$("#errorPopTotCnt").text(0);
+
+        // popup 영역
+        $tableError.clearData();
+		
+	}
+    
+  //#############################################################################################
+  //파일업로드 드래그앤 드롭
+  //#############################################################################################
+  var objDragAndDrop = $(".upload_area");
+  $(document).on("dragenter",".upload_area",function(e){
+    e.stopPropagation();
+    e.preventDefault();
+    //$(this).css('border', '2px solid #0B85A1');
+  });
+  $(document).on("dragover",".upload_area",function(e){
+    e.stopPropagation();
+    e.preventDefault();
+  });
+  $(document).on("drop",".upload_area",function(e){
+  	fn_loadAddActive();
+  	e.preventDefault();
+  	var files = e.originalEvent.dataTransfer.files;
+  	excelFileChange(files[0]);
+  });
+
+  $(document).on('dragenter', function (e){
+    e.stopPropagation();
+    e.preventDefault();
+  });
+  $(document).on('dragover', function (e){
+  	e.stopPropagation();
+  	e.preventDefault();
+  //objDragAndDrop.css('border', '2px dotted #0B85A1');
+  });
+  $(document).on('drop', function (e){
+    e.stopPropagation();
+    e.preventDefault();
+  });
+  //파일 드래그앤드롭 종료
+	
+	
+	
+	
+  
+
+	// 받는사람 선택삭제 버튼 처리해주기
+	$('#in_select_del').click(function(){
+		
+		if($tableExcel == null || $tableExcel == ""){
+			
+			alert("받는사람을 추가해 주세요.");
+			return false;
+		
+		}
+		
+		var selectedData = $tableExcel.getSelectedRows();
+		
+		if(selectedData == "" || selectedData == null){
+			
+			alert("삭제할 연락처를 선택해주세요.");
+			return false;
+		
+		}else{ // 선택한 Row 데이터 삭제하기
+			
+			if(confirm("선택하신 받는 사람을 삭제하시겠습니까?")){
+				
+				// 선택 데이터 삭제
+			    selectedData.forEach(row => row.delete());
+			    
+			    
+				totRows = $tableExcel.getRows().length;
+			    $("#excelRowTotCnt").text(totRows);
+			    
+				
+			}
+
+		}
+		
+	});
+
+	// 추가버튼
+	$('#btnAddrMassReg').click(function(){
+		
+		if($tableExcel.getData().length < 1){
+			alert("한 개 이상의 연락처를 입력하세요");
+			return false;
+		}
+//	 	else if (selectedData.length > 20000) {
+//	 		alert("2만줄 이상의 업로드는 데이터 부하로 업로드 할수 없습니다.");
+//	 		return false;
+//	 	}
+		
+		
+		// tableExcel 그룹의 select 요소들을 확인
+		var columns = $tableExcel.getColumns();
+		var isAddrPhoneNoSelected = columns.some(column => column.getField() === 'addrPhoneNo');
+
+		if (!isAddrPhoneNoSelected) {
+			alert('휴대폰이 선택되지 않았습니다.');
+			return false;
+
+		} 
+
+		var addrData = $tableExcel.getData().map((row, index) => ({
+		    name: row.addrNm,
+		    phone: removeDash(row.addrPhoneNo),
+		    rep1: row.addrInfo1,
+		    rep2: row.addrInfo2,
+		    rep3: row.addrInfo3,
+		    rep4: row.addrInfo4,
+		}));
+
+
+		
+		// 기존 tableL의 데이터를 가져옵니다.
+		var existingData = tableL.getData();
+		// 기존 데이터와 새로운 데이터를 합칩니다.
+		var combinedData = existingData.concat(addrData);
+
+
+		/** 
+		* @ phone을 기준으로 중복 제거 및 갯수 계산
+		* @ 결과 반환
+		* 	return {
+		* 		uniqueArray,       // 중복 제거된 배열                    
+		* 		uniqueCount, // 중복 제거된 데이터 개수  
+		* 		duplicateArray,    // 중복된 데이터 배열                   
+		* 		duplicateCount: duplicateArray.length // 중복된 데이터 개수
+		* 	};
+		*/
+		const result = removeDuplicatesAndCount(combinedData, 'phone');
+
+		
+		// 총 30만건이 넘으면 false
+		if (!validateRowLimit(result.uniqueCount)) {
+			return false;
+		}
+		
+
+        // 6. 수량/가격 계산
+        setAllCntData(result);
+        fn_priceClclt();
+		
+		
+		$('#closeBtn').click();
+	});
+		
+  
+
+	//받는사람 전체삭제 버튼 처리
+	$('#allDel').click(function(){
+		var data = $tableExcel.getRows();	
+		$tableExcel.clearData();
+		$("#excelRowTotCnt").text(0); //총건수 수정
+		$("#excelRowDupCnt").text(0); //중복건수 수정
+		$("#excelRowErrorCnt").text(0); //중복건수 수정
+		dupliPhoneDataRealList.length = 0;	// 중복 휴대폰번호 초기화
+        $tableError.clearData();
+		
+		// select box 초기화
+		$('.field-selector').each(function() { $(this).val(''); });
+		
+	});
+	
+  
+
+    //치환문자 있는 엑섹불러오기 버튼 클릭시 파일 첨부 실행
+    $('.c3').click(function(){ // 엑셀파일 불러오기 선택 시
+        
+        $("#excelFileC4").click();
+        
+    });
+});
+
+
+
+function excelFileChange(file) {
+	if (file) {
+
+		// 파일 크기 체크 (20MB)
+		const maxSize = 20 * 1024 * 1024; // 20MB in bytes
+		if (file.size > maxSize) {
+			alert('파일 크기는 20MB를 초과할 수 없습니다.');
+			return;
+		}
+		
+		fn_loadAddActive();
+		var reader = new FileReader();
+		var extension = file.name.split('.').pop().toLowerCase();
+		reader.onload = function(e) {
+			setTimeout(() => { // 파일 읽기 완료 후 실행되도록 함
+				if (extension === 'xlsx') {
+					var data = new Uint8Array(e.target.result);
+					var workbook = XLSX.read(data, {type: 'array'});
+					var firstSheet = workbook.Sheets[workbook.SheetNames[0]];
+					var jsonData = XLSX.utils.sheet_to_json(firstSheet, {header: 1});
+					// 문제 데이터를 확인하는 함수 호출
+					findInvalidDBCharacters(jsonData);
+					processExcelData(jsonData);
+				} else if (extension === 'xls') {
+					var data = e.target.result;
+					var workbook = XLSX.read(data, { type: 'binary' });
+					var firstSheet = workbook.Sheets[workbook.SheetNames[0]];
+					var jsonData = XLSX.utils.sheet_to_json(firstSheet, { header: 1 });
+
+					// 문제 데이터를 확인하는 함수 호출
+					findInvalidDBCharacters(jsonData);
+
+					
+					processExcelData(jsonData);
+				} else if (extension === 'txt') {
+					var textData = e.target.result;
+					processTextData(textData);
+				} else {
+					alert('지원되지 않는 파일 형식입니다.');
+				}
+				fn_loadRemoveActive();
+			}, 0); // 지연 없이 즉시 실행되도록 0ms 지연을 설정
+		};
+		if (extension === 'xlsx') {
+			reader.readAsArrayBuffer(file);
+		} else if (extension === 'xls') {
+			reader.readAsBinaryString(file); // xls 파일에 적절한 read 메서드 호출
+		} else if (extension === 'txt') {
+			reader.readAsText(file);
+		}
+	}
+}
+
+//문제 데이터를 확인하는 함수
+function findInvalidDBCharacters(jsonData) {
+	console.log('DB 입력 값 검사 중...');
+	const invalidCharPattern = /[\uD800-\uDBFF][\uDC00-\uDFFF]/; // 4바이트 유니코드 문자 (이모지 등)
+	for (let rowIndex = 0; rowIndex < jsonData.length; rowIndex++) {
+		const row = jsonData[rowIndex];
+		if (Array.isArray(row)) {
+			for (let colIndex = 0; colIndex < row.length; colIndex++) {
+				const cell = row[colIndex];
+				if (typeof cell === 'string' && invalidCharPattern.test(cell)) {
+					console.warn('허용되지 않는 문자: row', rowIndex + 1,', col ', colIndex + 1, ', value:', cell);
+					// 허용되지 않는 문자를 제거 (선택 사항)
+					row[colIndex] = cell.replace(invalidCharPattern, '');
+					console.log('수정된 값:', row[colIndex]);
+				}
+			}
+		}
+	}
+}
+
+
+// 엑셀 데이터 처리 함수
+function processExcelData(data) {
+    console.log('data : ', data);
+
+    let tableData = data.slice(1).map(row => {
+        return {
+            addrPhoneNo: row[0], // 필드명을 fn_phoneDupl()에서 사용하는 이름으로!
+            addrNm: "", // 이름 필드가 없어도 오류 방지용으로 추가
+        };
+    });
+
+    console.log("변환된 데이터: ", tableData);
+
+    // Tabulator에 데이터 설정
+    $tableExcel.setData(tableData);
+
+    // 중복/오류 처리 함수 호출
+    fn_phoneDupl($tableExcel);
+}
+
+
+
+/* function processExcelData(data) {
+	console.log('data : ', data);
+	var keys = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
+	var tableData = [];
+	var totalRows = data.length - 2; // 전체 데이터 수 (1, 2행 제외)
+	console.log('data: ', data);
+	
+	// 3번째 행부터 입력 
+	data.slice(0).forEach((row, index) => {
+		var rowData = {};
+		keys.forEach((key, idx) => { // index 변수명 변경 (내부와 외부에서 사용되므로 충돌 방지)
+// 			console.log('row[idx] : ', row[idx]);
+			   rowData[key] = row[idx] ? row[idx].trim() : ""; // 각 컬럼에 대해 기본값을 설정
+			rowData[key] = (typeof row[idx] === 'string') ? row[idx].trim() : row[idx];
+		});
+		tableData.push(rowData);
+	
+	});
+	console.log('tableData :: ', tableData);
+	updateTable(tableData);
+} */
+
+
+// 텍스트 데이터 처리 함수
+function processTextData(text) {
+	var lines = text.split('\n'); // 각 줄을 배열로 분리
+	var keys = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
+	var tableData = [];
+	
+	lines.forEach(line => {
+		var rowData = {};
+		var row = line.split(','); // 쉼표로 분리
+		keys.forEach((key, index) => {
+			rowData[key] = row[index] ? row[index].trim() : ""; // 각 컬럼에 대해 기본값을 설정
+		});
+		tableData.push(rowData);
+	});
+	
+	updateTable(tableData);
+}
+
+//공통 테이블 업데이트 함수
+function updateTable(tableData) {
+	$tableExcel.setColumns([ //Define Table Columns
+ 		{formatter:"rowSelection", titleFormatter:"rowSelection",clipboard:false, headerHozAlign:"center", hozAlign:"center", headerSort:false, cellClick:function(e, cell){
+ 	        cell.getRow().toggleSelect();
+	 		}
+	 	}, 
+		{formatter:"rownum", align:"center" ,title:"No", hozAlign:"center", headerHozAlign:"center", width:60},
+	 	{title:"A", field:"A", hozAlign:"center", headerHozAlign: "center", width:140, validator:["maxLength:100", "string"]},
+	 	{title:"B", field:"B", hozAlign:"center", headerHozAlign: "center", width:140, validator:["maxLength:100", "string"]},
+	 	{title:"C", field:"C", hozAlign:"center", headerHozAlign: "center", width:140, validator:["maxLength:100", "string"]},
+	 	{title:"D", field:"D", hozAlign:"center", headerHozAlign: "center", width:140, validator:["maxLength:100", "string"]},
+	 	{title:"E", field:"E", hozAlign:"center", headerHozAlign: "center", width:140, validator:["maxLength:100", "string"]},
+	 	{title:"F", field:"F", hozAlign:"center", headerHozAlign: "center", width:140, validator:["maxLength:100", "string"]}
+ 	]);
+
+	$tableExcel.setData(tableData).then(() => {
+		// excelRowTotCnt 업데이트
+		document.getElementById("excelRowTotCnt").innerText = tableData.length;
+	});
+	
+	fn_loadRemoveActive();
+}
+
+
+
+/* 
+* 타이틀 select 선택할때마다 실행해서         
+* 데이터테이블 필드값 수정                  
+*/
+function updateTableFields($objTabul) {
+	var currentData = $objTabul.getData();
+	var columns = [
+		{formatter: "rowSelection", titleFormatter: "rowSelection", clipboard: false, hozAlign: "center", headerHozAlign: "center", headerSort: false, cellClick: function(e, cell) {
+			cell.getRow().toggleSelect();
+		}}
+		,{formatter:"rownum", align:"center", title:"No", hozAlign:"center", headerHozAlign:"center", width:60}
+	];
+
+	var fieldMapping = [];
+	$('.field-selector').each(function(index) {
+		var selectedField = $(this).val();
+		//  ASCII 문자 코드 사용 - 65=A, 66=B ...
+		var field = String.fromCharCode(65 + index);
+		if (selectedField) {
+			columns.push({
+				title: field
+				, field: selectedField
+				, hozAlign: "center"
+				, headerHozAlign: "center"
+// 				, editor: "input"
+				, editor: false
+				, width: 140
+				, validator: ["maxLength:100", "string"]
+			});
+			fieldMapping.push(selectedField);
+		} else {
+			columns.push({
+				title: field
+				, field: field
+				, hozAlign: "center"
+				, headerHozAlign: "center"
+				, editor: false
+// 				, editor: "input"
+				, width: 140
+				, validator: ["maxLength:100", "string"]
+			});
+			fieldMapping.push(field);
+		}
+	});
+
+	var updatedData = currentData.map(row => {
+		console.log('row : ', row);
+		var newRow = {};
+		fieldMapping.forEach((field, index) => {
+			newRow[field] = row[Object.keys(row)[index]] ?? "";
+		});
+		return newRow;
+	});
+
+	$objTabul.setColumns(columns);
+	$objTabul.setData(updatedData);
+}
+
+
+/**
+ * @ 핸드폰 중복 데이터
+ * */
+function fn_phoneDupl($objTabul) {
+	 
+	$tableError.clearData();
+	
+	var data = $objTabul.getData();
+	var phoneNumberChk = false;
+	var existingNumbers = new Set(); // 배열에서 Set으로 변경
+	
+	let errorCount = 0; // 중복 번호 개수를 저장할 변수
+	let duplicateCount = 0; // 중복 번호 개수를 저장할 변수
+	
+	const errors = [];  // 오류 데이터를 저장할 배열
+	const newData = []; // 유효한 데이터만 저장할 새로운 배열
+	
+	data.forEach((row, index) => {
+		
+		const number = row.addrPhoneNo;
+		
+		// number가 null, undefined, 빈 문자열이거나 숫자인 경우 처리
+		if (!number || (typeof number === 'string' && !number.trim())){
+			console.log("number : ", number);
+			 return;
+		}
+		
+		const formattedNumber = formatPhoneNumber(number); // 번호 표준화
+		const cleanedNumber = formattedNumber.replace(/[^0-9]/g, ''); // 숫자만 남김
+		
+		if (!existingNumbers.has(cleanedNumber)) { // 중복 번호 체크
+		    if (isValidPhoneNumber(formattedNumber)) { // 유효성 검사
+		        row.addrPhoneNo = formattedNumber;
+		        existingNumbers.add(cleanedNumber); // 추가된 번호를 기존 목록에 추가
+		        newData.push(row); // 유효한 데이터만 새로운 배열에 추가
+		    } else {
+				// 오류: 유효성 통과 못함
+				errorCount++;
+				
+				errors.push({ 
+					name: row.addrNm, // 이름
+					phone: row.addrPhoneNo, // 폰번호
+					result: "오류" // 결과 메시지 추가
+				});
+		    }
+		} else {
+			// 중복
+			duplicateCount++;
+			
+			errors.push({ 
+				name: row.addrNm, // 이름
+				phone: row.addrPhoneNo, // 폰번호
+				result: "중복" // 결과 메시지 추가
+			});
+		}
+	});
+	
+	// data 배열을 newData 배열로 대체
+	
+	data = newData;
+	console.log('data : ', data);
+
+	// 수정된 데이터로 테이블 업데이트
+	$objTabul.setData(data);
+	// 오류 총 카운트
+    $("#excelRowTotCnt").text($objTabul.getDataCount());
+	// 중복 카운트
+	$("#excelRowDupCnt").text(duplicateCount);
+	// 에러 카운트
+	$("#excelRowErrorCnt").text(errorCount);
+	
+	// popup 영역
+	$("#errorPopTotCnt").text($objTabul.getDataCount());
+	// 중복 카운트
+	$("#errorPopDupCnt").text(duplicateCount);
+	// 에러 카운트
+	$("#errorPopErrorCnt").text(errorCount);
+	
+	
+	$tableError.setData(errors);
+	
+	if(errorCount > 0){
+		alert('휴대폰 형식에 맞지 않는 데이터는 삭제 후 업로드 됩니다.\nex) 발송불가 특수문자, 자릿수 오류 등');
+	}
+	
+	
+	
+}
+
+function fn_dupliPopupShow(){
+
+	$("#tableExcelDupliBtn").show();
+}
+
+
+// 상단 설명 더보기 
+function popMore(e){
+	$(e).closest(".pop_more_cont").toggleClass("pop_more_click");
+	
+	if($(e).closest(".pop_more_cont").is(".pop_more_click")){
+		$(e).html('숨기기');
+		$(e).append('<i></i>');
+	}else {
+		$(e).html('더보기');
+		$(e).append('<i></i>');
+	}
+}
+
+// excel 오류정보 테스트
+$(document).on('click', '#errorExcelBtn', function() {
+	if($tableError.getDataCount()<1){
+		alert('오류 정보가 없습니다.');
+		return false;
+	}
+	$tableError.download("xlsx", "error_data.xlsx");
+});
+
+
+</script>
+
+<!-- 중복전화번호 data-tooltip:addrMassDupli_layer -->
+<div class="tooltip-wrap">
+	<div class="popup-com addrMassDupli_layer" tabindex="0" data-tooltip-con="addrMassDupli_layer" data-focus="addrMassDupli_layer" data-focus-prev="addrMassDupli_layer-close" style="width: 270px; height: 500px;">
+		<div class="popup_heading">
+			<p>중복 휴대폰번호</p>
+			<button type="button" class="tooltip-close" data-focus="addrMassDupli_layer-close" onclick="setAddrDupliClose();"><img src="/publish/images/content/layerPopup_close.png" alt="팝업 닫기"></button>
+		</div>
+		<div class="layer_in" style="padding:20px 0px;" id="addrMassDupli_layer">
+		</div>
+		
+		<div class="popup_btn_wrap2" style="margin-top: 0px;">
+			<button type="button" class="tooltip-close" data-focus="addrMassDupli_layer-close" data-focus-next="addrMassDupli_layer">닫기</button>
+		</div>		
+		
+	</div>		
+</div>
+
+
+<!-- 주소록 상세 결과 팝업 data-tooltip:adr_popup14 -->
+	<div class="tooltip-wrap">
+		<div class="popup-com adr_layer adr_detail_result adr_popup14" tabindex="0" data-tooltip-con="adr_popup14" data-focus="adr_popup14" data-focus-prev="adr_popu14-close" style="width: 525px;z-index:125;">
+			<div class="popup_heading">
+				<p>주소록 상세 결과</p>
+				<button type="button" class="tooltip-close" data-focus="adr_popup14-close"><img src="/publish/images/content/layerPopup_close.png" alt="팝업 닫기"></button>
+			</div>
+			<div class="layer_in" style="padding:30px 20px;">
+				<div class="table_top">
+					<p>
+						총 <span class="c_e40000" id="errorPopTotCnt">0</span>건 
+						/ 중복 <span class="c_002c9a" id="errorPopDupCnt">0</span>건
+						/ 오류 <span class="c_002c9a" id="errorPopErrorCnt">0</span>건</p>
+						<button type="button" class="excel_btn btnType" id="errorExcelBtn"><i class="downroad"></i>엑셀 다운로드</button>
+				</div>
+				<div class="tb_wrap adr_list" id="tabulator_error">
+				<!-- $tableError 참고  -->
+                </div>
+                <ul class="cf_text_ul">
+                    <li>*중복번호는 하나의 번호만 등록됩니다.</li>
+                    <li>*휴대폰 형식에 맞지 않는 데이터는 삭제 후 업로드 됩니다.</li>
+                    <li>ex) 발송불가 특수문자, 자릿수 오류 등</li>
+                </ul>
+                <div class="popup_btn_wrap2">
+<!--                     <button type="button">저장</button> -->
+                    <button type="button" class="tooltip-close" data-focus="adr_popup14-close" data-focus-next="adr_popup14">닫기</button>                      
+                </div>
+			</div>
+		</div>
+	</div>
+
+<!--// 중복전화번호 팝업 -->
+				<div class="popup_heading">
+					<p>엑셀 불러오기</p>
+					<button type="button" class="tooltip-close" id="closeBtn" data-focus="popup07-close"><img src="/publish/images/content/layerPopup_close.png" alt="팝업 닫기"></button>
+			   </div>
+				<div class="layer_in execl_upload_layer" style="padding: 25px 30px;">
+<!-- 					<div class="list_tab_wrap2"> -->
+						<!-- tab button -->
+<!-- 						<ul class="list_tab" id="tbTabl">  -->
+<!-- 							<li class="tab active" data-tabul="tableExcel"><button type="button" onclick="popupTab(this,'1'); fn_tabToggle('1');">엑셀입력</button></li> -->
+<!-- 							<li class="tab" data-tabul="tableClip"><button type="button" onclick="popupTab(this,'2'); fn_tabToggle('2');">붙여넣기</button></li> -->
+<!-- 							<li class="tab" data-tabul="tableSelf"><button type="button" onclick="popupTab(this,'3'); fn_tabToggle('3');">직접입력</button></li> -->
+<!-- 						</ul>// tab button -->
+<!-- 					</div> -->
+					<!-- 엑셀입력 -->
+					<div class="popCont current pop_more_cont" id="popCont_1">
+						<div class="titBox">
+							<p>- 주소록은 한 번에 최대 30만건까지 등록(EXCEL파일, 최대용량 20MB) 가능합니다. </p>
+							<p>- 엑셀 파일에 비밀번호 설정, 제한된 보기, 수식 등이 설정되어 있는 경우 업로드가 불가합니다.</p>
+							<p>- 구분선(|), 역슬래시(\, ₩), 큰따옴표("), 이모지(이모티콘) 등 발송불가 특수문자는 저장되지 않습니다.</p>
+							<p>- 이름 200byte, [*1*]~[*4*] 200byte, 메모 250byte까지 입력 가능합니다.</p>
+							<p>- 주소록 등록이 어려우신 경우에는 <a href="<c:url value='/web/mjon/addragency/selectAddrAgencyList.do'/>" style="font-weight: bold; color: blue;">주소록 입력대행</a> 메뉴를 이용하실 수 있습니다. </p>
+						</div>
+						<div class="pop_more_wrap">
+								<button type="button" class="pop_more" onclick="popMore(this);">더보기<i></i></button>
+						</div>
+					</div><!--// 엑셀입력 -->
+
+					<!-- 공통 -->
+					<div>
+		                <table class="layer_tType1">
+		                    <caption>엑셀입력 표</caption>
+		                    <colgroup>
+		                        <col style="width: 95px">
+		                        <col style="width: auto">
+		                    </colgroup>
+		                    <tbody>
+		                        <tr>
+		                            <!-- <th>그룹 선택</th>
+		                            <td>
+		                                <label for="" class="label">그룹 선택</label>
+		                                <select id="addrGrpIdInfo" name="addrGrpIdInfo">
+		                                </select>
+	                                    <label for="" class="label">그룹명 입력</label>
+	                                    <input type="text" id="addrGrpNm" name="addrGrpNm" placeholder="새 그룹명을 입력해주세요." onfocus="this.placeholder=''" onblur="this.placeholder='새 그룹명을 입력해주세요.'"class="inputLight" style="width: 300px;">
+		                                <input type="file" id="excelFile" accept=".xls, .xlsx, .txt" style="display:none"/>
+		                                <button type="button" class="excel_btn2 btnType c3"><i class="uproad"></i>엑셀, TXT파일 업로드</button>
+		                            </td> -->
+		                            <td colspan="2" style="padding:10px 0;">
+		                            	<div class="file_upload_wrap" style="width:100%;display:flex;">
+						                	<div class="file_add upload_area">
+												<p><img src="/publish/images/content/file_add.png" alt="파일 붙여넣기">마우스로 엑셀파일을 여기에 끌어다 놓으세요</p>
+											</div>
+											<input type="file" id="excelFileC4" accept=".xls, .xlsx, .txt" style="display:none"/>
+					                        <button type="button" class="excel_btn2 btnType c3"><i class="uproad"></i>엑셀파일 업로드</button>
+						                </div>
+		                            </td>
+		                        </tr>
+		                    </tbody>
+		                </table>
+	                </div>
+	                
+					
+					<div class="excel_middle2">
+						<p>
+							총 <span class="c_e40000 fwBold" id="excelRowTotCnt">0</span>건 
+							/ 중복 <span class="c_002c9a fwBold" id="excelRowDupCnt">0</span>건
+							/ 오류 <span class="c_002c9a fwBold" id="excelRowErrorCnt">0</span>건
+							<button type="button" class="btn_list_detail" data-tooltip="adr_popup14"><img src="/publish/images/search.png"></button>
+						</p>
+<!-- 							&nbsp;  -->
+<!-- 							<button type="button" class="btnType btnType6" data-tooltip="addrMassDupli_layer" id="tableExcelDupliBtn">중복번호</button> -->
+<!-- 							&nbsp; -->
+<!-- 							<button type="button" class="btnType btnType6" data-tooltip="addrMassSaveDupli_layer" onclick="GetAddrMassSaveDupli()" id="btnAddrMassSaveDupli">중복번호</button> -->
+						</p>
+<!-- 						<button type="button" class="btnType btnType6 addCallToF">번호추가</button> -->
+					</div>
+					
+					
+					<!-- 
+
+					<div class="adr_excel" style="margin-top: 13px; overflow-x:auto;">
+                    <div class="adr_excel" style="margin-top: 13px;">
+                        thead
+                        <div class="adr_hd select_adr_hd msg" data-group="tableExcel">
+                        	<div style="width: 100px;"></div>
+                            <div style="width: 100px;"></div>
+                            <div style="width: 140px;">
+                            	<label for="" class="label"></label>
+								<select class="field-selector">
+									<option value="">선택하기</option>
+									<option value="addrNm">이름</option>
+									<option value="addrPhoneNo">휴대폰</option>
+									<option value="addrInfo1">[*1*]</option>
+									<option value="addrInfo2">[*2*]</option>
+									<option value="addrInfo3">[*3*]</option>
+									<option value="addrInfo4">[*4*]</option>
+									<option value="addrComment">메모</option>
+								</select>
+                            </div>
+                            <div style="width: 140px;">
+                                <label for="" class="label"></label>
+								<select class="field-selector">
+									<option value="">선택하기</option>
+									<option value="addrNm">이름</option>
+									<option value="addrPhoneNo">휴대폰</option>
+									<option value="addrInfo1">[*1*]</option>
+									<option value="addrInfo2">[*2*]</option>
+									<option value="addrInfo3">[*3*]</option>
+									<option value="addrInfo4">[*4*]</option>
+									<option value="addrComment">메모</option>
+								</select>
+                            </div>
+                            <div style="width: 140px;">
+                                <label for="" class="label"></label>
+								<select class="field-selector">
+									<option value="">선택하기</option>
+									<option value="addrNm">이름</option>
+									<option value="addrPhoneNo">휴대폰</option>
+									<option value="addrInfo1">[*1*]</option>
+									<option value="addrInfo2">[*2*]</option>
+									<option value="addrInfo3">[*3*]</option>
+									<option value="addrInfo4">[*4*]</option>
+									<option value="addrComment">메모</option>
+								</select>
+                            </div>
+                            <div style="width: 140px;">
+                                <label for="" class="label"></label>
+								<select class="field-selector">
+									<option value="">선택하기</option>
+									<option value="addrNm">이름</option>
+									<option value="addrPhoneNo">휴대폰</option>
+									<option value="addrInfo1">[*1*]</option>
+									<option value="addrInfo2">[*2*]</option>
+									<option value="addrInfo3">[*3*]</option>
+									<option value="addrInfo4">[*4*]</option>
+									<option value="addrComment">메모</option>
+								</select>
+                            </div>
+                            <div style="width: 140px;">
+                                <label for="" class="label"></label>
+								<select class="field-selector">
+									<option value="">선택하기</option>
+									<option value="addrNm">이름</option>
+									<option value="addrPhoneNo">휴대폰</option>
+									<option value="addrInfo1">[*1*]</option>
+									<option value="addrInfo2">[*2*]</option>
+									<option value="addrInfo3">[*3*]</option>
+									<option value="addrInfo4">[*4*]</option>
+									<option value="addrComment">메모</option>
+								</select>
+                            </div>
+                            <div style="width: 140px;">
+                                <label for="" class="label"></label>
+								<select class="field-selector">
+									<option value="">선택하기</option>
+									<option value="addrNm">이름</option>
+									<option value="addrPhoneNo">휴대폰</option>
+									<option value="addrInfo1">[*1*]</option>
+									<option value="addrInfo2">[*2*]</option>
+									<option value="addrInfo3">[*3*]</option>
+									<option value="addrInfo4">[*4*]</option>
+									<option value="addrComment">메모</option>
+								</select>
+                            </div>
+                            <div style="width: 125px;">
+                                <label for="" class="label"></label>
+								<select class="field-selector">
+									<option value="">선택하기</option>
+									<option value="addrNm">이름</option>
+									<option value="addrPhoneNo">휴대폰</option>
+									<option value="addrInfo1">[*1*]</option>
+									<option value="addrInfo2">[*2*]</option>
+									<option value="addrInfo3">[*3*]</option>
+									<option value="addrInfo4">[*4*]</option>
+									<option value="addrComment">메모</option>
+								</select>
+                            </div>
+                        </div>
+                     </div>		 -->			
+					
+					<div class="drag_drop_wrap callList_includ_box" id="tabulator_excel">
+<!-- 						<img src="/publish/images/content/excel.jpg" style="width: 100%;"> -->
+					</div>
+					<div class="excel_middle">
+						<div class="select_btnWrap clearfix">
+							<div>
+								<button type="button" id="allDel"><i class="remove_img"></i>전체삭제</button>
+								<button type="button" id="in_select_del"><i class="remove_img"></i>선택삭제</button>
+							</div>
+	
+						</div>
+					</div><!--// 공통 -->     
+	
+					<!-- 붙여놓기 설명 -->
+<!-- 	                <div class="req_area"> -->
+<!-- 	                  	<div class="text_box"> -->
+<!-- 	                    	- 휴대폰 번호가 입력된 txt 파일을 열어 복사(Ctrl+c) + 붙여넣기(Ctrl+v)로도 입력하실 수 있습니다.<br> -->
+<!-- 							- 휴대폰 번호는 필수입력 항목입니다.<br> -->
+<!-- 							- 이름,휴대폰 번호,[*1*],[*2*],[*3*],[*4*],메모 순서대로 입력해주세요.(예 : 010-1234-5678,홍길동,변수1…메모)<br> -->
+<!-- 							- 이름은 24byte, [*1*]~[*4*] 40byte, 메모는 250byte까지 입력 가능합니다.<br> -->
+<!-- 							- '오류 검사'를 통해 등록된 데이터에 전화번호 입력 오류를 확인하실 수 있습니다. -->
+<!-- 						</div> -->
+<!-- 	                </div> -->
+					<div class="popup_btn_wrap2" style="margin: 0 auto 30px auto;">
+						<button type="button" id="btnAddrMassReg">추가</button>
+						<button type="button" id="btnAddrMassClose" class="tooltip-close" data-focus="adr_popup07-close"  data-focus-next="popup07">닫기</button>                      
+					</div>
+				
+				</div>(No newline at end of file)
src/main/webapp/js/kakao/at/addr.js
--- src/main/webapp/js/kakao/at/addr.js
+++ src/main/webapp/js/kakao/at/addr.js
@@ -320,7 +320,7 @@
 	});
 	
 }
-
+/*
 //문자발송 받는사람 목록 주소록에 등록하기 기능 처리
 $('.registAddr').click(function(){
 	
@@ -384,7 +384,7 @@
 				rep4List[i] = rep4;
 			}
 			
-			/* if(name == "" || name == null){
+			 if(name == "" || name == null){
 				
 				alert("수신 목록에 이름이 없는 항목이 있습니다. 이름을 입력해 주세요");
 				return false;
@@ -418,7 +418,7 @@
 					rep4List[i] = rep4;
 				}
 				
-			} */
+			} 
 			
 		}
 		
@@ -520,4 +520,4 @@
 	
 	
 });
-
+*/
src/main/webapp/js/kakao/at/alimtalkExcel.js
--- src/main/webapp/js/kakao/at/alimtalkExcel.js
+++ src/main/webapp/js/kakao/at/alimtalkExcel.js
@@ -23,12 +23,6 @@
 		
 	});
 		
-	//치환문자 있는 엑섹불러오기 버튼 클릭시 파일 첨부 실행
-	$('.c3').click(function(){ // 엑셀파일 불러오기 선택 시
-		
-		$("#excelFile").click();
-		
-	});
 });
 	
 
src/main/webapp/js/kakao/at/tabulator.js
--- src/main/webapp/js/kakao/at/tabulator.js
+++ src/main/webapp/js/kakao/at/tabulator.js
@@ -21,14 +21,44 @@
         layout:"fitColumns",
         headerHozAlign:"center",
         validationMode:"highlight",
-	    headerHozAlign:"center",
-	    validationMode:"highlight",
-	    placeholder:"복사(Ctrl+C)한 내용을 여기에 붙여넣기(Ctrl+V) 해주세요.", //fit columns to width of table (optional)
-	    resizableColumns:false,
+        headerHozAlign:"center",
+        clipboard: true, // 클립보드 기능 활성화
+        clipboardPasteAction: "replace", // 붙여넣기 시 기존 데이터 교체 (또는 "update" 선택 가능)
+        validationMode:"highlight",
+        placeholder:"복사(Ctrl+C)한 내용을 여기에 붙여넣기(Ctrl+V) 해주세요.", //fit columns to width of table (optional)
+        resizableColumns:false,
+        clipboardPasteParser: function(clipboard){
+            const rows = clipboard.split("\n").filter(row => row.trim() !== "");
+    
+            // 1. 붙여넣기 데이터 → 가공
+            const newData = rows
+                .map(row => {
+                    const cols = row.split("\t");
+                    const phoneRaw = cols[0].trim();
+                    const phone = removeDash(phoneRaw);
+                    return { phone };
+                })
+                .filter(row => checkHpNum(row.phone));
+    
+            // 2. 기존 데이터 가져오기
+            const existingData = tableL.getData();
+    
+            // 3. 기존 데이터 + 붙여넣은 데이터 합치기
+            const combinedData = existingData.concat(newData);
+    
+            // 4. 중복 제거 + 갯수 카운트
+            const result = removeDuplicatesAndCount(combinedData, 'phone');
+    
+            // 5. 외부 처리 함수 호출
+            setAllCntData(result);
+            fn_priceClclt();
+    
+            // 6. 테이블에 반영할 데이터 리턴
+            return result;
+        },
         columnDefaults:{ // 공통설정
             hozAlign: "center",
             headerHozAlign: "center",
-            editor: "input",
             editor: false
         },
 	 	columns:[ //Define Table Columns
@@ -179,48 +209,50 @@
 	//최근전송내역 팝업 선택추가 버튼 처리(체크내역 받는사람 리스트로 추가해주기)
 	$(document).on('click', '#latestAddPhone', function (){
 
-		var addPhoneList = []; //신규로 추가할 전화번호 저장변수
-		
-		$("input:checkbox[name='latAddrChk']:checked").each(function(index){
-			
-			var chkPhone = $(this).val();
-			
-			if(!checkHpNum(chkPhone)){
-				
-				alert("올바른 전화번호가 아닙니다.");
-				return false;
-				
-			}else{
-				
-				addPhoneList.push({phone: removeDash(chkPhone.trim())});
-				
-			}
-			
-		});
-		
-		
-		if(addPhoneList.length > 0){
-			//연락처 추가해 주기
-			addPhoneInfo(addPhoneList);
-			$("#btnLatestAddPhoneClose").trigger("click");
-		}
-		else {
-			alert("연락처를 선택해주세요.");
-			return false;
-		}		
-		
-		//중복연락처 제거
-		//var removeDuplData = dupliPhoneData(addPhoneList);
-		
-		//받는사람 목록에 추가
-		//tableL.addData(removeDuplData);
-	    
-	    //총 받는사람 수 계산
-	    //totRows = tableL.getRows().length;
-	    //updateTotCnt(totRows);
-	    
-	  	//결제 금액 구하기
-	    //totalPriceSum(totRows);
+        var addPhoneList = [];
+        
+        $("input:checkbox[name='latAddrChk']:checked").each(function(){
+            var chkPhone = $(this).val().trim();
+
+            console.log('b chkPhone: ', chkPhone);
+            if(!checkHpNum(chkPhone)){
+//                alert("올바른 전화번호가 아닙니다.");
+                return false;
+            }
+//            console.log('a chkPhone: ', chkPhone);
+            addPhoneList.push(chkPhone);
+        });
+        
+        if(addPhoneList.length < 1) {
+           alert("연락처를 선택해주세요.");
+           return false;
+       }
+        
+        
+        // 1. 기존 로직: 연락처 추가 처리
+//        addPhoneInfo(addPhoneList);
+    
+        // 2. Tabulator에 넣기 위한 포맷 변환
+        const formattedData = addPhoneList.map(num => ({phone: num}));
+    
+        // 3. 기존 테이블 데이터와 합치기
+        const existingData = tableL.getData();
+        const combinedData = existingData.concat(formattedData);
+    
+        // 4. 중복 제거
+        const result = removeDuplicatesAndCount(combinedData, 'phone');
+    
+//        console.log('result: ', result);
+        // 5. 테이블에 반영
+//        tableL.setData(result);
+    
+        // 6. 수량/가격 계산
+        setAllCntData(result);
+        fn_priceClclt();
+    
+        // 7. 팝업 닫기
+        $("#btnLatestAddPhoneClose").trigger("click");
+        
 		
 	});
 	
@@ -311,63 +343,6 @@
 		$("input[name=bookAddrChk]").prop("checked", false);
 	}
 	
-	
-	//오류 검사 버튼 처리
-	$('.check_validity').click(function(){
-		
-		var invalid = tableL.getInvalidCells();		//오류 데이터 체크
-		var dataLen = tableL.getRows().length;		//연락처 데이터 갯수
-		var totLen = invalid.length;				//오류 데이터 갯수
-		var errMsg = "";							//최종 alert에 표시할 메시지 저장 변수
-		
-		if(dataLen > 0){ //연락처 정보가 있으면 수행
-			
-			if(totLen > 0){ //내용에 오류가 있으면 수행
-				
-				//오류 데이터 값 저장
-				for(var i=0; i < totLen; i++){
-					
-					var cellValue = invalid[i].getValue();
-					
-					if(i == 0){
-						
-						errMsg = cellValue;
-						
-					}else{
-						
-						errMsg = errMsg +", "+ cellValue;
-						
-					}
-					
-				}
-				
-				if(errMsg == "" || errMsg == null){
-					
-					alert("내용에 오류가 있습니다.");
-					return false;
-					
-				}else{
-					
-					alert(errMsg + "의 내용에 오류가 있습니다.");
-					return false;
-					
-				}
-				
-			}else{
-				
-				alert("오류 데이터가 없습니다.");
-				return false;
-				
-			}
-			
-		}else{
-			
-			alert("연락처를 입력해 주세요.");
-			return false;
-			
-		}
-		
-	});
 	
 });
 
Add a comment
List