2021년 01월 05일
업데이트:
프로젝트 구성
로그인 필터
마이페이지나 공지사항 작성 페이지는 일반 회원으로 로그인하거나, 관리자 계정으로 로그인 해야지만 접근할 수가 있다. 필터 처리를 따로 해주지 않을 시 주소창에 마이페이지 주소를 치면 로그인을 하지 않아도 접근할 수 있다.
- 미 로그인 시 마이페이지 접근
로그인을 해야만 접근할 수 있는 페이지들을 필터 처리해준다.
LoginFilter.java
@WebFilter(urlPatterns = {"/member/myPage.do", "/member/changePwd.do", "/member.secession.do",
"/member/updateMember.do", "/member/updatePwd.do", "/member/updateStatus.do"})
// urlPatterns : 주소를 여러개 쓰고 싶을 때
public class LoginFilter implements Filter {
public LoginFilter() {}
public void init(FilterConfig fConfig) throws ServletException {}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// ServletRequest 매개 변수를 HttpServletRequest로 다운 캐스팅
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse res = (HttpServletResponse)response;
// Session 얻어오기
HttpSession session = req.getSession();
Member loginMember = (Member)session.getAttribute("loginMember");
if(loginMember == null) {// 로그인이 되어 있지 않은 경우
// 메인 페이지로 강제 이동(redirect)
res.sendRedirect(req.getContextPath());
} else {
chain.doFilter(request, response);
// 이어져 있는 필더(다음 필터) 호출
// 다음 필터가 없으면 Servlet 또는 JSP로 이동한다.
}
}
}
- 필터 적용 시 부적절한 접근이면 메인 페이지로 돌아오게 된다.
게시판 연결
- 헤더
Board
클릭 시 홈페이지 이동
header.jsp
Board 클릭 시 Board 페이지로 넘어가도록 href로 경로를 잡아준다.
<li class="nav-item"><a class="nav-link" href="${contextPath}/board/list.do">Board</a></li>
Board 테이블 생성
View 만들기
DROP VIEW V_BOARD;
CREATE OR REPLACE VIEW V_BOARD
AS
SELECT BOARD_NO, BOARD_TITLE, BOARD_CONTENT, MEMBER_ID,
READ_COUNT, BOARD_CREATE_DT, BOARD_MODIFY_DT, CATEGORY_NM, BOARD_STATUS
FROM BOARD
JOIN MEMBER ON(BOARD_WRITER = MEMBER_NO)
JOIN CATEGORY USING(CATEGORY_CD);
BoardController.java
- 아래 코드 작성 시 헤더
board
를 클릭하면 흰 화면으로 이동한다.
@WebServlet("/board/*")
public class BoardController extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String uri = request.getRequestURI(); // 요청이 들어오는 주소 /wsp/board/list.do
String contextPath = request.getContextPath(); // /wsp
String command = uri.substring( (contextPath + "/board").length()); // /wsp/board의 길이만큼 잘라내기
String path = null;
RequestDispatcher view = null;
String swalIcon = null;
String swalTitle = null;
String swalText = null;
String errorMsg = null;
try {
BoardService service = new BoardService();
// 현재 페이지를 얻어옴
String cp = request.getParameter("cp");
// 게시글 목록 조회 controller -------------------------------------------
if(command.equals("/list.do")) {
errorMsg = "게시판 목록 조회 과정에서 오류 발생";
}
}catch (Exception e) {
e.printStackTrace();
path = "/WEB-INF/views/common/errorPage.jsp";
request.setAttribute("errorMsg", errorMsg);
view = request.getRequestDispatcher(path);
view.forward(request, response);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
페이징 처리하기
PageInfo.java
public class PageInfo { // 페이징 처리를 위한 값을 저장할 객체
// 얻어올 값
private int currentPage; // 현재 페이지 번호를 저장할 변수
private int listCount; // 전체 게시글 수를 저장할 변수
// 설정할 값
private int limit = 10; // 한 페이지에 보여질 게시글 목록 수
private int pageSize = 10; // 페이징바에 표시될 페이지 수
// 계산할 값
private int maxPage; // 전체 목록 페이지의 수 == 마지막 페이지
private int startPage; // 페이징바 시작 페이지 번호
private int endPage; // 페이징바 끝 페이지 번호
public PageInfo(int currentPage, int listCount) {
super();
this.currentPage = currentPage;
this.listCount = listCount;
// 전달받은 값과 명시적으로 선언된 값을 이용하여
// makePageInfo() 수행
makePageInfo();
}
public PageInfo(int currentPage, int listCount, int limit, int pageSize) {
super();
this.currentPage = currentPage;
this.listCount = listCount;
this.limit = limit;
this.pageSize = pageSize;
makePageInfo();
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getListCount() {
return listCount;
}
public void setListCount(int listCount) {
this.listCount = listCount;
makePageInfo();
}
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
this.limit = limit;
makePageInfo();
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
makePageInfo();
}
public int getMaxPage() {
return maxPage;
}
public void setMaxPage(int maxPage) {
this.maxPage = maxPage;
}
public int getStartPage() {
return startPage;
}
public void setStartPage(int startPage) {
this.startPage = startPage;
}
public int getEndPage() {
return endPage;
}
public void setEndPage(int endPage) {
this.endPage = endPage;
}
@Override
public String toString() {
return "PageInfo [currentPage=" + currentPage + ", listCount=" + listCount + ", limit=" + limit + ", pageSize="
+ pageSize + ", maxPage=" + maxPage + ", startPage=" + startPage + ", endPage=" + endPage + "]";
}
// 페이징 처리에 필요한 값을 계산하는 메소드
private void makePageInfo() {
// maxPage : 총 페이지 수 == 마지막 페이지
// 총 게시글 수 100개, 한 페이지에 보여지는 게시글 수 10개
// -> 총 페이지 수는 ? 101 / 10 = 10.1(올림처리) -> 11page
maxPage = (int)Math.ceil((double)listCount / limit);
// startPage : 페이징바 시작 번호
// 페이징바에 페이지를 10개씩 보여줄 경우
// 1, 11, 21, 31, . . . .
// 현재 페이지 11p -> 시작 페이지 11
// 현재 페이지 15p -> 시작 페이지 11
// 현재 페이지 20p -> 시작 페이지 11
startPage = (currentPage -1) / pageSize * pageSize +1;
// (11 - 1) / 10 * 1- + 1 = 11
// (15 - 1) / 10 * 1- + 1 = 11
// (20 - 1) / 10 * 1- + 1 = 11
// endPage : 페이징바의 끝 번호
// 페이징바에 페이지를 10개씩 보여줄 경우
// 10, 20, 30, 40 . . . . .
// 현재 페이지 11p -> 끝 페이지 20
// 현재 페이지 15p -> 끝 페이지 20
// 현재 페이지 20p -> 끝 페이지 20
endPage = startPage + pageSize - 1;
// 총 페이지의 수가 end 페이지보다 작을 경우
if(maxPage <= endPage) {
endPage = maxPage;
}
}
}
게시글 전체 조회
- 헤더
board
버튼 클릭 시 게시글 페이지로 이동
BoardController.java
// 현재 페이지를 얻어옴
String cp = request.getParameter("cp");
// 게시글 목록 조회 controller -------------------------------------------
if(command.equals("/list.do")) {
errorMsg = "게시판 목록 조회 과정에서 오류 발생";
// 1. 페이징 처리를 위한 값 계산 Service 호출
PageInfo pInfo = service.getPageInfo(cp);
// System.out.println(pInfo);
// 2. 게시글 목록 조회 비즈니스 로직 수행
List<Board> bList = service.selectBoardList(pInfo);
// pInfo에 있는 currentPage, limit를 사용해야지만
// 현재 페이지에 맞는 게시글 목록만 조회할 수 있음
for(Board b : bList) {
System.out.println(b);
}
path = "/WEB-INF/views/board/boardList.jsp";
request.setAttribute("bList", bList);
request.setAttribute("pInfo", pInfo);
view = request.getRequestDispatcher(path);
view.forward(request, response);
}
페이징 처리(BoardService.java)
/** 페이징 처리를 위한 값 계산 Service
* @param cp
* @return PageInfo(currentPage, listCount)
* @throws Exception
*/
public PageInfo getPageInfo(String cp) throws Exception {
Connection conn = getConnection();
// cp가 null일 경우
// 주소창에 보여지는 ?cp=n 부분을 정함
int currentPage = cp == null ? 1 : Integer.parseInt(cp);
// DB에서 전체 게시글 수를 조회하여 반환 받기
int listCount = dao.getListCount(conn);
close(conn);
// 얻어온 현재 페이지와, DB에서 조회한 전체 게시글 수를 이용하여
// PageInfo 객체 생성
return new PageInfo(currentPage, listCount);
}
BoardService.java
/** 게시글 목록 조회
* @param pInfo
* @return bList
* @throws Exception
*/
public List<Board> selectBoardList(PageInfo pInfo) throws Exception {
Connection conn = getConnection();
List<Board> bList = dao.selectBoardList(conn, pInfo);
close(conn);
return bList;
}
board-query.xml
<!-- 전체 게시글 수 조회 -->
<entry key="getListCount">
SELECT COUNT(*) FROM V_BOARD
WHERE BOARD_STATUS = 'Y'
</entry>
<!-- 지정된 페이지 게시글 목록 조회 -->
<!--
V_BOARD에서 최신글 중 11번째 부터 20번째 글을 모두 조회
인라인 뷰 : FROM 절에 작성된 서브쿼리
ROWNUM에 별칭을 주면 가상 컬럼이 아니라 실제 컬럼이 된다.
-->
<entry key="selectBoardList">
SELECT * FROM
(SELECT ROWNUM RNUM, V.*
FROM
(SELECT * FROM V_BOARD WHERE BOARD_STATUS='Y' ORDER BY BOARD_NO DESC) V )
WHERE RNUM BETWEEN ? AND ?
<!-- V.* : V 로 지정된 SELECT 구문 모든 컬럼을 조회 -->
</entry>
페이징 처리 (BoardDAO.java)
/** 전체 게시글 수 반환 DAO
* @param conn
* @return listCount
* @throws Exception
*/
public int getListCount(Connection conn) throws Exception {
int listCount = 0;
String query = prop.getProperty("getListCount");
try {
stmt = conn.createStatement();
rset = stmt.executeQuery(query);
if(rset.next()) {
listCount = rset.getInt(1);
}
} finally {
close(rset);
close(stmt);
}
return listCount;
}
BoardDAO.java
/** 게시글 목록 조회 DAO
* @param conn
* @param pInfo
* @return bList
* @throws Exception
*/
public List<Board> selectBoardList(Connection conn, PageInfo pInfo) throws Exception{
List<Board> bList = null;
String query = prop.getProperty("selectBoardList");
try {
// SQL 구문 조건절에 대입할 변수 생성
int startRow = (pInfo.getCurrentPage() - 1) * pInfo.getLimit() + 1;
int endRow = startRow + pInfo.getLimit() - 1;
pstmt = conn.prepareStatement(query);
pstmt.setInt(1, startRow);
pstmt.setInt(2, endRow);
rset = pstmt.executeQuery();
bList = new ArrayList<Board>();
while(rset.next()) {
Board board = new Board(rset.getInt("BOARD_NO"),
rset.getString("BOARD_TITLE"),
rset.getString("MEMBER_ID"),
rset.getInt("READ_COUNT"),
rset.getString("CATEGORY_NM"),
rset.getTimestamp("BOARD_CREATE_DT"));
bList.add(board);
}
} finally {
close(rset);
close(pstmt);
}
return bList;
}
게시글이 없을 때, 시간 처리 (boardList.jsp)
<%-- 게시글 목록 출력 --%>
<tbody>
<c:choose>
<c:when test="${empty bList}">
<tr>
<td colspan="6">존재하는 게시글이 없습니다.</td>
</tr>
</c:when>
<c:otherwise> <%-- 조회된 게시글 목록이 있을 때 --%>
<c:forEach var="board" items="${bList}">
<tr>
<td>${board.boardNo}</td>
<td>${board.categoryName}</td>
<td class="boardTitle">
${board.boardTitle}
</td>
<td>${board.memberId}</td>
<td>${board.readCount}</td>
<td>
<%-- 날짜 출력 모양 지정 --%>
<fmt:formatDate var="createDate"
value="${board.boardCreateDate}"
pattern="yyyy-MM-dd"/>
<fmt:formatDate var="today"
value="<%= new java.util.Date() %>"
pattern="yyyy-MM-dd"/>
<c:choose>
<%-- 글 작성일이 오늘이 아닐 경우 --%>
<c:when test="${createDate != today }">
${createDate}
</c:when>
<%-- 글 작성일이 오늘일 경우 --%>
<c:otherwise>
<fmt:formatDate value="${board.boardCreateDate}"
pattern="HH:mm"/>
</c:otherwise>
</c:choose>
</td>
</tr>
</c:forEach>
</c:otherwise>
</c:choose>
</tbody>
Pagination(boardList.jsp)
- 첫 번째 페이지
- 페이지 이동 시
<%-- 로그인이 되어있는 경우 --%>
<button type="button" class="btn btn-primary float-right" id="insertBtn" onclick="location.href = 'insertForm.do'">글쓰기</button>
<%---------------------- Pagination ----------------------%>
<%-- 페이징 처리 주소를 쉽게 사용할 수 있도록 미리 변수에 저장 --%>
<c:url var="pageUrl" value="/board/list.do"/>
<!-- 화살표에 들어갈 주소를 변수로 생성 -->
<c:set var="firstPage" value="${pageUrl}?cp=1"/>
<c:set var="lastPage" value="${pageUrl}?cp=${pInfo.maxPage}"/>
<%-- EL을 이용한 숫자 연산의 단점 : 연산이 자료형에 영향을 받지 않는다. --%>
<%--
<fmt : parseNumber> : 숫자 형태를 지정하여 변수 선언
integerOnly="true" : 정수로만 숫자 표현(소수점 버림)
--%>
<fmt:parseNumber var="c1" value="${(pInfo.currentPage - 1) / 10}" integerOnly="true"/>
<fmt:parseNumber var="prev" value="${c1 * 10}" integerOnly="true"/>
<c:set var="prevPage" value="${pageUrl}?cp=${prev}" />
<fmt:parseNumber var="c2" value="${(pInfo.currentPage + 9) / 10 }" integerOnly="true" />
<fmt:parseNumber var="next" value="${ c2 * 10 + 1}" integerOnly="true" />
<c:set var="nextPage" value="${pageUrl}?cp=${next}" />
<div class="my-5">
<ul class="pagination">
<%-- 현재 페이지가 10페이지 초과인 경우 --%>
<c:if test="${pInfo.currentPage > 10}">
<li> <%-- 첫 페이지로 이동(<<) --%>
<a class="page-link" href="${firstPage}"><<</a>
</li>
<li> <%-- 이전 페이지로 이동(<) --%>
<a class="page-link" href="${prevPage}"><</a>
</li>
</c:if>
<!-- 페이지 목록 -->
<c:forEach var="page" begin="${pInfo.startPage}" end="${pInfo.endPage}">
<c:choose>
<c:when test="${pInfo.currentPage == page}">
<li>
<a class="page-link">${page}</a>
</li>
</c:when>
<c:otherwise>
<li>
<a class="page-link" href="${pageUrl}?cp=${page}">${page}</a>
</li>
</c:otherwise>
</c:choose>
</c:forEach>
<%-- 다음 페이지가 마지막 페이지 미만인 경우 --%>
<c:if test="${next < pInfo.maxPage}">
<li>
<%-- 다음 페이지로 이동(>) --%>
<a class="page-link" href="${nextPage}">></a>
</li>
<li>
<%-- 마지막 페이지로 이동(>>) --%>
<a class="page-link" href="${lastPage}">>></a>
</li>
</c:if>
</ul>
</div>
댓글남기기