고래씌

[Spring] 6-1. 게시판 목록, 페이징 본문

Server/Spring

[Spring] 6-1. 게시판 목록, 페이징

고래씌 2024. 1. 23. 15:51

1. 게시판 목록

 

▶ boardListView.jsp

<%@ 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>Insert title here</title>
<style>
      #boardList {text-align:center;}
      #boardList>tbody>tr:hover {cursor:pointer;}
      #pagingArea {width:fit-content; margin:auto;}
      #searchForm {
          width:80%;
          margin:auto;
      }
      #searchForm>* {
          float:left;
          margin:5px;
      }
      .select {width:20%;}
      .text {width:53%;}
      .searchBtn {width:20%;}
      /* 썸네일 관련 스타일 */
        #boardList tr > td:nth-of-type(2){ /* 2번째 td(제목) */
            position: relative;
        }
      .list-thumbnail{
        max-width: 50px;
        max-height: 30px;
        position: absolute;
        left : -15px;
        top : 10px;
      }
</style>
</head>
<body>

	<jsp:include page="/WEB-INF/views/common/header.jsp" />
	
	<div class="content">
		<br><br>
		<div class="innerOuter" style="padding:5% 10%;">
			<h2>일반게시판</h2>
		<br><br>
		
		<c:if test="${not empty loginUser}">  <!-- 로그인 유저가 비어있지 않으면 -->
			<a class="btn btn-secondary" style="float:right" href="">
				글쓰기
			</a>
		</c:if>
		<br>
		<table id="boardList" class="table table-hover" align="center">
	        <thead>
	            <tr>
	                <th>글번호</th>
	                <th>제목</th>
	                <th>작성자</th>
	                <th>조회수</th>
	                <th>작성일</th>
	            </tr>
	        </thead>
	        <tbody>
	        	<tr>
	        		<td colspan="5">게시글이 없습니다.</td>
	        	</tr>
	        </tbody>
        </table>
        <br>
        
        <!-- 페이징 -->
        <div id="pagingArea">
        	<ul class="pagination">
        		<li class="page-item">
        			<a class="page-link">Previous</a>
        		</li>
        		
				<li class="page-item">
        			<a class="page-link">1</a>
        		</li>
				<li class="page-item">
        			<a class="page-link">2</a>
        		</li>
				<li class="page-item">
        			<a class="page-link">3</a>
        		</li>
				<li class="page-item">
        			<a class="page-link">4</a>
        		</li>
				<li class="page-item">
        			<a class="page-link">5</a>
        		</li>
				<li class="page-item">
        			<a class="page-link">6</a>
        		</li>
				<li class="page-item">
        			<a class="page-link">7</a>
        		</li>
				<li class="page-item">
        			<a class="page-link">8</a>
        		</li>
				<li class="page-item">
        			<a class="page-link">9</a>
        		</li>
				<li class="page-item">
        			<a class="page-link">10</a>
        		</li>
        		
				<li class="page-item">
        			<a class="page-link">Next</a>
        		</li>
        	</ul>
        </div>
        
        
        <!-- 검색기능 -->
        <br clear="both"> <!-- float 속성 해제 -->
        
        <form id="searchForm" method="get" align="center">
        	<div class="select">
        		<select class="custom-select" name="condition">
        			<option value="writer">작성자</option>
        			<option value="title">제목</option>
        			<option value="content">내용</option>
        			<option value="titleAndContent">제목+내용</option>
        		</select>
        	</div>
        	<div class="text">
        		<input type="text" class="form-control" name="keyword" />
        	</div>
        	<button type="submit" class="searchBtn btn btn-secondary">검색</button>
        </form>
		</div>
	</div>
	
	<jsp:include page="/WEB-INF/views/common/footer.jsp" />

</body>
</html>

 

 

 

=> 다음과 같이 폴더 및 파일 생성(BoardDao, BoardService는 interface 파일임)

 

 

▶ Board.java

package com.kh.spring.board.model.vo;

import java.sql.Date;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor // 기본생성자
@AllArgsConstructor
@Data // setter, getter, ...
public class Board {
	
	private int boardNo;
	private String boardTitle;
	private String boardContenxt;
	private String boardWrither; // id, userNo, name 동시에 저장
	private int count;
	private Date createDate;
	private String status;
	private String orginName;
	private String changeName;
	private String boardCd;   // boardType의 일반게시판, 사진게시판 등
}

 

 

▶ BoardType.java  => 게시판 종류를 저장하는 vo객체

package com.kh.spring.board.model.vo;

import lombok.Data;

@Data
public class BoardType {
	private String boardCd;
	private String boardName;  // 일반게시판, 사진게시판, 음악, 영화, 게임
}

 

 

- BoardController.java

 

