💫 페이징이 된 모습을 보기 위해 Borad를 복사해 PagingBorad 폴더를 만들었다.

 

🌀 페이징

[첫번째 페이지] [이전 블럭] [1] [2] [3] [4] [5] [다음 블럭] [마지막 페이지]

최종적으로 구현하고 싶은 모양이다.

각 페이지 번호를 누르면 페이지 넘버를 파라매터로 받아야한다.

그리고 파라매터로 받은 페이지 넘버가 주소에 표시되어야 한다. → url 정보를 넘겨줘야 함.

 

List.jsp

<%@page import="utils.BoardPage"%>
<%@page import="model1.board.BoardDAO"%>
<%@page import="model1.board.BoardDTO"%>
<%@page import="java.util.HashMap"%>
<%@page import="java.util.Map"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%
// DB에 연결
BoardDAO dao = new BoardDAO(application);

// form에서 액션 속성 생략 -> 자기 자신 페이지를 파라매터로 받는다. 
String searchField = request.getParameter("searchField");
String searchWord = request.getParameter("searchWord");

Map<String, Object> param = new HashMap<>();

// 검색조건으로 검색하려 할때
if (searchWord != null) {
	param.put("searchField", searchField);
	param.put("searchWord", searchWord);
}

// 전체 개수 구하기 selectCount()
// 검색 했을 때 개수 dao.selectCount(param)
// boardLists 에 DTO의 정보가 들어있다.
int totalCount = dao.selectCount(param);

int pageSize = Integer.parseInt(application.getInitParameter("POSTS_PER_PAGE"));
int blockPage = Integer.parseInt(application.getInitParameter("PAGES_PER_BLOCK"));
int totalPage = (int) Math.ceil( (double)totalCount / pageSize );

// 페이지 번호를 관리하는 변수
int pageNum = 1;

// 페이지 번호를 다른 걸로 선택하면 그 다른 페이지 번호를 받아온다
String pageTemp = request.getParameter("pageNum");

if (pageTemp != null && !pageTemp.equals("")){
	pageNum = Integer.parseInt(pageTemp);
}

// 게시물의 시작번호와 끝 번호 구하기
int start = (pageNum - 1) * pageSize + 1;
int end = pageNum * pageSize;

param.put("start", start);
param.put("end", end);

/* List<BoardDTO> boardLists = dao.selectList(param); */
List<BoardDTO> boardLists = dao.selectListPage(param);
dao.close();

%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<jsp:include page="../Common/Link.jsp" />
	<h2>목록 보기(List)</h2>

	<!-- 폼태그에 action 속성이 생략되면 자신의 현재 페이지를 요청한다. (List.jsp)-->
	<form method="get">
		<table border="1" width="90%">
			<tr>
				<td align="center">
				<select name="searchField">
						<option value="title">제목</option>
						<option value="content">내용</option>
				</select> 
				<input type="text" name="searchWord" /> 
				<input type="submit" value="검색하기" />
				</td>
			</tr>
		</table>
	</form>

	<table border="1" width="90%">
		<tr>
			<th width="10%">번호</th>
			<th width="50%">제목</th>
			<th width="15%">작성자</th>
			<th width="10%">조회수</th>
			<th width="15%">작성일</th>
		</tr>


<!-- 게시물이 없을 떄 -->
		<% if (boardLists.isEmpty()) {%>

		<tr>
			<td colspan="5" align="center">등록된 게시물이 없습니다^^*</td>
		</tr>

		<% }else{

		int virtualNum = 0;
		for(BoardDTO dto : boardLists){ 
		virtualNum = totalCount--;
		%>

		<!-- 게시물이 있을 때 -->
		<tr align="center">
		<!-- DTO에서 쓴 이름과 같게 -->
			<%-- <td><%= dto.getNum() %></td> --%>
			<td><%= virtualNum %></td>
			<td>
			<a href="View.jsp?num=<%= dto.getNum() %>"> <%= dto.getTitle() %> </a>

			</td>
			
			
			<td><%= dto.getId() %></td>
			<td><%= dto.getVisitcount() %></td>
			<td><%= dto.getPostDate() %></td>
		</tr>

		<%
		 } // for문 
		} // if else문
		%>

	</table>

	<table border="1" width="90%">
		<tr align="center">
		<td>
		<!-- [첫번째 페이지] [이전 블럭]  [1] [2] [3] [4] [5] [다음 블럭] [마지막 페이지] -->
		
		<!-- request.getRequestURI()로 주소값을 가지고 올 수 있다 -->
		<%= BoardPage.pagingStr(totalCount, pageSize, blockPage, pageNum, request.getRequestURI()) %>
		</td>
			<td align="right">
				<button type="button" onclick="location.href='write.jsp'">글쓰기</button>
			</td>
		</tr>
	</table>
