Last Modified : 2012.01.23
모델2
이번 글에서는 지금까지 구현한 게시판을 모델2로 변경한다.
모델2는 요청의 진입점이 JSP가 아닌 컨트롤러이다.
다음은 우리의 프로젝트에 쓸 컨트롤러이다.
BoardController.java
package net.java_school.model2;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import net.java_school.model2.action.Action;
public class BoardController extends HttpServlet {
private static final long serialVersionUID = -5791276596415264891L;
public void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException,IOException {
ActionForward forward = null;
Action action = null;
try {
RequestProcessor rp = RequestProcessor.getInstance();
String url = request.getServletPath();
action = rp.getAction(url);
forward = action.execute(request, response);
} catch (Exception e) {
throw new ServletException(e.getMessage());
}
if (forward.isRedirect()) {
String contextPath = request.getContextPath();
String url = contextPath + forward.getPath();
response.sendRedirect(url);
} else {
ServletContext sc = getServletContext();
RequestDispatcher rd = sc.getRequestDispatcher(forward.getPath());
rd.forward(request, response);
}
}
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException,IOException {
doPost(request, response);
}
}
RequestProcessor 은 사용자 요청 URL 로부터 요청을 처리할 액션을 생성하고 리턴한다.
RequestProcessor.java
package net.java_school.model2;
import net.java_school.model2.action.*;
public class RequestProcessor {
private static RequestProcessor instance = new RequestProcessor();
private RequestProcessor() {}
public static RequestProcessor getInstance() {
return instance;
}
public Action getAction (String url) throws Exception {
Action action = null;
if (url.startsWith("/bbs/list.do") ) {
action = new ListAction();
} else if (url.startsWith("/bbs/view.do")) {
action = new ViewAction();
} else if (url.startsWith("/bbs/addComment.do")) {
action = new AddCommentAction();
} else if (url.startsWith("/bbs/updateComment.do")) {
action = new UpdateCommentAction();
} else if (url.startsWith("/bbs/deleteComment.do")) {
action = new DeleteCommentAction();
} else if (url.startsWith("/bbs/deleteAttachFile.do")) {
action = new DeleteAttachFileAction();
} else if (url.startsWith("/bbs/delete.do")) {
action = new DeleteAction();
} else if (url.startsWith("/bbs/writeForm.do")) {
action = new WriteFormAction();
} else if (url.startsWith("/bbs/write.do")) {
action = new WriteAction();
} else if (url.startsWith("/bbs/modifyForm.do")) {
action = new ModifyFormAction();
} else if (url.startsWith("/bbs/modify.do")) {
action = new ModifyAction();
} else if (url.startsWith("/bbs/login.do")) {
action = new LoginAction();
} else if (url.startsWith("/bbs/signupForm.do")) {
action = new SignupFormAction();
} else if (url.startsWith("/bbs/signup.do")) {
action = new SignupAction();
} else if (url.startsWith("/bbs/logout.do")) {
action = new LogoutAction();
} else {
throw new Exception("잘못된 실행명령입니다. 다른 명령을 실행해 주십시요");
}
return action;
}
}
액션 인터페이스는 모든 액션이 구현해야 하는 인터페이스이다.
구현할 메소드는 execute()메소드 하나다.
Action.java
package net.java_school.model2.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.java_school.model2.ActionForward;
public interface Action {
public ActionForward execute (HttpServletRequest request, HttpServletResponse response) throws Exception;
}
ListAction 은 게시판의 목록을 출력하기 위한 액션이다.
ListAction.java
package net.java_school.model2.action;
import java.util.ArrayList;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.java_school.board.Article;
import net.java_school.board.BoardService;
import net.java_school.board.PagingVo;
import net.java_school.model2.ActionForward;
public class ListAction implements Action {
public ActionForward execute (HttpServletRequest request, HttpServletResponse response) throws Exception {
request.setCharacterEncoding("UTF-8");
int cPage = request.getParameter("cPage") == null ? 1 : Integer.parseInt(request.getParameter("cPage"));
String boardCd = request.getParameter("boardCd");
String searchWord = request.getParameter("searchWord");
PagingVo pagingVo = new PagingVo();
pagingVo.setcPage(cPage);
pagingVo.setBoardCd(boardCd);
pagingVo.setSearchWord(searchWord);
BoardService service = new BoardService();
service.setPagingVo(pagingVo);
ArrayList<Article> articleList = service.getArticleList();
String boardNm = service.getBoardNm();
int no = service.getArticleNum();
int prevPage = service.getPrevPage();
int nextPage = service.getNextPage();
int firstPage = service.getFirstPage();
int lastPage = service.getLastPage();
request.setAttribute("no", no);
request.setAttribute("list", articleList);
request.setAttribute("boardNm", boardNm);
request.setAttribute("prevPage", prevPage);
request.setAttribute("nextPage", nextPage);
request.setAttribute("firstPage", firstPage);
request.setAttribute("lastPage", lastPage);
request.setAttribute("pagingVo", pagingVo);
ActionForward forward = new ActionForward();
forward.setPath("/bbs/list.jsp");
return forward;
}
}
ActionForward 는 액션에서 만들어서 컨트롤러로 리턴한다.
ActionForward.java
package net.java_school.model2;
public class ActionForward {
private boolean isRedirect;
private String path;
public boolean isRedirect() {
return isRedirect;
}
public String getPath() {
return path;
}
public void setRedirect(boolean isRedirect) {
this.isRedirect = isRedirect;
}
public void setPath(String path) {
this.path = path;
}
}
모든 비즈니스 로직의 처리는 BoardService 통해서 이루어진다.
BoardService.java
package net.java_school.board;
import java.util.*;
public class BoardService {
private BoardDao boardDao = BoardDao.getInstance(); // 게시판관련 DAO
private PagingHelper pagingHelper; //페이징 처리 유틸리티 클래스
private PagingVo pagingVo;
public BoardService() {}
/*
* 게시판 목록
*/
public ArrayList<Article> getArticleList() {
int startRecord = pagingHelper.getStartRecord();
int endRecord = pagingHelper.getEndRecord();
return boardDao.getArticleList(this.pagingVo.getBoardCd(), this.pagingVo.getSearchWord(), startRecord, endRecord);
}
/*
* 특정 게시판의 총 게시물 갯수 구하기
*/
public int getTotalRecord(String boardCd, String searchWord) {
return boardDao.getTotalRecord(boardCd, searchWord);
}
/*
* 새로운 게시글 추가
*/
public void insert(Article article, AttachFile attachFile) {
boardDao.insert(article, attachFile);
}
/*
* 게시글 수정
*/
public void update(Article article, AttachFile attachFile) {
boardDao.update(article, attachFile);
}
/*
* 게시글 삭제
*/
public void delete(int articleNo) {
boardDao.delete(articleNo);
}
/*
* 조회수 증가
*/
public void increaseHit(int articleNo) {
boardDao.increaseHit(articleNo);
}
/*
* 게시글 상세보기
*/
public Article getArticle() {
return boardDao.getArticle(this.pagingVo.getArticleNo());
}
/*
* 게시글 상세보기 2012.1.22 추가
*/
public Article getArticle(int articleNo) {
return boardDao.getArticle(articleNo);
}
/*
* 다음글 보기
*/
public Article getNextArticle() {
return boardDao.getNextArticle(this.pagingVo.getArticleNo(), this.pagingVo.getBoardCd(), this.pagingVo.getSearchWord());
}
/*
* 이전글 보기
*/
public Article getPrevArticle() {
return boardDao.getPrevArticle(this.pagingVo.getArticleNo(), this.pagingVo.getBoardCd(), this.pagingVo.getSearchWord());
}
/*
* 게시글의 첨부파일 리스트
*/
public ArrayList<AttachFile> getAttachFileList() {
return boardDao.getAttachFileList(this.pagingVo.getArticleNo());
}
/*
* 첨부파일 삭제
*/
public void deleteFile(int attachFileNo) {
boardDao.deleteFile(attachFileNo);
}
/*
* 게시판이름 구하기
*/
public String getBoardNm() {
return boardDao.getBoardNm(this.pagingVo.getBoardCd());
}
/*
* 게시판종류 리스트 구하기
*/
public ArrayList<Board> getBoardList() {
return boardDao.getBoardList();
}
/*
* 덧글쓰기
*/
public void insertComment(Comment comment) {
boardDao.insertComment(comment);
}
/*
* 덧글수정
*/
public void updateComment(Comment comment) {
boardDao.updateComment(comment);
}
/*
* 덧글삭제
*/
public void deleteComment(int commentNo) {
boardDao.deleteComment(commentNo);
}
/*
* 덧글가져오기 2012.01.21 추가
*/
public Comment getComment(int commentNo) {
return boardDao.getComment(commentNo);
}
/*
* 게시글의 덧글리스트 구하기
*/
public ArrayList<Comment> getCommentList() {
return boardDao.getCommentList(this.pagingVo.getArticleNo());
}
public int getArticleNum() {
return pagingHelper.getArticleNum();
}
public int getPrevPage() {
return pagingHelper.getPrevPage();
}
public int getFirstPage() {
return pagingHelper.getFirstPage();
}
public int getLastPage() {
return pagingHelper.getLastPage();
}
public int getNextPage() {
return pagingHelper.getNextPage();
}
public PagingVo getPagingVo() {
return pagingVo;
}
public void setPagingVo(PagingVo pagingVo) {
this.pagingVo = pagingVo;
int totalRecord = boardDao.getTotalRecord(this.pagingVo.getBoardCd(), this.pagingVo.getSearchWord());
this.pagingHelper = new PagingHelper(totalRecord, this.pagingVo.getcPage(), this.pagingVo.getNumPerPage(),
this.pagingVo.getPagePerBlock());
}
}
PagingVo 는 게시판에서 페이지 처리를 위한 정보를 나타낸다.
PagingVo.java
package net.java_school.board;
public class PagingVo {
private int articleNo;
private String boardCd = "free";
private int cPage = 1;
private String searchWord;
private int numPerPage = 10;
private int pagePerBlock = 10;
public int getArticleNo() {
return articleNo;
}
public void setArticleNo(int articleNo) {
this.articleNo = articleNo;
}
public String getBoardCd() {
return boardCd;
}
public void setBoardCd(String boardCd) {
this.boardCd = boardCd;
}
public int getcPage() {
return cPage;
}
public void setcPage(int cPage) {
this.cPage = cPage;
}
public String getSearchWord() {
return searchWord;
}
public void setSearchWord(String searchWord) {
this.searchWord = searchWord;
}
public int getNumPerPage() {
return numPerPage;
}
public void setNumPerPage(int numPerPage) {
this.numPerPage = numPerPage;
}
public int getPagePerBlock() {
return pagePerBlock;
}
public void setPagePerBlock(int pagePerBlock) {
this.pagePerBlock = pagePerBlock;
}
}
실제로 페이징 처리를 하는 것은 PagingHelper 이다.
PagingHelper 는 페이지 처리를 하는데 PagingVo 의 정보를 이용한다.
페이징 처리와 관련된 BoardService 메소드를 호출하기 위해서는 먼저 BooardService 의 setPaginVo()메소드가 호출되어야만 한다.
PagingHelper.java
package net.java_school.board;
public class PagingHelper {
private int numPerPage;
private int pagePerBlock;
private int totalPage;
private int totalBlock;
private int block;
private int firstPage;
private int lastPage;
private int prevPage;
private int nextPage;
private int articleNum;
private int startRecord;
private int endRecord;
public PagingHelper(int totalRecord, int cPage, int numPerPage, int pagePerBlock) {
this.numPerPage = numPerPage;
this.pagePerBlock = pagePerBlock;
this.totalPage = ((totalRecord % numPerPage) == 0) ? totalRecord / numPerPage : totalRecord / numPerPage + 1;
this.totalBlock = ((totalPage % pagePerBlock) == 0) ? totalPage / pagePerBlock : totalPage / pagePerBlock + 1;
this.block = ((cPage % pagePerBlock) == 0) ? cPage / pagePerBlock : cPage / pagePerBlock + 1;
this.firstPage = (block - 1) * pagePerBlock + 1;
this.lastPage = block * pagePerBlock;
if (block >= totalBlock) {
this.lastPage = totalPage;
}
if (block > 1) {
this.prevPage = firstPage - 1;
}
if (block < totalBlock) {
this.nextPage = lastPage + 1;
}
this.articleNum = totalRecord - (cPage - 1) * numPerPage;
this.startRecord = (cPage - 1) * numPerPage + 1;
this.endRecord = startRecord + numPerPage - 1;
}
public int getNumPerPage() {
return numPerPage;
}
public void setNumPerPage(int numPerPage) {
this.numPerPage = numPerPage;
}
public int getPagePerBlock() {
return pagePerBlock;
}
public void setPagePerBlock(int pagePerBlock) {
this.pagePerBlock = pagePerBlock;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getTotalBlock() {
return totalBlock;
}
public void setTotalBlock(int totalBlock) {
this.totalBlock = totalBlock;
}
public int getBlock() {
return block;
}
public void setBlock(int block) {
this.block = block;
}
public int getFirstPage() {
return firstPage;
}
public void setFirstPage(int firstPage) {
this.firstPage = firstPage;
}
public int getLastPage() {
return lastPage;
}
public void setLastPage(int lastPage) {
this.lastPage = lastPage;
}
public int getPrevPage() {
return prevPage;
}
public void setPrevPage(int prevPage) {
this.prevPage = prevPage;
}
public int getNextPage() {
return nextPage;
}
public void setNextPage(int nextPage) {
this.nextPage = nextPage;
}
public int getArticleNum() {
return articleNum;
}
public void setArticleNum(int articleNum) {
this.articleNum = articleNum;
}
public int getStartRecord() {
return startRecord;
}
public void setStartRecord(int startRecord) {
this.startRecord = startRecord;
}
public int getEndRecord() {
return endRecord;
}
public void setEndRecord(int endRecord) {
this.endRecord = endRecord;
}
}
다음은 뷰에 해당하는 list.jsp 이다.
모델1과 비교해서 JSP페이지에는 로직이 대부분 사라진다.
/bbs/list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page import="net.java_school.board.*" %>
<%@ page import="java.util.*" %>
<%@ include file="inc/loginCheck.jsp" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="Keywords" content="게시판 목록" />
<meta name="Description" content="게시판 목록" />
<link rel="stylesheet" href="../css/screen.css" type="text/css" media="screen" />
<title>${boardNm }</title>
<script type="text/javascript">
//<![CDATA[
function goList(page) {
var form = document.getElementById("listForm");
form.cPage.value = page;
form.submit();
}
function goView(articleNo) {
var form = document.getElementById("viewForm");
form.articleNo.value = articleNo;
form.submit();
}
function goWrite() {
var form = document.getElementById("writeForm");
form.submit();
}
function logout() {
location.href="logout.do";
}
function modify() {
//TODO
alert("내정보수정!");
}
function search() {
var form = document.getElementById("searchForm");
form.submit();
}
//]]>
</script>
</head>
<body>
<div id="wrap">
<div id="header">
<%@ include file="../inc/header.jsp" %>
</div>
<div id="main-menu">
<%@ include file="../inc/main-menu.jsp" %>
</div>
<div id="container">
<div id="content" style="height: 800px;">
<div id="url-navi">BBS</div>
<!-- 본문 시작 -->
<h1>${boardNm }</h1>
<div id="bbs">
<div id="member">
<input type="button" value="로그아웃" onclick="logout();" />
<input type="button" value="내정보수정" onclick="modify();"/>
</div>
<table>
<tr>
<th style="width: 60px">NO</th>
<th>TITLE</th>
<th style="width: 84px;">DATE</th>
<th style="width: 60px;">HIT</th>
</tr>
<c:set var="no" value="${no }" />
<!-- 반복 구간 시작 -->
<c:forEach var="article" items="${list }" varStatus="status">
<tr>
<td style="text-align: center;">${no - status.index}</td>
<td>
<a href="javascript:goView('${article.articleNo }')">${article.title }</a>
<c:if test="${article.attachFileNum > 0 }">
<img src="images/attach.png" alt="첨부파일" />
</c:if>
<c:if test="${article.commentNum > 0 }">
<span class="bbs-strong">[${article.commentNum }]</span>
</c:if>
</td>
<td style="text-align: center;">${article.regdate }</td>
<td style="text-align: center;">${article.hit }</td>
</tr>
</c:forEach>
<!-- 반복 구간 끝 -->
</table>
<div id="paging" style="text-align: center;">
<c:if test="${prevPage > 0 }">
<a href="javascript:goList('${prevPage }')">[이전]</a>
</c:if>
<c:set var="firstPage" value="${firstPage }" />
<c:set var="lastPage" value="${lastPage }" />
<c:forEach var="i" begin="${firstPage}" end="${lastPage}">
<c:choose>
<c:when test="${pagingVo.cPage == i}">
<span class="bbs-strong">${i }</span>
</c:when>
<c:otherwise>
<a href="javascript:goList('${i }')">${i }</a>
</c:otherwise>
</c:choose>
</c:forEach>
<c:if test="${nextPage > 0 }">
<a href="javascript:goList('${nextPage }')">[다음]</a>
</c:if>
</div>
<div id="list-menu" style="text-align: right;">
<input type="button" value="새글쓰기" onclick="goWrite()" />
</div>
<div id="search" style="text-align: center;">
<form id="searchForm" action="list.do" method="post" style="margin: 0;padding: 0;">
<p style="margin: 0;padding: 0;">
<input type="hidden" name="boardCd" value="${pagingVo.boardCd}" />
<input type="text" name="searchWord" size="15" maxlength="30" />
<input type="submit" value="검색" style="height: 25px;line-height: 25px;" />
</p>
</form>
</div>
</div><!-- bbs 끝 -->
<!-- 본문 끝 -->
</div><!-- content 끝 -->
</div><!-- container 끝 -->
<div id="sidebar">
<%@ include file="inc/bbs-menu.jsp" %>
</div>
<div id="extra">
<%@ include file="../inc/extra.jsp" %>
</div>
<div id="footer">
<%@ include file="../inc/footer.jsp" %>
</div>
</div>
<div id="form-group">
<form id="listForm" action="list.do" method="post">
<p>
<input type="hidden" name="boardCd" value="${pagingVo.boardCd}" />
<input type="hidden" name="cPage" />
<input type="hidden" name="searchWord" value="${pagingVo.searchWord}" />
</p>
</form>
<form id="viewForm" action="view.do" method="post">
<p>
<input type="hidden" name="articleNo" />
<input type="hidden" name="boardCd" value="${pagingVo.boardCd}" />
<input type="hidden" name="cPage" value="${pagingVo.cPage}" />
<input type="hidden" name="searchWord" value="${pagingVo.searchWord}" />
</p>
</form>
<form id="writeForm" action="writeForm.do" method="post">
<p>
<input type="hidden" name="boardCd" value="${pagingVo.boardCd}" />
<input type="hidden" name="cPage" value="${pagingVo.cPage}" />
<input type="hidden" name="searchWord" value="${pagingVo.searchWord}" />
</p>
</form>
</div>
</body>
</html>
참고문서
스트럿츠 프레임워크 워크북 - 한빛미디어,박재성
- 이전 : JSTL