=> BoardController.java 맨위에 로그를 기록하기 위해 Slf4j를 등록하고 다음과 같이 등록

 

 

 

- BoardServiceImpl.java에는 다음과 같이 추가

 

 

 

- BaordDaoImpl.java에는 다음과 같이 추가

 

 

▶ BoardController.java

 

=> 이 방법으로하게 되면 게시판 타입이 100개라면 그 100개를 일일이 다 추가해야하는 번거러움이 발생한다!

=> 그래서 동적파라미터를 설정하여 이용하여 설정하도록 하겠다!

=> 동적파라미터 설정 : {변수명}  / 리액트에서는 : 변수명   으로 적었었음

 

	@GetMapping("/list/{boardCode}")  // 그럼 뒤에 C가되었든, T가되었든 다 받아준다!
	public String selectList(@PathVariable("boardCode") String boardCode,
			@RequestParam(value="currentPage", defaultValue="1") int currentPage,  // currentPage는 매개변수로 얻어옴
			Model model,  // Request Scope를 저장하기 위해서 model 사용
			@RequestParam Map<String, Object> paramMap) {
		// @PathVariable(변수명) : URL경로로 포함되어있는 값을 변수로 사용 가능
		// + PathVariable로 등록한 변수는 자동으로 requestScope에 저장이 된다.
		
		paramMap.put("boardCode", boardCode);
		
		log.info("paramMap = {}", paramMap);  // 내가 두번째 매개변수로 넣은 데이터가 저기 {}로 안으로 추가가 됨
		// slf4j에서 제공하고있는 로그를 이용. 앞으로는 출력문 대신에 log.info를 이용!
		
		// 1) 게시글 갯수 카운팅
		
		// 2) PageInfo 생성
		
		// 3) 게시글목록 조회후
		
		// 4) pageInfo, list를 model에 추가
		
		return "board/boardListView";
	}

 

 

@PathVariable(변수명) : URL경로로 포함되어있는 값을 변수로 사용 가능
+ PathVariable로 등록한 변수는 자동으로 requestScope에 저장이 된다.

 

☞ log.info 앞으로는 출력문 대신에 log.info를 이용한다! => 로그가 출력됨

로그

 

 

=> 여기 1), 2), 3), 4)를 모두 처리하기위해(BoardController) 추가할 예정

=> 아래에 페이징과 함께 처리하여 추가하도록 하겠다.

 

 


2. 페이징 처리, 게시글 목록

▶ PageInfo.java

package com.kh.spring.common.model.vo;

import lombok.Data;

@Data
public class PageInfo {
	private int listCount;
    private int currentPage;
    private int pageLimit;
    private int boardLimit;
    
    private int maxPage;  
    private int startPage;  // 시작페이지
    private int endPage;    // 종료페이지

}

 

 

▶ Pagination.java

package com.kh.spring.common.template;

import com.kh.spring.common.model.vo.PageInfo;

public class Pagination {
	
	public static PageInfo getPaageInfo(int listCount, int currentPage, int pageLimit, int boardLimit) {
		int maxPage =(int)(Math.ceil(((double)listCount/boardLimit)));
        int startPage = (currentPage-1) / pageLimit * pageLimit+1;
        int endPage = startPage+pageLimit-1;
        if(endPage>maxPage) {
            endPage=maxPage;
        }
        PageInfo pageinfo = new PageInfo();
        pageinfo.setBoardLimit(boardLimit);
        pageinfo.setCurrentPage(currentPage);
        pageinfo.setEndPage(endPage);
        pageinfo.setListCount(listCount);
        pageinfo.setMaxPage(maxPage);
        pageinfo.setPageLimit(pageLimit);
        pageinfo.setStartPage(startPage);
        return pageinfo;
	}

}

 

 

 

▶ boardListView.java

<%@ 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>Insert title here</title>
<style>
      #boardList {text-align:center;}
      #boardList>tbody>tr:hover {cursor:pointer;}
      #pagingArea {width:fit-content; margin:auto;}
      #searchForm {
          width:80%;
          margin:auto;
      }
      #searchForm>* {
          float:left;
          margin:5px;
      }
      .select {width:20%;}
      .text {width:53%;}
      .searchBtn {width:20%;}
      /* 썸네일 관련 스타일 */
        #boardList tr > td:nth-of-type(2){ /* 2번째 td(제목) */
            position: relative;
        }
      .list-thumbnail{
        max-width: 50px;
        max-height: 30px;
        position: absolute;
        left : -15px;
        top : 10px;
      }
