1. 개요
■ 지난 포스팅 : [JSP개발] 게시판 - 글쓰기 구현
지난 포스팅에서는 글쓰기를 구현했다. 이번에는 글 목록 보기 및 검색을 구현할 것이다. 추가 및 수정할 java와 jsp 파일은 위와 같다.
■ Java
BoardListAction.java : 게시글 목록을 보여주는 Action이다. 여기서 페이지 계산도 처리한다.
■ JSP
BoardListForm.jsp : 글 목록을 보여주는 화면이다. 지난번 틀만 만들어둔 것을 구현할 것이다.
2. 소스 코드
■ BoardDAO.java
DAO에는 글 목록 및 글 개수를 가져올 메서드를 추가한다.
글 목록을 가져올 메서드인 getBoardList( )를 작성한다. 이 메서드는 글 목록을 ArrayList에 담아서 BoardListAction으로 전달한다. 글 검색도 같이 처리하기 위해 인자로 검색 옵션, 검색 내용, 현재 페이지 번호를 갖고 있는 HashMap을 넘겨받는다.
검색 옵션(opt)이 null일 경우, 즉 글 목록 전체를 보여줄 경우는 위와 같은 쿼리를 작성한다. 글의 그룹번호 순으로 내림차순 정렬한다. 동일한 그룹번호가 있을 경우(= 답글이 있을 경우)에는 오름차순 정렬을 한다. 한 화면에는 총 10개의 글을 보여줄 것이므로 넘겨받은 페이지 번호에서부터 +9한 번호까지 글을 검색한다.
StringBuffer의 경우 재사용할 것이므로 하나의 작업이 끝나면 버퍼를 비워준다.
위는 제목으로 검색에 대한 부분이다. 글 목록 전체를 보여주는 것과 동일하나 차이가 나는 것은 제목으로 검색을 해줘야 하므로 제목에 대해 like 절을 이용하여 검색한다는 것이다.
153줄에 %를 앞뒤로 사용한 것은 해당 검색 내용을 제목 어딘가에 포함하고 있다면 모두 검색되도록 처리한 것이다.
다음으로 글의 개수를 가져오는 getBoardListCount( ) 메서드를 추가한다. getBoardList( ) 메서드와 동일한 인자를 넘겨받아서 글의 개수를 검색한다. 이 메서드가 필요한 이유는 페이지 처리에 글의 개수가 필요하기 때문이다.
여기서도 각 검색 조건에 따라 if문을 이용하여 처리를 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | package jsp.board.model; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import jsp.common.util.DBConnection; public class BoardDAO { private Connection conn; private PreparedStatement pstmt; private ResultSet rs; private static BoardDAO instance; private BoardDAO(){} public static BoardDAO getInstance(){ if(instance==null) instance=new BoardDAO(); return instance; } // 시퀀스를 가져온다. public int getSeq() { int result = 1; try { conn = DBConnection.getConnection(); // 시퀀스 값을 가져온다. (DUAL : 시퀀스 값을 가져오기위한 임시 테이블) StringBuffer sql = new StringBuffer(); sql.append("SELECT BOARD_NUM.NEXTVAL FROM DUAL"); pstmt = conn.prepareStatement(sql.toString()); // 쿼리 실행 rs = pstmt.executeQuery(); if(rs.next()) result = rs.getInt(1); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } close(); return result; } // end getSeq // 글 삽입 public boolean boardInsert(BoardBean board) { boolean result = false; try { conn = DBConnection.getConnection(); // 자동 커밋을 false로 한다. conn.setAutoCommit(false); StringBuffer sql = new StringBuffer(); sql.append("INSERT INTO MEMBER_BOARD"); sql.append("(BOARD_NUM, BOARD_ID, BOARD_SUBJECT, BOARD_CONTENT, BOARD_FILE"); sql.append(", BOARD_RE_REF, BOARD_RE_LEV, BOARD_RE_SEQ, BOARD_COUNT, BOARD_DATE)"); sql.append(" VALUES(?,?,?,?,?,?,?,?,?,sysdate)"); // 시퀀스 값을 글번호와 그룹번호로 사용 int num = board.getBoard_num(); pstmt = conn.prepareStatement(sql.toString()); pstmt.setInt(1, num); pstmt.setString(2, board.getBoard_id()); pstmt.setString(3, board.getBoard_subject()); pstmt.setString(4, board.getBoard_content()); pstmt.setString(5, board.getBoard_file()); pstmt.setInt(6, num); pstmt.setInt(7, 0); pstmt.setInt(8, 0); pstmt.setInt(9, 0); int flag = pstmt.executeUpdate(); if(flag > 0){ result = true; // 완료시 커밋 conn.commit(); } } catch (Exception e) { try { conn.rollback(); } catch (SQLException sqle) { sqle.printStackTrace(); } throw new RuntimeException(e.getMessage()); } close(); return result; } // end boardInsert(); // 글목록 가져오기 public ArrayList<BoardBean> getBoardList(HashMap<String, Object> listOpt) { ArrayList<BoardBean> list = new ArrayList<BoardBean>(); String opt = (String)listOpt.get("opt"); // 검색옵션(제목, 내용, 글쓴이 등..) String condition = (String)listOpt.get("condition"); // 검색내용 int start = (Integer)listOpt.get("start"); // 현재 페이지번호 try { conn = DBConnection.getConnection(); StringBuffer sql = new StringBuffer(); // 글목록 전체를 보여줄 때 if(opt == null) { // BOARD_RE_REF(그룹번호)의 내림차순 정렬 후 동일한 그룹번호일 때는 // BOARD_RE_SEQ(답변글 순서)의 오름차순으로 정렬 한 후에 // 10개의 글을 한 화면에 보여주는(start번째 부터 start+9까지) 쿼리 // desc : 내림차순, asc : 오름차순 ( 생략 가능 ) sql.append("select * from "); sql.append("(select rownum rnum, BOARD_NUM, BOARD_ID, BOARD_SUBJECT"); sql.append(", BOARD_CONTENT, BOARD_FILE, BOARD_COUNT, BOARD_RE_REF"); sql.append(", BOARD_RE_LEV, BOARD_RE_SEQ, BOARD_DATE "); sql.append("FROM"); sql.append(" (select * from MEMBER_BOARD order by BOARD_RE_REF desc, BOARD_RE_SEQ asc)) "); sql.append("where rnum>=? and rnum<=?"); pstmt = conn.prepareStatement(sql.toString()); pstmt.setInt(1, start); pstmt.setInt(2, start+9); // StringBuffer를 비운다. sql.delete(0, sql.toString().length()); } else if(opt.equals("0")) // 제목으로 검색 { sql.append("select * from "); sql.append("(select rownum rnum, BOARD_NUM, BOARD_ID, BOARD_SUBJECT"); sql.append(", BOARD_CONTENT, BOARD_FILE, BOARD_DATE, BOARD_COUNT"); sql.append(", BOARD_RE_REF, BOARD_RE_LEV, BOARD_RE_SEQ "); sql.append("FROM "); sql.append("(select * from MEMBER_BOARD where BOARD_SUBJECT like ? "); sql.append("order BY BOARD_RE_REF desc, BOARD_RE_SEQ asc)) "); sql.append("where rnum>=? and rnum<=?"); pstmt = conn.prepareStatement(sql.toString()); pstmt.setString(1, "%"+condition+"%"); pstmt.setInt(2, start); pstmt.setInt(3, start+9); sql.delete(0, sql.toString().length()); } else if(opt.equals("1")) // 내용으로 검색 { sql.append("select * from "); sql.append("(select rownum rnum, BOARD_NUM, BOARD_ID, BOARD_SUBJECT"); sql.append(", BOARD_CONTENT, BOARD_FILE, BOARD_DATE, BOARD_COUNT"); sql.append(", BOARD_RE_REF, BOARD_RE_LEV, BOARD_RE_SEQ "); sql.append("FROM "); sql.append("(select * from MEMBER_BOARD where BOARD_CONTENT like ? "); sql.append("order BY BOARD_RE_REF desc, BOARD_RE_SEQ asc)) "); sql.append("where rnum>=? and rnum<=?"); pstmt = conn.prepareStatement(sql.toString()); pstmt.setString(1, "%"+condition+"%"); pstmt.setInt(2, start); pstmt.setInt(3, start+9); sql.delete(0, sql.toString().length()); } else if(opt.equals("2")) // 제목+내용으로 검색 { sql.append("select * from "); sql.append("(select rownum rnum, BOARD_NUM, BOARD_ID, BOARD_SUBJECT"); sql.append(", BOARD_CONTENT, BOARD_FILE, BOARD_DATE, BOARD_COUNT"); sql.append(", BOARD_RE_REF, BOARD_RE_LEV, BOARD_RE_SEQ "); sql.append("FROM "); sql.append("(select * from MEMBER_BOARD where BOARD_SUBJECT like ? OR BOARD_CONTENT like ? "); sql.append("order BY BOARD_RE_REF desc, BOARD_RE_SEQ asc)) "); sql.append("where rnum>=? and rnum<=?"); pstmt = conn.prepareStatement(sql.toString()); pstmt.setString(1, "%"+condition+"%"); pstmt.setString(2, "%"+condition+"%"); pstmt.setInt(3, start); pstmt.setInt(4, start+9); sql.delete(0, sql.toString().length()); } else if(opt.equals("3")) // 글쓴이로 검색 { sql.append("select * from "); sql.append("(select rownum rnum, BOARD_NUM, BOARD_ID, BOARD_SUBJECT"); sql.append(", BOARD_CONTENT, BOARD_FILE, BOARD_DATE, BOARD_COUNT"); sql.append(", BOARD_RE_REF, BOARD_RE_LEV, BOARD_RE_SEQ "); sql.append("FROM "); sql.append("(select * from MEMBER_BOARD where BOARD_ID like ? "); sql.append("order BY BOARD_RE_REF desc, BOARD_RE_SEQ asc)) "); sql.append("where rnum>=? and rnum<=?"); pstmt = conn.prepareStatement(sql.toString()); pstmt.setString(1, "%"+condition+"%"); pstmt.setInt(2, start); pstmt.setInt(3, start+9); sql.delete(0, sql.toString().length()); } rs = pstmt.executeQuery(); while(rs.next()) { BoardBean board = new BoardBean(); board.setBoard_num(rs.getInt("BOARD_NUM")); board.setBoard_id(rs.getString("BOARD_ID")); board.setBoard_subject(rs.getString("BOARD_SUBJECT")); board.setBoard_content(rs.getString("BOARD_CONTENT")); board.setBoard_file(rs.getString("BOARD_FILE")); board.setBoard_count(rs.getInt("BOARD_COUNT")); board.setBoard_re_ref(rs.getInt("BOARD_RE_REF")); board.setBoard_re_lev(rs.getInt("BOARD_RE_LEV")); board.setBoard_re_seq(rs.getInt("BOARD_RE_SEQ")); board.setBoard_date(rs.getDate("BOARD_DATE")); list.add(board); } } catch (Exception e) { throw new RuntimeException(e.getMessage()); } close(); return list; } // end getBoardList // 글의 개수를 가져오는 메서드 public int getBoardListCount(HashMap<String, Object> listOpt) { int result = 0; String opt = (String)listOpt.get("opt"); // 검색옵션(제목, 내용, 글쓴이 등..) String condition = (String)listOpt.get("condition"); // 검색내용 try { conn = DBConnection.getConnection(); StringBuffer sql = new StringBuffer(); if(opt == null) // 전체글의 개수 { sql.append("select count(*) from MEMBER_BOARD"); pstmt = conn.prepareStatement(sql.toString()); // StringBuffer를 비운다. sql.delete(0, sql.toString().length()); } else if(opt.equals("0")) // 제목으로 검색한 글의 개수 { sql.append("select count(*) from MEMBER_BOARD where BOARD_SUBJECT like ?"); pstmt = conn.prepareStatement(sql.toString()); pstmt.setString(1, '%'+condition+'%'); sql.delete(0, sql.toString().length()); } else if(opt.equals("1")) // 내용으로 검색한 글의 개수 { sql.append("select count(*) from MEMBER_BOARD where BOARD_CONTENT like ?"); pstmt = conn.prepareStatement(sql.toString()); pstmt.setString(1, '%'+condition+'%'); sql.delete(0, sql.toString().length()); } else if(opt.equals("2")) // 제목+내용으로 검색한 글의 개수 { sql.append("select count(*) from MEMBER_BOARD "); sql.append("where BOARD_SUBJECT like ? or BOARD_CONTENT like ?"); pstmt = conn.prepareStatement(sql.toString()); pstmt.setString(1, '%'+condition+'%'); pstmt.setString(2, '%'+condition+'%'); sql.delete(0, sql.toString().length()); } else if(opt.equals("3")) // 글쓴이로 검색한 글의 개수 { sql.append("select count(*) from MEMBER_BOARD where BOARD_ID like ?"); pstmt = conn.prepareStatement(sql.toString()); pstmt.setString(1, '%'+condition+'%'); sql.delete(0, sql.toString().length()); } rs = pstmt.executeQuery(); if(rs.next()) result = rs.getInt(1); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } close(); return result; } // end getBoardListCount // DB 자원해제 private void close() { try { if ( pstmt != null ){ pstmt.close(); pstmt=null; } if ( conn != null ){ conn.close(); conn=null; } } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } // end close() } | cs |
■ BoardListAction.java
글 목록을 보여주는 Action이다. 여기서는 DAO에서 넘겨받은 글 개수를 이용하여 페이지 처리도 같이 한다.
먼저 화면에서 전달한 page라는 파라미터 값을 가져온다. 이 값은 현재 페이지 번호를 나타낸다. 그리고 검색 조건과 검색 내용도 가져온다.
다음으로 HashMap을 만들어 현재 페이지 번호 및 검색 관련 정보들을 담고, DAO에 전달한다. 그리고 DAO로부터 글의 개수와 글 목록을 넘겨받는다.
글의 개수를 이용하여 페이지를 처리한다. 한 화면에는 총 10개의 글을 보여줄 것이고 페이지는 5개를 보여준다. 그리고 각 페이지 정보를 request에 세팅한다.
그리고 글 목록도 request에 세팅하여 전달한다. 글목록 화면에서 전체 글의 수도 표시하고 싶다면 위의 주석 처리한 부분을 사용하면 된다. 글 목록 보기 및 검색은 단순한 조회 작업이므로 forward( )를 사용하면 된다. 그렇기에 setRedirect(false)로 지정한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | package jsp.board.action; import java.util.ArrayList; import java.util.HashMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import jsp.board.model.BoardBean; import jsp.board.model.BoardDAO; import jsp.common.action.Action; import jsp.common.action.ActionForward; public class BoardListAction implements Action { @Override public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception { ActionForward forward = new ActionForward(); // 현재 페이지 번호 만들기 int spage = 1; String page = request.getParameter("page"); if(page != null) spage = Integer.parseInt(page); // 검색조건과 검색내용을 가져온다. String opt = request.getParameter("opt"); String condition = request.getParameter("condition"); // 검색조건과 내용을 Map에 담는다. HashMap<String, Object> listOpt = new HashMap<String, Object>(); listOpt.put("opt", opt); listOpt.put("condition", condition); listOpt.put("start", spage*10-9); BoardDAO dao = BoardDAO.getInstance(); int listCount = dao.getBoardListCount(listOpt); ArrayList<BoardBean> list = dao.getBoardList(listOpt); // 한 화면에 10개의 게시글을 보여지게함 // 페이지 번호는 총 5개, 이후로는 [다음]으로 표시 // 전체 페이지 수 int maxPage = (int)(listCount/10.0 + 0.9); //시작 페이지 번호 int startPage = (int)(spage/5.0 + 0.8) * 5 - 4; //마지막 페이지 번호 int endPage = startPage + 4; if(endPage > maxPage) endPage = maxPage; // 4개 페이지번호 저장 request.setAttribute("spage", spage); request.setAttribute("maxPage", maxPage); request.setAttribute("startPage", startPage); request.setAttribute("endPage", endPage); // 글의 총 수와 글목록 저장 //request.setAttribute("listCount", listCount); request.setAttribute("list", list); // 단순 조회이므로 forward()사용 (= DB의 상태변화 없으므로) forward.setRedirect(false); forward.setNextPath("BoardListForm.bo"); return forward; } } | cs |
■ BoardCommand.properties
프로퍼티에는 ListAction 명령어를 추가했다.
1 2 3 4 5 6 7 | # Form Change BoardWriteForm.bo=jsp.board.action.BoardFormChangeAction BoardListForm.bo=jsp.board.action.BoardFormChangeAction # Action BoardWriteAction.bo=jsp.board.action.BoardWriteAction BoardListAction.bo=jsp.board.action.BoardListAction | cs |
■ BoardListForm.jsp
넘겨받은 글 목록을 <c:forEach> 태그를 이용해 화면에 출력한다. 위에 링크로 BoardDetailAction.bo로 되어있는데, 이것은 글 상세보기를 할 때 사용할 것이다.
글 목록을 출력했다면 페이지 번호를 출력한다. 한 화면에는 총 5개의 페이지만 보여줄 것이므로 5개를 넘어간다면 [다음]이라 표시되게 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | <%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <html> <head> <title>전체 게시글</title> <style type="text/css"> #wrap { width: 800px; margin: 0 auto 0 auto; } #topForm{ text-align :right; } #board, #pageForm, #searchForm{ text-align :center; } #bList{ text-align :center; } </style> <script type="text/javascript"> function writeForm(){ location.href="BoardWriteForm.bo"; } </script> </head> <body> <div id="wrap"> <!-- 글목록 위 부분--> <br> <div id="topForm"> <c:if test="${sessionScope.sessionID!=null}"> <input type="button" value="글쓰기" onclick="writeForm()"> </c:if> </div> <!-- 게시글 목록 부분 --> <br> <div id="board"> <table id="bList" width="800" border="3" bordercolor="lightgray"> <tr heigh="30"> <td>글번호</td> <td>제목</td> <td>작성자</td> <td>작성일</td> <td>조회수</td> </tr> <c:forEach var="board" items="${requestScope.list}"> <tr> <td>${board.board_num}</td> <td> <a href="BoardDetailAction.bo?num=${board.board_num}&pageNum=${pageNum}"> ${board.board_subject} </a> </td> <td> <a href="#"> ${board.board_id} </a> </td> <td>${board.board_date}</td> <td>${board.board_count}</td> </tr> </c:forEach> </table> </div> <!-- 페이지 넘버 부분 --> <br> <div id="pageForm"> <c:if test="${startPage != 1}"> <a href='BoardListAction.bo?page=${startPage-1}'>[ 이전 ]</a> </c:if> <c:forEach var="pageNum" begin="${startPage}" end="${endPage}"> <c:if test="${pageNum == spage}"> ${pageNum} </c:if> <c:if test="${pageNum != spage}"> <a href='BoardListAction.bo?page=${pageNum}'>${pageNum} </a> </c:if> </c:forEach> <c:if test="${endPage != maxPage }"> <a href='BoardListAction.bo?page=${endPage+1 }'>[다음]</a> </c:if> </div> <!-- 검색 부분 --> <br> <div id="searchForm"> <form> <select name="opt"> <option value="0">제목</option> <option value="1">내용</option> <option value="2">제목+내용</option> <option value="3">글쓴이</option> </select> <input type="text" size="20" name="condition"/> <input type="submit" value="검색"/> </form> </div> </div> </body> </html> | cs |
3. 실행결과
게시판 클릭 또는 글 작성 후 글 목록이 나타난다.
user로 검색했을 경우이다. 이때는 해당하는 글 1개만 검색이 되었다.
4. 소스코드 다운로드 (war 파일)
'코딩 > JSP' 카테고리의 다른 글
[JSP개발] 게시판 - 답글달기 (0) | 2016.12.16 |
---|---|
[JSP개발] 게시판 - 글 상세보기 및 파일 다운로드 (3) | 2016.12.14 |
[JSP개발] 게시판 - 글쓰기 구현 (4) | 2016.12.11 |
파일업로드 오류 Posted content type isn't multipart/form-data (0) | 2016.12.09 |
[JSP개발] EL(표현언어), JSTL(표준태그 라이브러리)적용 (3) | 2016.12.08 |