Jsp

[jsp] 회원 목록과 게시판 페이징 처리 + 회원 검색 기능

쟈근꿈틀이 2022. 5. 30. 10:59
728x90

[Ver.3]

페이징 처리를 위한 PageVO클래스 사용

 

pageVO.class
public class PageVO {
	private Integer pageNum; 
	private Integer step;
	private Integer start;
	private Integer end;
	private Integer totalCnt;
	private Integer totalPage;
	
	public PageVO() {
		step = 3;
	}
	
	public PageVO(int step) {
		this.step = step;
	}

	public Integer getPageNum() {
		return pageNum;
	}

	public void setPageNum(Integer pageNum) {
		this.pageNum = pageNum;
		setStart();
		setEnd();
	}

	public Integer getStep() {
		return step;
	}

	public Integer getStart() {
		return start;
	}

	private void setStart() {
		this.start = (pageNum - 1) * step;
	}

	public Integer getEnd() {
		return end;
	}

	private void setEnd() {
		this.end = start + step;
	}

	public Integer getTotalCnt() {
		return totalCnt;
	}

	public void setTotalCnt(Integer totalCnt) {
		this.totalCnt = totalCnt;
		setTotalPage();
	}

	public Integer getTotalPage() {
		return totalPage;
	}

	private void setTotalPage() {
		this.totalPage = totalCnt / step;
		if(totalCnt % step != 0)
			totalPage++;
	}
	
}

페이지별 start, end 페이지를 설정한다.

페이지 당 보여줄 회원 정보의 수는 기본 생성자를 호출하면 3개, 생성자에 매개변수가 있다면 그 숫자로 설정한다.

 

총 페이지 수는 회원의 수에서 페이지 당 보여줄 회원 정보의 수를 나눈 값인데, 나머지가 0이 아니면

총 페이지 수에 1을 더한다.

 

