이호영 이호영 04-24
5733 메인배너 이미지관리 좌표 추가
@1e7af0d77660844f8440fba130b3a094e36df24a
src/main/java/itn/com/uss/ion/bnr/pop/service/MainPopupManageService.java
--- src/main/java/itn/com/uss/ion/bnr/pop/service/MainPopupManageService.java
+++ src/main/java/itn/com/uss/ion/bnr/pop/service/MainPopupManageService.java
@@ -3,6 +3,7 @@
 import java.util.List;
 
 import itn.com.cmm.RestResponse;
+import itn.com.uss.ion.pwm.service.MainZoneLinkVO;
 
 /**
  * 개요
@@ -31,4 +32,5 @@
 
 	public RestResponse deleteMainPopupLink(MainPopupLinkVO mainPopupLinkVO);
 
+
 }
(No newline at end of file)
src/main/java/itn/com/uss/ion/bnr/pop/service/MainPopupVO.java
--- src/main/java/itn/com/uss/ion/bnr/pop/service/MainPopupVO.java
+++ src/main/java/itn/com/uss/ion/bnr/pop/service/MainPopupVO.java
@@ -52,7 +52,7 @@
 	
 	private String devicetype; 
 	
-	private List<MainPopupLinkVO> mainPopupLinkList; // 게시 종료일자
+	private List<MainPopupLinkVO> mainPopupLinkList;
 	
 	
 	
src/main/java/itn/com/uss/ion/bnr/pop/web/MainPopupController.java
--- src/main/java/itn/com/uss/ion/bnr/pop/web/MainPopupController.java
+++ src/main/java/itn/com/uss/ion/bnr/pop/web/MainPopupController.java
@@ -9,6 +9,7 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
 
+import itn.com.uss.ion.pwm.service.MainZoneLinkVO;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpStatus;
@@ -234,8 +235,8 @@
 		}
 		return ResponseEntity.ok(mainPopupManageService.deleteMainPopupLink(mainPopupLinkVO));
 	}
-	
-	
+
+
 	
 	
 
src/main/java/itn/com/uss/ion/pwm/service/EgovPopupManageService.java
--- src/main/java/itn/com/uss/ion/pwm/service/EgovPopupManageService.java
+++ src/main/java/itn/com/uss/ion/pwm/service/EgovPopupManageService.java
@@ -3,6 +3,7 @@
 import java.util.List;
 import java.util.Map;
 
+import itn.com.cmm.RestResponse;
 import itn.com.uss.ion.bnr.pop.service.MainPopupVO;
 
 /**
@@ -176,4 +177,5 @@
 	public void updateMainPopup(MainPopupVO mainPopupVO) throws Exception;
 
 
+	RestResponse deleteMainZoneLink(MainZoneLinkVO mainZoneLinkVO);
 }
(No newline at end of file)
 
src/main/java/itn/com/uss/ion/pwm/service/MainZoneLinkVO.java (added)
+++ src/main/java/itn/com/uss/ion/pwm/service/MainZoneLinkVO.java
@@ -0,0 +1,38 @@
+package itn.com.uss.ion.pwm.service;
+
+import itn.com.cmm.ComDefaultVO;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+
+/**
+ * 
+ * @author 		: 이호영
+ * @fileName 	: MainPopupLinkVO.java 
+ * @date 		: 2025.02.26
+ * @description : 
+ * =========================================================== 
+ * DATE          AUTHOR   NOTE 
+ * ----------------------------------------------------------- *
+ * 2025.02.26    이호영          최초 생성
+ * 
+ * 
+ * 
+ */
+@Getter
+@Setter
+public class MainZoneLinkVO extends ComDefaultVO implements Serializable  {
+
+
+//	/**
+//	 * @description :
+//	 */
+//	private static final long serialVersionUID = 1998370534684278351L;
+
+	private String mazId; // 메인존ID
+	private String mlink; // 링크
+	private String coords; // 링크좌표
+	private String linkId; // Auto_Increment PK
+}
src/main/java/itn/com/uss/ion/pwm/service/MainzoneVO.java
--- src/main/java/itn/com/uss/ion/pwm/service/MainzoneVO.java
+++ src/main/java/itn/com/uss/ion/pwm/service/MainzoneVO.java
@@ -16,8 +16,10 @@
 package itn.com.uss.ion.pwm.service;
 
 import java.io.Serializable;
+import java.util.List;
 
 import itn.com.cmm.ComDefaultVO;
