웹개발 수업/Server
[Day +83 / Server]1. 검색 후 페이징 출력하기, 2. 수정/삭제(본인만)
Chole Woo
2021. 10. 21. 15:39
211021 목
1. 검색시 페이지 목록 출력되게 하기
1) boardListview
%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -jstl을 쓰기 위해서 우선 입력해 줌 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>게시판</title> *CSS생략함 </head> <body> <jsp:include page="/WEB-INF/views/common/menubar.jsp" /> <div class="outer"> <div class="wrap"> <div class="board_area"> <div class="board_title"> <h1>게시판</h1> </div> <div class="board_list"> <ul class="board_header"> <li class="id">글번호</li> <li class="category">분류</li> <li class="title">제목</li> <li class="writer">작성자</li> <li class="count">조회수</li> <li class="date">작성일</li> </ul> <c:forEach var="board" items="${ boardList }"> <%-- 이 ul이 하나의 게시글 단위 --%> <ul class="board_ul" onclick="detailView(${ board.bid })"> <%-- function에 현재 board.bid 넘김 --%> <li class="id">${ board.bid }</li> <li class="category">${ board.cname }</li> <li class="title">${ board.btitle }</li> <li class="writer">${ board.userName }</li> <li class="count">${ board.bcount }</li> <li class="date">${ board.createDate }</li> </ul> </c:forEach> </div> <c:if test="${!empty param.searchCondition && !empty param.searchValue }"> <c:set var="searchParam" value="&searchCondition=${ param.searchCondition }&searchValue=${param.searchValue}"></c:set> </c:if> <ul class="board_paging"> <!-- 맨 처음으로 이동하는 버튼 (<<) --> <li><a href="${ contextPath }/board/list?page=1${searchParam}"><<</a></li> <!-- 이전 페이지로 (<) --> <li> <c:choose> <%-- choose - when - otherwise = if - else if - else --%> <c:when test="${ pi.page > 1 }"> <a href="${ contextPath }/board/list?page=${ pi.page - 1 }${searchParam}"><</a> </c:when> <c:otherwise> <a href="#"><</a> </c:otherwise> </c:choose> </li> <!-- 최대 10개의 페이지 목록 --> <c:forEach var="p" begin="${ pi.startPage }" end="${ pi.endPage }"> <li> <c:choose> <c:when test="${ p eq pi.page }"> <a href="#" class="current_page">${ p }</a> <%-- 이해 안 됨 --%> </c:when> <c:otherwise> <a href="${ contextPath }/board/list?page=${ p }${searchParam}">${ p }</a> </c:otherwise> </c:choose> </li> </c:forEach> <!-- 다음 페이지로 (>) --> <li> <c:choose> <c:when test="${ pi.page < pi.maxPage }"> <a href="${ contextPath }/board/list?page=${ pi.page + 1 }${searchParam}">></a> </c:when> <c:otherwise> <a href="#">></a> </c:otherwise> </c:choose> </li> <!-- 맨 끝으로 이동하는 버튼 (>>) --> <li><a href="${ contextPath }/board/list?page=${ pi.maxPage }${searchParam}">>></a></li> </ul> </div> ![]() <div class="search_area"> <form method="get" action="${contextPath }/board/list"> -method : 전달은 get방식 -action : 수신 대상 <select id="searchCondition" name="searchCondition"> -아이디와 네임을 searchCondition으로 지정 <option value="title" <c:if test= "${param.searchCondition == 'title' }">selected</c:if>>제목</option> : getParameter("searchCondition")의 값이 title일 경우 selected가 된다. 즉, 셀렉트 옵션들 중 제목이 선택된다는 뜻 <option value="content" <c:if test= "${param.searchCondition == 'content' }">selected</c:if>>내용</option> <option value="writer" <c:if test= "${param.searchCondition == 'writer' }">selected</c:if>>작성자</option> </select> <span class="input_area"> <input type="search" name="searchValue" value="${ param.searchValue }"> </span> <!-- 로그인한 유저일 경우에만 눌렀을 때 작성할 화면으로 이동할 수 있게 --> <!-- 로그인 안 했을 경우 버튼도 안 보임 --> <button type="submit">검색하기</button> -submit : 용도가 제출용 <c:if test="${ !empty loginUser }"> -loginUser가 비어있지 않다면 작성하기 클릭시 location.href='${ contextPath }/board/insert로 이동하라 <button type="button" onclick="location.href='${ contextPath }/board/insert'">작성하기</button> </c:if> </form> </div> </div> </div> ![]() <c:choose> <c:when test="${ !empty loginUser }"> <script> function detailView(bid) { location.href='${ contextPath }/board/detail?bid=' + bid; } </script> </c:when> <c:otherwise> <script> function detailView(bid) { alert('로그인 후 이용 가능합니다.'); location.href='${ contextPath }/login'; } </script> </c:otherwise> </c:choose> </body> </html> |
*Get방식과 Post방식 비교 -Get 방식 : 쿼리스트링이라는 형식으로 전송됨. 속도가 빠르지만 보안에 취약하고 전송량이 제한적이다. -Post방식 : 전송이 느리지만 보안처리가 되어있고 전송량의 제한이 없다. Q. Post방식은 어디로 값이 저장됨? |
*JSP에서 Select란?![]() -여러 개 중 하나를 선택할 수 있는 태그 중 하나 *Option : select안에 쓴다 <select> <option>제목</option> <option>작성자</option> </select> |
*c:if 조건문 (c:if test="조건식") -java의 if문과 비슷한 역할을 하는 태그 -조건식은 test라는 속성의 값으로 지정해야 하며 반드시 EL형식으로 기술해야 함 <c:if test="${num1 > num2 }"> num1이 num2보다 큽니다. </c:if> => 이 경우 num1>num2 값이 true일 경우에만 출력된다 |
*Input 1. type : 입력 태그의 유형 2. value : 입력 태그의 초기값, 사용자가 변경 가능 3. name : 서버로 전달되는 이름 <input type="search" name="searchValue" value="${ param.searchValue }"> 즉, 타입은 서치가 가능하도록, 서버로 전달되는 이름은 searchValue, 입력 초기값은 ${ param.searchValue }로 받아오게 Q. searchValue? |
*조건문(c:choose, c:when, c:otherwise) -java의 if - else if - else문과 비슷한 역할을 하는 태그 -각 조건들은 c:choose문의 하위 요소로 c:when을 통해 작성(else 값으로는 c:otherwise) <c:set var="no" value="5" /> <c:choose> <c:when test="${no eq 0}"> 처음뵙겠습니다 </c:when> <c:when test="${no eq 1}"> 다시 뵙게되어 반갑습니다. </c:when> <c:otherwise> 안녕하세요. </c:otherwise> </c:choose> |
2. BoardListServlet
package board.controller; import java.io.IOException; import java.util.Map; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import board.model.service.BoardService; import board.model.vo.Search; /** * Servlet implementation class BoardListServlet */ @WebServlet("/board/list") -매핑 public class BoardListServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public BoardListServlet() { super(); // TODO Auto-generated constructor stub } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // *page : 현재 요청 페이지 // 기본적으로 게시판은 1페이지부터 시작 int page = 1; // 하지만 페이지 전환시 전달받은 현재 페이지가 있을 경우 해당 페이지를 page로 적용 if(request.getParameter("page") != null) { page = Integer.parseInt(request.getParameter("page")); } // 검색 관련 파라미터 추출 String searchCondition = request.getParameter("searchCondition"); -사용자가 BoardListView의 searchCondtion(셀렉트 옵션 / 제목, 작성자, 내용)을 고르는 요청을 보낸 것을 확인해서 searchCondition에 넣는다는 뜻 String searchValue = request.getParameter("searchValue"); // 페이징과 관련된 데이터, 조회된 boardList를 map에 담아 리턴 Map<String, Object> map = new BoardService().selectList(page, new Search(searchCondition, searchValue)); // 맵에서 꺼내기 request.setAttribute("pi", map.get("pi")); request.setAttribute("boardList", map.get("boardList")); RequestDispatcher view = request.getRequestDispatcher("/WEB-INF/views/board/boardListView.jsp"); view.forward(request, response); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } } |
*getParameter란? 웹(Web) 환경은 HTTP 프로토콜 위에서 동작하고 있습니다. HTTP 프로토콜은 간단하게 얘기해서, 클라이언트가 서버에 무언가(보통은 웹페이지)를 요청(request)하면, 서버가 이 요청에 해당하는 것을 응답(response) 해주는 구조로 되어있습니다. HTTP 요청을 보낼 때, 파라미터(parameter)를 함께 끼워보낼 수 있습니다. 가령, 로그인을 할 때, 로그인 폼(form : 양식)을 입력하고, 버튼을 누르죠. 그러면, HTTP 요청 안에 폼 내용이 함께 끼워져서 서버로 날아가게 됩니다. 로그인 폼 내용이 바로, HTTP 요청의 파라미터(parameter)가 되는 겁니다. 이러한 HTTP 요청을 받은 서버는 이제 어떤 일을 할까요? 파라미터로 날아온 로그인 폼을 일단 봐야지, 뭘하든 하겠죠. 그러려면 일단 파라미터 값을 얻어내야 하는데... HTTP 요청의 파라미터 값을 얻기 위해 사용하는 것이 request.getParameter() 메쏘드입니다. 가령, 로그인 폼에, ID를 입력하는 <input type="text" name="id">가 있었다면, 서블릿에서 String strId = request.getParameter("id"); 와 같은 방식으로, 클라이언트가 입력한 ID가 뭐였는지 알아낼 수 있습니다. 로그인에 성공했다면, 이제 어떻게 할까요? 클라이언트의 회원정보를 DB에서 읽어서 페이지에 뿌려주기로 합시다. 그러려면, 서블릿은 회원정보를 JSP에게 보내줘야 합니다. 그래야 뿌리죠. 그런데 어떻게??? 다른 곳으로 정보를 넘겨주기 위해서 request 객체의 속성(attribute)을 사용합니다. (더 정확하게는, 웹 애플리케이션 상에서 정보를 공유하기 위해서 속성을 사용합니다.) 가령, 회원정보 중에서 '취미'를 JSP에게 넘겨주기 위해서, 서블릿에서 request.setAttribute("hobby", strHobby); 로 속성을 집어넣고, JSP에서 <% String strHobby = (String)request.getAttribute("hobby"); %><% String hobby = (String)request.getAttribute("hobby"); %>로 속성을 얻는거죠. -출처 : http://blog.naver.com/dbxhvi?Redirect=Log&logNo=100033562897 ![]() |
3. Search 생성
package board.model.vo; public class Search { private String searchCondition; private String searchValue; public Search() {} public Search(String searchCondition, String searchValue) { super(); this.searchCondition = searchCondition; this.searchValue = searchValue; } public String getSearchCondition() { return searchCondition; } public void setSearchCondition(String searchCondition) { this.searchCondition = searchCondition; } public String getSearchValue() { return searchValue; } public void setSearchValue(String searchValue) { this.searchValue = searchValue; } } |
*생성자 : new 연산자로 클래스로부터 객체를 생성할 떄 호출되어 객체의 초기화를 담당한다. *객체 초기화 : 필드를 초기화하거나 메소드를 호출해서 객체를 사용할 준비를 하는 것을 의미. 생성자를 실행하지 않고는 클래스로부터 객체를 만들 수 없다. new 연산자에 의해 생성자가 성공적으로 실행되면 힙 영역에 객체가 생성되고 객체의 번지가 리턴된다. 리턴된 객체의 번지는 클래스 변수에 저장된다. |
4. BoardListServlet
/** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // *page : 현재 요청 페이지 // 기본적으로 게시판은 1페이지부터 시작 int page = 1; // 하지만 페이지 전환시 전달받은 현재 페이지가 있을 경우 해당 페이지를 page로 적용 if(request.getParameter("page") != null) { page = Integer.parseInt(request.getParameter("page")); } // 검색 관련 파라미터 추출 String searchCondition = request.getParameter("searchCondition"); -사용자가 BoardListView의 searchCondtion(셀렉트 옵션 / 제목, 작성자, 내용)을 고르는 요청을 보낸 것을 확인해서 searchCondition에 넣는다는 뜻 String searchValue = request.getParameter("searchValue"); // 페이징과 관련된 데이터, 조회된 boardList를 map에 담아 리턴 Map<String, Object> map = new BoardService().selectList(page, new Search(searchCondition, searchValue)); Q. 객체를 사용하기 전에 초기화를 위해서 search 생성자를 만들어준 것일까? // 맵에서 꺼내기 request.setAttribute("pi", map.get("pi")); request.setAttribute("boardList", map.get("boardList")); RequestDispatcher view = request.getRequestDispatcher("/WEB-INF/views/board/boardListView.jsp"); view.forward(request, response); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } } |
*컬렉션 프레임워크를 사용하는 이유 : 기존에는 많은 데이터를 처리하기 위해 배열을 사용했지만 크기가 고정되어있고 삽입 및 삭제 시간이 오래걸린다는 불편한 점이 많았다. 따라서 이를 보완하기 위해 자바에서 동적 배열 개념인 컬렉션 프레임워크를 제공하였다. 대표적으로 List, Map, Set이 있다. *Map : Key와 Value의 한쌍으로 이루어지는 데이터의 집합. -Key에 대한 중복이 없으며 순서를 보장하지 않는다. -뛰어난 검색 속도를 가진다. -인덱스가 따로 존재하지 않기 때문에 iterator를 사용한다. *Map의 종류와 특징 1) HashMap -Key에 대한 중복이 없으며 순서를 보장하지 않는다. -Key와 Value 값으로 NULL을 허용한다. -동기화가 보장되지 않는다. -검색에 가장 뛰어난 성능을 가진다. 2) HashTable -동기화가 보장되어 병렬 프로그래밍이 가능하고 HashMap 보다 처리속도가 느리다. -Key와 Value 값으로 NULL을 허용하지 않는다. 3) LinkedHashMap -입력된 순서를 보장한다. 4) TreeMap -이진 탐색 트리(Red-Black Tree)를 기반으로 키와 값을 저장한다. -Key 값을 기준으로 오름차순 정렬되고 빠른 검색이 가능하다. -저장 시 정렬을 하기 때문에 시간이 다소 오래 걸린다. ![]() 출처: https://cocoon1787.tistory.com/527 [코딩 공부 일지] |
*new 연산자란? 클래스 객체 변수 = new 클래스(); - new는 클래스 타입의 인스턴스(객체)를 생성해주는 역할을 담당한다. - new 연산자를 통해 메모리(Heap 영역)에 데이터를 저장할 공간을 할당받고 그 공간의 참조값(reference value /해시코드)을 객체에게 반환하여 주고 이어서 생성자를 호출하게 된다. 출처 : https://devlogofchris.tistory.com/35 |
5. Service 변경하기
public Map<String, Object> selectList(int page, Search search) { // JDBCTemplate 임포트 필요 Connection conn = getConnection(); // 1. 게시글 총 개수 구하기 int listCount = boardDao.getListCount(conn, search); // System.out.println(listCount); // 2. PageInfo 객체 만들기 PageInfo pi = new PageInfo(page, listCount, 10, 10); // 3. 페이징 처리된 게시글 목록 조회 List<Board> boardList = boardDao.selectList(conn, pi, search); Map<String, Object> returnMap = new HashMap<>(); // System.out.println(listCount); //갯수 출력됨 // System.out.println(pi); // System.out.println(boardList); returnMap.put("pi", pi); returnMap.put("boardList", boardList); return returnMap; } |
6. board-query
<entry key="getTitleListCount"> SELECT COUNT(*) FROM BOARD WHERE BTYPE = 1 AND STATUS = 'Y' AND BTITLE LIKE '%' || ?|| '%' -즉, BTYPE이 1이고 STATUS가 Y이고 제목에 '사랑'이라는 단어가 들어가는 모든 행을 조회하시오. 총 58개가 있음. 따라서 58이라는 숫자가 반환됨. </entry> <entry key="getContentListCount"> SELECT COUNT(*) FROM BOARD WHERE BTYPE = 1 AND STATUS = 'Y' AND BCONTENT LIKE '%' || ?|| '%'</entry> <entry key="getWriterListCount"> SELECT COUNT(*) FROM BOARD B JOIN MEMBER ON(BWRITER = USER_NO) WHERE BTYPE = 1 AND B.STATUS = 'Y' AND USER_NAME LIKE '%' || ?|| '%' </entry> |
7. BoardDao변경하기
public int getListCount(Connection conn, Search search) { PreparedStatement pstmt = null; ResultSet rset = null; int listCount = 0; // return 구문 String sql = boardQuery.getProperty("getListCount"); *sql이란 getListCount를 넣은 값을 반환한 곳 // 검색된 목록을 조회해야 하는 경우 다른 SQL문 수행 if(search.getSearchCondition() != null && search.getSearchValue() != null) { -search에 있는 searchCondition(옵션값)에 값이 선택되어 있고 searchValue(인풋값)에 값이 입력되어 있다면 if(search.getSearchCondition().equals("title")) { sql = boardQuery.getProperty("getTitleListCount"); -searchCondition(옵션 값)에 값이 있는데 그것이 바로 title(제목)이라면 쿼리문 중 getTitleListCount의 값을 반환하라 즉, 옵션 중 제목이 선택된 행의 수를 반환하라 } else if(search.getSearchCondition().equals("content")) { sql = boardQuery.getProperty("getContentListCount"); } else if(search.getSearchCondition().equals("writer")) { sql = boardQuery.getProperty("getWriterListCount"); } } try { pstmt = conn.prepareStatement(sql); -sql을 pstmt에 넣는다 : 이제 pstmt의 기능을 본격적으로 사용하겠다. 쿼리문에서 ? 값을 찾겠다. if(search.getSearchCondition() != null && search.getSearchValue() != null) { pstmt.setString(1, search.getSearchValue()); } rset = pstmt.executeQuery(); -결과라는 뜻의 rest에 pstmt에서 찾아온 값을 넣겠다. if(rset.next()) { listCount = rset.getInt(1); } *next() = 다음 데이터로 한칸 이동한다 } catch (SQLException e) { e.printStackTrace(); } finally { // close 임포트 처리 해주세요 close(rset); close(pstmt); } return listCount; } |
*Statement 대신 preparedStatement 사용하는 이유 그 이유는 Statement 객체에서 사용한 createStatement()라는 메소드 때문입니다. 이것을 사용할 경우 사용자의 입력 값을 미리 만들어 놓은 sql문에 적용한 후 컴파일을 하기 때문에 사용자의 입력 값에 따라 쿼리문의 형태가 바뀔 수 있어 보안에 취약합니다. 사용자가 입력 값과 함께 'OR 1=1'과 같은 코드를 함께 전달할 경우 모든 사용자의 정보 등이 노출될 수 있기 때문입니다. 따라서 해결책으로 Statement 객체의 preparedStatement(query) 메소드를 사용하였습니다. 이것은 미리 개발자가 작성한 쿼리문을 컴파일 하고 ?로 처리한 부분에 사용자의 입력 값을 넣기 때문에 쿼리문의 형태가 바뀌지 않아 보안성을 높일 수 있습니다. *PreparedStatement 와 Statement의 가장 큰 차이점은 캐시(cache) 사용여부이다. 1) 쿼리 문장 분석 2) 컴파일 3) 실행 Statement를 사용하면 매번 쿼리를 수행할 때마다 1) ~ 3) 단계를 거치게 되고, PreparedStatement는 처음 한 번만 세 단계를 거친 후 캐시에 담아 재사용을 한다는 것이다. 만약 동일한 쿼리를 반복적으로 수행한다면 PreparedStatment가 DB에 훨씬 적은 부하를 주며, 성능도 좋다. -출처 : https://oper6210.tistory.com/157 *rset.next() 말그대로...로우가 이동하는겁니다. 로우가 10개있으면...가장처음 수행될때 첫번쨰 로우로 로우 이동이 되겟지요... 로우이동은 ResultSet 내부에서 이동이되고.. 리턴값으로 true false를 리턴하는겁니다.. *row : 행 *column : 열 |
8. board-query
<entry key="selectTitleList"> SELECT * FROM (SELECT ROWNUM RNUM, A.* FROM (SELECT BID , BTYPE , CNAME , BTITLE , BCONTENT , USER_NAME , BCOUNT , CREATE_DATE , B.MODIFY_DATE , B.STATUS FROM BOARD B JOIN CATEGORY USING(CID) JOIN MEMBER ON(BWRITER = USER_NO) WHERE BTYPE = 1 AND B.STATUS = 'Y' AND BTITLE LIKE '%' || ? || '%' ORDER BY BID DESC) A) WHERE RNUM BETWEEN ? AND ? </entry> <entry key="selectContentList"> SELECT * FROM (SELECT ROWNUM RNUM, A.* FROM (SELECT BID , BTYPE , CNAME , BTITLE , BCONTENT , USER_NAME , BCOUNT , CREATE_DATE , B.MODIFY_DATE , B.STATUS FROM BOARD B JOIN CATEGORY USING(CID) JOIN MEMBER ON(BWRITER = USER_NO) WHERE BTYPE = 1 AND B.STATUS = 'Y' AND BCONTENT LIKE '%' || ? || '%' ORDER BY BID DESC) A) WHERE RNUM BETWEEN ? AND ? </entry> <entry key="selectWriterList"> SELECT * FROM (SELECT ROWNUM RNUM, A.* FROM (SELECT BID , BTYPE , CNAME , BTITLE , BCONTENT , USER_NAME , BCOUNT , CREATE_DATE , B.MODIFY_DATE , B.STATUS FROM BOARD B JOIN CATEGORY USING(CID) JOIN MEMBER ON(BWRITER = USER_NO) WHERE BTYPE = 1 AND B.STATUS = 'Y' AND USER_NAME LIKE '%' || ? || '%' ORDER BY BID DESC) A) WHERE RNUM BETWEEN ? AND ? </entry> |
8. BoardDao변경하기
public List<Board> selectList(Connection conn, PageInfo pi, Search search) { PreparedStatement pstmt = null; ResultSet rset = null; String sql = boardQuery.getProperty("selectList"); List<Board> boardList = new ArrayList<>(); // 검색시 수행할 쿼리문 변경 if(search.getSearchCondition() != null && search.getSearchValue() != null) { if(search.getSearchCondition().equals("title")) { sql = boardQuery.getProperty("selectTitleList"); } else if(search.getSearchCondition().equals("content")) { sql = boardQuery.getProperty("selectContentList"); } else if(search.getSearchCondition().equals("writer")) { sql = boardQuery.getProperty("selectWriterList"); } } try { pstmt = conn.prepareStatement(sql); int startRow = (pi.getPage() - 1) * pi.getBoardLimit() + 1; // 예) 5(4) * 10(11) = 44 int endRow = startRow + pi.getBoardLimit() - 1; // 예) 44 + 10(9) 53? int index=1; // 검색 sql 실행시 if(search.getSearchCondition() != null && search.getSearchValue() != null) { pstmt.setString(index++, search.getSearchValue()); } pstmt.setInt(index++, startRow); pstmt.setInt(index, endRow); rset = pstmt.executeQuery(); while(rset.next()) { Board board = new Board(); board.setBid(rset.getInt("bid")); board.setCname(rset.getString("cname")); board.setBtitle(rset.getString("btitle")); board.setUserName(rset.getString("user_name")); board.setBcount(rset.getInt("bcount")); board.setCreateDate(rset.getDate("create_date")); boardList.add(board); } } catch (SQLException e) { e.printStackTrace(); } finally { close(rset); close(pstmt); } return boardList; } |
9. BoardService
package board.model.service; import java.sql.Connection; import java.util.HashMap; import java.util.List; import java.util.Map; import board.model.dao.BoardDao; import board.model.vo.Board; import board.model.vo.PageInfo; import board.model.vo.Search; import notice.model.vo.Notice; import static common.JDBCTemplate.*; public class BoardService { private BoardDao boardDao = new BoardDao(); public Map<String, Object> selectList(int page, Search search) { // JDBCTemplate 임포트 필요 Connection conn = getConnection(); // 1. 게시글 총 개수 구하기 int listCount = boardDao.getListCount(conn, search); // System.out.println(listCount); // 2. PageInfo 객체 만들기 PageInfo pi = new PageInfo(page, listCount, 10, 10); // 3. 페이징 처리된 게시글 목록 조회 List<Board> boardList = boardDao.selectList(conn, pi, search); Map<String, Object> returnMap = new HashMap<>(); // System.out.println(listCount); //갯수 출력됨 // System.out.println(pi); // System.out.println(boardList); returnMap.put("pi", pi); returnMap.put("boardList", boardList); return returnMap; } |
2. 페이징에서 마지막 번호를 눌렀을 때 전체 페이지로 이동되는 오류 수정하기
boardlistview
<%@ 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 charset="UTF-8"> <title>게시판</title> </head> <body> <jsp:include page="/WEB-INF/views/common/menubar.jsp" /> <div class="outer"> <div class="wrap"> <div class="board_area"> <div class="board_title"> <h1>게시판</h1> </div> <div class="board_list"> <ul class="board_header"> <li class="id">글번호</li> <li class="category">분류</li> <li class="title">제목</li> <li class="writer">작성자</li> <li class="count">조회수</li> <li class="date">작성일</li> </ul> <c:forEach var="board" items="${ boardList }"> <%-- 이 ul이 하나의 게시글 단위 --%> <ul class="board_ul" onclick="detailView(${ board.bid })"> <%-- function에 현재 board.bid 넘김 --%> <li class="id">${ board.bid }</li> <li class="category">${ board.cname }</li> <li class="title">${ board.btitle }</li> <li class="writer">${ board.userName }</li> <li class="count">${ board.bcount }</li> <li class="date">${ board.createDate }</li> </ul> </c:forEach> </div> <c:if test="${!empty param.searchCondition && !empty param.searchValue }"> <c:set var="searchParam" value="&searchCondition=${ param.searchCondition }&searchValue=${param.searchValue}"></c:set> </c:if> <ul class="board_paging"> <!-- 맨 처음으로 이동하는 버튼 (<<) --> <li><a href="${ contextPath }/board/list?page=1${searchParam}"><<</a></li> <!-- 이전 페이지로 (<) --> <li> <c:choose> <%-- choose - when - otherwise = if - else if - else --%> <c:when test="${ pi.page > 1 }"> <a href="${ contextPath }/board/list?page=${ pi.page - 1 }${searchParam}"><</a> </c:when> <c:otherwise> <a href="#"><</a> </c:otherwise> </c:choose> </li> <!-- 최대 10개의 페이지 목록 --> <c:forEach var="p" begin="${ pi.startPage }" end="${ pi.endPage }"> <li> <c:choose> <c:when test="${ p eq pi.page }"> <a href="#" class="current_page">${ p }</a> <%-- 이해 안 됨 --%> </c:when> <c:otherwise> <a href="${ contextPath }/board/list?page=${ p }${searchParam}">${ p }</a> </c:otherwise> </c:choose> </li> </c:forEach> <!-- 다음 페이지로 (>) --> <li> <c:choose> <c:when test="${ pi.page < pi.maxPage }"> <a href="${ contextPath }/board/list?page=${ pi.page + 1 }${searchParam}">></a> </c:when> <c:otherwise> <a href="#">></a> </c:otherwise> </c:choose> </li> <!-- 맨 끝으로 이동하는 버튼 (>>) --> <li><a href="${ contextPath }/board/list?page=${ pi.maxPage }${searchParam}">>></a></li> </ul> </div> <div class="search_area"> <form method="get" action="${contextPath }/board/list"> <select id="searchCondition" name="searchCondition"> <option value="title" <c:if test= "${param.searchCondition == 'title' }">selected</c:if>>제목</option> <option value="content" <c:if test= "${param.searchCondition == 'content' }">selected</c:if>>내용</option> <option value="writer" <c:if test= "${param.searchCondition == 'writer' }">selected</c:if>>작성자</option> </select> <span class="input_area"> <input type="search" name="searchValue" value="${ param.searchValue }"> </span> <button type="submit">검색하기</button> <c:if test="${ !empty loginUser }"> <button type="button" onclick="location.href='${ contextPath }/board/insert'">작성하기</button> <!-- 로그인한 유저일 경우에만 눌렀을 때 작성할 화면으로 이동할 수 있게 --> <!-- 로그인 안 했을 경우 버튼도 안 보임 --> </c:if> </form> </div> </div> </div> <c:choose> <c:when test="${ !empty loginUser }"> <script> function detailView(bid) { location.href='${ contextPath }/board/detail?bid=' + bid; } </script> </c:when> <c:otherwise> <script> function detailView(bid) { alert('로그인 후 이용 가능합니다.'); location.href='${ contextPath }/login'; } </script> </c:otherwise> </c:choose> </body> </html> |
3. 본인만 수정 삭제 되도록 하는 버튼 넣기
1. boardDetailView
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>게시판</title> 생략 </head> <body> <jsp:include page="/WEB-INF/views/common/menubar.jsp" /> <div class="outer"> <div class="wrap"> <div class="board_area"> <div class="board_title"> <h1>게시판</h1> </div> <div class="board_content"> <div class="subject"> <span> 글번호 : ${board.bid }</span> <span> 조회수 : ${board.bcount }</span> <span> 작성자 : ${board.userName }</span> <span> 작성일 : <fmt:formatDate value="${board.createDate }" type="both" pattern="yyyy.MM.dd HH:mm:ss"/> </span> <span> 수정일 : <fmt:formatDate value="${board.modifyDate }" type="both" pattern="yyyy.MM.dd HH:mm:ss"/></span> </div> <div> <h4> <span class="title_span"> </span> 분류 </h4> <p>${board.cname }</p> <h4> <span class="title_span"> </span> 제목 </h4> <p>${ board.btitle }</p> <h4> <span class="title_span"> </span> 내용 </h4> <pre class="content">${ board.bcontent }</pre> </div> <div class="reply_area"> <h4> <span class="title_span"> </span> 댓글 </h4> <div class="reply_write"> <textarea class="reply_content"></textarea> <button>댓글등록</button> </div> <div class="reply_list"> </div> </div> <div class="btn_area"> <button type="button" onclick="location.href='${contextPath}/board/list'">목록으로</button> <c:if test="${loginUser.userNo == board.bwriter }"> <button type="button" onclick="updateBoardView();">수정하기</button> <button type="button" onclick="deleteBoard();">삭제하기</button> </c:if> </div> </div> </div> </div> </div> <c:if test="${loginUser.userNo == board.bwriter }"> <form name="boardForm" method="post"> <input type="hidden" name="bid" value="${board.bid }"> </form> <script> function updateBoardView(){ document.forms.boardForm.action = "${contextPath}/board/updateView"; document.forms.boardForm.submit(); } function deleteBoard(){ if(confirm("이 게시글을 삭제하시겠습니까?")){ document.forms.boardForm.action = "${contextPath}/board/delete"; document.forms.boardForm.submit(); } } </script> </c:if> </body> </html> |
2. BoardUpdateViewServlet
package board.controller; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import board.model.service.BoardService; import board.model.vo.Board; /** * Servlet implementation class BoardUpdateViewServlet */ @WebServlet("/board/updateView") public class BoardUpdateViewServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public BoardUpdateViewServlet() { super(); // TODO Auto-generated constructor stub } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int bid = Integer.parseInt(request.getParameter("bid")); Board board = new BoardService().selectBoard(bid); if(board != null) { request.setAttribute("board", board); request.getRequestDispatcher("/WEB-INF/views/board/boardUpdateView.jsp").forward(request, response); } else { request.setAttribute("message", "수정할 게시글을 불러오는데 실패했습니다"); request.getRequestDispatcher("/WEB-INF/views/common/errorpage.jsp").forward(request, response); } } } |
3. BoardUpdateView 넣기
<%@ 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 charset="UTF-8"> <title>게시판</title> <style> .outer { width: 800px; margin: auto; } .wrap { width: 780px; margin: 100px auto; } .board_title { border-bottom: 1px solid #282A35; } .board_content { padding: 0px 20px; } .board_content .content { margin-bottom: 30px; } .input_area { border: solid 1px #dadada; padding: 10px 10px 14px 10px; background: white; } .input_area select { width: 150px; height: 30px; border: 0px; } .input_area input { width: 700px; height: 30px; border: 0px; } .input_area input:focus, .input_area select:focus { outline: none; } .textarea { resize: none; border: solid 1px #dadada; } .textarea:focus { outline: none; } .title_span { background-color: #282A35; } .board_area button { width: 100px; height: 35px; border: 0px; color: white; background: #282A35; margin: 5px; cursor: pointer; } .btn_area { text-align: center; border-top: 1px solid #282A35; padding: 30px; } </style> </head> <body> <jsp:include page="/WEB-INF/views/common/menubar.jsp" /> <div class="outer"> <div class="wrap"> <div class="board_area"> <div class="board_title"> <h1>게시글 수정</h1> </div> <div class="board_content"> <form method="post" action="${contextPath}/board/update"> <input type="hidden" name="bid" value="${board.bid }"> <div class="content"> <h4> <span class="title_span"> </span> 분류 </h4> <span class="input_area"> <select name="category"> <option value="10" <c:if test="${ board.cid ==10 }">selected</c:if>>공통</option> <option value="20" <c:if test="${ board.cid ==20 }">selected</c:if>>운동</option> <option value="30" <c:if test="${ board.cid ==30 }">selected</c:if>>등산</option> <option value="40" <c:if test="${ board.cid ==40 }">selected</c:if>>게임</option> <option value="50" <c:if test="${ board.cid ==50 }">selected</c:if>>낚시</option> <option value="60" <c:if test="${ board.cid ==60 }">selected</c:if>>요리</option> <option value="70" <c:if test="${ board.cid ==70 }">selected</c:if>>기타</option> </select> </span> <h4> <span class="title_span"> </span> 제목 </h4> <span class="input_area"> <input type="text" name="title" value="${board.btitle }" required> </span> <h4> <span class="title_span"> </span> 내용 </h4> <textarea class="textarea" rows="20" cols="100" name="content" required>${board.bcontent}</textarea> </div> <div class="btn_area"> <button type="button" onclick="location.href='${contextPath}/board/list'">목록으로</button> <button type="submit">수정하기</button> </div> </form> </div> </div> </div> </div> </body> </html> |