boardList.jsp를 만들기 전에 TDD로 우리가 원하는대로 페이징이 되는지 테스트 해볼 것이다.
board.jsp
: 게시물 내용을 보여주는 화면.
: 현재 페이지의 값을 돌려줘야 화면이 나온다.
: 현재 페이지, 다음 페 이지에는 각 페이지 번호가 거기에 맞게 링크가 걸려있어야 된다.
: 왼쪽화살표는 젤 첫번째 화면보다 하나 더 작은 값인 10이 들어가야 하며 오른쪽 화살표는 21이 돼야 한다.
LIMIT
이라는 구문을 사용해야 한다.✔️ offset
: 맨 처음부터 얼마나 떨어져 있는가를 의미
✔️ row_count
: 읽어올 row의 수
✔️ domain 패키지에 PageHandler 클래스 생성
1. totalPage (전체 페이지 갯수)구하기
2. beginPage (네비게이션 첫 번째)구하기
3. endPage (네비게이션 마지막)구하기
올림(총 게시물 겟수 / 한 페이지의 크기(고정값))
25 / 10 * 10 = 20 + 1
일의 자리를 일단 날려야함
- 일의자리 날리는 식: 25 / 10 * 10 = 20
+ 1 해주면 beginPage 나옴
Math.min(beginPage + naviSize(페이지 네비게이션의 크기) -1, totalPage)
Math.min()
을 써 준다.package kr.ac.jipark09.domain;
public class PageHandler {
private int totalCnt; // 총 게시물 갯수
private int pageSize; // 한 페이지의 크기
private int naviSize = 10; // 페이지 내비게이션의 크기
private int totalPage; // 전체 페이지의 갯수
private int page; // 현재 페이지
private int beginPage; // 내비게이션의 첫 번째 페이지
private int endPage; // 내비게이션의 마지막 페이지
private boolean showPrev; // 이전 페이지로 이동하는 링크를 보여줄 것인지의 여부 (1페이지만 있을 때는 있으면 안되니까)
private boolean showNext; // 다음 페이지로 이동하는 링크를 보여줄 것인지의 여부 (10의 배수로 채워지지 않았을 때 있으면 안되니까)
// totalCnt와 page만 받아울 경우 pageSize는 기본 10으로 설정
public PageHandler(int totalCnt, int page) {
this(totalCnt, page, 10);
}
// 계산을 해서 페이지에 보여줄 화면을 구성
public PageHandler(int totalCnt, int page, int pageSize) {
this.totalCnt = totalCnt;
this.page = page;
this.pageSize = pageSize;
// totalPage = 전체게시물 / 10 (올림) => pageSize 정수 주의!!! 정수랑 정수랑 나눠서 소수점 x
totalPage = (int)Math.ceil(totalCnt / (double)pageSize);
// 25 / 10 * 10 + 1 = 11
// (page - 1) => 10 / 10 * 10 + 1 = 11되버림. 10의 자리는 그 따음페이지로 넘어가면 안됨. 10의 자리때문에 -1 해줘야함
beginPage = (page - 1) / naviSize * 10 + 1;
endPage = Math.min(beginPage + naviSize - 1, totalPage);
showPrev = beginPage != 1;
showNext = endPage != totalPage;
}
public int getTotalCnt() {
return totalCnt;
}
public void setTotalCnt(int totalCnt) {
this.totalCnt = totalCnt;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getNaviSize() {
return naviSize;
}
public void setNaviSize(int naviSize) {
this.naviSize = naviSize;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public int getBeginPage() {
return beginPage;
}
public void setBeginPage(int beginPage) {
this.beginPage = beginPage;
}
public int getEndPage() {
return endPage;
}
public void setEndPage(int endPage) {
this.endPage = endPage;
}
public boolean isShowPrev() {
return showPrev;
}
public void setShowPrev(boolean showPrev) {
this.showPrev = showPrev;
}
public boolean isShowNext() {
return showNext;
}
public void setShowNext(boolean showNext) {
this.showNext = showNext;
}
// 페이지 네비게이션을 프린트하는 메서드
public void print() {
System.out.println("page = " + page);
System.out.print(showPrev ? "[PREV] " : "");
for(int i = beginPage; i <= endPage; i++) {
System.out.print(i + " ");
}
System.out.println(showNext ? "[NEXT]" : "");
}
@Override
public String toString() {
return "PageHandler{" +
"totalCnt=" + totalCnt +
", pageSize=" + pageSize +
", naviSize=" + naviSize +
", totalPage=" + totalPage +
", page=" + page +
", beginPage=" + beginPage +
", endPage=" + endPage +
", showPrev=" + showPrev +
", showNext=" + showNext +
'}';
}
}
✔️ Test 해 보기
package kr.ac.jipark09.domain;
import org.junit.Test;
import static org.junit.Assert.*;
public class PageHandlerTest {
@Test
public void test() {
PageHandler ph = new PageHandler(250, 1); // pageSize = 10
assertTrue(ph.getBeginPage() == 1);
assertTrue(ph.getEndPage() == 10);
}
@Test
public void test2() {
PageHandler ph = new PageHandler(255, 26);
ph.print();
System.out.println("ph=" + ph);
assertTrue(ph.getBeginPage() == 21);
assertTrue(ph.getEndPage() == 26);
}
}
✔️ boardMapper.xml과 BoardDaoImpl 생성
<?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="kr.ac.jipark09.BoardMapper">
<!--게시물 넘버 주면 해당 게시물 나옴 -->
<select id="select" parameterType="int" resultType="BoardDto">
SELECT bno, title, content, writer, view_cnt, comment_cnt, reg_date
FROM board
WHERE bno = #{bno}
</select>
<!-- 전체 게시물 가져옴: 등록일 역순, 번호 역순 -->
<select id="selectAll" resultType="BoardDto">
SELECT bno, title, content, writer, view_cnt, comment_cnt, reg_date
FROM board
ORDER BY reg_date DESC, bno DESC
</select>
<!--총 게시물 수 -->
<select id="count" resultType="int">
SELECT count(*) FROM board
</select>
<!-- 모든 정보 지우기 -->
<delete id="deleteAll">
DELETE FROM board
</delete>
<delete id="delete" parameterType="map">
DELETE FROM board WHERE bno = #{bno} and writer = #{writer}
</delete>
<!--관리자 권한 체크해서 관리자가 지움-->
<delete id="deleteForAdim">
DELETE FROM board WHERE bno = #{bno}
</delete>
<!-- dto주면 입력함-->
<insert id="insert" parameterType="BoardDto">
INSERT INTO board
(title, content, writer)
VALUES
(#{title}, #{content}, #{writer})
</insert>
<sql id="selectFromBoard">
SELECT bno, title, content, writer, view_cnt, comment_cnt, reg_date
FROM board
</sql>
<select id="selectPage" parameterType="map" resultType="BoardDto">
<include refid="selectFromBoard"/>
ORDER BY reg_date DESC, bno DESC
LIMIT #{offset}, #{pageSize}
</select>
<update id="update" parameterType="BoardDto">
UPDATE board
SET title = #{title}
, content = #{content}
, up_date = now()
WHERE bno = #{bno}
</update>
<update id="updateCommentCnt" parameterType="map">
UPDATE board
SET comment_cnt = comment_cnt + #{cnt}
WHERE bno = #{bno}
</update>
<!-- 조회했을 때 조회 수 하나 올림-->
<update id="increaseViewCnt" parameterType="int">
UPDATE board
SET view_cnt = view_cnt + 1
WHERE bno = #{bno}
</update>
</mapper>
package kr.ac.jipark09.dao;
import kr.ac.jipark09.domain.BoardDto;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Repository
public class BoardDaoImpl implements BoardDao {
@Autowired
SqlSession session;
String namespace = "kr.ac.jipark09.BoardMapper.";
@Override
public BoardDto select(Integer bno) throws Exception {
return session.selectOne(namespace + "select", bno);
}
@Override
public List<BoardDto> selectAll() throws Exception {
return session.selectList(namespace + "selectAll");
}
@Override
public int count() throws Exception {
return session.selectOne(namespace + "count");
}
@Override
public int deleteAll() throws Exception {
return session.delete(namespace + "deleteAll");
}
@Override
public int delete(Integer bno, String writer) throws Exception {
Map map = new HashMap<>();
map.put("bno", bno);
map.put("writer", writer);
return session.delete(namespace + "delete", map);
}
@Override
public int deleteForAdim() throws Exception {
return session.delete(namespace + "deleteForAim");
}
@Override
public int insert(BoardDto dto) throws Exception {
return session.insert(namespace + "insert", dto);
}
@Override
public int selectFromBoard() throws Exception {
return session.selectOne(namespace + "selectFromBoard");
}
@Override
public List<BoardDto> selectPage(Map map) throws Exception {
return session.selectList(namespace + "selectPage", map);
}
@Override
public int update(BoardDto dto) throws Exception {
return session.update(namespace + "update", dto);
}
@Override
public int updateCommentCnt(Integer cnt, Integer bno) throws Exception {
Map map = new HashMap();
map.put("cnt", cnt);
map.put("bno", bno);
return session.update(namespace + "updateCommentCnt", map);
}
@Override
public int increaseViewCnt(Integer bno) throws Exception {
return session.update(namespace + "increaseViewCnt", bno);
}
}
xml과 비교하며 만든다. 매치 시켜 줘야 한다.
resultType이 없는 것은 int로 반환
BoardDao 인터페이스 생성해주기! (원래는 먼저 생성하고 구현해야 하지만... 인텔리제이를 쓰는 자는 그렇게 하지 않쥐..!)
✔️ BoardDaoImpl Test 하기
package kr.ac.jipark09.dao;
import kr.ac.jipark09.dao.BoardDao;
import kr.ac.jipark09.domain.BoardDto;
import org.junit.*;
import org.junit.runner.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.test.context.*;
import org.springframework.test.context.junit4.*;
import java.util.*;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/root-context.xml"})
public class BoardDaoImplTest {
@Autowired
private BoardDao boardDao;
@Test
public void countTest() throws Exception {
boardDao.deleteAll();
assertTrue(boardDao.count()==0);
BoardDto boardDto = new BoardDto("no title", "no content", "asdf");
assertTrue(boardDao.insert(boardDto)==1);
assertTrue(boardDao.count()==1);
assertTrue(boardDao.insert(boardDto)==1);
assertTrue(boardDao.count()==2);
}
@Test
public void deleteAllTest() throws Exception {
boardDao.deleteAll();
assertTrue(boardDao.count()==0);
BoardDto boardDto = new BoardDto("no title", "no content", "asdf");
assertTrue(boardDao.insert(boardDto)==1);
assertTrue(boardDao.deleteAll()==1);
assertTrue(boardDao.count()==0);
boardDto = new BoardDto("no title", "no content", "asdf");
assertTrue(boardDao.insert(boardDto)==1);
assertTrue(boardDao.insert(boardDto)==1);
assertTrue(boardDao.deleteAll()==2);
assertTrue(boardDao.count()==0);
}
@Test
public void deleteTest() throws Exception {
boardDao.deleteAll();
assertTrue(boardDao.count()==0);
BoardDto boardDto = new BoardDto("no title", "no content", "asdf");
assertTrue(boardDao.insert(boardDto)==1);
Integer bno = boardDao.selectAll().get(0).getBno();
assertTrue(boardDao.delete(bno, boardDto.getWriter())==1);
assertTrue(boardDao.count()==0);
assertTrue(boardDao.insert(boardDto)==1);
bno = boardDao.selectAll().get(0).getBno();
assertTrue(boardDao.delete(bno, boardDto.getWriter()+"222")==0);
assertTrue(boardDao.count()==1);
assertTrue(boardDao.delete(bno, boardDto.getWriter())==1);
assertTrue(boardDao.count()==0);
assertTrue(boardDao.insert(boardDto)==1);
bno = boardDao.selectAll().get(0).getBno();
assertTrue(boardDao.delete(bno+1, boardDto.getWriter())==0);
assertTrue(boardDao.count()==1);
}
@Test
public void insertTest() throws Exception {
boardDao.deleteAll();
BoardDto boardDto = new BoardDto("no title", "no content", "asdf");
assertTrue(boardDao.insert(boardDto)==1);
boardDto = new BoardDto("no title", "no content", "asdf");
assertTrue(boardDao.insert(boardDto)==1);
assertTrue(boardDao.count()==2);
boardDao.deleteAll();
boardDto = new BoardDto("no title", "no content", "asdf");
assertTrue(boardDao.insert(boardDto)==1);
assertTrue(boardDao.count()==1);
}
@Test
public void selectAllTest() throws Exception {
boardDao.deleteAll();
assertTrue(boardDao.count()==0);
List<BoardDto> list = boardDao.selectAll();
assertTrue(list.size() == 0);
BoardDto boardDto = new BoardDto("no title", "no content", "asdf");
assertTrue(boardDao.insert(boardDto)==1);
list = boardDao.selectAll();
assertTrue(list.size() == 1);
assertTrue(boardDao.insert(boardDto)==1);
list = boardDao.selectAll();
assertTrue(list.size() == 2);
}
@Test
public void selectTest() throws Exception {
boardDao.deleteAll();
assertTrue(boardDao.count()==0);
BoardDto boardDto = new BoardDto("no title", "no content", "asdf");
assertTrue(boardDao.insert(boardDto)==1);
Integer bno = boardDao.selectAll().get(0).getBno();
boardDto.setBno(bno);
BoardDto boardDto2 = boardDao.select(bno);
assertTrue(boardDto.equals(boardDto2));
}
@Test
public void selectPageTest() throws Exception {
boardDao.deleteAll();
for (int i = 1; i <= 10; i++) {
BoardDto boardDto = new BoardDto(""+i, "no content"+i, "asdf");
boardDao.insert(boardDto);
}
Map map = new HashMap();
map.put("offset", 0);
map.put("pageSize", 3);
List<BoardDto> list = boardDao.selectPage(map);
assertTrue(list.get(0).getTitle().equals("10"));
assertTrue(list.get(1).getTitle().equals("9"));
assertTrue(list.get(2).getTitle().equals("8"));
map = new HashMap();
map.put("offset", 0);
map.put("pageSize", 1);
list = boardDao.selectPage(map);
assertTrue(list.get(0).getTitle().equals("10"));
map = new HashMap();
map.put("offset", 7);
map.put("pageSize", 3);
list = boardDao.selectPage(map);
assertTrue(list.get(0).getTitle().equals("3"));
assertTrue(list.get(1).getTitle().equals("2"));
assertTrue(list.get(2).getTitle().equals("1"));
}
@Test
public void updateTest() throws Exception {
boardDao.deleteAll();
BoardDto boardDto = new BoardDto("no title", "no content", "asdf");
assertTrue(boardDao.insert(boardDto)==1);
Integer bno = boardDao.selectAll().get(0).getBno();
System.out.println("bno = " + bno);
boardDto.setBno(bno);
boardDto.setTitle("yes title");
assertTrue(boardDao.update(boardDto)==1);
BoardDto boardDto2 = boardDao.select(bno);
assertTrue(boardDto.equals(boardDto2));
}
@Test
public void increaseViewCntTest() throws Exception {
boardDao.deleteAll();
assertTrue(boardDao.count()==0);
BoardDto boardDto = new BoardDto("no title", "no content", "asdf");
assertTrue(boardDao.insert(boardDto)==1);
assertTrue(boardDao.count()==1);
Integer bno = boardDao.selectAll().get(0).getBno();
assertTrue(boardDao.increaseViewCnt(bno)==1);
boardDto = boardDao.select(bno);
assertTrue(boardDto!=null);
assertTrue(boardDto.getView_cnt() == 1);
assertTrue(boardDao.increaseViewCnt(bno)==1);
boardDto = boardDao.select(bno);
assertTrue(boardDto!=null);
assertTrue(boardDto.getView_cnt() == 2);
}
}
✔️ @Service 만들기
package kr.ac.jipark09.service;
import kr.ac.jipark09.dao.BoardDao;
import kr.ac.jipark09.domain.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
import java.util.*;
@Service
public class BoardServiceImpl implements BoardService {
@Autowired
BoardDao boardDao;
@Override
public int getCount() throws Exception {
return boardDao.count();
}
@Override
public int remove(Integer bno, String writer) throws Exception {
return boardDao.delete(bno, writer);
}
@Override
public int write(BoardDto boardDto) throws Exception {
return boardDao.insert(boardDto);
}
@Override
public List<BoardDto> getList() throws Exception {
return boardDao.selectAll();
}
@Override
public BoardDto read(Integer bno) throws Exception {
BoardDto boardDto = boardDao.select(bno);
boardDao.increaseViewCnt(bno);
return boardDto;
}
@Override
public List<BoardDto> getPage(Map map) throws Exception {
return boardDao.selectPage(map);
}
@Override
public int modify(BoardDto boardDto) throws Exception {
return boardDao.update(boardDto);
}
}
✔️ @Controller 만들기
package kr.ac.jipark09.Controller;
import kr.ac.jipark09.domain.*;
import kr.ac.jipark09.service.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
import org.springframework.ui.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.*;
import javax.servlet.http.*;
import java.time.*;
import java.util.*;
@Controller
@RequestMapping("/board")
public class BoardController {
@Autowired
BoardService boardService;
// 먼저 기본적으로 로그인 체크를 해준다. 누가 게시물을 썼는지 확인을 하기 위해
@GetMapping("/list")
public String list(int page, int pageSize, Model model, HttpServletRequest request) {
if(!loginCheck(request))
return "redirect:/login/login?toURL=" + request.getRequestURL(); // 로그인을 안했으면 로그인 화면으로 이동
try {
// page, pageSize를 파라미터로 받아서 offset과 pageSize를 map에 저장
Map map = new HashMap();
map.put("offset", (page - 1) * pageSize);
map.put("pageSize", pageSize);
List<BoardDto> list = boardService.getPage(map);
model.addAttribute("list", list); // jsp로 보냄
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
}
return "boardList"; // 로그인을 한 상태이면, 게시판 화면으로 이동
}
private boolean loginCheck(HttpServletRequest request) {
// 1. 세션을 얻어서
HttpSession session = request.getSession();
// 2. 세션에 id가 있는지 확인, 있으면 true를 반환
return session.getAttribute("id")!=null;
}
}
✔️ boardList.jsp 만들기
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>fastcampus</title>
<link rel="stylesheet" href="<c:url value='/css/menu.css'/>">
</head>
<body>
<div id="menu">
<ul>
<li id="logo">fastcampus</li>
<li><a href="<c:url value='/'/>">Home</a></li>
<li><a href="<c:url value='/board/list'/>">Board</a></li>
<li><a href="<c:url value='/login/login'/>">login</a></li>
<li><a href="<c:url value='/register/add'/>">Sign in</a></li>
<li><a href=""><i class="fas fa-search small"></i></a></li>
</ul>
</div><div style="text-align:center">
<body>
<table border="1">
<tr>
<th>번호</th>
<th>제목</th>
<th>이름</th>
<th>등록일</th>
<th>조회수</th>
</tr>
<c:forEach var="board" items="${list}">
<tr>
<td>${board.bno}</td>
<td>${board.title}</td>
<td>${board.writer}</td>
<td>${board.reg_date}</td>
<td>${board.view_cnt}</td>
</tr>
</c:forEach>
</table>
</body>
</div>
</body>
</html>
✔️ 톰캣 실행
✔️ 네비게이션을 달아보자
package kr.ac.jipark09.Controller;
import kr.ac.jipark09.domain.*;
import kr.ac.jipark09.service.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
import org.springframework.ui.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.*;
import javax.servlet.http.*;
import java.time.*;
import java.util.*;
@Controller
@RequestMapping("/board")
public class BoardController {
@Autowired
BoardService boardService;
// 먼저 기본적으로 로그인 체크를 해준다. 누가 게시물을 썼는지 확인을 하기 위해
@GetMapping("/list")
public String list(Integer page, Integer pageSize, Model model, HttpServletRequest request) {
if(!loginCheck(request))
return "redirect:/login/login?toURL=" + request.getRequestURL(); // 로그인을 안했으면 로그인 화면으로 이동
if(page == null) {
page = 1;
}
if(pageSize == null) {
pageSize = 10;
}
try {
// pageHandler 활용
int totalCnt = boardService.getCount();
PageHandler pageHandler = new PageHandler(totalCnt, page, pageSize);
// page, pageSize를 파라미터로 받아서 offset과 pageSize를 map에 저장
Map map = new HashMap();
map.put("offset", (page - 1) * pageSize);
map.put("pageSize", pageSize);
List<BoardDto> list = boardService.getPage(map);
model.addAttribute("list", list); // jsp로 보냄
model.addAttribute("ph", pageHandler); // jsp에서 pageHandler를 가지고 페이지를 나타냄
} catch (Exception e) {
e.printStackTrace();
} finally {
}
return "boardList"; // 로그인을 한 상태이면, 게시판 화면으로 이동
}
private boolean loginCheck(HttpServletRequest request) {
// 1. 세션을 얻어서
HttpSession session = request.getSession();
// 2. 세션에 id가 있는지 확인, 있으면 true를 반환
return session.getAttribute("id")!=null;
}
}
Reference
: https://fastcampus.co.kr/dev_academy_nks