관리 메뉴

거니의 velog

(19) 게시판 > 공지사항 페이지 > 코드 리뷰 본문

대덕인재개발원/대덕인재개발원_최종 포트폴리오

(19) 게시판 > 공지사항 페이지 > 코드 리뷰

Unlimited00 2024. 2. 16. 16:41

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<c:set value="${pageContext.request.contextPath }" var="contextPath" />
<!DOCTYPE html>
<html lang="ko">
    <head>
        <meta charset="UTF-8" />
        <meta name="author" content="부루마불" />
        <meta name="description" content="대덕인재개발원 7월반 4조 부루마불의 여기갈래 프로젝트입니다." />
        <meta name="keywords" content="부루마불, 여기갈래, 여행, 통합, 플랫폼" />
        <meta name="copyright" content="대덕인재개발원" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>여기갈래 &gt; 게시판</title>
        
        <!-- 사용자 페이지 > 헤더세팅 영역 -->
        <tiles:insertAttribute name="headerSettings" />
    	
    	<!-- 
    		내부 CSS 
    		- 내부 CSS는 외부 CSS의 스타일링을 건드리지 않기 위해서
    		개별적인 페이지에다 스타일을 적용할 때 종종 쓴다.
    		- 보통 프로토타이핑 테스트용이나, FE Leader의 판단에 따라 여기에
    		추가적인 스타일을 작성한다.
    	-->
    	<style>
    		.msub a {
    			color: #333;
    		}
    	</style>
    	
    	<c:if test="${not empty message }">
		    <script type="text/javascript">
		        //alert("${message}");
		        $(function(){
		        	// 성공시
		        	<c:if test="${msgflag eq 'su'}">
		        		Swal.fire({
		                    title: "성공",
		                    text: "${message}",
		                    icon: "success"
		                });
		        	</c:if>
		        	// 실패시
		        	<c:if test="${msgflag eq 'fa'}">
			        	Swal.fire({
		                    title: "실패",
		                    text: "${message}",
		                    icon: "error"
		                });
		        	</c:if>
		        	// 정보성 메시지
		        	<c:if test="${msgflag eq 'in'}">
			        	Swal.fire({
		                    title: "안내",
		                    text: "${message}",
		                    icon: "info"
		                });
		        	</c:if>
		        });
		        <c:remove var="message" scope="request" />
		        <c:remove var="message" scope="session" />
		    </script>
		</c:if>
		
	</head>
    <body>
        
        <!-- 사용자 페이지 > 헤더 영역 -->
        <tiles:insertAttribute name="header" />
        
        <!-- 사용자 페이지 > 게시판 구현 영역 -->
        <tiles:insertAttribute name="userBoardContainer" />
        
        <!-- 사용자 페이지 > 푸터 영역 -->
		<tiles:insertAttribute name="footer" />
		
		<!-- 사용자 페이지 > 자바스크립트 세팅 영역 -->
		<tiles:insertAttribute name="settings" />

    </body>
</html>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
    
<!-- 공지사항 css -->
<link href="${contextPath }/resources/css/userNoticeBoard.css" rel="stylesheet" />

<!-- 공지사항 게시판 리스트 영역 -->
<section class="noticeListContainer emptySpace">
    <article class="communityHeadStyle">
        <div class="comImgBox">
            <img src="${contextPath }/resources/images/communityBgImg.jpg" alt="커뮤니티 배경 이미지" />
        </div>
        <div>
            <h3>커뮤니티</h3>
            <span>COMMUNITY</span>
        </div>
    </article>
    <article class="noticeListContents cen">
        <div>
            <h4>공지사항</h4>
        </div>
        <div class="searchBoardCont">
            <form action="" id="searchForm" name="searchForm">
            	<input type="hidden" id="page" name="page" />
                <div class="btn-group">
                	<button type="button" class="btn btn-primary btn-sm">총 ${pagingVO.totalRecord } 개 게시물</button>
                	<button type="button" class="btn btn-secondary btn-sm">${pagingVO.currentPage } / ${pagingVO.totalPage } 페이지</button>
                </div>
                <div>
                    <div>
                        <select class="form-control" id="searchType" name="searchType">
                            <option value="title" <c:if test="${searchType eq 'title' }">selected</c:if>>제목</option>
                            <option value="writer" <c:if test="${searchType eq 'writer' }">selected</c:if>>작성자</option>
                            <option value="both" <c:if test="${searchType eq 'both' }">selected</c:if>>제목+작성자</option>
                        </select>
                        <i class="fas fa-chevron-down"></i>
                    </div>
                    <div>
                        <input class="form-control" type="text" id="searchWord" name="searchWord" placeholder="검색어 입력" value="${searchWord }" />
                        <button type="submit" id="searchBtn">
                            <i class="fas fa-search"></i>
                        </button>
                    </div>
                </div>
            </form>
        </div>
        <div class="imporNoticeListCont">
        	<c:set value="${importantNoticeList }" var="impList" />
            <ul>
            	<c:choose>
            		<c:when test="${empty impList }">
            			<li class="impNoticeList">
                            <div style="text-align: center;">
								등록된 중요공지 게시글이 존재하지 않습니다.
                            </div>
                        </li>
            		</c:when>
            		<c:otherwise>
            			<c:forEach items="${impList }" var="imp" varStatus="vs">
            				<li class="impNoticeList">
			                    <div class="textDropVerticalAlign">
			                        <span>중요공지</span>
			                        <span class="textDrop">${imp.boTitle }</span>
			                        <fmt:parseDate var="impParseData" value="${imp.boDate }" pattern="yyyy-MM-dd HH:mm:ss" />
			                    	<span><fmt:formatDate value="${impParseData }" pattern="yyyy-MM-dd"/></span>
			                        <span class="plusMinus">
			                            <i class="fas fa-plus"></i>
			                        </span>
			                    </div>
			                    <div class="visibleDiv">
			                    
			                        <p style="white-space: pre-wrap;">${imp.boContent }</p>
			                        
			                        <!-- 반복 구간 -->
			                        <%-- <c:out value="${imp.noticeFileList}"/>
			                        <c:forEach items="${imp.noticeFileList}" var="noticeFile">
			                        	<p>fileNos == ${noticeFile.fileNos }</p>
			                        	<p>fileNames == ${noticeFile.fileNames }</p>
			                        	<p>fileSizes == ${noticeFile.fileSizes }</p>
			                        	<p>fileMimes == ${noticeFile.fileMimes }</p>			                        
			                        </c:forEach> --%>
			                        
			                        <c:if test="${not empty imp.noticeFileList }">
			                        	<c:forEach items="${imp.noticeFileList}" var="noticeFile">
			                        		<c:set value="${fn:split(noticeFile.fileNames, ',') }" var="fnamearr" />
				                        	<c:set value="${fn:split(noticeFile.fileNos, ',') }" var="filenoarr" />
				                            <c:forEach items="${fnamearr }" var="fname" varStatus="status">
							                        <div class="imporNoticeFileDown">
							                            <span class="badge bg-primary">파일 다운로드</span>
								                            <a class="fileDownload" dataFileNo="${filenoarr[status.index] }" href="#">${fname }</a>
							                            <i class="fas fa-file-download"></i>
							                        </div>
				                            </c:forEach>
				                        </c:forEach>
			                        </c:if>
			                        
			                    </div>
			                </li>
            			</c:forEach>
            		</c:otherwise>
            	</c:choose>
            </ul>
        </div>
        <div class="noticeListCont">
            <ul>
                <li class="noticeListHead">
                    <span>번호</span>
                    <span>제목</span>
                    <span>등록일</span>
                    <span>작성자</span>
                    <span>조회수</span>
                </li>
                <c:set value="${pagingVO.dataList }" var="noticeList" />
                <c:choose>
                	<c:when test="${empty noticeList }">
                		<li class="noticeList textDropVerticalAlign">
                            <div style="text-align: center;">
								등록된 공지사항 게시글이 존재하지 않습니다.
                            </div>
                        </li>
                	</c:when>
                	<c:otherwise>
                		<c:forEach items="${noticeList }" var="notice">
                			<li class="noticeList textDropVerticalAlign">
			                    <span>${notice.boNo }</span>
			                    <span class="textDrop">
			                    	<c:if test="${not empty fileExistNoticeList }">
			                    		<c:forEach items="${fileExistNoticeList }" var="fileExistList">
			                    			<c:if test="${fileExistList.boNo eq notice.boNo }">
						                        <i class="fas fa-paperclip"></i>
			                    			</c:if>
			                    		</c:forEach>
			                    	</c:if>
			                        <a href="/notice/user/detail.do?boNo=${notice.boNo }">
			                            ${notice.boTitle }
			                        </a>
			                        <c:if test="${notice.boImpor eq 'y' }">
			                        	<u style="text-decoration: none;" class="badge bg-danger">중요공지</u>
			                        </c:if>
			                    </span>
			                    <fmt:parseDate var="parseData" value="${notice.boDate }" pattern="yyyy-MM-dd HH:mm:ss" />
			                    <span><fmt:formatDate value="${parseData }" pattern="yyyy-MM-dd"/></span>
			                    <span>${notice.boWriter }</span>
			                    <span>${notice.boHit }</span>
			                </li>
                		</c:forEach>
                	</c:otherwise>
                </c:choose>
            </ul>
        </div>
        <div id="pagingArea">
            ${pagingVO.pagingHTML }
        </div>
        <c:if test="${sessionInfo.memCategory eq '03' }">
	        <div class="noticeBtnGroup">
	            <button type="button" class="btn btn-primary" id="noticeAddBtn">등록</button>
	        </div>
        </c:if>
    </article>
