클라우드 융합 Full-stack 웹 개발자 양성과정/Servlet, JSP

Servlet/JSP - 공지사항삭제, 일반게시판목록조회, 일반게시글 작성1

thesunset 2022. 11. 15. 17:41

1. 공지사항 삭제 

<view/noticeDetailView>

<a class="btn btn-sm btn-danger" href="<%=contextPath%>/deleteNotice.no?nno=<%=n.getNoticeNo()%>">삭제</a>

get방식으로 noticeNo을 함께 보내주기 

 

<com.kh.notice.controller>NoticeDeleteNotice

int noticeNo = Integer.parseInt(request.getParameter("nno"));

int result = new NoticeService().deleteNotice(noticeNo);

if(result>0) {
    response.sendRedirect(request.getContextPath()+"/list.no");
}else {
    request.setAttribute("errorMsg", "공지사항 삭제에 실패하였습니다.");
    request.getRequestDispatcher("views/common/errorPage.jsp").forward(request, response);
}

성공했을 경우, NoticeList목록으로 돌아가기 => response.sendRedirect()방식을 이용해 url을 지정해주기 

실패했을 경우 alert창을 띄워준 뒤 에러페이지를 보내주기

 

<com.kh.notice.model.service>NoticeService

public int deleteNotice(int noticeNo) {

    Connection conn = getConnection();

    int result = new NoticeDao().deleteNotice(conn, noticeNo);

    if(result>0) {
        commit(conn);
    }else {
        rollback(conn);
    }
    close(conn);
    return result;
}

<com.kh.notice.model.dao>NoticeDao

public int deleteNotice(Connection conn, int noticeNo) {

    int result = 0; 
    PreparedStatement pstmt = null;
    String sql = prop.getProperty("deleteNotice");

    try {
        pstmt = conn.prepareStatement(sql);
        pstmt.setInt(1, noticeNo);

        result = pstmt.executeUpdate();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close(pstmt);
    }

    return result;
}

<notice-mapper>"deleteNotice

<entry key="deleteNotice">
	UPDATE 
	    NOTICE
	    SET
	        STATUS = 'N'
	    WHERE 
	    	NOTICE_NO = ?
	    AND 
	    	STATUS = 'Y'

</entry>

 

2. 일반게시판 목록 조회 

일반게시판의 특징

- 첨부파일

- 페이징처리

<menubar.jsp>

<div class="menu"><a href="<%= contextPath%>/list.bo?cpage=1">일반게시판</a></div>

경로를 .list.bo라고 지정해준 뒤 get방식으로 현재 사용자가 몇 번 째 페이지인지도 함께 보내주기(기본값=1)

 

<com.kh.board.controlloer>BoardListController

1) 인코딩 => GET방식 쿼리스트링으로 요청 /jsp/list.bo?cpage=1

2) request에서 값뽑기

3) 가공처리하기 

더보기

페이징 처리 

1. 필요한 변수들

1) listCount : 현재 일반 게시판의 게시글 총 개수 => BOARD로부터 조회 COUNT(*)활용(STATUS='Y')

2) currentPage : 현재 페이지 (즉, 사용자가 요청한 페이지) => request.getParameter("cpage")

3) pageLimit : 페이지 하단에 보여질 페이징 바의 최대 개수 (10개로 고정)

4) boardLimit : 한 페이지에 보여질 게시글의 최대 개수 => 10개로 고정

5) maxPage : 가장 마지막 페이지가 몇 번인지(총 페이지 개수) 

6) startPage : 페이지 하단에 보여질 페이징바의 시작 수 

7) endPage : 페이지 하단에 보여질 페이징바의 끝 수 

int listCount;
int currentPage; 
int pageLimit; 
int boardLimit; 

int maxPage;
int startPage; 
int endPage;

* 게시글은 언제나 변할 수 있기 때문에 고정된 값이 아닌  변수로 지정해주어야 함

1) listCount :총 게시글 수 