</body>
</html>

 

BoardPage.jsp

package utils;

public class BoardPage {
	public static String pagingStr(int totalCount, int pageSize, int blockPage, int pageNum, String reqUrl) {
		
		String pagingStr = "";
		
		// 전체 페이지수 계산 , totalPages = 마지막 페이지 수
		int totalPages = (int) Math.ceil( (double)totalCount / pageSize );
		
		// 이전 페이지 블록 바로가기 출력
		// ( (현재 페이지 - 1 ) /  PAGES_PER_BLOCK) ) *  PAGES_PER_BLOCK + 1
		int pageTemp = ((pageNum - 1 ) / blockPage) *  blockPage + 1;
		
		// 현재페이지의 시작 값이 1이면 [첫 페이지]와 [이전 블록]은 안보인다. 
		if(pageTemp != 1) {
			pagingStr += "<a href='" + reqUrl + "?pageNum=1'>[첫 페이지]</a>";
			pagingStr += "&nbsp;";
			pagingStr += "<a href='" + reqUrl + "?pageNum=" + (pageTemp - 1) + "'>[이전 블록]</a>";
		}
		
		// 각 블록에 페이지 번호 출력
		int blockCount = 1;
		while ( blockPage >= blockCount && pageTemp <= totalPages ) {
										   // 마지막 블록에도 번호 비활성화
			// 현재 선택한 번호 비활성화
			if (pageTemp == pageNum) {
				pagingStr += "&nbsp;[" + pageTemp + "]&nbsp;";
			} else {
				pagingStr += "&nbsp;<a href='" + reqUrl + "?pageNum=" + pageTemp + "'>[" + pageTemp + "]</a>&nbsp;";
			}
			
			pageTemp++; // 6 7 8 9 10 다음 블록으로 넘아가 11 
			blockCount++;  // 1 2 3 4 5 다음 블록 6
		}
		
//		System.out.println(pageTemp + " : " + totalCount);
		
		// 다음 페이지 블록 바로가기 출력
		if(pageTemp <= totalPages) {
			pagingStr += "<a href='" + reqUrl + "?pageNum=" + (pageTemp) + "'>[다음 블록]</a>";
			pagingStr += "&nbsp;";
			pagingStr += "<a href='" + reqUrl +  "?pageNum=" + totalPages + "'>[마지막 페이지]</a>";
		}
		
		return pagingStr;
	}
}

 

BoardDAO.java

package model1.board;

import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import common.JDBConnect;
import jakarta.servlet.ServletContext;

public class BoardDAO extends JDBConnect {

	public BoardDAO(ServletContext application) {
		super(application);
	}