</section>

<%-- <c:out value="${fileExistNoticeList }" /> --%>

<!-- 공지사항 js -->
<script src="${contextPath }/resources/js/userNoticeBoard.js"></script>
<script>
    $(function(){
        // 공통 함수
        $.importantNoticeBoardShowFn();
        $.noticeBoardPagingFn();
        
        var noticeAddBtn = $("#noticeAddBtn");
        noticeAddBtn.click(function(){
            location.href = "/notice/${sessionInfo.memId }/register.do";
        });
        
        var fileDownload = $(".fileDownload");
        fileDownload.on("click", function(event){
        	event.preventDefault();
        	var fileNo = $(this).attr("dataFileNo");
        	location.href = "/notice/user/download.do?fileNo=" + fileNo;
        });
        
        // 종횡비 함수
        var comImgBox = $(".comImgBox");
        var comImg = $(".comImgBox img");
        $.ratioBoxH(comImgBox, comImg);
    });
</script>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
    
<!-- 공지사항 css -->
<link href="${contextPath }/resources/css/userNoticeBoard.css" rel="stylesheet" />

<c:set value="등록" var="name" />
<c:if test="${status eq 'u' }">
	<c:set value="수정" var="name" />
</c:if>

<!-- 공지사항 게시판 등록 영역 -->
<section class="noticeRegisterContainer emptySpace">
    <article class="communityHeadStyle">
        <div class="comImgBox">
            <img src="${contextPath }/resources/images/communityBgImg.jpg" alt="커뮤니티 배경 이미지" />
        </div>
        <div>
            <h3>커뮤니티</h3>
            <span>COMMUNITY</span>
        </div>
    </article>
    <article class="noticeRegisterContents cen">
        <div>
            <h4>공지사항 ${name }</h4>
        </div>
        <div class="card card-dark">
            <form action="/notice/${sessionInfo.memId }/register.do" method="post" enctype="multipart/form-data" id="noticeForm" name="noticeForm">
                
                <c:if test="${status eq 'u' }">
                	<input type="hidden" name="boNo" value="${notice.boNo }" />
                </c:if>
                
                <div class="card-body">
                    <div class="form-group">
                        <label for="boTitle">제목을 입력해주세요</label>
                        <%-- <input type="text" id="boTitle" name="boTitle" class="form-control" placeholder="제목 입력" value="<c:out value="${notice.boTitle }" escapeXml="true" />" /> --%>
                        <input type="text" id="boTitle" name="boTitle" class="form-control" placeholder="제목 입력" value="${notice.boTitle }" />
                    </div>
                    <div class="form-group">
                        <label for="boContent">내용을 입력해주세요</label>
                        <%-- <textarea id="boContent" name="boContent" class="form-control" rows="14"><c:out value="${notice.boContent }" escapeXml="true" /></textarea> --%>
                        <textarea id="boContent" name="boContent" class="form-control" rows="14">${notice.boContent }</textarea>
                    </div>
                    <div class="form-group">
                        <label for="boContent">파일 업로드</label>
                        <input type="file" class="form-control" id="boFile" name="boFile" multiple="multiple">
                    </div>
                    <div class="form-group">
                        <div>
                            <input class="form-check-input" type="checkbox" id="boImpor" name="boImpor" value="y" <c:if test="${notice.boImpor eq 'y' }">checked</c:if> />
                            <label class="form-check-label" for="boImpor">중요 공지 등록</label>
                        </div>
                        <div>
                            <button id="noticeRegBtn" type="button" class="btn btn-outline-primary">공지사항 ${name }</button>
                            <c:if test="${status ne 'u' }">
	                            <button id="noticeListBtn" type="button" class="btn btn-outline-info" onclick="javascript:location.href='/notice/list.do'">목록으로 돌아가기</button>
                            </c:if>
                            <c:if test="${status eq 'u' }">
                            	<button id="noticeListBtn" type="button" class="btn btn-outline-danger" onclick="javascript:location.href='/notice/user/detail.do?boNo=${notice.boNo }'">취소</button>
                            </c:if>
                        </div>
                    </div>
                </div>
                
                <c:if test="${not empty notice.noticeFileList }">
                	<div class="card-footer bg-white">
						<div>
							<span class="badge bg-danger">파일 삭제</span>
						</div>
						<div class="row">
							<c:forEach items="${notice.noticeFileList }" var="noticeFile">
								<div class="col-md-2 fileDownCont">
									<div style="height: 140px;">
										<c:choose>
											<c:when test="${fn:split(noticeFile.fileMime, '/')[0] eq 'image' }">
												<div class="previewImgBox">
			                            			<img src="/resources/notice/${notice.boNo }/${fn:split(noticeFile.fileSavepath, '/')[1] }" alt="파일 다운로드 이미지" />
			                            		</div>
											</c:when>
											<c:otherwise>
												<i class="fas fa-file-download"></i>
											</c:otherwise>
										</c:choose>
									</div>
									<div class="plexHeight">
			                            <h6>${noticeFile.fileName } (${noticeFile.fileFancysize })</h6>
			                        </div>
			                        <div>
			                            <button type="button" class="btn btn-danger btn-sm noticeFileDownBtn" dataFileNo="${noticeFile.fileNo }">DELETE</button>
			                        </div>
								</div>
							</c:forEach>
						</div>
                	</div>
                </c:if>
                
            </form>
        </div>
    </article>
</section>

<!-- 공지사항 js -->
<script src="${contextPath }/resources/js/userNoticeBoard.js"></script>
<script src="${contextPath }/resources/ckeditor/ckeditor.js"></script>
<script>
    $(function(){
        // 공통 함수
        $.noticeFormCKED();
        $.noticeRegisterValidationChkFn();
        $.fileDownBoxHeightFn();
        $.removeFileFn();
        
        // 종횡비 함수
        var comImgBox = $(".comImgBox");
        var comImg = $(".comImgBox img");
        $.ratioBoxH(comImgBox, comImg);
        $.eachPreviewImgBoxResizeFn();
    });
</script>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

<!-- 공지사항 css -->
<link href="${contextPath }/resources/css/userNoticeBoard.css" rel="stylesheet" />