listCount = new BoardService().selectListCount();//107

 2)  currentPage : 현재페이지 (==사용자가 요청한 페이지)

currentPage = Integer.parseInt(request.getParameter("cpage"));//1

 - get으로 보내준 방식이니까 getParameter로 뽑아오기 

3) pageLimit : 페이징바의 최대 개수 

pageLimit = 10;

 - 개발자가 임의로 지정한 값

4) boardLimit : 한 페이지에 보여질 게시글의 최대 개수

boardLimit = 10;

 - 개발자가 임의로 지정한 값

5)  maxPage : 가장 마지막 페이지가 몇 번 페이지인지(총 페이지 개수) 한페이지에 10개 => 11개의 페이지 

==> boardLimit, listCount에 영향을 받음

 - 공식 구하기 

단, boardLimit이 10이라는 가정 하에 규칙을 정해보자 

 총 개수 (listCount)  연산자 boardLimit(10개) 연산 결과  maxPage(마지막페이지)
100개 / 10개 = 10.0 10번페이지
102개 / 10개 = 10.2 11번페이지
107개 / 10개 = 10.7 11번페이지
110개 / 10개 = 11.0 11번페이지
111개 / 10개 = 11.1 12번페이지

=> 나눗셈 결과 (listCount/boardLimit)를 올림처리를 할 경우 maxPage가 된다. 

< STEP >

 1) listCount를 double로 형변환
 2) listCount / boardLimit
 3) Math.ceil() => 결과를 올림 처리 
 4) 결과값을 int로 형변환

maxPage = (int)Math.ceil((double)listCount/boardLimit);

6) startPage : 페이지 하단에 보여질 페이징 바의 시작 수 

==> pageLimit, currentPage에 영향을 받음

- 공식 구하기 

단, pageLimit이 10이라는 가정하에 규칙을 구해보자 

startPage = 1, 11, 21, 31, 41 => n* 10 + 1

만약 pageLimit 5였다면
startPage : 1, 6, 11, 16, => n * 5 + 1

 

즉, startPage = n * pageLimit + 1

currentPage -  StartPage
1 - 1 5 - 1 10  - 1
13 - 11 20 - 11 28 - 21

=> 1~10  : n*10 + 1 => n=0
=> 11~20 : n*10 + 1 => n=1
=> 21~30 : n*10 + 1 => n=2

1 ~ 10 / 10=>0 0/1
11~20 / 10=>1 1/2
21~30 / 10=>2 2/3

 => 1씩 모자름 

1 ~ 9 / 10=>0
10~19 / 10=>1
20~29 / 10=>2

n = (currentPage-1) /pageLimit

startPage = (currentPage-1)/pageLimit*pageLimit+1;

startPage = (currentPage-1) / pageLimit * pageLimit + 1;

 7) endPage : 페이지 하단에 보여질 페이징바의 끝 수 

==> startPage, pageLimit 에 영향을 받음 (단, maxPage도 마지막 페이징 바에 대해서 영향을 준다)

- 공식 구하기 

 단, pageLimit이 10이라는 가정
 startPage : 1=> endPage : 10
 startPage : 11=> endPage : 20
 startPage : 21=> endPage : 30
 
 => endPage = startPage + pageLimit - 1;

endPage = startPage + pageLimit - 1;

 만약, startPage가 11이어서 endPage에는 20이 들어갔는데
maxPage가 13이라면?
=> endPage값을 maxPage값으로 바꿔줘야 함

if(endPage>maxPage) {
    endPage = maxPage;
}

 2. VO에 담기 (미리 VO만들어 놓기)

PageInfo pi = new PageInfo(listCount, currentPage, pageLimit, boardLimit, maxPage, startPage, endPage);

 

4) Service로 값 보내기

ArrayList<Board> list = new BoardService().selectList(pi);

5) 응답화면 지정 

request.setAttribute("list", list); //우리가 실제로 조회한 페이지에 보여질 10개의 게시글
request.setAttribute("pi", pi); //페이징 바를 만들 때 필요한 변수 