+import itn.com.uss.ion.bnr.pop.service.MainPopupLinkVO;
 import lombok.Getter;
 import lombok.Setter;
 
@@ -102,5 +104,7 @@
 	
 	private String topTxt = "";
 	private String lowTxt = "";
-	
+
+
+    private List<MainZoneLinkVO> mainZoneLinkList;
 }
src/main/java/itn/com/uss/ion/pwm/service/impl/EgovPopupManageServiceImpl.java
--- src/main/java/itn/com/uss/ion/pwm/service/impl/EgovPopupManageServiceImpl.java
+++ src/main/java/itn/com/uss/ion/pwm/service/impl/EgovPopupManageServiceImpl.java
@@ -10,7 +10,10 @@
 
 import javax.annotation.Resource;
 
+import itn.com.cmm.RestResponse;
+import itn.com.uss.ion.pwm.service.*;
 import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Service;
 
 import egovframework.rte.fdl.cmmn.EgovAbstractServiceImpl;
@@ -18,12 +21,6 @@
 import egovframework.rte.psl.dataaccess.util.EgovMap;
 import itn.com.uss.ion.bnr.pop.service.MainPopupLinkVO;
 import itn.com.uss.ion.bnr.pop.service.MainPopupVO;
-import itn.com.uss.ion.pwm.service.EgovPopupManageService;
-import itn.com.uss.ion.pwm.service.MainzoneVO;
-import itn.com.uss.ion.pwm.service.PopupManageVO;
-import itn.com.uss.ion.pwm.service.PopupzoneVO;
-import itn.com.uss.ion.pwm.service.SocialVO;
-import itn.com.uss.ion.pwm.service.SortVO;
 
 /**
  * 개요
@@ -242,6 +239,14 @@
 	@Override
 	public void insertMainzone(MainzoneVO mainzoneVO) throws Exception {
 		dao.insertMainzone(mainzoneVO);
+
+
+		if(CollectionUtils.isNotEmpty(mainzoneVO.getMainZoneLinkList())) {
+
+			List<MainZoneLinkVO> mainZoneLinkListVO = getMainZoneLinkList(mainzoneVO);
+//			mainPopupLinkListVO.stream().forEach(t-> System.out.println(t.toString()));
+			dao.insertMainZoneLinkInfo(mainZoneLinkListVO);
+		}
 	}
 	
 	@Override
@@ -306,6 +311,18 @@
 	@Override
 	public void updateMainzone(MainzoneVO mainzoneVO) throws Exception {
 		dao.updateMainzone(mainzoneVO);
+
+		if(CollectionUtils.isNotEmpty(mainzoneVO.getMainZoneLinkList())) {
+
+			List<MainZoneLinkVO> mainPopupLinkListVO =  getMainZoneLinkList(mainzoneVO);
+
+			dao.deleteMainZoneLinkInfo(mainzoneVO.getMazId());
+
+			dao.insertMainZoneLinkInfo(mainPopupLinkListVO);
+		}
+
+
+
 	}
 	
 	@Override
@@ -413,6 +430,18 @@
 		
 	}
 
+	@Override
+	public RestResponse deleteMainZoneLink(MainZoneLinkVO mainZoneLinkVO) {
+
+		dao.deleteMainZoneLinkId(mainZoneLinkVO);
+
+		return RestResponse.builder()
+				.status(HttpStatus.OK) // 200, Series.SUCCESSFUL, "OK"
+				.msg("삭제 되었습니다.")
+				.build();
+
+	}
+
 	private List<MainPopupLinkVO> getMainPopupLinkList(MainPopupVO mainPopupVO) {
 		
 		List<MainPopupLinkVO> mainPopupLinkListVO = mainPopupVO.getMainPopupLinkList();
@@ -427,5 +456,19 @@
 		return mainPopupLinkListVO;
 	}
 
+	private List<MainZoneLinkVO> getMainZoneLinkList(MainzoneVO mainzoneVO) {
+
+		List<MainZoneLinkVO> mainZoneLinkListVO = mainzoneVO.getMainZoneLinkList();
+
+		// 기존 sort 값을 숫자로 변환하여 오름차순 정렬
+		mainZoneLinkListVO.sort(Comparator.comparingInt(vo -> vo.getSort()));
+		// 2. 정렬된 상태에서 sort 값을 연속적인 숫자로 변경 (1, 2, 3, ...)
+		IntStream.range(0, mainZoneLinkListVO.size())
+			.forEach(i -> mainZoneLinkListVO.get(i).setSort(i + 1));
+
+		mainZoneLinkListVO.stream().forEach(t-> t.setMazId(mainzoneVO.getMazId()));
+		return mainZoneLinkListVO;
+	}
+
 
 }
(No newline at end of file)
src/main/java/itn/com/uss/ion/pwm/service/impl/PopupManageDAO.java
--- src/main/java/itn/com/uss/ion/pwm/service/impl/PopupManageDAO.java
+++ src/main/java/itn/com/uss/ion/pwm/service/impl/PopupManageDAO.java
@@ -1,17 +1,13 @@
 package itn.com.uss.ion.pwm.service.impl;
 import java.util.List;
 
+import itn.com.uss.ion.pwm.service.*;
 import org.springframework.stereotype.Repository;
 
 import egovframework.rte.psl.dataaccess.util.EgovMap;
 import itn.com.cmm.service.impl.EgovComAbstractDAO;
 import itn.com.uss.ion.bnr.pop.service.MainPopupLinkVO;
 import itn.com.uss.ion.bnr.pop.service.MainPopupVO;
-import itn.com.uss.ion.pwm.service.MainzoneVO;
-import itn.com.uss.ion.pwm.service.PopupManageVO;
-import itn.com.uss.ion.pwm.service.PopupzoneVO;
-import itn.com.uss.ion.pwm.service.SocialVO;
-import itn.com.uss.ion.pwm.service.SortVO;
 
 /**
  * 개요
@@ -306,6 +302,19 @@
 	}
 
 	public void deleteMainPopupLinkInfo(String popId) {
-		delete("MainzoneManage.deleteMainPopupLinkInfo", popId); 
+		delete("MainzoneManage.deleteMainPopupLinkInfo", popId);
+	}
+
+	public void deleteMainZoneLinkId(MainZoneLinkVO mainZoneLinkVO) {
+		delete("MainzoneManage.deleteMainZoneLinkId", mainZoneLinkVO);
+	}
+
+
+	public void deleteMainZoneLinkInfo(String mazId) {
+		delete("MainzoneManage.deleteMainZoneLinkInfo", mazId);
+	}
+
+	public void insertMainZoneLinkInfo(List<MainZoneLinkVO> mainZoneLinkListVO) {
+		insert("MainzoneManage.insertMainZoneLinkInfo", mainZoneLinkListVO);
 	}
 }
(No newline at end of file)
src/main/java/itn/com/uss/ion/pwm/web/EgovPopupManageController.java
--- src/main/java/itn/com/uss/ion/pwm/web/EgovPopupManageController.java
+++ src/main/java/itn/com/uss/ion/pwm/web/EgovPopupManageController.java
@@ -11,16 +11,18 @@
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
+import itn.com.cmm.RestResponse;
+import itn.com.uss.ion.pwm.service.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 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.Model;
 import org.springframework.ui.ModelMap;
 import org.springframework.validation.BindingResult;
-import org.springframework.web.bind.annotation.ModelAttribute;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.MultipartHttpServletRequest;
 import org.springframework.web.servlet.ModelAndView;
@@ -38,10 +40,6 @@
 import itn.com.cmm.service.EgovFileMngUtil;
 import itn.com.cmm.service.FileVO;
 import itn.com.cmm.util.RedirectUrlMaker;
-import itn.com.uss.ion.pwm.service.EgovPopupManageService;
-import itn.com.uss.ion.pwm.service.MainzoneVO;
-import itn.com.uss.ion.pwm.service.PopupManageVO;
-import itn.com.uss.ion.pwm.service.PopupzoneVO;
 
 /**
  * 개요
@@ -686,8 +684,7 @@
             HttpServletRequest request, Model model, HttpSession session)
             throws Exception  {
     	
-    	System.out.println("??????");
-    	
+
     	MainzoneVO mainzoneVO = new MainzoneVO();
     	if("Modify".equals((String)commandMap.get("pageType"))){  //수정
     		String mazId = (String)commandMap.get("selectedId");
@@ -738,7 +735,8 @@
     	}
     	
         //model.addAttribute("sortList", sortList);
-	    model.addAttribute("mainzoneVO", mainzoneVO);	
+	    model.addAttribute("mainzoneVO", mainzoneVO);
+
 
 	    System.out.println("mainzoneVO :: "+ mainzoneVO.toString());
 	    /* 타겟 코드 */