<!-- 공지사항 게시판 상세 영역 -->
<section class="noticeDetailContainer emptySpace">
    <article class="communityHeadStyle">
        <div class="comImgBox">
            <img src="${contextPath }/resources/images/communityBgImg.jpg" alt="커뮤니티 배경 이미지" />
        </div>
        <div>
            <h3>커뮤니티</h3>
            <span>COMMUNITY</span>
        </div>
    </article>
    <article class="noticeDetailContents cen">
        <div>
            <h4>공지사항</h4>
        </div>
        <div class="card card-dark">
            <div class="card-body">
                <div class="form-group">
                    <ul class="noticeDetailTbl">
                        <li>
                            <div class="textDropVerticalAlign">
                                <span class="textDrop">
                                	<%-- <c:out value="${noticeDetail.boTitle }" /> --%>
                                	${noticeDetail.boTitle }
                                </span>
                                <span>${noticeDetail.boWriter }</span>
                                <fmt:parseDate var="detailParseData" value="${noticeDetail.boDate }" pattern="yyyy-MM-dd HH:mm:ss" />
                                <span><fmt:formatDate value="${detailParseData }" pattern="yyyy-MM-dd"/></span>
                                <span>조회 ${noticeDetail.boHit }</span>
                                <span>
                                    <a href="javascript:window.print()">
                                        <i class="fas fa-print"></i>
                                    </a>
                                </span>
                            </div>
                        </li>
                        <li>
                            <%-- <div class="form-control" style="overflow: auto; position: relative; min-height: 380px;"><c:out value="${noticeDetail.boContent }" /></div> --%>
                            <div class="form-control" style="overflow: auto; position: relative; min-height: 380px;">${noticeDetail.boContent }</div>
                        </li>
                        
                       	<c:if test="${not empty prevNextInfo }">
	                        <li class="prevNextBtnGroup">
	                        	<c:forEach items="${prevNextInfo }" var="btnInfo" varStatus="stat">
	                        		<c:if test="${btnInfo.prevnextFlag eq 'prev' }">
			                            <div>
			                                <a href="/notice/user/detail.do?boNo=${btnInfo.boNo }" class="textDropVerticalAlign">
			                                    <span>
			                                        <i class="fas fa-long-arrow-alt-left"></i>
			                                    </span>
			                                    <span>
			                                        <i class="badge bg-secondary">
														이전글
			                                        </i>
			                                    </span>
			                                    <span class="textDrop">
			                                    	${btnInfo.boTitle }
			                                    </span>
			                                </a>
			                            </div>
			                            <c:if test="${fn:length(prevNextInfo) eq 1 }">
				                            <div style="text-align: right; line-height: 100px; padding: 0px 20px;">
				                            	다음 글이 없습니다.
				                            </div>
			                            </c:if>
			                         </c:if>
	                        		 <c:if test="${btnInfo.prevnextFlag eq 'next' }">
	                        			<c:if test="${fn:length(prevNextInfo) eq 1 }">
				                            <div style="text-align: left; line-height: 100px; padding: 0px 20px;">
				                            	이전 글이 없습니다.
				                            </div>
			                            </c:if>
			                            <div>
			                                <a href="/notice/user/detail.do?boNo=${btnInfo.boNo }" class="textDropVerticalAlign">
			                                    <span>
			                                        <i class="fas fa-long-arrow-alt-right"></i>
			                                    </span>
			                                    <span>
			                                        <i class="badge bg-secondary">
														다음글
			                                        </i>
			                                    </span>
			                                    <span class="textDrop">
			                                    	${btnInfo.boTitle }
			                                    </span>
			                                </a>
			                            </div>
			                         </c:if>
	                            </c:forEach>
	                        </li>
                        </c:if>
                        
                    </ul>
                </div>
                
                <form action="/notice/${sessionInfo.memId }/delete.do" method="post" id="deleteForm">
                	<input type="hidden" name="boNo" value="${noticeDetail.boNo }" />
                </form>
                
                <div class="form-group">
                	<c:if test="${sessionInfo.memCategory eq '03' }">
	                    <button id="noticeDeleteBtn" type="button" class="btn btn-outline-danger">삭제</button>
	                    <button id="noticeModifyBtn" type="button" class="btn btn-outline-warning">수정</button>
                	</c:if>
                    <button id="userListGoBtn" type="button" class="btn btn-outline-success">목록</button>
                </div>
            </div>
            <c:if test="${not empty noticeDetail.noticeFileList }">
	            <div class="card-footer bg-white">
	                <div>
	                    <span class="badge bg-dark">파일 다운로드</span>
	                </div>
	                <div class="row">
	                	<!-- 반복 구간 -->
                		<c:forEach items="${noticeDetail.noticeFileList }" var="noticeFile">
                			<div class="col-md-2 fileDownCont">
		                        <div style="height: 140px;">
		                            <c:choose>
		                            	<c:when test="${fn:split(noticeFile.fileMime, '/')[0] eq 'image' }">
		                            		<div class="previewImgBox">
		                            			<img src="/resources/notice/${noticeDetail.boNo }/${fn:split(noticeFile.fileSavepath, '/')[1] }" alt="파일 다운로드 이미지" />
		                            		</div>
		                            	</c:when>
		                            	<c:otherwise>
		                            		<i class="fas fa-file-download"></i>
		                            	</c:otherwise>
		                            </c:choose>
		                        </div>
		                        <div class="plexHeight">
		                            <h6>${noticeFile.fileName } (${noticeFile.fileFancysize })</h6>
		                        </div>
		                        <div>
		                            <button type="button" class="btn btn-secondary btn-sm noticeFileDownBtn" dataFileNo="${noticeFile.fileNo }">DOWNLOAD</button>
		                        </div>
		                    </div>
                		</c:forEach>
	                </div>
	            </div>
            </c:if>
        </div>
    </article>
</section>

<!-- 공지사항 js -->
<script src="${contextPath }/resources/js/userNoticeBoard.js"></script>
<script src="${contextPath }/resources/ckeditor/ckeditor.js"></script>
<script>
    $(function(){
        // 공통 함수
        $.fileDownBoxHeightFn();
        
        // 파일 다운로드 함수
        var noticeFileDownBtn = $(".noticeFileDownBtn");
        noticeFileDownBtn.on("click", function(){
        	var fileNo = $(this).attr("dataFileNo");
        	location.href = "/notice/user/download.do?fileNo=" + fileNo;
        });
        
        // 목록으로 이동
        var userListGoBtn = $("#userListGoBtn");
        userListGoBtn.click(function(){
        	location.href = "/notice/list.do";
        });
        
        // 수정 이동
        var noticeModifyBtn = $("#noticeModifyBtn");
        noticeModifyBtn.click(function(){
        	location.href = "/notice/${sessionInfo.memId }/modify.do?boNo=${noticeDetail.boNo }";
        });
        
        // 삭제 이동
        var noticeDeleteBtn = $("#noticeDeleteBtn");
        var deleteForm = $("#deleteForm");
        noticeDeleteBtn.click(function(){
        	
        	//var agreeFlag = confirm("정말로 삭제하시겠습니까?");
        	//if(agreeFlag) {
        	//	deleteForm.submit();
        	//}
        	
        	Swal.fire({
    		    title: "정말로 삭제하시겠습니까?",
    		    showDenyButton: true,
    		    confirmButtonText: "예",
    		    denyButtonText: "아니오"
    		}).then((result) => {
    		    if (result.isConfirmed) {
    		    	Swal.fire({
    				    title: "공지사항 삭제",
    				    text: "공지사항을 삭제합니다.",
    				    icon: "info"
    				}).then((result) => {
    				    // 모달이 닫힌 후에 실행될 코드
    				    if (result.isConfirmed) {
    				        // 확인 버튼이 클릭되었을 때
    				    	deleteForm.submit();
    				    }
    				});
    		    } else if (result.isDenied) {
    		        Swal.fire({
    					title: "공지사항 삭제",
    					text: "공지사항 삭제를 취소합니다.",
    					icon: "error"
    				});
    		    }
    		});
        	
        });
        
        // 종횡비 함수
        var comImgBox = $(".comImgBox");
        var comImg = $(".comImgBox img");
        $.ratioBoxH(comImgBox, comImg);
        $.eachPreviewImgBoxResizeFn();
    });
</script>

/* 공지사항 게시판 스타일 */
.noticeListContainer {
    position: relative;
}

.communityHeadStyle {
    height: 310px;
    position: relative;
    overflow: hidden;
}

.comImgBox {
    position: relative;
    height: inherit;
}

.comImgBox img {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) scale(1.17);
}

.communityHeadStyle>div:last-of-type {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: white;
    text-align: center;
}

.communityHeadStyle>div:last-of-type h3 {
    font-weight: 700;
    font-size: 2.3rem;
    margin-bottom: 15px;
}

.communityHeadStyle>div:last-of-type span {
    color: white;
    opacity: 0.6;
}

.noticeListContents,
.noticeRegisterContents,
.noticeDetailContents {
    padding: 70px 0px;
}

.noticeListContents>div:first-of-type,
.noticeRegisterContents>div:first-of-type,
.noticeDetailContents>div:first-of-type {
    padding: 0px 0px 40px;
    text-align: center;
}

.noticeListContents>div:first-of-type>h4,
.noticeRegisterContents>div:first-of-type>h4,
.noticeDetailContents>div:first-of-type>h4 {
    font-weight: 700;
    font-size: 2rem;
    margin-bottom: 0px;
}

.searchBoardCont {
    margin-bottom: 40px;
}

#searchForm {
    overflow: auto;
}