</style>
</head>
<body>

	<jsp:include page="/WEB-INF/views/common/header.jsp" />
	
	<div class="content">
		<br><br>
		<div class="innerOuter" style="padding:5% 10%;">
			<h2>일반게시판</h2>
		<br><br>
		
		<c:if test="${not empty loginUser}">  <!-- 로그인 유저가 비어있지 않으면 -->
			<a class="btn btn-secondary" style="float:right" href="">
				글쓰기
			</a>
		</c:if>
		<br>
		<table id="boardList" class="table table-hover" align="center">
	        <thead>
	            <tr>
	                <th>글번호</th>
	                <th>제목</th>
	                <th>작성자</th>
	                <th>조회수</th>
	                <th>작성일</th>
	            </tr>
	        </thead>
	        <tbody>
	        	<c:choose>
	        		<c:when test="${empty list }"> <!-- 게시글이 없다면 -->
			        	<tr>
			        		<td colspan="5">게시글이 없습니다.</td>
			        	</tr>
	        		</c:when>
	        		<c:otherwise>
			        	<c:forEach var="board" items="${list }">
			        		<tr>
			        			<td>${board.boardNo }</td>
			        			<td>${board.boardTitle }</td>
			        			<td>${board.boardWriter }</td>
			        			<td>${board.count}</td>
			        			<td>${board.createDate }</td>
			        		</tr>
			        	</c:forEach>
	        		</c:otherwise>
	        	</c:choose>
	        	
	        </tbody>
        </table>
        <br>
        
        <!-- 페이징 -->
        <c:set var="url" value="${boardCode}?currentPage="/> 
        
        <div id="pagingArea">
        	<ul class="pagination">

<%--         		<c:if test="${pi.currentPage eq 1}" > <!-- currentPage가 1과 같을때 --> --%>
<!--         			<li class="page-item"> -->
<!--         				절대경로 방식 -->
<%-- 						<a href="${contextPath}/board/list/${boardCode}?currentPage=${pi.currentPage -1}" class="page-link">Previous</a> --%>
						
<!-- 						상대경로 방식 -->
<!-- 						<a class="page-link">Previous</a> -->
<!-- 					</li> -->
<%-- 				</c:if> --%>
				
				<c:if test="${pi.currentPage ne 1}"> 
					<li class="page-item">
						<!-- 절대경로 방식 -->
<%-- 						<a href="${contextPath}/board/list/${boardCode}?currentPage=${pi.currentPage -1}" class="page-link">Previous</a> --%>
						
						<!-- 상대경로 방식 -->
						<a href="${url}${pi.currentPage -1}" class="page-link">Previous</a>
					</li>
				</c:if>

        		<c:forEach var="p" begin="${pi.startPage }" end="${pi.endPage }">
        			<li class="page-item">
        				<a href="${url}${p}" class="page-link">${p}</a>
        			</li>
        		</c:forEach>
        		
<%--         		<c:if test="${pi.currentPage eq pi.maxPage }"> --%>
<!--         			<li class="page-item"> -->
<!--         				<a class="page-link">NEXT</a> -->
<!--         			</li> -->
<%--         		</c:if> --%>
        		
        		<c:if test="${pi.currentPage ne pi.maxPage }" >
        			<li class="page-item">
        				<a href="${url}${pi.currentPage +1}" class="page-link">NEXT</a>
<%--         				<a href="${boardCode}?currentPage=${pi.currentPage +1}" class="page-link">NEXT</a> --%>
        			</li>
        		</c:if>

        	</ul>
        </div>
        
        
        <!-- 검색기능 -->
        <br clear="both"> <!-- float 속성 해제 -->
        
        <form id="searchForm" method="get" align="center" action="${boardCode}" >
        	<div class="select">
        		<select class="custom-select" name="condition">
        			<option value="writer">작성자</option>
        			<option value="title">제목</option>
        			<option value="content">내용</option>
        			<option value="titleAndContent">제목+내용</option>
        		</select>
        	</div>
        	<div class="text">
        		<input type="text" class="form-control" name="keyword" />
        	</div>
        	<button type="submit" class="searchBtn btn btn-secondary">검색</button>
        </form>
		</div>
	</div>
	
	<jsp:include page="/WEB-INF/views/common/footer.jsp" />

</body>
</html>

 

 

▶ BoardController.java

int listCount = boardService.selectListCount(paramMap);  List<Board> list = boardService.selectList(pi, paramMap);

 

