글을 작성하면서 구현을 하다가 (1/), (2/), (3/)까지는 이어졌는데, 갑자기 구현에 신경을 쓰느라 이번 글에서는 어느 정도 완성된 버전에서 다시 기술한다.
맥은 어느 정도 유지가 되지만, 이전 글에서 스무드하게 이어지지 못해 이해에 어려움이 있을 수도 있을 것 같다.
1. 게시판 관리 기능 중 "게시판 목록 보기"
Header의 메뉴 창에서 "관리자" 메뉴를 선택하면 "게시판 목록 보기" 창으로 이동하게 된다. url은 "localhost:8080/board/manager/list" 이다. 즉, 게시판 목록 보기 기능을 구현한 것이다.
1.1 BoardController에서 url /manager/list를 처리
@RequestMapping(value="/list", method=RequestMethod.GET)
public void list(Criteria cri, Model model) throws Exception { // View로는 기본적으로 list.jsp가 사용됨
List<BoardVO> list = service.list(cri); // 게시판 목록정보를 가져 온다.
for(int i = 0;i < list.size();i++) { // 게시판의 게시글 수를 가져와 저장한다.
BoardVO board = list.get(i);
board.setNofArticle(aService.listCount()); //
}
model.addAttribute("list", list); // 게시판 목록 정보를 model에 저장한다.
PageMaker pm = new PageMaker(); // 페이징 처리를 위해 필요한 정보를 담을 클래스를 인스턴스화
pm.setCri(cri); // 페이징 처리를 위한 기준을 저장 (cri는 기본값이 있지만, 사용
pm.setTotalCount(service.listCount()); // 자가 파라미터로 제공 가능. 전체 게시판의 수를 얻는다.
model.addAttribute("pageMaker", pm); // 페이징 정보를 model에 저장
} // view는 기본적으로 list.jsp
1.2 list.jsp : "게시판 목록 보기" view
<%@ 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="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>게시판 목록 보기</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/common.css"/>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/boardlist.css"/>
</head>
<body>
<header> <!-- 헤더 영역 정의 -->
<jsp:include page="../common/header.jsp" flush="false"/>
</header>
<section id="wrap"> <!-- 본문 영역 정의 -->
<section id="lnav"> <!-- 사이드 메뉴 영역 정의 -->
<jsp:include page="../common/lnav.jsp" flush="false"/>
</section>
<section id="content"> <!-- 내용 영역 정의 -->
<h1>게시판 목록 보기</h1>
<table border="1">
<c:if test="${pageMaker.totalCount > 0}">
<tr>
<td colspan="6">
${pageMaker.cri.pageStart}-${pageMaker.cri.pageEnd}
[${pageMaker.cri.page}/${pageMaker.totalPageCount}]
</td>
</tr>
</c:if>
<c:choose>
<c:when test="${list.size() == 0}">
<tr>
<td colspan="6">
게시판이 없습니다.
</td>
</tr>
</c:when>
<c:otherwise>
<tr>
<td style="text-align: center" width="6%">번호</td>
<td style="text-align: center" width="21%">게시판 이름</td>
<td style="text-align: center" width="13%">게시판 유형</td>
<td style="text-align: center" width="32%">URL</td>
<td style="text-align: center" width="17%">생성일</td>
<td style="text-align: center" width="11%">게시글 수</td>
</tr>
<c:forEach var="board" items="${list}">
<tr>
<td>${board.board_id}</td>
<td>
<c:set var="query" value="board_id=${board.board_id}&page=${pageMaker.cri.page}
&perPageNum=${pageMaker.cri.perPageNum}"/>
<a href="<c:url value="manager/read?${query}"/>">${board.board_name}</a>
</td>
<td>${board.board_type}</td>
<td>${board.url}</td>
<td><fmt:formatDate value="${board.creating_date}" pattern="yyyy-MM-dd" /></td>
<td>${board.nofArticle}</td>
</tr>
</c:forEach>
<tr>
<td colspan="6">
<c:if test="${pageMaker.prev == true}">
<a href="<c:url value="/manager/list?page=${pageMaker.startPage-1}&perPageNum=
${pageMaker.cri.perPageNum}"/>">이전</a>
</c:if>
<c:forEach var="pno" begin="${pageMaker.startPage}" end="${pageMaker.endPage}">
<a href="<c:url value="/manager/list?page=${pno}&perPageNum=
${pageMaker.cri.perPageNum}" />">[${pno}]</a>
</c:forEach>
<c:if test="${pageMaker.next == true}">
<a href="<c:url value="/manager/list?page=${pageMaker.endPage+1}
&perPageNum=${pageMaker.cri.perPageNum}"/>">다음</a>
</c:if>
</td>
</tr>
</c:otherwise>
</c:choose>
<tr>
<td colspan="6">
<a href="write">게시판 만들기</a>
</td>
</tr>
</table>
</section>
</section>
<footer> <!-- 풋터 영역 정의 -->
<jsp:include page="../common/footer.jsp" flush="false"/>
</footer>
</body>
</html>
아래의 그림과 같은 내용이 보여진다. (물론 처음에는 아무것도 없겠지만... 지금은 게시판 1개를 추가한 것)
1.3 boardlist.css : 위 그림과 같은 테이블 형태의 게시판 목록을 보여주도록 꾸며주는 파일
@CHARSET "UTF-8";
#content table {
width: 100%;
border: 1px solid black;
border-collapse: collapse;
border-spacing: 0px;
margin-top: 20px;
}
#content td {
border: 1px solid black;
border-collapse: collapse;
border-spacing: 0px;
padding: 5px;
}
2. 게시판 생성 : "게시판 만들기" 메뉴 동작
2.1 BoardController /manager/write 동작
위 list.jsp 파일에서 "게시판 만들기" 버튼을 누르면 BoardController로 /manager/list가 전달된다.
@RequestMapping(value="/write", method=RequestMethod.GET)
public String write() {
return "/manager/writeForm"; // writeForm.jsp를 보여준다.
}
2.2 writeForm.jsp : 게시판 추가를 위한 Form 입력
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>게시판 만들기</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/common.css"/>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/boardwrite.css"/>
</head>
<body>
<header> <!-- 헤더 영역 정의 -->
<jsp:include page="../common/header.jsp" flush="false"/>
</header>
<section id="wrap"> <!-- 본문 영역 정의 -->
<section id="lnav"> <!-- 사이드 메뉴 영역 정의 -->
<jsp:include page="../common/lnav.jsp" flush="false"/>
</section>
<section id="content"> <!-- 내용 영역 정의 -->
<h2>게시판 만들기</h2>
<form action="<c:url value='write' />" method="post">
게시판 이름: <input type="text" name="board_name" size="20"/> <br/>
게시판 유형: <select name="board_type">
<option value="기본">기본</option>
<option value="Q&A">Q&A</option>
<option value="이미지">이미지</option>
<option value="공지사항">공지사항</option>
<option value="자료실">자료실</option>
<option value="방명록">방명록</option>
</select><br>
url : <input type="text" name="url" size="40"><br>
비밀글 허용여부 : <input type="radio" name="secret" value="T"> 감추기
<input type="radio" name="secret" value="F" checked="checked"> 보여주기<br>
읽기 권한 : <select name="read_allow">
<option value="all">모두 허용</option>
<option value="login">로그인 후</option>
<option value="customer">고객</option>
<option value="business">상인</option>
<option value="admin">관리자</option>
</select><br>
쓰기 권한 : <select name="write_allow">
<option value="all">모두 허용</option>
<option value="login">로그인 후</option>
<option value="customer">고객</option>
<option value="business">상인</option>
<option value="admin">관리자</option>
</select><br>
댓글 쓰기 권한 : <select name="reply_allow">
<option value="all">모두</option>
<option value="login">로그인 후</option>
<option value="customer">고객</option>
<option value="business">상인</option>
<option value="admin">관리자</option>
</select><br>
수정 권한 : <select name="modify_allow">
<option value="all">모두</option>
<option value="login">로그인 후</option>
<option value="customer">고객</option>
<option value="business">상인</option>
<option value="writer">작성자</option>
<option value="admin">관리자</option>
</select><br>
삭제 권한 : <select name="remove">
<option value="writer">작성자</option>
<option value="admin">관리자</option>
</select><br>
다운로드 권한 : <select name="download">
<option value="all">모두</option>
<option value="login">로그인 후</option>
<option value="customer">고객</option>
<option value="business">상인</option>
<option value="admin">관리자</option>
</select><br>
업로드 권한 : <select name="upload">
<option value="all">모두</option>
<option value="login">로그인 후</option>
<option value="customer">고객</option>
<option value="business">상인</option>
<option value="admin">관리자</option>
</select><br>
첨부 파일 수 : <input type="number" name="nAttach" min="0" max="5"><br>
첨부 파일 크기제한(한 개) : <select name="aSize">
<option value="0">0</option>
<option value="50KB">50KB</option>
<option value="200KB">200KB</option>
<option value="1MB">1MB</option>
<option value="10MB">10MB</option>
<option value="100MB">100MB</option>
</select><br>
내용 표시 형식 : <select name="display_format">
<option value="1">제목만 표시</option>
<option value="2">내용 일부 표시</option>
<option value="3">내용 전부 표시</option>
</select><br>
게시판 설명: <br/>
<textarea name="board_desc" cols="80" rows="3"></textarea>
<br/>
<input id="addBtn" type="submit" value="전송" />
</form>
</section>
</section>
<footer> <!-- 풋터 영역 정의 -->
<jsp:include page="../common/footer.jsp" flush="false"/>
</footer>
</body>
</html>
2.3 boardwrite.css : 위 그림과 같은 "게시판 만들기" view의 디자인 제공
@CHARSET "UTF-8";
#content form {
margin-top: 20px;
line-height: 1.5;
}
#content textarea {
line-height: 1.5;
}
#content #addBtn {
width: 200px;
font-size: 14px;
padding: 10px;
background-color: blue;
color: white;
border-radius: 5px 5px 5px 5px;
}
2.4 BoardController /manager/write (Post) 동작
@RequestMapping(value="/write", method=RequestMethod.POST)
public String writePro(BoardVO board) { // 게시판 정보를 입력 받아
service.add(board); // 데이터베이스에 저장하고
return "redirect:/manager/list"; // 게시판 목록 보기로 이동
}
2.5 게시판 추가 시 메뉴 변경
- 게시판을 추가하면 그에 따라 메뉴도 추가해 주어야 한다. 메뉴는 header.jsp에서 javascript로 처리를 한다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- 게시판 관리 기능에 의하여 게시판의 추가, 삭제, 변경 등에 대한 처리를 하는 javascript 파일 -->
<script type="text/javascript" src="${pageContext.request.contextPath}/resources/js/jquery-1.11.3.min.js"></script>
<script type="text/javascript" charset="utf-8" src="${pageContext.request.contextPath}/resources/js/header.js"></script>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/css/header.css"/>
<script type="text/javascript" charset="utf-8">
sessionStorage.setItem("contextpath", "${pageContext.request.contextPath}");
</script>
<nav>
<div id="gnbMenu">
<ul id="gnb">
<li class="item" id="item1">
<a href="" class="menu">지식창고</a>
<div class="sub" id="sub1">
<ul class="subGnb" id="subGnb1">
<!-- 게시판 추가로 인해 게시판 보기 메뉴가 추가될 영역 (javascript로 처리 : header.js)
<li><a href="${pageContext.request.contextPath}/article/list?board_id=1&p=1">시 모음</a></li>
-->
</ul>
</div>
</li>
<li class="item" id="item2">
<a class="menu" href="<c:url value="/manager/list"/>">관리자</a>
</li>
</ul>
</div>
</nav>
2.6 header.js
$(function() {
var ctx = getContextPath(); // 프로젝트의 contextPath를 가져오는 부분
getMenuInfo(); // 서버로부터 메뉴처리를 위한 게시판 정보를 가져 온다.
var gnbArea = document.getElementById("gnb");
var menuItemAll = gnbArea.getElementsByTagName("li");
var menuItem = new Array();
var currentItem, prevItem;
for(i=0;i<menuItemAll.length;i++){
if((menuItemAll[i].className).indexOf("item") > -1){
menuItem.push(menuItemAll[i]);
}
}
for(i=0;i<menuItem.length;i++){
var link = menuItem[i].getElementsByTagName("a")[0];
var layer = menuItem[i].getElementsByTagName("div")[0];
var subLinks = menuItem[i].getElementsByTagName("div")[0].getElementsByTagName("a");
link.onmouseover = layer.onmouseover = link.onfocus = function(e){
currentItem = this.parentNode;
if(prevItem){
removeClass(prevItem,"on");
}
addClass(currentItem,"on");
prevItem = currentItem;
}
link.onmouseout = layer.onmouseout = function(e){
removeClass(this.parentNode,"on");
}
link.onkeydown = function(e){
if (event.shiftKey && event.keyCode == 9){
removeClass(this.parentNode,"on");
}
}
for(j=0;j<subLinks.length;j++){
subLinks[subLinks.length-1].onblur = function(e){
removeClass(this.parentNode.parentNode.parentNode.parentNode,"on");
}
}
}
// 서버로부터 게시판에 대한 메뉴 처리를 위해 게시판 정보를 가져오는 부분
function getMenuInfo() { // 서버로 /manager/getMenuInfo 요청
$.get(ctx + "/manager/getMenuInfo", function(data, status) {
var str = "";
$(data).each(function() {
str += "<li><a href='" + ctx + "/article/list?board_id=" + this.board_id + "'>" + this.board_name + "</a></li>";
});
$('#subGnb1').html(str);
});
}
// 프로젝트의 contextPath를 구하는 함수 : sessionStorage를 사용 (보통 header.jsp에서 script를 사용하여
// sessionStorage에 contextPath를 저장하고, 이 파일에서 그것을 이용한다.
function getContextPath() {
return sessionStorage.getItem("contextpath");
}
// 액션 : 해당 클래스를 보여주기 위하여 on 클래스를 추가하는 루틴
function addClass(ele, cls){
var eleCln = ele.className;
if(eleCln.indexOf(cls) == -1){
ele.className = eleCln + " " + cls;
}
}
// 해당 클래스를 안보이게 처리하기 위해 on class를 삭제하는 루틴
function removeClass(ele, cls){
var eleCln = ele.className;
ele.className = eleCln.split(" " + cls)[0];
}
});
2.7 서버에서 /manager/getMenuInfo 처리하는 부분
header.js에서 메뉴 처리를 위해 서버로 게시판 정보를 요청하는 /manager/getMenuInfo 부분이 있는데, 이에 대한 서버의 처리 부분이다.
@RequestMapping(value="/getMenuInfo", method=RequestMethod.GET)
public @ResponseBody List<MenuVO> getMenuInfo() {
List<MenuVO> list = service.getMenuInfo();
return list;
}
- MenuVO 정의 : 메뉴 처리를 위해 필요한 정보를 나타낸다. (board_id, url, board_name)
package com.example.board.domain;
public class MenuVO {
private int board_id; // 게시판 아이디 (게시판을 고유하게 식별)
private String url; // 게시글을 관리하기 위한 페이지로 이동을 위한 URL 정보
private String board_name; // 메뉴에 표시되는 글
// getter, setter, toString() 등은 생략...
}
2.8 boardMapper.xml : 데이터베이스에서 MenuInfo를 추출하는 부분
<!-- 게시판에 따른 메뉴 정보를 얻어내는 부분 -->
<select id="getMenuInfo" resultType="com.example.board.domain.MenuVO">
select board_id, url, board_name from board where board_id > 0 order by board_id desc
</select>