#searchForm>div:first-of-type {
    float: left;
    line-height: 38px;
}

#searchForm>div:last-of-type {
    float: right;
    width: 400px;
}

#searchForm>div:last-of-type>div:first-of-type,
#searchForm>div:last-of-type>div:last-of-type {
    position: relative;
    float: left;
}


#searchForm>div:last-of-type>div:first-of-type i {
    position: absolute;
    top: 12px;
    right: 10px;
}

#searchForm>div:last-of-type>div:last-of-type {
    float: right;
}

#searchType {
    padding-right: 30px;
}

#searchBtn {
    position: absolute;
    top: 6px;
    right: 10px;
    border: none;
    background-color: transparent;
    display: block;
    width: 25px;
    outline: none;
    border-radius: 4px;
    border-radius: 4px;
    color: #333;
    transition: all 0.4s;
}

#searchBtn:hover {
    background-color: #333;
    color: white;
}

.imporNoticeListCont {
    margin-bottom: 40px;
}

.imporNoticeListCont ul {
    list-style: none;
    margin-bottom: 0px;
    padding-left: 0px;
}

.impNoticeList {
    border-top: 1px solid #DC1C2C;
    transition: all 0.4s;
}

.textDropVerticalAlign {
    cursor: pointer;
}

.impNoticeList p {
    margin-bottom: 0px;
}

.impNoticeList:last-of-type {
    border-bottom: 1px solid #DC1C2C;
}

.impNoticeList>div:first-of-type {
    padding: 20px 0px;
    transition: all 0.4s;
}

.impNoticeList>div:first-of-type:hover {
    background-color: #eee;
}

.impNoticeList>div:first-of-type span:first-of-type {
    width: 100px;
    color: #75819B;
}

.impNoticeList>div:first-of-type span:nth-of-type(2) {
    width: calc(100% - 262px);
}

.impNoticeList>div:first-of-type span:nth-of-type(3) {
    width: 100px;
}

.impNoticeList>div:first-of-type span:last-of-type {
    width: 50px;
    text-align: right;
    color: #75819B;
}

.visibleDiv {
    display: none;
    /* min-height: 380px; */
}

.visibleDiv p {
    padding: 10px 0px;
	overflow: auto;
	position: relative;
}

.imporNoticeFileDown {
    padding-bottom : 20px;
}

.imporNoticeFileDown span,
.imporNoticeFileDown a {
    margin-right: 5px;
}

.noticeListCont {
    margin-bottom: 40px;
}

.noticeListCont ul {
    list-style: none;
    margin-bottom: 0px;
    padding-left: 0px;
}

.noticeListCont li {
    padding: 10px 0px;
}

.noticeListCont span {
    display: inline-block;
    text-align: center;
}

.noticeListCont span:first-of-type {
    width: 50px;
}

.noticeListCont span:nth-of-type(2) {
    width: calc(100% - 335px);
    padding-right: 30px;
    position: relative;
    text-align: left;
}

/* .noticeListCont span:nth-of-type(2) i {
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
} */

.noticeListCont span:nth-of-type(3){
    width: 100px;
}

.noticeListCont span:nth-of-type(4){
    width: 100px;
}

.noticeListCont span:last-of-type {
    width: 70px;
}

.noticeListHead {
    border-top: 2px solid #333;
    padding: 15px 0px!important;
    background-color: #eee;
}

.noticeListHead span {
    font-weight: 700;
    text-align: center!important;
}

.noticeList {
    border-top: 1px solid #ddd;
}

.noticeList:last-of-type {
    border-bottom: 1px solid #ddd;
}

#pagingArea {
    margin-bottom: 40px;
}

#pagingArea ul {
    margin: auto;
    display: flex;
    justify-content: center;
}

#pagingArea .page-link {
    cursor: pointer;
}

.noticeBtnGroup {
    text-align: right;
}

.noticeRegisterContents .form-group {
    margin-bottom: 10px;
}

.noticeRegisterContents .form-group label {
    margin-bottom: 10px;
}

.fileDownCont {
    padding: 10px;
}

.fileDownCont h6 {
    margin-bottom: 0px;
    padding: 0px 10px;
}

.fileDownCont>div:first-of-type {
    text-align: center;
    font-size: 5rem;
    background-color: #eee;
    padding: 10px 0px;
    border-radius: 4px 4px 0px 0px;
}

.fileDownCont>div:nth-of-type(2) {
    background-color: #ddd;
    text-align: center;
    padding: 10px 0px;
}

.fileDownCont>div:last-of-type button {
    width: 100%;
    border-radius: 0px 0px 4px 4px;
}

#noticeForm .form-group:last-of-type {
    padding-top: 10px;
    overflow: auto;
}

#noticeForm .form-group:last-of-type>div:first-of-type {
    float: left;
}

#noticeForm .form-group:last-of-type>div:first-of-type label {
    margin-left: 10px;
}

#noticeForm .form-group:last-of-type>div:last-of-type {
    float: right;
}

#noticeForm .form-group:last-of-type>div:last-of-type button:last-of-type {
    margin-left: 10px;
}

.noticeDetailContents p {
    margin-bottom: 0px;
}

.noticeDetailContents a {
    color: #333;
}

.noticeDetailTbl {
    list-style: none;
    padding-left: 0px;
    margin-bottom: 0px;
}

.noticeDetailTbl li:first-of-type div {
    padding: 10px 0px 20px;
    cursor: auto;
}

.noticeDetailTbl li:first-of-type span {
    display: inline-block;
    width: 110px;
    text-align: center;
    position: relative;
    color: #555;
}

.noticeDetailTbl li:first-of-type span:first-of-type {
    color: black;
    font-size: 1.3rem;
    padding: 0px;
    width: calc(100% - 390px);
    text-align: left;
}

.noticeDetailTbl li:first-of-type span:last-of-type {
    width: 25px;
    height: 25px;
    background-color: #ddd;
    border-radius: 50%;
    margin-left: 20px;
    transition: all 0.4s;
}

.noticeDetailTbl li:first-of-type span:last-of-type i {
    font-size: 0.9rem;
}

.noticeDetailTbl li:first-of-type span:last-of-type:hover {
    background-color: black;
}

.noticeDetailTbl li:first-of-type span:last-of-type:hover i {
    color: white;
}

.noticeDetailTbl li:first-of-type span::after {
    content: "";
    position: absolute;
    width: 1px;
    height: 14px;
    background-color: #555;
    right: 0px;
    top: 6px;
}

.noticeDetailTbl li:first-of-type span:last-of-type::after,
.noticeDetailTbl li:first-of-type span:first-of-type::after {
    content: none;
}

.noticeDetailTbl li:nth-of-type(2) {
    margin-bottom: 20px;
}

.prevNextBtnGroup {
    overflow: auto;
    margin-bottom: 20px;
}

.prevNextBtnGroup>div {
    width: 300px;
    height: 100px;
    border-radius: 4px;
    position: relative;
    background-color: #eee;
}

.prevNextBtnGroup span {
    display: inline-block;
    width: 50px;
}

.prevNextBtnGroup span i {
    font-style: normal;
    padding: 10px 15px;
}

.prevNextBtnGroup>div:first-of-type {
    float: left;
}

.prevNextBtnGroup>div:last-of-type {
    float: right;
}

.prevNextBtnGroup span:first-of-type {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    text-align: center;
    width: 50px;
    height: 50px;
    line-height: 50px;
    background-color: #ddd;
    border-radius: 50%;
    font-size: 1.3rem;
}

.prevNextBtnGroup>div:first-of-type span:first-of-type {
    left: 10px;
}

.prevNextBtnGroup>div:first-of-type span:first-of-type,
.prevNextBtnGroup>div:last-of-type span:first-of-type {
    transition: all 0.4s;
}

.prevNextBtnGroup>div:last-of-type span:first-of-type {
    right: 10px;
}

.prevNextBtnGroup>div:first-of-type:hover span:first-of-type,
.prevNextBtnGroup>div:last-of-type:hover span:first-of-type {
    background-color: black;
}

.prevNextBtnGroup>div:first-of-type:hover span:first-of-type i,
.prevNextBtnGroup>div:last-of-type:hover span:first-of-type i {
    color: white;
}

.prevNextBtnGroup span:not(:first-of-type) {
    display: block;
    margin: 0px 70px;
    width: calc(100% - 80px);
    height: calc(100% / 2);
    line-height: 50px;
}