@@ -851,7 +849,27 @@
 		RedirectUrlMaker redirectUrlMaker = new RedirectUrlMaker("uss/ion/pwm/listPopup.do");	
 		return redirectUrlMaker.getRedirectUrl();
 	}
-	
+
+
+
+	@ResponseBody
+	@RequestMapping(value="/uss/ion/bnr/mainZoneLinkDeleteAjax.do")
+	public ResponseEntity<?> mainZoneLinkDeleteAjax(
+			@RequestBody MainZoneLinkVO mainZoneLinkVO) throws Exception {
+
+		Boolean isAuthenticated = EgovUserDetailsHelper.isAuthenticated();
+
+		if(!isAuthenticated) {
+			return ResponseEntity.ok(
+					RestResponse.builder()
+							.status(HttpStatus.UNAUTHORIZED) // 401 권한 인증 에러
+							.msg("로그인을 하셔야 이용 가능합니다.")
+							.build()
+			);
+		}
+		return ResponseEntity.ok(egovPopupManageService.deleteMainZoneLink(mainZoneLinkVO));
+	}
+
 	/**
 	 * 시간을 LIST를 반환한다.
 	 * @return  List
@@ -954,7 +972,7 @@
 
         return ret;
     }
-    
+
     
     
 }
(No newline at end of file)
src/main/java/itn/let/mjo/msg/web/MjonMsgController.java
--- src/main/java/itn/let/mjo/msg/web/MjonMsgController.java
+++ src/main/java/itn/let/mjo/msg/web/MjonMsgController.java
@@ -211,7 +211,7 @@
         
         paginationInfo.setTotalRecordCount(resultList.size() > 0 ? ((MjonMsgVO)resultList.get(0)).getTotCnt() : 0);
 		model.addAttribute("paginationInfo", paginationInfo);
-		
+
     	// 로그인VO에서  사용자 정보 가져오기
     	LoginVO	loginVO = (LoginVO)EgovUserDetailsHelper.getAuthenticatedUser();
     	String admId = loginVO == null ? "" : loginVO.getId();
src/main/resources/egovframework/sqlmap/let/uss/ion/bnr/MainPopupManage_SQL_Mysql.xml
--- src/main/resources/egovframework/sqlmap/let/uss/ion/bnr/MainPopupManage_SQL_Mysql.xml
+++ src/main/resources/egovframework/sqlmap/let/uss/ion/bnr/MainPopupManage_SQL_Mysql.xml
@@ -161,6 +161,8 @@
 	</select>
 
 
+
+
 	
 	<select id="mainPopup.selectMainPopupListRolling" resultMap="MainPopupResultMap">
 		/* mainPopup.selectMainPopupListRolling */