request.getRequestDispatcher("views/board/boardListView.jsp").forward(request, response);

< com.kh.board.model.service>BoardService

1) listCount(게시글 수)

public int selectListCount() {

    Connection conn = getConnection();

    int listCount = new BoardDao().selectListCount(conn);
    //SELECT문의 결과는 ResultSet
    //게시글의 총 개수는 정수형
    close(conn);

    return listCount;
}

2) selectList(리스트 조회)

public ArrayList<Board> selectList(PageInfo pi) {

    Connection conn = getConnection();

     ArrayList<Board> list = new BoardDao().selectList(conn, pi);

     close(conn);

     return list;
}

< com.kh.board.model.dao>BoardDao

- DB와 연결

private Properties prop = new Properties();

public BoardDao() {
    String fileName = BoardDao.class.getResource("/sql/board/board-mapper.xml").getPath();

    try {
        prop.loadFromXML(new FileInputStream(fileName));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

- selectListCount

public int selectListCount(Connection conn) {
    //SELECT => ResultSet 하지만 반환은 int
    int listCount = 0;
    ResultSet rset = null;
    PreparedStatement pstmt = null;
    String sql = prop.getProperty("selectListCount");

    try {
        pstmt = conn.prepareStatement(sql);

        rset = pstmt.executeQuery();

        if(rset.next()) {
            listCount = rset.getInt("COUNT(*)");
        }

    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close(rset);
        close(pstmt);
    }
    return listCount;
}

- selectList

    public ArrayList<Board> selectList(Connection conn, PageInfo pi) {

    //SELECT문 => RESULTSET => 여러 행이므로 ArrayList<Borad>
    ArrayList<Board> list = new ArrayList();

    PreparedStatement pstmt = null;
    ResultSet rset = null;
    String sql = prop.getProperty("selectList");

    try {
        pstmt = conn.prepareStatement(sql);

        int startRow = (pi.getCurrentPage()-1) * pi.getBoardLimit() + 1;
        int endRow = startRow + pi.getBoardLimit() - 1;
        pstmt.setInt(1, startRow);
        pstmt.setInt(2, endRow);

        rset = pstmt.executeQuery();

        while(rset.next()) {
            //하기전 String category필드 생성 후 생성자, getter,setter생성하기
            list.add(new Board( rset.getInt("BOARD_NO"),
                                rset.getString("CATEGORY_NAME"),
                                rset.getString("BOARD_TITLE"),
                                rset.getString("USER_ID"),
                                rset.getInt("COUNT"),
                                rset.getDate("CREATE_DATE")
                    ));
        }

    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close(rset);
        close(pstmt);
    }

    return list;
    }

* 인라인 뷰 활용
1) ORDER BY 순서가 가장 마지막인데 가장 처음에 실행이 필요함
    일단 정렬해주는 SELECT문을 만듬 => 서브쿼리
2) 서브쿼리를 FROM절에 넣음 + ROWNUM
        currentPage == 1 => 시작값1, 끝값10
        currentPage == 2 => 시작값11, 끝값20
        currentPage == 3 => 시작값21, 끝값30
        시작값 = (currentPage - 1) * boardLimit + 1
        끝값 = 시작값 + boardLimit-1

 

<쿼리문>

- selectListCount

<entry key="selectListCount">
SELECT 
    COUNT(*)
FROM 
    BOARD
WHERE  
    STATUS = 'Y'
AND 
    BOARD_TYPE = 1

</entry>

- selectList

<entry key="selectList">
SELECT * 
    FROM(SELECT 
                ROWNUM RNUM, 
                A.*
            FROM
                (SELECT 
                        BOARD_NO,
                        CATEGORY_NAME,
                        BOARD_TITLE,
                        USER_ID,
                        COUNT,
                        CREATE_DATE
                    FROM 
                        CATEGORY
                    JOIN 
                        BOARD B USING(CATEGORY_NO)
                    JOIN 
                        MEMBER ON(BOARD_WRITER = USER_NO)
                    WHERE 
                        BOARD_TYPE = 1
                    AND 
                        B.STATUS = 'Y'

                    ORDER 
                       BY
                        CREATE_DATE DESC) A )
    WHERE 
          RNUM BETWEEN ? AND ?
