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>

참고문서

스트럿츠 프레임워크 워크북 - 한빛미디어,박재성