selectAll(): 전체 회원 정보 반환
public ArrayList<MemberDTO> selectAll() {
		String sql = "SELECT * FROM session_concept";
		ArrayList<MemberDTO> list = new ArrayList<>();

		try {
			ps = conn.prepareStatement(sql);
			rs = ps.executeQuery();

			while (rs.next()) {
				list.add(new MemberDTO(rs.getString("id"), rs.getString("pw"), rs.getString("name"),
						rs.getString("email")));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (rs != null)
					rs.close();
				if (ps != null)
					ps.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return list;
}

 

selectBySearch: 회원 검색 + 회원 정보 반환 기능
public ArrayList<MemberDTO> selectBySearch(String search, String value) {
		ArrayList<MemberDTO> list = new ArrayList<>();
		String sql = "";
		if(search.equals("id")){
			sql = "SELECT * FROM session_concept WHERE id like ?";
		}else{
			sql = "SELECT * FROM session_concept WHERE email like ?";
		}
		
		try {
			ps = conn.prepareStatement(sql);
			ps.setString(1, "%"+value+"%");
			rs = ps.executeQuery();
			
			while(rs.next()) {
				list.add(new MemberDTO(rs.getString("id"), rs.getString("pw"), rs.getString("name"), rs.getString("email")));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return list;
}

dao의 selectAll() 또는 selectBySearch()를 호출해 회원 정보 목록을 전달 받는다.

페이징 처리는 list.jsp에서 처리한다.

 

list.jsp
<html>
<head>
<meta charset="UTF-8">
<title>list</title>
</head>
<body>
	<%
		String p = request.getParameter("pageNum");
		int pageNum = 1;
		if(p != null || p != ""){
			try{
				pageNum = Integer.parseInt(p);
			}catch(Exception e){
				
			}
		}
		if(pageNum < 0)
			pageNum = 1;
		
		MemberDAO dao = new MemberDAO();
		ArrayList<MemberDTO> members = dao.selectAll();
		
		String mode = request.getParameter("mode");
		String search = request.getParameter("search");
		String value = request.getParameter("value");
		
		if(mode != null && mode.equals("search")){
			if(search.equals("id") || search.equals("email")){
				if(value == null || value == "")
					out.print("<script>alert('검색어를 입력해주세요')</script>");
				else
					members = dao.selectBySearch(search, value);
			}else
				members = dao.selectAll();
		}
		
		PageVO pageVo = new PageVO(4);
		pageVo.setPageNum(pageNum);
		pageVo.setTotalCnt(members.size());
	%>
	<table border="1">
		<tr>
			<th>아이디 </th>
			<th>비밀번호 </th>
			<th>이름 </th>
			<th>이메일 </th>
		</tr>
		<% 
			for(int i = pageVo.getStart(); i < pageVo.getEnd() && i < pageVo.getTotalCnt(); i++){ 
				MemberDTO member = members.get(i);
		%>
			<tr>
				<td><%=member.getId() %></td>
				<td><%=member.getPw() %></td>
				<td><%=member.getName() %></td>
				<td><%=member.getEmail() %></td>
			</tr>
		<%} %>
	</table>
	<div>
		<%if(pageNum > 1){ %>
			<a href="list.jsp?pageNum=<%=pageNum-1 %>&mode=search&search=<%=search%>&value=<%=value %>">[이전]</a>
		<%} %>
		<%for(int i = 1; i <= pageVo.getTotalPage(); i++){ %>
			<%if(pageNum == i) {%>
				<a href="list.jsp?pageNum=<%=i %>&mode=search&search=<%=search%>&value=<%=value %>"><b><%=i %></b></a>
			<%} else { %>
				<a href="list.jsp?pageNum=<%=i %>&mode=search&search=<%=search%>&value=<%=value %>"><%=i %></a>
			<%} %>
		<%} %>
		<%if(pageNum < pageVo.getTotalPage()){ %>
		<a href="list.jsp?pageNum=<%=pageNum+1 %>&mode=search&search=<%=search%>&value=<%=value %>">[다음]</a><br>
		<%} %>
	</div>
	<div>
		<form action="list.jsp?mode=search" method="post">
			<select name="search">
				<option value="all">전체</option>
				<option value="id">아이디</option>
				<option value="email">이메일</option>
			</select>
			<input type="text" name="value">
			<input type="submit" value="검색">
		</form>
	</div>
	<br>
	<div>
		<a href="index.jsp" style="text-decoration: none">인덱스 페이지로 이동</a>
	</div>
</body>
</html>

클라이언트의 요청 url의 파라미터인 pageNum을 통해 보여줄 페이지를 설정한다.

이때, pageNum이 null 또는 문자열과 숫자의 조합 또는 음수이면 pageNum = 1로 설정하였다.

 

members에는 dao의 selectAll()이 반환한 값을 저장하였는데, 만약 사용자가 검색을 통해 특정 회원 목록을 보기 원하고

검색어를 입력했다면 members에 dao의 selectBySearch(검색 기준, 검색어)를 호출한다.

pageNum, members의 크기를 pageVo객체 각 setter()에 넘겨준다.

 

전달 받은 회원의 정보를 페이징 처리한 후 화면에 출력하기 위해 for문을 사용한다.

pageVo의 start는 각 페이지별 보여줄 첫 번째 회원의 인덱스 값, end는 마지막 회원의 인덱스 값이다.

 

for문의 조건을 "i가 pageVo.getEnd()보다 작다"로 작성하면 문제가 발생한다.

회원이 11명이라고 가정하자!

1페이지에서 0 ~ 3번 째 회원의 정보를, 2페이지에서 4 ~ 7번째 회원의 정보를 잘 출력한다.

마지막 페이지인 3페이지에서 8 ~ 12번 째 회원의 정보에 접근하려고 시도한다.

이때, 12번 째 회원은 없으므로 MemberDTO member = members.get(i)에서 NullPointerException이 발생한다.

 

따라서 for문의 조건을 "i가 pageVo.getEnd()보다 작고 i가 pageVo.getTotalCnt()보다 작다"로 작성해야 위와 같은 오류를 발생시키지 않는다.

 

*코드 첨부

 

PageVO.java
0.00MB
MemberDAO.java
0.01MB
list.jsp
0.00MB

 


[Ver.2]

 

7명의 회원 정보를 한 페이지 당 3개씩 3페이지에 나눠서 보여주고 있다.

 

selectAll: 회원 목록 페이징 기능 
public ArrayList<MemberDTO> selectAll(int pageNum, int step) {
		String sql = "SELECT * FROM (SELECT ROWNUM as RN, id, pw, name, email FROM session_concept)A WHERE A.RN between ? and ?";
		ArrayList<MemberDTO> list = new ArrayList<>();

		try {
			ps = conn.prepareStatement(sql);
			ps.setInt(1, ((pageNum - 1) * step) + 1);
			ps.setInt(2, pageNum * step);
			rs = ps.executeQuery();

			while (rs.next()) {
				list.add(new MemberDTO(rs.getString("id"), rs.getString("pw"), rs.getString("name"),
						rs.getString("email")));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (rs != null)
					rs.close();
				if (ps != null)
					ps.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return list;
}

 

selectBySearch: 회원 검색 + 회원 목록 페이징 기능 
public ArrayList<MemberDTO> selectBySearch(String search, String value, int pageNum, int step) {
		String sql = "";
		if(search.equals("id")) {
			sql = "SELECT * FROM (SELECT ROWNUM as RN, id, pw, name, email FROM session_concept WHERE id like ?)A WHERE A.RN between ? and ?";
		}else if(search.equals("email")) {
			sql = "SELECT * FROM (SELECT ROWNUM as RN, id, pw, name, email FROM session_concept WHERE email like ?)A WHERE A.RN between ? and ?";
		}
		ArrayList<MemberDTO> list = new ArrayList<>();

		try {
			ps = conn.prepareStatement(sql);
			ps.setString(1, "%"+value+"%");
			ps.setInt(2, ((pageNum - 1) * step) + 1);
			ps.setInt(3, pageNum * step);
			rs = ps.executeQuery();

			while (rs.next()) {
				list.add(new MemberDTO(rs.getString("id"), rs.getString("pw"), rs.getString("name"),
						rs.getString("email")));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (rs != null)
					rs.close();
				if (ps != null)
					ps.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return list;
}

클라이언트는 모든 회원 정보들을 볼 수도 있고, 검색한 기준에 부합하는 회원 정보만을 볼 수도 있다.

 

		MemberDAO memberDao = new MemberDAO();
		ArrayList<MemberDTO> members = null;
	
		int step = 3;
		int cnt = memberDao.getCnt();
		int totalPage = cnt % step == 0 ? cnt / step : (cnt / step + 1);
		
		String p = request.getParameter("pageNum");
		int pageNum = 1;
		if(p != null && p != ""){
			try{
				pageNum = Integer.parseInt(p);
			}catch(Exception e){
				//pageNum의 값에 문자가 포함되어 있으면 1페이지를 보여줌 
				pageNum = 1;
			}
			//없는 페이지를 요청하면 1페이지를 보여줌 
			if(pageNum < 1 || pageNum > totalPage)
				pageNum = 1;
		}
		members = memberDao.selectAll(pageNum, step);
		
		String mode = request.getParameter("mode");
		String search = "", value = "";
		if(mode != null && mode.equals("search")){
			search = request.getParameter("search");
			value = request.getParameter("value");
			if(search.equals("id") || search.equals("email")){
				if(value == null || value == ""){
					out.print("<script>alert('검색어를 입력하세요')</script>");
				}else{
					cnt = memberDao.getSearchCnt(search, value);
					totalPage = cnt % step == 0 ? cnt / step : (cnt / step + 1);
					members = memberDao.selectBySearch(search, value, pageNum, step);
				}
			}
		}

없는 페이지 요청, 하단의 페이지를 클릭하여 접근하지 않았을 때, 검색 기능을 사용하지 않았을 경우 dao의 selectAll()을 통해 모든 게시글을 ArrayList<>()로 전달 받아 출력한다.

이때 하단에 해당 페이지로 이동하게 하는 기능을 구현하기 위해서 dao의 getCnt()로 총 회원의 수를 구한다.

 

만약 사용자가 검색을 통해 특정 회원 정보만을 보기 원한다면 검색어(value)가 빈 값이 아닌지를 확인하고, 검색어가 존재한다면 dao의 selectBySearch()로 특정 회원의 정보를 받아온다.

dao의 getSearchCnt()로 검색 기준에 부합하는 회원의 수를 전달 받아서 바뀐 회원의 수에 맞게 totalPage를 재설정한다. 

 

[list.jsp 하단 코드] 원하는 페이지로 이동할 수 있는 기능
<%if(pageNum > 1){ %>
	<a href="list.jsp?pageNum=<%=pageNum-1 %>&mode=search&search=<%=search%>&value=<%=value %>">[이전]</a>
<%} %>
<%for(int i = 1; i <= totalPage; i++){ %>
<%if(pageNum == i) {%>
	<a href="list.jsp?pageNum=<%=i %>&mode=search&search=<%=search%>&value=<%=value %>"><b><%=i %></b></a>
<%} else { %>
	<a href="list.jsp?pageNum=<%=i %>&mode=search&search=<%=search%>&value=<%=value %>"><%=i %></a>
<%} %>
<%} %>
<%if(pageNum < totalPage){ %>
	<a href="list.jsp?pageNum=<%=pageNum+1 %>&mode=search&search=<%=search%>&value=<%=value %>">[다음]</a><br>
<%} %>

각 페이지로 이동할 시 url에 검색 조건을 넘겨주어 페이지가 이동하여도 검색 조건을 유지할 수 있도록 하였다.

 


Test

 

아이디에 a가 포함된 회원은 총 4명이다.

 

검색 기준을 아이디로 선택하고 검색어에 'a'를 입력한 후 검색 버튼을 누른다.

 

조건을 만족하는 회원이 4명이므로 2페이지가 나오고, 해당 페이지가 1페이지이므로 이전 페이지가 존재하지 않기 때문에 [이전]이 출력되지 않음을 확인할 수 있다. 

 

2페이지로 이동해도 아이디에 a가 포함된 회원 정보가 잘 출력되는 것을 볼 수 있다.

해당 페이지가 마지막 페이지이므로 다음 페이지가 존재하지 않기 때문에 [다음]이 출력되지 않았다.

 

이메일을 기준으로 검색하는 기능도 잘 구현되었다.

 

*코드 첨부

 

MemberDAO.java
0.01MB
list.jsp
0.00MB


[Ver.1]

페이징 처리 전

DB에 저장된 게시글의 수가 많을 때 한 페이지에 모든 게시글을 보여준다면 가독성이 떨어질 것이다.

페이지 당 부분적으로 게시글을 보여준다면 화면이 세로로 길어지지 않아 가독성이 높아질 것이다.

이렇게 한 페이지 당 정해진 개수의 게시글을 보여주는 것페이징 처리라고 한다.

 

페이징 처리 후

 

특정한 페이지에 보여줄 게시글(CenterDTO)의 정보를 가져오는 함수 

 

public ArrayList<CenterDTO> selectPage(int page) {
		String query = "SELECT * FROM care_center WHERE num > ? and num <= ? ORDER BY num DESC";
		ArrayList<CenterDTO> list = new ArrayList<>();
		
		try {
			ps = conn.prepareStatement(query);
			ps.setInt(1, (page - 1) * 3);
			ps.setInt(2, page * 3);
			rs = ps.executeQuery();
			
			while (rs.next()) {
				list.add(new CenterDTO(rs.getInt("num"), rs.getString("subject"), rs.getString("content"),
						rs.getString("id"), rs.getInt("hit"), rs.getString("write_time"), rs.getString("file_name")));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (rs != null)
					rs.close();
				if (ps != null)
					ps.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return list;
	}

 

 

selectPage는 페이지의 번호를 매개변수로 전달받는다.

한 페이지당 3개의 게시글을 보여주고자 한다.

 

예를 들어, page가 1이면 게시글의 num이 1~3인 게시물을, page가 2이면 num이 4~6인 게시글을 가져와야 한다.

따라서 쿼리문의 where절의 조건인 num > ? and num <= ?에 각각 (page - 1) * 보여줄 게시물의 수, page * 보여줄 게시글의 수를 전달하였다.

 


list.jsp
<%@page import="member.CenterDTO"%>
<%@page import="java.util.ArrayList"%>
<%@page import="member.CenterDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%@ include file = "../header.jsp" %>
<% 
	CenterDAO dao = new CenterDAO();
	ArrayList<CenterDTO> list = dao.selectAll();	
	
	int currentPage = 0, totalPage = list.size() / 3;
	if(totalPage % 3 != 0) 
		totalPage += 1; 
	if((request.getParameter("page") != null)){
		currentPage = Integer.parseInt(request.getParameter("page"));
		if(currentPage >= 1 && currentPage <= totalPage)
			list = dao.selectPage(currentPage);
		else
			response.sendRedirect("list.jsp");
	}
%>
<div id="img_cen"></div>

<nav id="nav_sub">
	<ul>
		<li> <a href="list.jsp"> 게시글 목록 </a></li>
		<li> <a href="write.jsp"> 게시글 작성 </a></li>
		<li> <a href="delete.jsp"> 게시글 삭제 </a></li>
		<li> <a href="modify.jsp"> 게시글 수정 </a></li>
	</ul>
</nav>

<article id="article_sub">
	<h1>게시글 목록</h1>
	<table>
		<tr>
			<th>번호</th>
			<th class="title">제목</th>
			<th>작성자</th>
			<th>날짜</th>
			<th>조회수</th>
		</tr>
		<%if(!list.isEmpty()){
			for(CenterDTO dto : list){ %>
				<tr>
					<td><%=dto.getNum() %></td>
					<td class="subject"><a href="view.jsp?num=<%=dto.getNum() %>"><%=dto.getSubject() %></a></td>
					<td><%=dto.getId() %></td>
					<td><%=dto.getWrite_time() %></td>
					<td><%=dto.getHit() %></td>
				</tr>
		<%}
		}else{%>
			<tr>
				<td colspan="5">게시물이 없습니다</td>
			</tr>
		<%} %>
	</table>
	<form id="list_form">
		<div>
			<span>
				<select class="search">
					<option value="title" selected="selected">제목</option>
					<option value="id">아이디</option>
				</select>
				<input type="text" id="value">
				<input type="submit" value="검색">
			</span>
		</div>
	</form>
	<div class="clear">
		<div id="page_control">
			<span><a href="list.jsp?page=<%=currentPage-1 %>">Prev</a></span>
		<%
			for(int i = 1; i <= totalPage; i++){ 
		%>
			<span><a href="list.jsp?page=<%=i %>"><%=i %></a></span>
		<%} %>
			<span><a href="list.jsp?page=<%=currentPage+1 %>">Next</a></span>
		</div>
		<div id="center_button">
			<a href="write.jsp"><img src="../images/write.png"/></a>
		</div>
	</div>
</article>
<%@ include file="../footer.jsp"%>

 

클라이언트가 list.jsp를 요청했을 때, 쿼리 스트링의 값에 page변수가 존재하지 않는다면 모든 게시글을 하나의 페이지에 보여준다.

 

 

클라이언트의 요청의 매개변수인 page가 존재한다면 문자형태인 page를 int로 변환한다.

변환한 page변수의 값이 음수거나 게시판의 마지막 페이지보다 크다면 list.jsp를 웹 서버에 재요청한다. 

그렇지 않다면 전달 받은 page에 맞는 게시글들을 화면에 보여준다.

 

이때 totalPage는 DB에 저장된 총 게시글의 수를 한 페이지 당 보여줄 게시글의 수로 나눈 몫이다.

예를 들어, 총 게시글의 수가 10개일 때는 totalPage가 3이고 나머지가 1이 된다.

이 경우 세 번째 페이지까지 1~9번째 게시글들을 출력하고, 마지막 10번 째 게시글은 출력되지 않는다.

 

따라서 totalPage % 3이 0이 아닐 때는 즉, 나머지가 존재하면 totalPage에 1을 더한 값을 게시판의 총 페이지의 수로 정한다.

 

 

또, 하단에는 특정 페이지에 접근할 수 있는 a태그를 만들었다.

총 게시판의 페이지 수만큼의 a태그를 만들고, 이전 페이지와 다음 페이지로 넘어가는 a태그도 구현하였다.

 

이때 currentPage-1 또는 currentPage+1이 음수이거나 게시판의 마지막 페이지를 넘는다면 list.jsp로 새롭게 요청을 보내 모든 게시글을 보여주도록 하였다.

728x90
댓글수0