17장. 모델2 방식으로 효율적으로 개발하기 - ③ 답변형 게시판(답글쓰기, 페이징 구현)
답글 쓰기 구현
- 그냥 글쓰기와 같은데 parentNO만 (세션에 저장해서) 넘겨주는 것만 다름
<과정>
- view.jsp에서 replyForm.do로 현재글 번호를 parentNO라는 이름으로 바인딩
- replyForm.do에서는 parentNO를 넘겨 받아 세션에 저장 후 replyForm.jsp로 넘겨줌
- replyForm.jsp에서 글 작성 후 addReply.do로 넘겨주고, 여기서 parentNO를 포함해 VO객체를 만들어 service에 넘겨줌
- service에서는 새 글 추가한 메서드였던 dao.insertNewArticle을 호출해서 저장 >> 나는 그냥 service에서 addArticle호출 함
- 다시 컨트롤러에서 돌아와서도 새 글 추가와 기능 수행(사진 업로드 등)
- view.jsp
function fn_reply_form(url, parentNO){
var form=document.createElement("form");
form.setAttribute("method","post");
form.setAttribute("action",url);
var inputParentNO=document.createElement("input");
inputParentNO.setAttribute("type","hidden");
inputParentNO.setAttribute("name","parentNO");
inputParentNO.setAttribute("value",parentNO);
form.appendChild(inputParentNO);
document.body.appendChild(form);
form.submit();
}
...
<input type=button value="답글쓰기" onClick="fn_reply_form('${contextPath}/board/replyForm.do', ${article.articleNO})">
- controller
private void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
...
HttpSession session;
...
else if(action.contentEquals("/replyForm.do")) {
int parentNO=Integer.parseInt(request.getParameter("parentNO"));
session=request.getSession();
session.setAttribute("parentNO", parentNO);
nextPage="/board06/replyForm.jsp";
}
else if(action.contentEquals("/addReply.do")) {
session=request.getSession();
int parentNO=(int) session.getAttribute("parentNO");
session.removeAttribute("parentNO");
Map<String,String> articleMap=upload(request,response);
String title=articleMap.get("title");
String content=articleMap.get("content");
String imageFileName=articleMap.get("imageFileName");
articleVO.setParentNO(parentNO);
articleVO.setId("ki");
articleVO.setTitle(title);
articleVO.setContent(content);
articleVO.setImageFileName(imageFileName);
int articleNO=boardService.addArticle(articleVO);
if(imageFileName!=null && imageFileName.length()!=0) {
File srcFile=new File(ARTICLE_IMAGE_REPO+"\\temp\\"+imageFileName);
File destDir=new File(ARTICLE_IMAGE_REPO+"\\"+articleNO);
destDir.mkdir();
FileUtils.moveFileToDirectory(srcFile, destDir, true);
PrintWriter pw=response.getWriter();
pw.print("<script> alert('답글을 추가했습니다.'); location.href='"+request.getContextPath()+"/board/listArticles.do' </script>");
return;
}
}
- replyForm.jsp
<form name="frmReply" method="post" action="${contextPath}/board/addReply.do" enctype="multipart/form-data">
...
<td align="right"> 글쓴이: </td>
<td><input type="text" size="5" value="ki" disabled /> </td>
...
<!--그 외에는 글쓰기 jsp와 동일 -->
페이징 구현
- 글 10개 당 한 페이지(pageNum), 페이지 10개 당 한 섹션(section)!!
- 오라클에서 제공하는 rownum이용
- 페이징 기능을 추가한 글 목록 조회 SQL문(dao에서 query문)
SELECT * FROM(
SELECT ROWNUM as recNum,
LVL,
articleNO,
parentNO,
title,
content,
id,
writedate
FROM (
SELECT LEVEL as LVL,
articleNO,
parentNO,
title,
content,
id,
writedate
From t_board
START WITH parentNO=0
CONNECT BY PRIOR articleNO=parentNO
ORDER SIBLINGS BY articleNO DESC)
)
where recNum between (section-1)*100+(pageNum-1)*10+1 and (section-1)*100+pageNum*10
>> ROWNUM : 계층형으로 조회된 레코드에서 보이는대로 매겨진 글 번호
>> recNum between ~ and ~ : section과 pageNum으로 recNum 범위를 정해서 그에 맞는 글들을 조회
<과정>
1) 최초 요청 시 1페이지를 보여줘야 하므로 section과 pageNum 모두 1로 세팅(처음엔 null)
(ㄴ 그 이후에는 (페이지를 누를때) view.jsp에서 받아옴)
2) section, pageNum를 HashMap에 저장한 것을 매개변수로 담아 service의 listArticles를 호출(dao에서 위의 query문 사용)
3) service/dao에서는 해당 페이지에 존재하는 글 리스트와 현재 존재하는 모든 글의 수를 HashMap에 저장한 것을 반환
4) section, pageNum, 해당 페이지에 존재하는 글들, 현재 존재하는 모든 글 수(페이지 표시를 위함) 이 네개를 HashMap에 저장해 listArticles.jsp를 호출
5) listArticles에서는 모든 글수를 100미만, 100, 100이상인 세 경우로 나뉘어서 페이징을 끝을 화면에 표시
ㄴ 해당 페이지를 누르면 ~/listArticles.do?section=0?pageNum=0로 전송해서 1)로 돌아감!
- controller
if (action == null || action.equals("/listArticles.do")) {
String _section=request.getParameter("section");
String _pageNum=request.getParameter("pageNum");
int section=Integer.parseInt(_section==null ? "1" : _section);
int pageNum=Integer.parseInt(_pageNum==null ? "1" : _pageNum);
Map<String,Integer> pagingMap=new HashMap<String,Integer>();
pagingMap.put("section", section);
pagingMap.put("pageNum",pageNum);
Map articlesMap = boardService.listArticles(pagingMap);
articlesMap.put("section", section);
articlesMap.put("pageNum", pageNum);
request.setAttribute("articlesMap", articlesMap);
nextPage = "/board07/listArticles.jsp";
}
-service
public Map listArticles(Map<String, Integer> pagingMap) {
List<ArticleVO> articlesList=boardDAO.selectAllArticles(pagingMap);
int totArticle=boardDAO.selectTotArticles();
Map articlesMap= new HashMap();
articlesMap.put("articlesList", articlesList);
articlesMap.put("totArticle", totArticle);
return articlesMap;
}
- dao
public List<ArticleVO> selectAllArticles(Map<String, Integer> pagingMap) {
List<ArticleVO> articles=new ArrayList();
try {
con=dataFactory.getConnection();
String query="SELECT * FROM( SELECT ROWNUM as recNum,LVL,articleNO,parentNO,title,content,id,writedate\r\n" +
" FROM ( SELECT LEVEL as LVL, articleNO,parentNO,title,content,id,writedate\r\n" +
" From t_board" +
" START WITH parentNO=0" +
" CONNECT BY PRIOR articleNO=parentNO" +
" ORDER SIBLINGS BY articleNO DESC)" +
" )" +
" where recNum between (?-1)*100+(?-1)*10+1 and (?-1)*100+?*10";
pstmt=con.prepareStatement(query);
int section=pagingMap.get("section");
int pageNum=pagingMap.get("pageNum");
pstmt.setInt(1, section);
pstmt.setInt(2, pageNum);
pstmt.setInt(3, section);
pstmt.setInt(4, pageNum);
ResultSet rs=pstmt.executeQuery();
while(rs.next()) {
ArticleVO articleVO=new ArticleVO();
articleVO.setLevel(rs.getInt("LVL"));
articleVO.setArticleNO(rs.getInt("articleNO"));
articleVO.setParentNO(rs.getInt("parentNO"));
articleVO.setTitle(rs.getString("content"));
articleVO.setId(rs.getString("id"));
articleVO.setWriteDate(rs.getDate("writeDate"));
articles.add(articleVO);
}
rs.close();
pstmt.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
return articles;
}
public int selectTotArticles() {
try {
con=dataFactory.getConnection();
String query="select count(articleNO) from t_board";
pstmt=con.prepareStatement(query);
ResultSet rs=pstmt.executeQuery();
if(rs.next())
return rs.getInt(1);
rs.close();
pstmt.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
- listArticles.jsp
<c:set var="section" value="${articlesMap.section }"/>
<c:set var="pageNum" value="${articlesMap.pageNum }"/>
<c:set var="totArticle" value="${articlesMap.totArticle }"/>
<c:set var="articlesList" value="${articlesMap.articlesList }"/>
...
<style>
.no-uline {text-decoration:none;}
.sel-page{text-decoration:none;color:red;}
.cls1 {text-decoration:none;}
.cls2{text-align:center; font-size:30px;}
</style>
</head>
<body>
...
</table>
<div class="cls2">
<c:if test="${totArticle!=null }">
<c:choose>
<c:when test="${totArticle<100 }">
<c:forEach var="page" begin="1" end="${totArticle/10 +1 }" step="1">
<%-- /10은 몫, 마지막 페이지를 위해 쓴 것 --%>
<c:choose>
<c:when test="${page==pageNum }">
<a class="sel-page" href='${contextPath }/board/listArticles.do?section=${section }&pageNum=${pageNum}'">${page }</a>
</c:when>
<c:otherwise>
<a class="no-uline" href='${contextPath }/board/listArticles.do?section=${section }&pageNum=${pageNum}'>${page }</a>
</c:otherwise>
</c:choose>
</c:forEach>
</c:when>
<c:when test="${totArticle==100 }">
<c:forEach var="page" begin="1" end="10">
<c:choose>
<c:when test="${page==pageNum }">
<a class="sel-page" href='${contextPath }/board/listArticles.do?section=${section }&pageNum=${pageNum}'">${page }</a>
</c:when>
<c:otherwise>
<a class="no-uline" href='${contextPath }/board/listArticles.do?section=${section }&pageNum=${pageNum}'>${page }</a>
</c:otherwise>
</c:choose>
</c:forEach>
</c:when>
<c:when test="${totArticle>100 }">
<c:forEach var="page" begin="1" end="10">
<c:if test="${section>1 && page==1 }">
<a class="no-uline" href='${contextPath }/board/listArticles.do?section=${section-1 }&pageNum=1'>prev</a>
</c:if>
<c:choose>
<c:when test="${page==pageNum }">
<a class="sel-page" href='${contextPath }/board/listArticles.do?section=${section }&pageNum=${page}'">${(section-1)*10+page }</a>
</c:when>
<c:otherwise>
<a class="no-uline" href='${contextPath }/board/listArticles.do?section=${section }&pageNum=${page}'>${(section-1)*10+page }</a>
</c:otherwise>
</c:choose>
<c:if test="${section<totArticle/100+1 && page==10 }">
<a class="no-uline" href='${contextPath }/board/listArticles.do?section=${section+1 }&pageNum=1'>next</a>
</c:if>
</c:forEach>
</c:when>
</c:choose>
</c:if>
</div>
<br><br>
<a href="${contextPath}/board/articleForm.do"><p class="cls2">글쓰기</p></a>
</body>
</html>