.prevNextBtnGroup>div:first-of-type span:not(:first-of-type) {
    margin-right: 10px;
}

.prevNextBtnGroup>div:last-of-type span:not(:first-of-type) {
    margin-left: 10px;
    text-align: right;
}

.noticeDetailContents .form-group:last-of-type {
    text-align: right;
}

.noticeDetailContents .form-group:last-of-type button {
    margin-left: 10px;
}

.noticeDetailContents .form-group:last-of-type button:first-of-type {
    margin-left: 0px;
}

.previewImgBox {
	position: relative;
	width: 100%;
	height: 100%;
	overflow: hidden;
}

.previewImgBox img {
	display: block;
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
}

@media all and (max-width: 880px) {
    
    .prevNextBtnGroup>div {
        width: auto;
    }
    
    .prevNextBtnGroup>div>a>span:not(:first-of-type) {
        display: none;
    }
    
}

// 공지사항 > 중요공지 > 아코디언 게시판 기능
$.importantNoticeBoardShowFn = function(){
    var textDropVerticalAlign = $(".textDropVerticalAlign");
    textDropVerticalAlign.click(function(){
        var thisIs = $(this);
        var impNoticeTblCenter = thisIs.find(".plusMinus");
        var visibleDiv = thisIs.parent().find(".visibleDiv").is(":visible");
        console.log("visibleDiv : ", visibleDiv);
        if(visibleDiv) { // 게시글이 눈에 보임
            thisIs.parent().find(".visibleDiv").stop().slideUp(300);
            impNoticeTblCenter.html("<i class='fas fa-plus'></i>");
        }else { // 게시글이 눈에 보이지 않음
            thisIs.parent().find(".visibleDiv").stop().slideDown(300);
            impNoticeTblCenter.html("<i class='fas fa-minus'></i>");
        }
        // 방금 누른 것을 제외한 나머지 div는 싹 닫는다.
        thisIs.parent().siblings(".impNoticeList").children(".visibleDiv").slideUp(300);
        thisIs.parent().siblings(".impNoticeList").find(".plusMinus").html("<i class='fas fa-plus'></i>");
    });
};

$.noticeBoardPagingFn = function(event){
	var pagingArea = $("#pagingArea");
	var searchForm = $("#searchForm");
	pagingArea.on("click", "a", function(event){
		event.preventDefault(); // a태그의 이벤트를 block
		var pageNo = $(this).attr("data-page");
		console.log("pageNo : ", pageNo);
		searchForm.find("#page").val(pageNo);
		searchForm.submit();
	});
};

$.noticeFormCKED = function(){
    CKEDITOR.replace("boContent", {
		filebrowserUploadUrl: '/imageUpload.do'
	});
    CKEDITOR.config.height = "500px"; // CKEDITOR 높이 설정
};

$.noticeRegisterValidationChkFn = function(){
	var noticeRegBtn = $("#noticeRegBtn");
	var noticeForm = $("#noticeForm");
	var boTitle = $("#boTitle");
    noticeRegBtn.on("click", function(){
    
        var titleFlag = $.falsyCheckFn(boTitle, "제목");
		var boContent = CKEDITOR.instances.boContent.getData();
        
        if(!titleFlag) return;
        if(!boContent) {
            //alert("내용을 입력해 주세요.");
            Swal.fire({
				title: "안내",
				text: "내용을 입력해 주세요.",
				icon: "info"
			});
            return false;
        }
        
        if($(this).text() == "공지사항 수정"){
        	noticeForm.attr("action", "/notice/admin/modify.do");
        }
        
        noticeForm.submit();
    });
};

// 다운로드 박스 높이를  같게 조절하는 함수
$.fileDownBoxHeightFn = function(){
    var fileDownCont = $(".fileDownCont");
    var topBoxH = 0;
    fileDownCont.each(function(i, v){
        var thisIs = $(this);
        var boxH = thisIs.find(".plexHeight").height();
        //console.log("boxH : " + boxH);
        if(topBoxH < boxH) {
            topBoxH = boxH;
        }
        //console.log("topBoxH : " + topBoxH);
    });
    fileDownCont.siblings().find(".plexHeight").height(topBoxH);
};

// 다운로드 미리보기 이미지 각각의 종횡비 변경 함수
$.eachPreviewImgBoxResizeFn = function(){
    $(".fileDownCont").each(function(i, v){
        var thisIs = $(this);
        var previewImgBox = thisIs.find(".previewImgBox");
        var previewImg = thisIs.find("img");
        $.ratioBoxH(previewImgBox, previewImg);
    });
};

// 이미지 파일 삭제 처리 함수
$.removeFileFn = function(){
	var noticeFileDelBtn = $(".noticeFileDownBtn");
	noticeFileDelBtn.click(function(){
		var thisIs = $(this);
		var fileDelCont = $(".fileDownCont");
		var delFileNo = thisIs.attr("dataFileNo");
		//console.log("delFileNo : " + delFileNo);
		$("#noticeForm").append("<input type='hidden' name='delNoticeNo' value='"+ delFileNo +"' />");
		thisIs.parents(".fileDownCont").hide();
		console.log("파일이 누네 보이니? : " + fileDelCont.is(":visible"));
		var delFileVisible = fileDelCont.is(":visible");
		if(!delFileVisible) {
			thisIs.parents(".card-footer").hide();
		}
	});
};

package kr.or.ddit.users.board.vo;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import lombok.Data;

@Data
public class NoticeVO {

	private int boNo;
	private String boTitle;
	private String boContent;
	private String boWriter;
	private String boDate;
	private int boHit;
	private String boImpor;
	private String prevnextFlag;
	
	private Integer[] delNoticeNo;
	private MultipartFile[] boFile;
	private List<NoticeFileVO> noticeFileList;
	
	private NoticeFileVO noticeFileVO;
	
	public void setBoFile(MultipartFile[] boFile) {
		this.boFile = boFile;
		if(boFile != null) {
			List<NoticeFileVO> noticeFileList = new ArrayList<NoticeFileVO>();
			for(MultipartFile item : boFile) {
				if(StringUtils.isBlank(item.getOriginalFilename())) {
					continue;
				}
				NoticeFileVO boardFileVO = new NoticeFileVO(item);
				noticeFileList.add(boardFileVO);
			}
			this.noticeFileList = noticeFileList;
		}
	}
	
}

package kr.or.ddit.users.board.vo;

import org.apache.commons.io.FileUtils;
import org.springframework.web.multipart.MultipartFile;

import lombok.Data;

@Data
public class NoticeFileVO {

	private MultipartFile item;
	private Integer boNo;
	private Integer fileNo;
	private String fileName;
	private Long fileSize;
	private String fileFancysize;
	private String fileMime;
	private String fileSavepath;
	private Integer fileDowncount;
	
	public NoticeFileVO() {}
	public NoticeFileVO(MultipartFile item) {
		this.item = item;
		this.fileName = item.getOriginalFilename();
		this.fileSize = item.getSize();
		this.fileMime = item.getContentType();
		this.fileFancysize = FileUtils.byteCountToDisplaySize(fileSize); 
	}
	
	private String fileNos;
	private String fileNames;
	private String fileSizes;
	private String fileFancysizes;
	private String fileMimes;
	private String fileSavepaths;
	private String fileDowncounts;
	
}

package kr.or.ddit.users.board.controller;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import kr.or.ddit.users.board.service.NoticeService;
import kr.or.ddit.users.board.vo.NoticeFileVO;
import kr.or.ddit.users.board.vo.NoticeVO;
import kr.or.ddit.users.login.vo.MemberVO;
import kr.or.ddit.utils.MediaUtils;
import kr.or.ddit.utils.ServiceResult;
import kr.or.ddit.vo.PaginationInfoVO;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
@RequestMapping("/notice")
public class NoticeBoardController {
	
	@Inject
	private NoticeService noticeService;