=> paramMap을 넣는 이유는 map형태의 데이터여서 좀 더 다양한 형태의 값을 추가할 수 있음!

 

	@GetMapping("/list/{boardCode}")  // 그럼 뒤에 C가되었든, T가되었든 다 받아준다!
	public String selectList(@PathVariable("boardCode") String boardCode,
			@RequestParam(value="currentPage", defaultValue="1") int currentPage,   // currentPage는 매개변수로 얻어옴
			Model model,  // Request Scope를 저장하기 위해서 model 사용
			@RequestParam Map<String, Object> paramMap) {
		// @PathVariable(변수명) : URL경로로 포함되어있는 값을 변수로 사용 가능
		// + PathVariable로 등록한 변수는 자동으로 requestScope에 저장이 된다.
		
		paramMap.put("boardCode", boardCode);
		
		log.info("paramMap = {}", paramMap);  // 내가 두번째 매개변수로 넣은 데이터가 저기 {}로 안으로 추가가 됨
		// slf4j에서 제공하고있는 로그를 이용. 앞으로는 출력문 대신에 log.info를 이용!
		
		// 1) 게시글 갯수 카운팅
		int listCount = boardService.selectListCount(paramMap);  // map형태의 데이터여서 좀더 다양한 형태의 값을 추가할 수 있음
		int pageLimit = 10;
		int boardLimit = 5;
		
		// 2) PageInfo 생성
		PageInfo pi = Pagination.getPaageInfo(listCount, currentPage, pageLimit, boardLimit);
		
		// 3) 게시글목록 조회후
		List<Board> list = boardService.selectList(pi, paramMap);
		
		// 4) pageInfo, list를 model에 추가
		model.addAttribute("list", list);
		model.addAttribute("pi", pi);
		model.addAttribute("param",paramMap); // boardCode값 전달
		
		log.info("BoardList=", list);
		System.out.println(list);
		return "board/boardListView";
	}

 

 

▶ BoardService.java

package com.kh.spring.board.model.service;

import java.util.List;
import java.util.Map;

import com.kh.spring.board.model.vo.Board;
import com.kh.spring.common.model.vo.PageInfo;

public interface BoardService {

//	페이징 처리
	int selectListCount(Map<String, Object> paramMap);

//	게시판 목록
	List<Board> selectList(PageInfo pi, Map<String, Object> paramMap);

}

 

 

 

▶ BoardServiceImpl.java

package com.kh.spring.board.model.service;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.kh.spring.board.model.dao.BoardDao;
import com.kh.spring.board.model.vo.Board;
import com.kh.spring.common.model.vo.PageInfo;

import lombok.extern.slf4j.Slf4j;

@Slf4j // 로그
@Service
public class BoardServiceImpl implements BoardService{

	@Autowired
	private BoardDao boardDao;

//	페이징
	@Override
	public int selectListCount(Map<String, Object> paramMap) {
		return boardDao.selectListCount(paramMap);
	}

//	게시판 목록
	@Override
	public List<Board> selectList(PageInfo pi, Map<String, Object> paramMap) {
		return boardDao.selectList(pi, paramMap);
	}
}

 

 

▶ BoardDao.java

package com.kh.spring.board.model.dao;

import java.util.List;
import java.util.Map;

import com.kh.spring.board.model.vo.Board;
import com.kh.spring.common.model.vo.PageInfo;

public interface BoardDao {

//	페이징
	int selectListCount(Map<String, Object> paramMap);

//	게시글 목록
	List<Board> selectList(PageInfo pi, Map<String, Object> paramMap);
}

 

▶ BoardDaoImpl.java

package com.kh.spring.board.model.dao;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.kh.spring.board.model.vo.Board;
import com.kh.spring.common.model.vo.PageInfo;

@Repository
public class BoardDaoImpl implements BoardDao{
	
	@Autowired
	private SqlSession sqlSession;

//	페이징
	@Override
	public int selectListCount(Map<String, Object> paramMap) {
		return sqlSession.selectOne("boardMapper.selectListCount", paramMap);
	}

//	게시글 목록
	@Override
	public List<Board> selectList(PageInfo pi, Map<String, Object> paramMap) {
		
		int offset = (pi.getCurrentPage()-1) * pi.getBoardLimit();
		int limit = pi.getBoardLimit();
		
		RowBounds rowBounds = new RowBounds(offset, limit);
		
		return sqlSession.selectList("boardMapper.selectList", paramMap, rowBounds); // 만약 넘길게 없다면 null이라도 선언해야함(두번째 매개변수)
	}
}

 

 

 

▶ board-mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="boardMapper">

	<!-- 페이징 -->
	<select id="selectListCount" parameterType="map" resultType="int">
		SELECT COUNT(*)
		FROM BOARD
		WHERE STATUS = 'Y' AND BOARD_CD= #{boardCode}
	</select>
	

	
	<!-- 게시글 목록 -->
	<!-- 결과값은 board로 담아줘야함 -->
	<select id="selectList" resultType="board" parameterType="map">  
		SELECT BOARD_NO, BOARD_TITLE, M.USER_NAME AS BOARD_WRITER, BOARD_CONTENT, COUNT, CREATE_DATE
		FROM BOARD B
		LEFT JOIN MEMBER M ON (BOARD_WRITER = USER_NO) 
		WHERE B.STATUS='Y' AND BOARD_CD= #{boardCode}
		ORDER BY BOARD_NO DESC
	</select>

</mapper>