	// 1. 검색 조건에 맞는 게시물의 개수를 반환합니다.
	public int selectCount(Map<String, Object> map) {

		int totalcount = 0;

		// 복잡한 쿼리문은 오류날 확률이 높으므로 디벨로퍼에서 미리 실행해보고 옮기는 것이 좋다.
		String query = " select count(*) from board ";

		// searchWord를 선택하면 이름, 내용으로 분류해서 count 해줘야 한다.
		if (map.get("searchWord") != null) {
			query += " where " + map.get("searchField") + "" + " like '%" + map.get("searchWord") + "%'";

			// where title like '%제목%';
		}

		try {

			psmt = con.prepareStatement(query);
			rs = psmt.executeQuery();

			if (rs.next()) {
				totalcount = rs.getInt(1);
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

		return totalcount;
	}

	// 2. 검색 조건에 맞는 게시물 목록을 반환합니다.
	public List<BoardDTO> selectList(Map<String, Object> map) {

		List<BoardDTO> bbs = new ArrayList<>();

		String query = " select * from board ";

		if (map.get("searchWord") != null) {
			query += " where " + map.get("searchField") + " like '%" + map.get("searchWord") + "%'";
		}

		query += " order by num desc ";

		try {

			psmt = con.prepareStatement(query);
			rs = psmt.executeQuery();

			while (rs.next()) {
				// 이 정보들을 DTO에 넣어준다
				BoardDTO dto = new BoardDTO();
				// DB 컬럼명
				dto.setNum(rs.getString("num"));
				dto.setTitle(rs.getString("title"));
				dto.setContent(rs.getString("content"));
				dto.setPostDate(rs.getDate("postdate"));
				dto.setId(rs.getString("id"));
				dto.setVisitcount(rs.getString("visitcount"));

				// DTO는 가장 최근의 정보만 읽어온다.
				// 읽어와서 DTO에 담긴 정보를 다른 곳에 저장해야 목록을 볼 수 있다.
				// collection ArayList()에 저장
				bbs.add(dto);

			}

		} catch (Exception e) {
			e.printStackTrace();
		}

		// 목록 데이터가 담긴 bbs를 리턴한다.
		return bbs;
	}

	// 3. 검색 조건에 맞는 게시물 목록을 반환합니다(페이징 기능 지원).
	public List<BoardDTO> selectListPage(Map<String, Object> map) {
		
		List<BoardDTO> bbs = new ArrayList<>();
		
		String query = " select * "
				+ " from ( "
				+ " select rownum rm, p.* "
				+ " from ( "
				+ " select * "
				+ " from board ";
				
		if (map.get("searchWord") != null) {
			query += " where " + map.get("searchField") + " like '%" + map.get("searchWord") + "%'";
		}
				
				query += " order by num desc "
						+ " ) p "
						+ " ) "
						+ " where rm between ? and ? ";

		try {

			psmt = con.prepareStatement(query);
			psmt.setString(1, map.get("start").toString());
			psmt.setString(2, map.get("end").toString());
			
			rs = psmt.executeQuery();
			
			while (rs.next()) {
				
				BoardDTO dto = new BoardDTO();
				// DB 컬럼명
				dto.setNum(rs.getString("num"));
				dto.setTitle(rs.getString("title"));
				dto.setContent(rs.getString("content"));
				dto.setPostDate(rs.getDate("postdate"));
				dto.setId(rs.getString("id"));
				dto.setVisitcount(rs.getString("visitcount"));

				bbs.add(dto);

			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return bbs;
	}

	// 4. 게시글 데이터를 받아 DB에 추가합니다.
	// 실제 데이터를 insert할 수 있는 기능
	public int insertWrite(BoardDTO dto) {

		// insert 구문이 들어갔는지 확인
		int result = 0;

		String query = " insert into board " + " (num, title, content, id, visitcount) "
				+ " values (seq_board_num.nextval,?,?,?,0) ";

		try {

			psmt = con.prepareStatement(query);
			psmt.setString(1, dto.getTitle());
			psmt.setString(2, dto.getContent());
			// 로그인한 ID 받아오기 -> session 영역의 정보를 받아온다
			psmt.setString(3, dto.getId());

			result = psmt.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		}

		return result;
	}

	// 5. 지정한 게시물을 찾아 내용을 반환합니다.
	public BoardDTO selectView(String num) {

		BoardDTO dto = new BoardDTO();

		String query = " select B.*, M.name " + " from member M inner join board B " + " on M.id = B.id"
				+ " where num = ? ";

		try {

			psmt = con.prepareStatement(query);
			psmt.setString(1, num);
			rs = psmt.executeQuery();

			if (rs.next()) {

				dto.setNum(rs.getString(1));
				dto.setTitle(rs.getString(2));
				dto.setContent(rs.getString("content"));
				dto.setPostDate(rs.getDate("postdate"));
				dto.setId(rs.getString("id"));
				dto.setVisitcount(rs.getString(6));
				dto.setName(rs.getString("name"));

				// System.out.println("kkkkkk : " + dto.getId());

			}

		} catch (Exception e) {
			e.printStackTrace();
		}

		return dto;

	}

	// 6. 지정한 게시물의 조회수를 1 증가시킵니다.
	public void updateVisitCount(String num) {

		String query = " update board " + " set visitcount = visitcount + 1 " + " where num = ? ";

		try {

			psmt = con.prepareStatement(query);
			psmt.setString(1, num);
			psmt.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// 7. 지정한 게시물을 수정합니다.
	public int updateEdit(BoardDTO dto) {

		int result = 0;

		String query = " update board " + " set title = ? , content = ? " + " where num = ? ";

		try {

			psmt = con.prepareStatement(query);
			psmt.setString(1, dto.getTitle());
			psmt.setString(2, dto.getContent());
			psmt.setString(3, dto.getNum());

			result = psmt.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		}

		return result;
	}

	// 8. 지정한 게시물을 삭제합니다.
	public int deletePost(BoardDTO dto) {

		int result = 0;

		String query = " delete from board where num = ? ";

		try {

			psmt = con.prepareStatement(query);
			psmt.setString(1, dto.getNum());
			
			result = psmt.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		}

		return result;
	}

}

 

주소 : http://localhost:9999/firstjsp/PagingBoard/List.jsp?**pageNum=3**

이동하는 페이지에 따라 주소값이 달라진다!

[ 각 페이지 번호를 누르면 페이지 넘버를 파라매터로 받아야한다.

그리고 파라매터로 받은 페이지 넘버가 주소에 표시되어야 한다. → url 정보를 넘겨줘야 함. ]

이 부분에 대한 처리가 됐다.

 

 

😞 그런데 여기서 문제.. 발생

그런데 마지막 10 페이지 이후로 글이 안나온다..

그리고 마지막 페이지로 이동하는 태그도 보이지 않는다!!!!!

 

원인)

		<% }else{

		int virtualNum = 0;
		for(BoardDTO dto : boardLists){ 
		virtualNum = totalCount--;
		%>

List.jsp 의 이 부분에서 계속 totalCount을 감소시키고 있었다.

 

해결) 스스로 해보기

		<% }else{

		int virtualNum = totalCount - (pageNum - 1) * pageSize;
		for(BoardDTO dto : boardLists){ 
			// virtualNum = totalCount--;
		%>

		<!-- 게시물이 있을 때 -->
		<tr align="center">
		<!-- DTO에서 쓴 이름과 같게 -->
			<%-- <td><%= dto.getNum() %></td> --%>
			<td><%= virtualNum-- %></td>

totalCount를 사용하지 않고 virtualNum 만 따로 계산을 해줬다.

 

강사님의 식)

		<% }else{

		int virtualNum = 0;
		for(BoardDTO dto : boardLists){ 
			virtualNum =  totalCount - ((pageNum - 1) * pageSize + CountNum++);
		%>

		<!-- 게시물이 있을 때 -->
		<tr align="center">
		<!-- DTO에서 쓴 이름과 같게 -->
			<%-- <td><%= dto.getNum() %></td> --%>
			<td><%= virtualNum %></td>

 

 

 

이렇게 하면 마지막페이지까지 잘 나오고, virtualNum도 잘 나온다.

 

 

처음 페이징 할 때 중요 )

총 게시판에 몇 개의 글이 들어있는지 알아야 함.

그리고 개발자는 한 페이지에 몇 개의 글을 보여줄지 정하기.

한 페이지에 보여줄 글의 개수와 한 페이지에서 보여줄 페이지의 개수를 정하는 것이 가장 먼저. (중요)

한 페이지에 보여줄 글의 개수 , 한 페이지에서 보여줄 페이지의 개수 각 변수 만들기

 


이 코드를 안보고 짤 수 있을 정도로 흐름을 이해해야 프로젝트든 취업이든 할 수 있음.


 

 

 🎉 댓글을 달 수 있는 게시판 만들기

 

PagingBoard 폴더 복사해서 ReplyBoard 폴더 만들기

reply라고 하는 댓글을 저장하는 테이블을 만들어야 한다.

board 테이블과 reply 테이블은 식별관계.

글이 있어야 댓글을 쓸 수 있기 때문이다.

테이블 설계와 임의의 데이터(댓글) 하나 넣어보기

 

 

오라클 DB

create sequence seq_reply_no
start with 1;

drop table reply;

create table reply(
replyno number primary key,
replycomment varchar2(1000),
id varchar2(20),
regidate date default sysdate,
num number REFERENCES board(num)
);

-- 댓글 데이터 넣기
insert into reply
values (seq_reply_no.nextval, '페이징 처리-100 댓글1', 'musthave', sysdate, 109);

commit;

select * from reply;

-- 댓글 보기 
select replyno, replycomment, r.id, r.regidate, r.num
from reply r inner join board b
on r.num = b.num
where r.num = 109;

 

ReplyDAO.java

package model1.replay;

import java.util.ArrayList;
import java.util.List;

import common.JDBConnect;
import jakarta.servlet.ServletContext;

public class ReplyDAO extends JDBConnect {
	
	public ReplyDAO (ServletContext application) {
		super(application);
	}
	
	public List<ReplyDTO> selectReply(String num){
		List<ReplyDTO> replybbs = new ArrayList();
		
		String query  = " select replyno, replycomment, r.id, r.regidate, r.num "
				+ " from reply r inner join board b "
				+ " on r.num = b.num "
				+ " where r.num = ? "
				+ " order by replyno asc ";
		
		try {
			psmt = con.prepareStatement(query);
			psmt.setString(1, num);
			
			rs = psmt.executeQuery();
			
			while(rs.next()) {
	            
	            ReplyDTO dto = new ReplyDTO();
	            
	            dto.setReplyno(rs.getString(1));
	            dto.setReplycomment(rs.getString(2));
	            dto.setId(rs.getString(3));
	            dto.setRegidate(rs.getString(4));
	            dto.setNum(rs.getString(5));
	            
	            replybbs.add(dto);
	         }
	         
	         
	      }catch (Exception e) {
	         e.printStackTrace();
	      }
	      
	      return replybbs;
	      
	   }
	
	public int insertReply(ReplyDTO dto) {
		
		int result = 0;
		
		String query  = " insert into reply "
				+ " (replyno, replycomment, id, num) "
				+ " values (seq_board_num.nextval,?, ?, ?)";
		
		try {
			psmt = con.prepareStatement(query);
			psmt.setString(1, dto.getReplycomment());
			psmt.setString(2, dto.getId());
			psmt.setString(3, dto.getNum());

			result = psmt.executeUpdate();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return result;
	}
}

 

ReplyDTO.java

package model1.replay;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ReplyDTO {
	// 컬럼명
private String replyno;
private String replycomment;
private String id;
private String regidate;
private String num;

}

 

View.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ include file="./IsLoggedIn.jsp"%>
<%@page import="model1.board.BoardDTO"%>
<%@page import="model1.board.BoardDAO"%>
<%@page import="model1.replay.ReplyDAO"%>
<%@page import="model1.replay.ReplyDTO"%>
<%@page import="java.util.List"%>

<%
String num = request.getParameter("num");
BoardDAO dao = new BoardDAO(application);
ReplyDAO replyDAO = new ReplyDAO(application);

// 글에 대한 상세보기 -> 조회수 증가 처리
dao.updateVisitCount(num);

// 상세정보 보기 (원글 정보)
BoardDTO dto = dao.selectView(num);

List<ReplyDTO> replyLists = replyDAO.selectReply(num);
%>


<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

<script>
	function deletePost() {
		let confirmed = confirm("정말로 삭제하시겠습니까?");

		if (confirmed) {
			let form = document.writeFrm; // 이름이 writeFrm인 폼 선택
			// 입력 폼에 값이 없지만 post 방식으로 넘긴다
			form.method = "post"; // 전송 방식
			form.action = "DeleteProcess.jsp"; // 전송 경로
			// 객체 = 기능 + 속성
			form.submit(); // 폼값전송

		}
	}
	
	function vaildateForm(form){
		if (form.replycontent.value == ""){
			alert("댓글 내용을 입력하세요.");
			form.replycontent.focus();
			return false;
		}
	}
	
</script>
</head>

<body>
	<%@ include file="./IsLoggedIn.jsp"%>
	<h2>회원제 게시판 - 상세보기(View)</h2>
	<form name="writeFrm">
		<input type="hidden" name="num" value="<%=num%>">

		<table border="1" width="90%">

			<tr>
				<td>번호</td>
				<td><%=dto.getNum()%></td>
				<td>작성자</td>
				<td><%=dto.getName()%></td>
			</tr>

			<tr>
				<td>작성일</td>
				<td><%=dto.getPostDate()%></td>
				<td>조회수</td>
				<td><%=dto.getVisitcount()%></td>
			</tr>

			<tr>
				<td>제목</td>
				<td colspan="3"><%=dto.getTitle()%></td>
			</tr>

			<tr>
				<td>내용</td>
				<!-- 줄바꿈이 안되어 저장되기 때문에 강제 줄바꿈을 해줘야 한다 -->
				<td colspan="3" height="100px"><%=dto.getContent().replace("\r\n", "<br/>")%></td>
			</tr>

			<tr>
				<td colspan="4" align="center">
					<!-- 글을 작성한 사람이 아니라면 수정하기, 삭제하기 버튼 안보이게 --> <%
					if (session.getAttribute("UserId") != null 
					&& session.getAttribute("UserId").toString().equals(dto.getId())) {
 %>
					<button type="button"
						onclick="location.href = 'Edit.jsp?num=<%=dto.getNum()%>'">수정하기</button>
					<button type="button" onclick="deletePost()">삭제하기</button> <%
 }
 %>
					<button type="button" onclick="location.href = 'List.jsp'">목록보기</button>
				</td>
			</tr>

		</table>


	</form>

	<p>댓글</p>

	<table border="1" width="90%">
		<%if (replyLists.isEmpty()) {%>
		<tr>
			<td colspan="5" align="center">등록된 댓글이 없습니다^^*</td>
		</tr>
		<%}else{ %>
		<tr>
			<th width="10%">번호</th>
			<th width="50%">댓글내용</th>
			<th width="15%">작성자</th>
			<th width="20%">날짜</th>
			<th width="5%">원글번호</th>
		</tr>

		<%
			for (ReplyDTO rDto : replyLists){
				 %>
		<tr align="center">
			<td><%= rDto.getReplyno() %></td>
			<td><%= rDto.getReplycomment() %></td>
			<td><%= rDto.getId() %></td>
			<td><%= rDto.getRegidate() %></td>
			<td><%= rDto.getNum() %></td>
		</tr>
		<%
				 }
				 }
				 %>
	</table>

	<h3>회원제 게시판 - 댓글(Reply)</h3>
	<form name="WriteFrm" method="post" action="ReplyProcess.jsp"
		onsubmit="return validateForm(this)">

		<input type="hidden" name="num" value="<%=dto.getNum() %>">

		<table border="1" width="90%">
			<tr>
				<td>댓글 내용</td>
				<td><textarea name="replycontent" style="width: 90%;"></textarea>
				</td>
			</tr>
			<tr>
				<td colspan="2" align="center">
					<button type="submit">작성완료</button>
					<button type="reset">다시입력</button>
				</td>
			</tr>
		</table>
	</form>
</body>

</html>

 

파라메터를 잘 못읽어서 null 을 받아올 때도 있었지만 수정 끝에 댓글 처리가 잘 되는 것을 확인 할 수 있었다. 

 

 

+ 주말동안은 댓글 수정, 삭제 버튼을 만들고 기능을 추가해보았다.

 

+ Recent posts