src/main/resources/egovframework/sqlmap/let/uss/pwm/PopupManage_SQL_Mysql.xml
--- src/main/resources/egovframework/sqlmap/let/uss/pwm/PopupManage_SQL_Mysql.xml
+++ src/main/resources/egovframework/sqlmap/let/uss/pwm/PopupManage_SQL_Mysql.xml
@@ -12,7 +12,8 @@
 	<typeAlias  alias="PopupManageVO" type="itn.com.uss.ion.pwm.service.PopupManageVO" />
 	<typeAlias  alias="popupzoneVO" type="itn.com.uss.ion.pwm.service.PopupzoneVO"/>
 	<typeAlias  alias="mainzoneVO" type="itn.com.uss.ion.pwm.service.MainzoneVO"/>
-	
+	<typeAlias  alias="mainZoneLinkVO" type="itn.com.uss.ion.pwm.service.MainZoneLinkVO"/>
+
 	<typeAlias  alias="mainPopupVO" type="itn.com.uss.ion.bnr.pop.service.MainPopupVO"/>
 	<typeAlias  alias="mainPopupLinkVO" type="itn.com.uss.ion.bnr.pop.service.MainPopupLinkVO"/>
 	
@@ -39,6 +40,37 @@
 		<result property="scrollType" 			column="SCROLL_TYPE" 			columnIndex="17"/>
 		<result property="nttCn" 				column="NTT_CN" 				columnIndex="18"/>
 		<result property="sortNum" 				column="SORT_NUM" 				columnIndex="19"/>