</entry>

<boardListView>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.util.ArrayList, com.kh.board.model.vo.Board, com.kh.common.model.vo.PageInfo"%>
<%
	ArrayList<Board> list = (ArrayList<Board>)request.getAttribute("list");
	PageInfo pi = (PageInfo)request.getAttribute("pi");
	
	//페이징바를 만들 때 필요한 변수 미리 세팅
	int currentPage = pi.getCurrentPage();
	int startPage = pi.getStartPage();
	int endPage = pi.getEndPage();
	int maxPage = pi.getMaxPage();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>일반게시판</title>
<style>
	.outer{
		width: 1000px;
		margin: auto;
		background-color:  #88A201;
		margin-top: 5px;
		color: white;
	}
	.list-area{
	text-align : center;
	border : 1px solid white
	}
	
	.list-area>tbody>tr:hover{
		cursor : pointer;
		background :rgb(200, 225, 131);
		color : black;
	}
</style>
</head>
<body>
	<%@ include file="../common/menubar.jsp" %>

	<div class="outer">
		<br>
		<h2 align="center">일반게시판</h2>
		<br>

		<table class="list-area" align="center">
			<thead>
				<th width="70">글번호</th>
				<th width="80">카테고리</th>
				<th width="300">제목</th>
				<th width="100">작성자</th>
				<th width="50">조회수</th>
				<th width="100">작성일</th>
			</thead>
			<tbody>
				<!-- 게시글 출력 : 게시글이 있는지 없는지 => isEmpty()이용해서 조건 부여  -->
				<%if(list.isEmpty()){ %>
				<tr>
					<td colspan="6">조회된 게시글이 없습니다.</td>
				</tr>
				
				<%} else {%>
					<%for(Board b : list){ %>
					<tr>
						<td><%= b.getBoardNo() %></td>
						<td><%= b.getCategory() %></td>
						<td><%= b.getBoardTitle() %></td>
						<td><%= b.getBoardWriter() %></td>
						<td><%= b.getCount() %></td>
						<td><%= b.getCreateDate() %></td>
					</tr>
					<%} %>
				<%} %>
			</tbody>
		</table>
	<br><br><br>

	
	<div align="center" class="pasing-area">
	
		<%if(startPage != currentPage){ %>
			<button class="btn btn-sm btn-info" onclick="location.href='<%= contextPath%>/list.bo?cpage=<%=currentPage-1%>'">&lt;</button>
		<%} %>
		
		<%for(int i = startPage; i<=endPage; i++){ %>	
			<%if(currentPage != i){ %>
			<button class="btn btn-sm btn-info" onclick="location.href='<%=request.getContextPath() %>/list.bo?cpage=<%= i %>'"><%= i %></button>
			<%}else{ %>
			<button class="btn btn-sm btn-info" disabled><%= i %></button>
			<%} %>
		<%} %>
		
		<%if(maxPage != currentPage){ %>
			<button class="btn btn-sm btn-info" onclick="location.href='<%= contextPath%>/list.bo?cpage=<%=currentPage+1%>'">&gt;</button>
		<%} %>
		
	</div>
	<br><br><br>
	</div>
</body>
</html>

* 페이징 바 만들기 => 미리 PageInfo 객체에 담아온 변수를 이용해서 지정하기 

3. 일반 게시글 작성 1

 

+ 더미데이터 넣을 때 엑셀 파일 넣기

우클릭 > 데이터 임포트 > 찾아보기로 파일 선택 > 다음 > *3 > 열정의에서 no를 nextval로 인식 못하고 문자로 인식하기 때문에 오류 - 그냥 무시 > 완료 > 행에 대한 삽입 실패 > 예 > 맨 마지막에 COMMIT 입력하고 스크립트 실행 