	// 공지사항 리스트 출력
	@CrossOrigin(origins = "http://localhost")
	@RequestMapping(value = "/list.do", method = RequestMethod.GET)
	public String noticeList(
			@RequestParam(name = "page", required = false, defaultValue = "1") int currentPage,
			@RequestParam(required = false, defaultValue = "title") String searchType,
			@RequestParam(required = false) String searchWord,
			Model model
			) {
		
		/** 자료 수집 및 정의 */
		Map<String,Object> param = new HashMap<>();
		param.put("currentPage", currentPage);
		param.put("searchType", searchType);
		param.put("searchWord", searchWord);
		
		/** 서비스 호출 */
		// 공지사항 게시판 리스트 가져오기
		noticeService.noticeList(param);
		
		/** 반환 자료 */
		PaginationInfoVO<NoticeVO> pagingVO = (PaginationInfoVO<NoticeVO>) param.get("pagingVO");
		List<NoticeVO> importantNoticeList = (List<NoticeVO>) param.get("importantNoticeList");
		List<NoticeVO> fileExistNoticeList = (List<NoticeVO>) param.get("fileExistNoticeList");
		
		/** 자료 검증 */ 
		log.info("pagingVO : " + pagingVO.toString());
		log.info("importantNoticeList : " + importantNoticeList.toString());
		log.info("importantNoticeList : " + importantNoticeList.toString());
		log.info("fileExistNoticeList : " + fileExistNoticeList.toString());
		
		/** 자료 반환 */ 
		model.addAttribute("searchType", searchType);
		model.addAttribute("searchWord", searchWord);
		model.addAttribute("pagingVO", pagingVO);
		model.addAttribute("importantNoticeList", importantNoticeList);
		model.addAttribute("fileExistNoticeList", fileExistNoticeList);
		
		return "board/noticeBoardList";
		
	}
	
	// 공지사항 파일 다운로드
	@RequestMapping(value = "/user/download.do", method = RequestMethod.GET)
	public ResponseEntity<byte[]> fileDownload(int fileNo) throws Exception {
		
		InputStream in = null;
		ResponseEntity<byte[]> entity = null;
		
		String fileName = null;
		NoticeFileVO fileVO = noticeService.selectFileInfo(fileNo);
		if(fileVO != null) {
			try {
				fileName = fileVO.getFileName();
				
				String formatName = fileName.substring(fileName.lastIndexOf(".") + 1);
				MediaType mType = MediaUtils.getMediaType(formatName);
				HttpHeaders headers = new HttpHeaders();
				in = new FileInputStream(fileVO.getFileSavepath());
				
				fileName = fileName.substring(fileName.indexOf("_") + 1);
				headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
				headers.add("Content-Disposition", "attachment; filename=\""+ new String(fileName.getBytes("UTF-8"), "ISO-8859-1") +"\"");
				
				entity = new ResponseEntity<byte[]>(IOUtils.toByteArray(in), headers, HttpStatus.CREATED);
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				in.close();
			}
		}else {
			entity = new ResponseEntity<byte[]>(HttpStatus.BAD_REQUEST);
		}
		
		return entity;
		
	}
	
	// 공지사항 상세보기
	@CrossOrigin(origins = "http://localhost")
	@RequestMapping(value = "/user/detail.do")
	public String noticeDetail(int boNo, Model model) {
		
		/** 자료수집 및 정의 */
		Map<String,Object> param = new HashMap<>();
		param.put("boNo", boNo);
		
		/** 서비스 호출 */
		noticeService.noticeDetail(param);
		
		/** 반환 자료 */
		NoticeVO noticeVO = (NoticeVO) param.get("noticeVO");
		List<NoticeVO> prevNextList = (List<NoticeVO>) param.get("prevNextList");
		
		/** 자료검증 */
		log.info("noticeVO : " + noticeVO);
		log.info("prevNextList : " + prevNextList);
		
		/** 자료반환 */
		model.addAttribute("noticeDetail", noticeVO);
		model.addAttribute("prevNextInfo", prevNextList);
		
		return "board/noticeBoardDetail";
		
	}
	
	///////////////////////////////////////////////////////////////////////////
	
	// 공지사항 등록
	@CrossOrigin(origins = "http://localhost")
	@RequestMapping(value = "/admin/register.do", method = RequestMethod.GET)
	public String noticeRegisterForm() {
		return "board/noticeBoardForm";
	}
	
	@RequestMapping(value = "/admin/register.do", method = RequestMethod.POST)
	public String noticeRegister(
			HttpServletRequest req,
			NoticeVO noticeVO, 
			RedirectAttributes ra,
			Model model
			) throws Exception {
		
		String goPage = "";
		
		Map<String, String> errors = new HashMap<String, String>();
		if(StringUtils.isBlank(noticeVO.getBoTitle())) {
			errors.put("boTitle", "제목을 입력해 주세요");
		}
		if(StringUtils.isBlank(noticeVO.getBoContent())) {
			errors.put("boContent", "내용을 입력해 주세요");
		}
		
		if(errors.size() > 0) {
			model.addAttribute("errors", errors);
			model.addAttribute("msgflag", "fa");
			model.addAttribute("noticeVO", noticeVO);
			goPage = "board/noticeBoardForm";
		}else {
			HttpSession session = req.getSession();
			MemberVO memberVO = (MemberVO) session.getAttribute("sessionInfo");
			noticeVO.setBoWriter(memberVO.getMemName()); // 로그인 한 사용자의 이름으로 작성자 넣기
			ServiceResult result = noticeService.insertNotice(req, noticeVO);
			
			if(result.equals(ServiceResult.OK)) {
				ra.addFlashAttribute("message", "공지사항 게시글이 성공적으로 등록되었습니다!");
				ra.addFlashAttribute("msgflag", "su");
				goPage = "redirect:/notice/list.do";
			}else {
				// 성공, 실패, 정보
				// 성공 : su
				// 실패 : fa
				// 정보 : in
				model.addAttribute("message", "서버 에러, 다시 시도해주세요!");
				model.addAttribute("msgflag", "fa");
				model.addAttribute("noticeVO", noticeVO);
				goPage = "board/noticeBoardForm";
			}
		}
		
		return goPage;
		
	}
	
	// 공지사항 삭제
	@RequestMapping(value = "/admin/delete.do", method = RequestMethod.POST)
	public String noticeDelete(
			RedirectAttributes ra,
			int boNo,
			Model model
			) {
		
		String goPage = "";
		
		ServiceResult result = noticeService.deleteNotice(boNo);
		if(result.equals(ServiceResult.OK)) {
			ra.addFlashAttribute("message", "공지사항 삭제가 완료되었습니다");
			ra.addFlashAttribute("msgflag", "su");
			goPage = "redirect:/notice/list.do";
		}else {
			ra.addFlashAttribute("message", "공지사항 삭제에 실패했습니다");
			ra.addFlashAttribute("msgflag", "fa");
			goPage = "redirect:/notice/user/detail.do?boNo=" + boNo;
		}
		
		return goPage;
		
	}
	
	// 공지사항 수정
	@CrossOrigin(origins = "http://localhost")
	@RequestMapping(value = "/admin/modify.do", method = RequestMethod.GET)
	public String noticeModifyForm(int boNo, Model model) {
		NoticeVO noticeVO = noticeService.selectNotice(boNo);
		model.addAttribute("notice", noticeVO);
		model.addAttribute("status", "u");
		return "board/noticeBoardForm";
	}
	
	@RequestMapping(value = "/admin/modify.do", method = RequestMethod.POST)
	public String noticeModify(
			HttpServletRequest req,
			NoticeVO noticeVO,
			Model model,
			RedirectAttributes ra
			) {
		
		String goPage = "";
		
		HttpSession session = req.getSession();
		MemberVO memberVO = (MemberVO) session.getAttribute("sessionInfo");
		noticeVO.setBoWriter(memberVO.getMemName()); // 로그인 한 사용자의 이름으로 작성자 넣기
		ServiceResult result = noticeService.modifyNotice(req, noticeVO);
		
		if(result.equals(ServiceResult.OK)) {
			ra.addFlashAttribute("message", "공지사항 수정이 완료되었습니다.");
			ra.addFlashAttribute("msgflag", "su");
			goPage = "redirect:/notice/user/detail.do?boNo=" + noticeVO.getBoNo();
		}else {
			model.addAttribute("notice", noticeVO);
			model.addAttribute("message", "공지사항 수정이 실패했습니다!");
			ra.addFlashAttribute("msgflag", "fa");
			model.addAttribute("status", "u");
			goPage = "board/noticeBoardForm";
		}
		
		return goPage;
		
	}
	
}

package kr.or.ddit.users.board.service;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import kr.or.ddit.users.board.vo.NoticeFileVO;
import kr.or.ddit.users.board.vo.NoticeVO;
import kr.or.ddit.utils.ServiceResult;

public interface NoticeService {

	public void noticeList(Map<String, Object> param);
	public void noticeDetail(Map<String, Object> param);
	