+	</resultMap>
+
+	<resultMap id="MainZoneLinkResultMap" class="itn.com.uss.ion.pwm.service.MainZoneLinkVO">
+		<result property="mazId" column="MAZ_ID" />
+		<result property="mlink" column="MLINK" />
+		<result property="coords" column="COORDS" />
+		<result property="linkId" column="LINK_ID" />
+	</resultMap>
+
+	<resultMap id="MainzoneResultMap" class="itn.com.uss.ion.pwm.service.MainzoneVO">
+		<result property="mazId" column="MAZ_ID" />
+		<result property="upfile" column="UPFILE" />
+		<result property="upfileUrl" column="UPFILEURL" />
+		<result property="content" column="CONTENT" />
+		<result property="mlink" column="MLINK" />
+		<result property="istarget" column="ISTARGET" />
+		<result property="regdt" column="REGDT" />
+		<result property="del" column="DEL" />
+		<result property="sort" column="SORT" />
+		<result property="mainzoneImage" column="MAINZONE_IMAGE" />
+		<result property="mainzoneImageFile" column="MAINZONE_IMAGE_FILE" />
+		<result property="mazNm" column="MAZ_NM" />
+		<result property="useYn" column="USE_YN" />
+		<result property="moddt" column="MODDT" />
+		<result property="registerId" column="REGISTERID" />
+		<result property="deviceType" column="DEVICETYPE" />
+		<result property="ntceBgnde" column="NTCE_BGNDE" />
+		<result property="ntceEndde" column="NTCE_ENDDE" />
+
+		<result property="mainZoneLinkList" column="MAZ_ID" select="MainzoneManage.selectMainZoneVOLink" />
+
 	</resultMap>
 
 	<!-- 팝업창관리:: 메인 게시물정보 -->
@@ -675,27 +707,39 @@
 				SORT >= #startSort#  AND SORT <= #endSort#
 		]]>
 	</update>
-	
-	<select id="MainzoneManage.selectMainzoneVO" parameterClass="String" resultClass="mainzoneVO">
+
+	<select id="MainzoneManage.selectMainZoneVOLink" parameterClass="String" resultMap="MainZoneLinkResultMap">
+
+		/* mainPopup.selectMainZoneVOLink */
 		SELECT
-			MAZ_ID AS MAZID,
+			MAZ_ID
+			, MLINK
+			, COORDS
+			, LINK_ID
+		FROM MAINZONE_LINK
+		WHERE MAZ_ID = #mazId#
+	</select>
+
+	<select id="MainzoneManage.selectMainzoneVO" parameterClass="String" resultMap="MainzoneResultMap">
+		SELECT
+			MAZ_ID,
 			UPFILE,
-	    	CONCAT("/UPLOADROOT/POPUPZONE/",UPFILE ) AS UPFILEURL,
+	    	CONCAT("/UPLOADROOT/POPUPZONE/",UPFILE ) as UPFILEURL,
 	    	CONTENT,
 	    	MLINK,
 	    	ISTARGET,
 	    	REGDT,
 	    	DEL,
 	    	SORT,
-	    	MAINZONE_IMAGE AS MAINZONEIMAGE ,
-	    	MAINZONE_IMAGE_FILE AS MAINZONEIMAGEFILE,
-	    	MAZ_NM AS MAZNM,
-	    	USE_YN AS USEYN,
+			MAINZONE_IMAGE,
+	    	MAINZONE_IMAGE_FILE,
+	    	MAZ_NM,
+	    	USE_YN,
 	    	DATE_FORMAT(MODDT, '%Y-%m-%d %T') MODDT ,
 	    	(SELECT USER_NM FROM LETTNEMPLYRINFO WHERE ESNTL_ID = REGISTER_ID) REGISTERID,
-	    	DEVICETYPE AS deviceType,
-	    	NTCE_BGNDE AS ntceBgnde,
-			NTCE_ENDDE AS ntceEndde
+	    	DEVICETYPE,
+	    	NTCE_BGNDE,
+			NTCE_ENDDE
 		FROM MAINZONE
 		WHERE MAZ_ID=#mazId#
 	</select>
@@ -1096,6 +1140,24 @@
 		
 	</insert>
 
+	<insert id="MainzoneManage.insertMainZoneLinkInfo" parameterClass="java.util.List">
+		INSERT INTO MAINZONE_LINK (
+		MAZ_ID
+		,MLINK
+		,COORDS
+			)
+		VALUES
+		<iterate conjunction=",">
+			(
+				#[].mazId#
+				, #[].mlink#
+				, #[].coords#
+			)
+		</iterate>
+
+
+	</insert>
+
 
 	<update id="MainzoneManage.updateMainPopup" parameterClass="mainPopupVO">
 		UPDATE MAIN_POPUP 
@@ -1130,6 +1192,17 @@
 			DELETE FROM MAIN_POPUP_LINK
 			WHERE POP_ID = #popId#
 	</delete>
