어리바리 신입 개발자의 얼렁뚱땅 개발 기록 ✨
[ Java / SpringBoot / MVC / RESTful] RESTful한 게시판 만들기9 / 게시글 목록에 페이징 추가하기 (DTO와 본문
REST API
[ Java / SpringBoot / MVC / RESTful] RESTful한 게시판 만들기9 / 게시글 목록에 페이징 추가하기 (DTO와
낫쏘링 2023. 9. 12. 17:24728x90
[ DTO VS VO]
- DTO (Data Transfer Object) / 가변 객체
데이터를 계층간 교환하기 위해 사용한다. 즉, 로직을 가지지 않는 순수하게 데이터를 담고 전달하는 객체이다.
때문에 getter, setter, toString 정도의 메서드만 갖고 있다.
- VO (Value Object) / 불변 객체
Read-Only의 특징을 지닌다. 즉, 데이터를 전달만 할 뿐 setter가 존재하지 않는다.
불변 객체라는 게 무슨 뜻이지...? 값이 바뀌지 않고 오직 Read만 한다고..?
값이 바뀌지 않는다면 그냥 상수를 말하는 거 아닌가?
상수를 쓸거면 굳이 VO를 쓸 이유가 있나? 라는 생각을했는데
VO는 setter가 아니라 반듯이 생성자를 생성해서 사용한다는 점에서 불변 객체라는거였다.
이게 무슨 뜻이냐면 아래 처럼 처음 사용할 때 생성자를 통해서 값을 넣어주면
그 이후로 setter를 사용할 수 없기 때문에 처음 생성한 그 상태에서 불변한 객체라는 뜻이다.
만약 새로운 값이 필요하면 또 아래처럼 새롭게 생성자를 생성해서 새로운 객체를 생성해야 한다.PagingVo paging = new PagingVo(1,3);
PagingVo paiging2 = new PagingVo(4,9);
지금까지는 DTO를 사용해서 데이터를 주고 받았다.
페이징을 구현하기 위해 VO를 사용해 볼 생각이다.
[ 1. 게시글 개수 조회하기 ]
<select id="selectCount" resultType="int"> /* 게시글 개수 조회 */ <![CDATA[ SELECT COUNT(*) FROM t_board WHERE deleted_yn = 'N' ]]> </select>
/** 게시글 개수 조회 mapper */ int selectCount() throws Exception;
[ 2. PagingVo 생성 ]
@Getter @ToString public class PagingVo { private int rowsCount; private int currentPage; private int totalPage; private int startPage; private int endPage; private int startIndex; private int rowPerPage = 10; public static final int PAGE_NUM = 10; /** 생성자 메서드*/ public PagingVo (int currentPage, int rowsCount) throws Exception{ this.currentPage = currentPage; // 몇 번째 행 부터 조회할 것인지 계산 startIndex = (currentPage-1)*rowPerPage; // 전체 게시글 개수를 한 페이지에 보여질 행의 개수로 나누어서 페이징의 가장 마지막 숫자(총 페이징 수)를 계산한다. // double로 계산 후에 소숫점을 올림 처리하기 위해 Math.ceil 사용 후 int로 형변환 totalPage = (int) Math.ceil((double)rowsCount / rowPerPage); // 페이징의 가장 처음 번호(처음에는 당연히 1 -> currentPage가 바뀔 때마다 바뀔 예정) startPage = 1; // 화면에 보이는 마지막 페이징 숫자(currentPage가 바뀔 때마다 바뀔 예정) // rowPerPage는 한 페이지에 보일 행의 개수 / endPage는 한 페이지에 보일 페이징의 마지막 번호 endPage = (totalPage < PAGE_NUM)? totalPage : PAGE_NUM; // currentPage가 바뀔 때마다 startPge와 endPage가 바뀐다. // 페이징이 처음에 1~10이기 때문에 currentPage가 6이되는 순간 startPager가 2로, endPage가 11로 바뀐다. // totalPage에도 조건을 거는 이유는 만약 게시글이 적어서 totalPage가 10보다 적을 경우 // 페이징이 시작번호와 끝 번호가 바뀔 이유가 없기 때문 if(currentPage >= 6 && totalPage > 10) { startPage = currentPage - 5; endPage = currentPage + 4; // 현재 페이지에 보이는 페이징 번호가 마지막 페이징 숫자보다 같거나 클 경우 // 더 이상 시작 번호와 끝 번호가 바뀌지 않도록 한다. if(endPage >= totalPage) { startPage = totalPage - 9; endPage = totalPage; } } } }
[ 3. BoardPagingDto 생성 ]
@Getter @Setter @ToString @AllArgsConstructor public class BoardPagingDto { private List<BoardDto> boardList; private PagingVo paging; }
[ 4. 게시글 조회 쿼리 수정 ]
<select id="selectBoardList" resultType="BoardDto"> /* 기존 게시글 내역 조회 */ <![CDATA[ SELECT board_idx, title, hit_cnt, created_datetime FROM t_board WHERE deleted_yn = 'N' ORDER BY board_idx DESC ]]> </select>
<select id="selectBoardList" parameterType="PagingVo" resultType="BoardDto"> /* 수정 후 게시글 내역 조회 */ <![CDATA[ SELECT board_idx, title, hit_cnt, created_datetime FROM t_board WHERE deleted_yn = 'N' ORDER BY board_idx DESC ]]> <if test="startIndex != null and startIndex > -1 "> LIMIT #{startIndex},#{rowPerPage}; </if> </select>
/** 게시글 내역 조회 mapper 매개변수 수정 */ List<BoardDto> selectBoardList(PagingVo paging) throws Exception;
[ 5. BoardServie 추가 및 수정 ]
/** 페이징 */ public PagingVo selectPaging(int currentPage) throws Exception{ int rowsCount = boardMapper.selectCount(); PagingVo paging = new PagingVo(currentPage, rowsCount); return paging; } /** 게시글 내역 조회 */ public BoardPagingDto selectBoardList(int currentPage) throws Exception{ PagingVo paging = selectPaging(currentPage); List<BoardDto> boardList = boardMapper.selectBoardList(paging); BoardPagingDto boardPaging = new BoardPagingDto(boardList, paging); return boardPaging; }
[ 6. BoardController 수정 ]
/** 기존 게시판 목록 조회 화면 */ @GetMapping("/board") public String openBoardList(Model model) throws Exception{ BoardDto boardList = boardService.selectBoardList(); model.addAttribute("title","게시판 목록"); model.addAttribute("boardList", boardList); return "/board/restBoardList"; }
/** 수정 후 게시판 목록 조회 화면 */ @GetMapping("/board") public String openBoardList(@RequestParam(value="currentPage", required = false ,defaultValue = "1") int currentPage, Model model) throws Exception{ BoardPagingDto boardPaging = boardService.selectBoardList(currentPage); model.addAttribute("title","게시판 목록"); model.addAttribute("boardPaging", boardPaging); return "/board/restBoardList"; }
[ 7. html 수정 ]
<!-- 페이징 구현 태그 추가 --> <!-- 가장 첫 페이지거나 마지막 페이지일 경우 혹은 현재 페이지일 경우 label 태그를 그린다. --> <!-- 그 외의 경우 a 태그를 그린다. --> <tfoot> <tr> <td colspan="4" class="text-center" th:with="paging=${boardPaging.paging}"> <a class="btn" th:if="${paging.currentPage>1}" th:href="@{/board}">처음으로</a> <label th:unless="${paging.currentPage>1}">처음으로</label> <a class="btn" th:if="${paging.currentPage>1}" th:href="@{/board(currentPage=${paging.currentPage-1})}">이전</a> <label th:unless="${paging.currentPage>1}">이전</label> <th:block th:each="num : ${#numbers.sequence(paging.startPage,paging.endPage)}" > <a class="btn " th:if="${paging.currentPage != num}" th:href="@{/board(currentPage=${num})}" th:text="${num}"></a> <label class="current-page-btn" th:if="${paging.currentPage == num}" th:text="${num}"></label> </th:block> <a class="btn" th:if="${paging.currentPage<paging.totalPage}" th:href="@{/board(currentPage=${paging.currentPage+1})}">다음</a> <label th:unless="${paging.currentPage<paging.totalPage}">다음</label> <a class="btn" th:if="${paging.currentPage<paging.totalPage}" th:href=@{/board(currentPage=${paging.totalPage})}>마지막으로</a> <label th:unless="${paging.currentPage<paging.totalPage}">마지막으로</label> </td> </tr> </tfoot>
<!--tbody에서 ${#lists.size(boardList)}를 ${#lists.size(boardPaging.boardList)}로 수정 --> <!--서버에서 받아오는 데이터의 형태가 바뀌었기 때문에 boardList 앞에 boardPaging. 추가해줘야한다. --> <tbody> <!-- th:if - 타임리프 문법을 사용 할 경우에 쓰는 if 문 --> <!-- if문 내의 조건이 일치해야만 해당 tr이 생성된다. --> <!-- th:each - 타임리프 문법을 사용할 경우에 쓰는 반복문 ( ${list}에 들어있는 값을 모두 순회하면서 tr 태그를 생성한다. )--> <tr th:if="${#lists.size(boardPaging.boardList)} > 0" th:each="list : ${boardPaging.boardList}"> <td th:text="${list.boardIdx}"></td> <!-- th:href - 타임리프 문법을 사용할 경우에 쓰는 href 속성 --> <!-- href="/board/" th:attrappend="href=${list.boardIdx}" 로도 작성 가능하다. --> <td class="title"><a th:href="'/board/' + ${list.boardIdx}" th:text="${list.title}"></a></td> <td th:text="${list.hitCnt}"></td> <td th:text="${list.createdDatetime}"></td> </tr> <!-- th:unless - 타임리프 문법을 사용할 경우에 쓴는 if 문 / 단, if와 반대로 조건이 일치하지 않을 경우 tr 생성 --> <tr th:unless="${#lists.size(boardPaging.boardList)} > 0"> <td colspan="4">조회된 결과가 없습니다.</td> </tr> </tbody>
페이징이 구현 됐다. 데이터를 많이 넣지 않아서 페이징이 10까지 나오지 않고 4까지만 나온다.
728x90