	public ServiceResult insertNotice(HttpServletRequest req, NoticeVO noticeVO) throws Exception;
	public NoticeFileVO selectFileInfo(int fileNo);
	public NoticeVO selectNotice(int boNo);
	public ServiceResult deleteNotice(int boNo);
	public ServiceResult modifyNotice(HttpServletRequest req, NoticeVO noticeVO);

}

package kr.or.ddit.users.board.service.impl;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;

import kr.or.ddit.mapper.NoticeBoardMapper;
import kr.or.ddit.users.board.service.NoticeService;
import kr.or.ddit.users.board.vo.NoticeFileVO;
import kr.or.ddit.users.board.vo.NoticeVO;
import kr.or.ddit.users.login.vo.MemberVO;
import kr.or.ddit.utils.FileUploadUtils;
import kr.or.ddit.utils.ServiceResult;
import kr.or.ddit.vo.PaginationInfoVO;

@Service
@Transactional(rollbackFor = Exception.class)
public class NoticeServiceImpl implements NoticeService {

	@Inject
	private NoticeBoardMapper noticeMapper;
	
	@Override
	public void noticeList(Map<String, Object> param) {
		
		/** 파라미터 조회 */
		int currentPage = (int) param.get("currentPage");
		String searchType = (String) param.get("searchType");
		String searchWord = (String) param.get("searchWord");
		
		/** 파라미터 정의 */
		PaginationInfoVO<NoticeVO> pagingVO = new PaginationInfoVO<NoticeVO>();
		List<NoticeVO> importantNoticeList = new ArrayList<NoticeVO>();
		List<NoticeVO> fileExistNoticeList = new ArrayList<NoticeVO>();
		
		/** 메인로직 처리 */
		// 검색 기능
		if(StringUtils.isNotBlank(searchWord)) {
			pagingVO.setSearchType(searchType);
			pagingVO.setSearchWord(searchWord);
		}
		pagingVO.setCurrentPage(currentPage);
		
		// 총 게시글 수 가져오기
		int totalRecord = noticeMapper.selectNoticeCount(pagingVO);
		pagingVO.setTotalRecord(totalRecord);
		
		// 총 게시글을 리스트로 받아오기
		List<NoticeVO> dataList = noticeMapper.selectNoticeList(pagingVO);
		pagingVO.setDataList(dataList);
		
		// 중요공지 리스트 가져오기
		importantNoticeList = noticeMapper.importantNoticeList();
		
		// 파일이 들어있는 게시글 리스트 가져오기
		fileExistNoticeList = noticeMapper.fileExistNoticeList();
		
		/** 반환자료 저장 */
		param.put("pagingVO", pagingVO);
		param.put("importantNoticeList", importantNoticeList);
		param.put("fileExistNoticeList", fileExistNoticeList);
		
	}
	
	@Override
	public void noticeDetail(Map<String, Object> param) {

		/** 파라미터 조회 */
		int boNo = (int) param.get("boNo");
		
		/** 파라미터 정의 */
		NoticeVO noticeVO = new NoticeVO();
		List<NoticeVO> prevNextList = new ArrayList<NoticeVO>();
		
		/** 메인로직 처리 */
		// 조회수 증가
		noticeMapper.incrementHit(boNo);
		
		// 게시판 상세 정보 가져오기
		noticeVO = noticeMapper.selectNotice(boNo);
		
		// 이전글/이후글 정보 가져오기
		prevNextList = noticeMapper.prevNextInfo(boNo);
		
		/** 반환자료 저장 */
		param.put("noticeVO", noticeVO);
		param.put("prevNextList", prevNextList);
		
	}

	@Override
	public ServiceResult insertNotice(HttpServletRequest req, NoticeVO noticeVO) throws Exception {
		ServiceResult result = null;
		
		int status = noticeMapper.insertNotice(noticeVO);
		if(status > 0) {
			List<NoticeFileVO> noticeFileList = noticeVO.getNoticeFileList();
			FileUploadUtils.noticeBoardFileUpload(noticeFileList, noticeVO.getBoNo(), req, noticeMapper);
			result = ServiceResult.OK;
		}else {
			result = ServiceResult.FAILED;
		}
		
		return result;
	}

	@Override
	public NoticeFileVO selectFileInfo(int fileNo) {
		return noticeMapper.selectFileInfo(fileNo);
	}

	@Override
	public NoticeVO selectNotice(int boNo) {
		noticeMapper.incrementHit(boNo); // 조회수 증가
		return noticeMapper.selectNotice(boNo);
	}

	@Override
	public ServiceResult deleteNotice(int boNo) {
		ServiceResult result = null;
		
		// 먼저 파일부터 삭제한다.
		noticeMapper.deleteNoticeFile(boNo);
		
		// 그 다음에 게시글을 삭제한다.
		int status = noticeMapper.deleteNotice(boNo);
		if(status > 0) {
			result = ServiceResult.OK;
		}else {
			result = ServiceResult.FAILED;
		}
		
		return result;
	}

	@Override
	public ServiceResult modifyNotice(HttpServletRequest req, NoticeVO noticeVO) {
		ServiceResult result = null;
		
		int status = noticeMapper.modifyNotice(noticeVO); // 공지사항 수정
		if(status > 0) { // 공지사항 수정 완료
			// 게시글 정보에서 파일 목록을 가져오기
			List<NoticeFileVO> noticeFileList = noticeVO.getNoticeFileList();
			
			try {
				// 공지사항 업로드 진행
				FileUploadUtils.noticeBoardFileUpload(noticeFileList, noticeVO.getBoNo(), req, noticeMapper);
				
				// 기존에 등록되어 있는 파일 목록들 중, 수정하기 위해 delete 버튼을 눌러 삭제 처리로 넘겨준 파일 번호들
				Integer[] delNoticeNo = noticeVO.getDelNoticeNo();
				
				if(delNoticeNo != null) {
					for(int i = 0; i > delNoticeNo.length; i++) {
						// 삭제할 파일 번호 중, 파일 번호에 해당하는 게시판 파일 정보를 가져와 물리적인 삭제를 진행
						NoticeFileVO noticeFileVO = noticeMapper.selectNoticeFile(delNoticeNo[i]);
						File file = new File(noticeFileVO.getFileSavepath());
						file.delete(); // 기존 파일이 업로드된 경로에 파일 삭제
					}
					noticeMapper.deleteNoticeFileList(delNoticeNo); // 파일번호에 해당하는 파일 데이터를 DB에서 삭제
				}
				
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			result = ServiceResult.OK;
		}else {
			result = ServiceResult.FAILED;
		}
		
		return result;
	}

}

package kr.or.ddit.mapper;

import java.util.List;

import kr.or.ddit.users.board.vo.NoticeFileVO;
import kr.or.ddit.users.board.vo.NoticeVO;
import kr.or.ddit.vo.PaginationInfoVO;

public interface NoticeBoardMapper {

	public int selectNoticeCount(PaginationInfoVO<NoticeVO> pagingVO);
	public List<NoticeVO> selectNoticeList(PaginationInfoVO<NoticeVO> pagingVO);
	public List<NoticeVO> importantNoticeList();
	public List<NoticeVO> fileExistNoticeList();
	
	public int insertNotice(NoticeVO noticeVO);
	public void insertNoticeBoardFile(NoticeFileVO noticeFileVO);
	public NoticeFileVO selectFileInfo(int fileNo);
	public void incrementHit(int boNo);
	public NoticeVO selectNotice(int boNo);
	public List<NoticeVO> prevNextInfo(int boNo);
	public void deleteNoticeFile(int boNo);
	public int deleteNotice(int boNo);
	
	public int modifyNotice(NoticeVO noticeVO);
	public NoticeFileVO selectNoticeFile(Integer integer);
	public void deleteNoticeFileList(Integer[] delNoticeNo);

}

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="kr.or.ddit.mapper.NoticeBoardMapper">
	
	<resultMap type="noticeVO" id="noticeMap">
		<id property="boNo" column="bo_no" />
		<result property="boNo" column="bo_no" />
		<result property="boTitle" column="bo_title" />
		<result property="boContent" column="bo_content" />
		<result property="boWriter" column="bo_writer" />
		<result property="boDate" column="bo_date" />
		<result property="boHit" column="bo_hit" />
		<result property="boImpor" column="bo_impor" />
		<collection property="noticeFileList" resultMap="noticeFileMap" />
	</resultMap>
	