-	
-	
+
+	<delete id="MainzoneManage.deleteMainZoneLinkId" parameterClass="mainZoneLinkVO">
+
+			DELETE FROM MAINZONE_LINK
+			WHERE LINK_ID = #linkId#
+	</delete>
+
+
+	<delete id="MainzoneManage.deleteMainZoneLinkInfo" parameterClass="String">
+
+		DELETE FROM MAINZONE_LINK
+		WHERE MAZ_ID = #mazId#
+	</delete>
 </sqlMap>
(No newline at end of file)
src/main/webapp/WEB-INF/jsp/uss/ion/pwm/MainZoneModify.jsp
--- src/main/webapp/WEB-INF/jsp/uss/ion/pwm/MainZoneModify.jsp
+++ src/main/webapp/WEB-INF/jsp/uss/ion/pwm/MainZoneModify.jsp
@@ -63,6 +63,45 @@
 	
 });
 
+
+
+function addLinkRow() {
+	// 링크 목록 tbody
+	let tbody = document.getElementById("linkTable");
+	let rowCount = tbody.getElementsByTagName("tr").length;  // 현재 tr 개수 가져오기
+
+	// 새로운 tr 생성
+	let newRow = document.createElement("tr");
+
+	let rowCountP = rowCount + 1;
+	// 첫 번째 컬럼 (링크 주소)
+	let linkTh = document.createElement("th");
+	linkTh.innerHTML = '<span>['+rowCountP+']링크주소</span>';
+	let linkTd = document.createElement("td");
+	linkTd.innerHTML = '<input type="text" name="mainZoneLinkList['+rowCount+'].mlink" class="mlink" maxlength="200" />';
+
+	// 두 번째 컬럼 (링크 좌표)
+	let coordTh = document.createElement("th");
+	coordTh.innerHTML = '<span>링크좌표</span>';
+	let coordTd = document.createElement("td");
+	coordTd.innerHTML = '<input type="text" name="mainZoneLinkList['+rowCount+'].coords" maxlength="200" />';
+
+	// 세 번째 컬럼 (링크 좌표)
+	let sortTd = document.createElement("td");
+	sortTd.setAttribute("colspan", "2");
+	sortTd.innerHTML = '<input type="button" class="btnType2" value="삭제" onclick="fn_linkDel(\'\')" />';
+	// tr에 추가
+	newRow.appendChild(linkTh);
+	newRow.appendChild(linkTd);
+	newRow.appendChild(coordTh);
+	newRow.appendChild(coordTd);
+	newRow.appendChild(sortTd);
+
+	// tbody에 추가
+	tbody.appendChild(newRow);
+}
+
+
 /**
  * URL에서 빈 값의 파라미터를 제거하는 함수
  * @param {string} url 원본 URL 문자열
@@ -84,7 +123,7 @@
 
 			let params = new URLSearchParams(urlObj.search);
 
-			// ❗ 값이 비어있는 모든 파라미터 제거
+			//  값이 비어있는 모든 파라미터 제거
 			for (let [key, value] of [...params.entries()]) {  // `params.entries()`를 배열로 변환하여 반복
 				if (!value.trim()) {  // 값이 비어있는 경우 제거
 					params.delete(key);
@@ -170,7 +209,7 @@
 }
 
 function validate(method_parm) {
-	frm = document.writeForm;
+	var frm = document.writeForm;
 	if(frm.mazNm.value=="") {
 		alert("비주얼명을 입력해 주십시오");
 		frm.mazNm.focus();
@@ -210,17 +249,10 @@
 
 	console.log("ntceBgndeYYYMMDD ::: "+ntceBgndeYYYMMDD);
 	console.log("ntceEnddeYYYMMDD ::: "+ntceEnddeYYYMMDD);
-	
-	if(ntceBgndeYYYMMDD ==""){
-		
-		alert("게시기간 시작일을 입력해 주세요.");
+
+	if(ntceBgndeYYYMMDD == "" || ntceEnddeYYYMMDD == ""){
+		alert("게시기간을 입력해 주세요.");
 		return false;
-		
-	}else if(ntceEnddeYYYMMDD == ""){
-		
-		alert("게시기간 종료일을 입력해 주세요.");
-		return false;
-		
 	}else{
 		
 		var iChkBeginDe = Number( ntceBgndeYYYMMDD.replaceAll("-","") );
@@ -235,18 +267,48 @@
 		frm.ntceEndde.value = ntceEnddeYYYMMDD.replaceAll('-','') + fn_egov_SelectBoxValue('ntceEnddeHH') +  fn_egov_SelectBoxValue('ntceEnddeMM');
 		
 	}
-	
-	var msg = "메인 배너를 등록하시겠습니까?";
-	
-	if(method_parm == "mainzone_U"){
-		
-		msg ="메인 배너를 수정하시겠습니까?";
-		
+
+	// 4. 전체 링크 vs 상세 링크 중복 및 쌍(Pair) 체크
+	var mlinkVal = frm.mlink.value.trim(); // 전체링크주소 필드
+	var detailRows = document.querySelectorAll('#linkTable tr'); // [1], [2]... 상세 링크 행들
+	var hasDetailLink = false;
+
+	for (var i = 0; i < detailRows.length; i++) {
+		// name 속성에 mlink와 coords가 포함된 input을 찾음
+		var rowLink = detailRows[i].querySelector('input[name*=".mlink"]');
+		var rowCoord = detailRows[i].querySelector('input[name*=".coords"]');
+
+		if (rowLink && rowCoord) {
+			var lVal = rowLink.value.trim();
+			var cVal = rowCoord.value.trim();
+
+			// 주소와 좌표 중 하나만 입력된 경우 (XOR 체크)
+			if ((lVal !== "" && cVal === "") || (lVal === "" && cVal !== "")) {
+				alert("[" + (i + 1) + "] 링크주소와 링크좌표를 모두 입력하거나 모두 비워주세요.");
+				if (lVal === "") rowLink.focus();
+				else rowCoord.focus();
+				return false;
+			}
+
+			// 둘 다 값이 있는 경우 상세 링크가 존재한다고 판단
+			if (lVal !== "" && cVal !== "") {
+				hasDetailLink = true;
+			}
+		}
 	}
-	
+
+	// 전체 링크(mlink)와 상세 링크(이미지맵)가 둘 다 입력된 경우
+	if (mlinkVal !== "" && hasDetailLink) {
+		alert("전체 링크 주소와 상세 링크(이미지맵)를 동시에 입력할 수 없습니다.\n한 가지 방식만 사용해 주세요.");
+		frm.mlink.focus();
+		return false;
+	}
+
+	// 5. 최종 저장 확인
+	var msg = (method_parm == "mainzone_U") ? "메인 배너를 수정하시겠습니까?" : "메인 배너를 등록하시겠습니까?";
 	if(!confirm(msg)){
 		return false;
-	}	
+	}
 	goSave(method_parm);
 }
 function fn_egov_downFile(atchFileId, fileSn){
@@ -279,6 +341,55 @@
 	}
 
 	return  FValue;
+}
+
+
+function fn_linkDel(p_linkId) {
+	// event.target을 저장
+	const $target = $(event.target);
+
+	if (!p_linkId) {
+
+		$target.closest('tr').remove();
+	}else{
+
+		var p_mazId = $('#mazId').val();
+
+
+		var sendData = {
+			"mazId" : p_mazId 
+			, "linkId" : p_linkId
+		}
+
+		$.ajax({
+			type: 'POST',
+			url: '<c:url value="/uss/ion/bnr/mainZoneLinkDeleteAjax.do" />',
+			contentType: 'application/json',
+			data: JSON.stringify(sendData),
+			dataType: 'json',
+			success : function(data) {
+				alert(data.msg);
+				console.log('data : ', data);
+				if(data.status == 'OK')
+				{
+					console.log('data OK : ', data);
+					$target.closest('tr').remove();
+				}
+				else
+				{
+					// 실패 처리 로직
+				}
+			},
+			error : function(jqXHR, textStatus, errorThrown) {
+				console.error("AJAX Error:", textStatus, errorThrown);
+				console.error("Response:", jqXHR.responseText);
+			}
+		});
+
+	}
+
+
+
 }
 
 </script>
@@ -320,8 +431,12 @@
 				<p class="right fwMd"><span class="tType4 c_e40000 fwBold">*</span>는 필수입력 항목입니다.</p>
 				<table class="tbType2">
 					<colgroup>
-						<col style="width: 15%">
-						<col style="width: 85%">
+						<col style="width: 10%">
+						<col style="width: 30%">
+						<col style="width: 10%">
+						<col style="width: 20%">
+						<col style="width: 10%">
+						<col style="width: 20%">
 					</colgroup>
 					<tbody>
 					<%-- 	<tr>
@@ -352,7 +467,7 @@
 						</c:if>
 						<tr>
 							<th><span>원본이미지</span></th>
-							<td>
+							<td colspan="5">
 								<c:if test="${mainzoneVO.mazId == ''}">
 									<div class="imgBox"></div>
 								</c:if>
@@ -376,19 +491,19 @@
 		<!-- 				</tr> -->
 						<tr>
 							<th><span class="reqArea">비주얼명</span></th>
-							<td>
+							<td colspan="5">
 								<form:input path="mazNm" maxlength="30" />
 							</td>
 						</tr>
 						<tr>
 							<th><span class="reqArea">대체텍스트</span></th>
-							<td>
+							<td colspan="5">
 								<form:input path="content" maxlength="500" />
 							</td>
 						</tr>
 						<tr>
 							<th><span class="reqArea">사용여부</span></th>
-							<td>
+							<td colspan="5">
 								<input type="radio" name="useYn" id="useY" value="Y" style="margin-left: 13px; margin-right: 5px;" ${mainzoneVO.useYn eq 'Y' or mainzoneVO.useYn eq '' ? 'checked="checked"' : ''} /><label for="">사용</label>
 								<input type="radio" name="useYn" id="useN" value="N" style="margin-left: 13px; margin-right: 5px;" ${mainzoneVO.useYn eq 'N' ? 'checked="checked"' : ''} /><label for="">미사용</label>
 							</td>
@@ -396,13 +511,13 @@
 						
 						<tr>
 							<th><span class="reqArea">노출순서</span></th>
-							<td colspan="3">
+							<td colspan="5">
 								<form:input path="sort" maxlength="10" onkeyup="this.value=this.value.replace(/[^-\.0-9]/g,'')"/>
 							</td>
 						</tr>
 						<tr>
 							<th><span class="reqArea">게시기간</span></th>
-							<td>
+							<td colspan="5">
 								<input type="hidden" name="cal_url" id="cal_url" value="<c:url value='/sym/cmm/EgovNormalCalPopup.do'/>" >
 							    <input type="text" class="date_format" name="ntceBgndeYYYMMDD" id="ntceBgndeYYYMMDD" size="10" maxlength="10" class="readOnlyClass" value="<c:out value="${fn:substring(mainzoneVO.ntceBgnde, 0, 4)}"/>-<c:out value="${fn:substring(mainzoneVO.ntceBgnde, 4, 6)}"/>-<c:out value="${fn:substring(mainzoneVO.ntceBgnde, 6, 8)}"/>" readonly>
 							    
@@ -431,14 +546,40 @@
 							</td>
 						</tr>
 						<tr>
-							<th><span>링크주소</span></th>
-							<td colspan="3">
+							<th><span>전체링크주소</span></th>
+							<td colspan="5">
 								<form:input path="mlink" maxlength="200" />
+							</td>
+						</tr>
+						<tbody id="linkTable">
+						<c:choose>
+							<c:when test="${not empty mainzoneVO.mainZoneLinkList}">
+								<c:forEach var="link" items="${mainzoneVO.mainZoneLinkList}" varStatus="status">
+									<tr>
+										<th><span>[${status.index + 1}]링크주소</span></th>
+										<td>
+											<form:input path="mainZoneLinkList[${status.index}].mlink" class="mlink" maxlength="200" />
+										</td>
+										<th><span>링크좌표</span></th>
+										<td>
+											<form:input path="mainZoneLinkList[${status.index}].coords" class="mlink" maxlength="200" />
+										</td>
+										<td colspan="2">
+											<input type="button" class="btnType2" value="삭제" onclick="fn_linkDel('${link.linkId }'); return false;">
+										</td>
+									</tr>
+								</c:forEach>
+							</c:when>
+						</c:choose>
+						</tbody>
+						<tr>
+							<td colspan="4">
+								<button type="button" class="btnType1" onclick="addLinkRow()">링크 추가</button>
 							</td>
 						</tr>
 						<tr>
 							<th><span class="reqArea">파일 첨부</span></th>
-							<td class="upload_area">
+							<td class="upload_area" colspan="5">
 								<div class="file_upload_box no_img_box fileWrap">
 									<table>	
 										<colgroup>
src/main/webapp/js/ncms_common.js
--- src/main/webapp/js/ncms_common.js
+++ src/main/webapp/js/ncms_common.js
@@ -406,7 +406,7 @@
         cache: false,
         //timeout: 600000,
         success: function (returnData, status) {
-			if(status == 'success'){ // status 확인 필요한가. 석세스 안뜨면 에러 가지 않나
+			if(status == 'success'){
 				if("fail"==returnData.result){
 					alert(returnData.message);
 					return;
Add a comment
List