<div align="right" style="width:840px">
    <% if(loginUser != null){ %>
        <a href="<%=contextPath %>/enrollForm.bo" class="btn btn-info btn-sm">글 작성</a>
        <br><br>
    <%} %>
</div>

- 로그인한 회원만 보여지는 버튼 : loginUser가 null이 아니면 보이도록

 

- 작성폼 띄우기 : 작성 폼 안에 DB에서 조회한 CATEGORY목록을 SELETE태그의 OPTION으로 띄워줄 것

ArrayList<Category> list = new BoardService().selectCategory();

request.setAttribute("list", list);
request.getRequestDispatcher("views/board/boardEnrollForm.jsp").forward(request, response);

=> 조회한 카테고리 list를 작성폼으로 넘겨줘서 카테고리 select태그 안에 넣어줄 예정

 

< com.kh.board.model.service>BoardService

public ArrayList<Category> selectCategory() {

    Connection conn = getConnection();

    ArrayList<Category> list = new BoardDao().selectCategoryList(conn);

    close(conn);

    return list;
}

< com.kh.board.model.dao>BoardDao

public ArrayList<Category> selectCategoryList(Connection conn) {

    //SELECT문 => ResultSet => 7행 => List

    ArrayList<Category> list = new ArrayList();
    PreparedStatement pstmt = null;
    ResultSet rset = null;

    String sql = prop.getProperty("selectCategoryList");

    try {
        pstmt = conn.prepareStatement(sql);

        rset = pstmt.executeQuery();

        while(rset.next()) {
            list.add(new Category(
                                rset.getInt("CATEGORY_NO"),
                                rset.getString("CATEGORY_NAME")
                    ));

        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close(rset);
        close(pstmt);
    }


    return list;
}

- 가져온 list로 boardEnrollForm 카테고리만들기 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.util.ArrayList, com.kh.board.model.vo.Category"%>
<%
    ArrayList<Category> list = (ArrayList<Category>)request.getAttribute("list");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>일반게시글 작성</title>
<style>
	body{
		box-sizing: border-box;
	}
	.outer{
		width: 1000px;
		margin: auto;
		background-color:  #88A201;
		margin-top: 5px;
		color: white;
	}
	
	#enroll-form input, #enroll-form textarea{
		width: 100%;
		box-sizing: border-box;
	}
	#enroll-form>table{
		border: 1px solid wheat;
	}
</style>
</head>
<body>
    <%@ include file="../common/menubar.jsp" %>

    <div class="outer">
    
    <br>
    <h2 align="center">일반게시글 작성하기</h2>
    <br>   

    
 
    <form action="<%=contextPath%>/insert.bo" method="post" id="enroll-form" enctype="multipart/form-data">
    <!-- 제목, 내용, 카테고리, 제출버튼, 글쓴이, 첨부파일 -->
    <!-- 작성자의 회원번호를 hidden으로 같이 넘겨서 board테이블에 INSERT하게 만들 것 -->
        <input type="hidden" name="userNo" value="<%=loginUser.getUserNo()%>">
        <table align="center">
            <tr>
                <th width="150">카테고리</th>
                <td width="600">
                    <select name="category">
                        <% for(Category c : list){ %>
                            <option value="<%= c.getCategoryNo()%>"><%= c.getCategoryName()%></option>
                        <%}%>
                    </select>
                </td>
            </tr>
            <tr>
                <th>제목</th>
                <td><input type="text" name="title" required></td>
            </tr>
            <tr>
                <th>내용</th>
                <td>
                    <textarea name="content" style="resize: none;" rows="10" required></textarea>
                </td>
            </tr>
            <tr>
                <th>첨부파일</th>
                <td><input type="file" name="upfile"></td>
            </tr>
        </table>

        <br>
        <div align="center">
            <button type="submit">작성</button>
            <button type="reset">취소</button>
        </div>
    </form>

    </div>

</body>
</html>

* 파일을 첨부하는 요청을 할 때는 반드시 form태그에 enctype="multipart/form-data" ****오타주의