	<resultMap type="noticefileVO" id="noticeFileMap">
		<id property="fileNo" column="file_no" />
		<result property="fileNo" column="file_no" />
		<result property="fileName" column="file_name" />
		<result property="fileSize" column="file_size" />
		<result property="fileFancysize" column="file_fancysize" />
		<result property="fileMime" column="file_mime" />
		<result property="fileSavepath" column="file_savepath" />
		<result property="fileDowncount" column="file_downcount" />
		
		<result property="fileNos" column="file_nos" />
		<result property="fileNames" column="file_names" />
		<result property="fileSizes" column="file_sizes" />
		<result property="fileFancysizes" column="file_fancysizes" />
		<result property="fileMimes" column="file_mimes" />
		<result property="fileSavepaths" column="file_savepaths" />
		<result property="fileDowncounts" column="file_downcounts" />
	</resultMap>

	<select id="selectNoticeCount" parameterType="pagingVO" resultType="int">
	
		select count(bo_no) from notice 
		where 1=1 
		
		<!-- <include refid="noticeSearch" /> -->
		<if test="searchType != null and searchType == 'title'">
			and (bo_title like '%' || #{searchWord} || '%')
		</if>
		<if test="searchType != null and searchType == 'writer'">
			and (bo_writer like '%' || #{searchWord} || '%')
		</if>
		<if test="searchType != null and searchType == 'both'">
			and (bo_title like '%' || #{searchWord} || '%')
			or (bo_writer like '%' || #{searchWord} || '%')
		</if>
		
	</select>
	
	<select id="selectNoticeList" parameterType="pagingVO" resultType="noticeVO">
	
		<include refid="commonMapper.pagingHeader" />
		
			select 
				bo_no
				, bo_title
				, bo_content
				, bo_writer
				, bo_date
				, bo_hit
				, bo_impor
			from notice n
			where 1=1 
			
			<!-- <include refid="noticeSearch" /> -->
			<if test="searchType != null and searchType == 'title'">
				and (bo_title like '%' || #{searchWord} || '%')
			</if>
			<if test="searchType != null and searchType == 'writer'">
				and (bo_writer like '%' || #{searchWord} || '%')
			</if>
			<if test="searchType != null and searchType == 'both'">
				and (bo_title like '%' || #{searchWord} || '%')
				or (bo_writer like '%' || #{searchWord} || '%')
			</if>
			
		<include refid="commonMapper.pagingFooter" />
		
	</select>
	
	<select id="importantNoticeList" resultMap="noticeMap">
	
		<include refid="commonMapper.pagingHeader" />
		
	        SELECT
			    n.bo_no,
			    n.bo_title,
			    n.bo_content,
			    n.bo_writer,
			    n.bo_date,
			    n.bo_hit,
			    n.bo_impor,
			    LISTAGG(nf.file_no, ',') WITHIN GROUP (ORDER BY nf.file_no) AS file_nos,
			    LISTAGG(nf.file_name, ',') WITHIN GROUP (ORDER BY nf.file_no) AS file_names,
			    LISTAGG(nf.file_size, ',') WITHIN GROUP (ORDER BY nf.file_no) AS file_sizes,
			    LISTAGG(nf.file_fancysize, ',') WITHIN GROUP (ORDER BY nf.file_no) AS file_fancysizes,
			    LISTAGG(nf.file_mime, ',') WITHIN GROUP (ORDER BY nf.file_no) AS file_mimes,
			    LISTAGG(nf.file_savepath, ',') WITHIN GROUP (ORDER BY nf.file_no) AS file_savepaths,
			    LISTAGG(nf.file_downcount, ',') WITHIN GROUP (ORDER BY nf.file_no) AS file_downcounts
			FROM
			    notice n
			LEFT OUTER JOIN
			    noticefile nf 
			ON 
				n.bo_no = nf.bo_no
			WHERE
			    1=1 
			AND 
				n.bo_impor = 'y'
			GROUP BY
			    n.bo_no, n.bo_title, n.bo_content, n.bo_writer, n.bo_date, n.bo_hit, n.bo_impor
			ORDER BY
			    n.bo_no DESC
				    
		<include refid="commonMapper.pagingFooters" />
		     
	</select>
	
	<select id="fileExistNoticeList" resultType="noticeVO">
		select
		    DISTINCT a.bo_no
		from 
		    notice a
		    , noticefile b
		where 1=1
		and a.bo_no = b.bo_no
	</select>
	
	<insert id="insertNotice" parameterType="noticeVO" useGeneratedKeys="true">
		<selectKey keyProperty="boNo" order="BEFORE" resultType="int">
			select seq_notice.nextval from dual
		</selectKey>
			INSERT INTO notice (
			    bo_no,
			    bo_title,
			    bo_content,
			    bo_writer,
			    bo_date,
			    bo_hit,
			    bo_impor
			) VALUES (
			    #{boNo},
			    #{boTitle},
			    #{boContent},
			    #{boWriter},
			    sysdate,
			    0,
			    #{boImpor}
			)
	</insert>
	
	<insert id="insertNoticeBoardFile" parameterType="noticefileVO">
		INSERT INTO noticefile (
		    file_no,
		    bo_no,
		    file_name,
		    file_size,
		    file_fancysize,
		    file_mime,
		    file_savepath,
		    file_downcount
		) VALUES (
		    seq_noticefile.nextval,
		    #{boNo},
		    #{fileName},
		    #{fileSize},
		    #{fileFancysize},
		    #{fileMime},
		    #{fileSavepath},
		    0
		)
	</insert>
	
	<select id="selectFileInfo" parameterType="int" resultType="noticefileVO">
		SELECT
		    file_no,
		    bo_no,
		    file_name,
		    file_size,
		    file_fancysize,
		    file_mime,
		    file_savepath,
		    file_downcount
		FROM
		    noticefile
		WHERE file_no = #{fileNo}
	</select>
	
	<update id="incrementHit" parameterType="int">
		update notice 
		set 
			bo_hit = bo_hit + 1 
		where bo_no = ${boNo}
	</update>
	
	<select id="selectNotice" parameterType="int" resultMap="noticeMap">
		select 
		    n.bo_no
		    , n.bo_title
		    , n.bo_content
		    , n.bo_writer
		    , n.bo_date
		    , n.bo_hit
		    , n.bo_impor
		    , nf.file_no
		    , nf.file_name
		    , nf.file_size
		    , nf.file_fancysize
		    , nf.file_mime
		    , nf.file_savepath
		    , nf.file_downcount 
		from 
			notice n 
		left outer join 
			noticefile nf on(n.bo_no = nf.bo_no)
		where 
			1=1  
		and 
			n.bo_no = ${boNo}
	</select>
	
	<select id="prevNextInfo" parameterType="int" resultType="noticeVO">
		select a.*
		from(
		    select bo_no, bo_title, bo_writer, bo_date, bo_hit, 'prev' as "prevnext_flag", row_number() over (order by bo_no desc) rnum from notice
		    <![CDATA[
		    	where bo_no < ${boNo}
		    ]]>
		) a
		where a.rnum = 1
		union all
		select a.*
		from(
		    select bo_no, bo_title, bo_writer, bo_date, bo_hit, 'next' as "prevnext_flag", row_number() over (order by bo_no asc) rnum from notice
		    <![CDATA[
		    	where bo_no > ${boNo}
		    ]]>
		) a
		where a.rnum = 1
	</select>
	
	<delete id="deleteNoticeFile" parameterType="int">
		DELETE FROM noticefile
		WHERE bo_no = #{boNo}
	</delete>
	
	<delete id="deleteNotice" parameterType="int">
		DELETE FROM notice
		WHERE bo_no = #{boNo}
	</delete>
	
	<update id="modifyNotice" parameterType="noticeVO">
		UPDATE notice
		SET
		    bo_title = #{boTitle},
		    bo_content = #{boContent},
		    bo_writer = #{boWriter},
		    bo_date = sysdate,
		    bo_impor = #{boImpor}
		WHERE bo_no = #{boNo}
	</update>
	
	<select id="selectNoticeFile" parameterType="int" resultType="noticefileVO">
		SELECT
		    file_no,
		    file_name,
		    file_size,
		    file_fancysize,
		    file_mime,
		    file_savepath,
		    file_downcount
		FROM
		    noticefile
		WHERE file_no = #{fileNo}
	</select>
	
	<delete id="deleteNoticeFileList">
		delete from noticefile 
		<where>
			file_no in 
			<foreach collection="array" item="fileNo" open="(" close=")" separator=",">
				${fileNo }
			</foreach>
		</where>
	</delete>

</mapper>