스프링 부트(Spring Boot)로 간단한 게시판 만들기 - 최종

2022. 8. 13. 15:35How to become a real programmer

7) 게시물 페이징

 - 게시물 페이징 처리

를 해야할 차례이다. 

페이징 처리란 무엇일까? 말 그대로 페이지화 시키는 것이다.

한 게시판에 만약 글이 만개 있다고 하면, 만 개의 글이 한 페이지에 나타나진 않을 것이다. 보통 한 페이지에 열개, 스무개의 글을 놓고 밑에 페이지 1, 2, 3, 4 ,,, 처럼 나타내어 사람들이 글에 접근하기 쉽고 로딩하는데 어렵지 않게 처리를 한다. 이러한 과정을 페이징 처리라고 하고, 한번 해보도록 하자.

이를위해 BoardController에서, boardlist 부분을 수정해야 한다.

아래와 같이 Pageable에 대한 설정을 해주도록 하자.

@GetMapping("/board/list")
    public String boardList(Model model, @PageableDefault(page = 0, size = 10, sort = "id", direction = Sort.Direction.DESC) Pageable pageable){ // 데이터를 담아 페이지로 보내기 위해 Model 자료형을 인자로
        model.addAttribute("list", boardService.boardlist(pageable)); // boardService에서 생성한 boardlist메소드를 통해 list가 반환되는데 해당 list를 "list"라는 이름으로 넘겨주겠다는 것(html에 나올 수 있게)
        return "boardlist";
    }

매개변수로 전달하는 Pageable은 org.springframework.data.domain의 Pageable이어야 한다.

이렇게 Controller를 설정했으면 서비스 부분에도 수정이 필요하기에 서비스로 이동하자.

기존에 public List<Board> 부분을 아래와 같이 바꿔주어야 한다.

// 게시글 리스트 불러오기 처리
    public Page<Board> boardlist(Pageable pageable){
        return boardRepository.findAll(pageable); //Board라는 class가 담긴 list를 찾아 반환 , 매개변수가 없는 경우에는 public list이지만, 매개변수로 pageable을 주면 public pableable로 바뀜
    }

Pageable 변수를 받을 수 있도록 설정 후 findAll에 pageable을 넘겨주도록 바꾼다.

기존엔 public List 였지만 pageable 변수를 쓴 후로는 public Page로 바꿔주어야 한다. 이렇게 하고 실행해보자.

이렇게 10개의 글을 id의 내림차순으로 sort하여 리스트를 보여주는 것을 볼 수 있다. 만약 다음페이지를 보고 싶다면 주소에 page값을 직접 설정해주면 된다.

page는 0부터 시작하므로 1은 두번째 페이지를 불러오는 것을 볼 수 있다. 그리고 만약 보여주고싶은 게시물 수를 설정하려면 주소창에 size를 추가해주면 된다.

짠 이렇게 가능하다. Pageable이라는 JPA를 활용하여 아주 간단하게 페이징 처리가 가능한 것이다.

 

7) 게시물 페이징

 - 게시물 리스트 페이지에서 페이징 처리

 

클릭할 수 있는 페이지로 관리하기 위해 우선은 변수로서, nowPage(현재 페이지), startPage(블럭에서 보여줄 시작 페이지), endPage(블럭에서 보여줄 마지막 페이지)를 선언해주겠다. 그리고 타임리프 문법을 살짝 설명하자면 th:text(태그 안에 데이터를 출력), th:each(반복문), th:each"${number:#number(시작번호, 끝번호)}"(시작번호에서 끝번호까지 반복) 을 뜻한다.

 

BoardController을 수정하자.

boardlist에 pageable을 넘겨주는 것을 따로 빼주어 관리하도록 Page<Board> list를 작성하였다.

 @GetMapping("/board/list")
    public String boardList(Model model, @PageableDefault(page = 0, size = 10, sort = "id", direction = Sort.Direction.DESC) Pageable pageable){ // 데이터를 담아 페이지로 보내기 위해 Model 자료형을 인자로


        Page<Board> list = boardService.boardlist(pageable);

        int nowPage = list.getPageable().getPageNumber() + 1; // 현재 페이지를 가져옴 , 0에서 시작하기에 처리를 위해 + 1
        int startPage = Math.max(nowPage - 4, 1); // Math.max(a, b) -- a 와 b 중 큰 값을 반환 --> 그냥 nowPAge - 4만 하면 nowpage가 1인 경우 -3도 가능하기에 이를 방지하기 위함
        int endPage = Math.min(nowPage + 5, list.getTotalPages()); // totalPage보다 크면 안되기에 두개 중 최소값 반환하는 Math.min을 사용

        model.addAttribute("list", list); // boardService에서 생성한 boardlist메소드를 통해 list가 반환되는데 해당 list를 "list"라는 이름으로 넘겨주겠다는 것(html에 나올 수 있게)
        model.addAttribute("nowPage", nowPage);
        model.addAttribute("startPage", startPage);
        model.addAttribute("endPage", endPage);
        return "boardlist";
    }

page와 관련된 변수 세개를 선언, 그리고 boardlist.html파일로 넘겨준다. thymeleaf를 사용하기 위해 model.addAttribute로 넘겨줄 수 있다. 

boardlist로 이동해서 출력할 수 있게 하자.

<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org">
  <head>
    <meta charset="UTF-8">
    <title>게시글 리스트 페이지</title>
  </head>
  <style>
    .layout{
        width : 500px;
        margin : 0 auto;
        margin-top : 40px;
    }
  </style>
    <body>
      <div class="layout">
        <table>
          <thead>
            <tr>
              <th>글 번호</th>
              <th>제목</th>
            </tr>
          </thead>
          <tbody>
            <tr th:each="board : ${list}">
              <td th:text="${board.id}">1</td>
              <td>
                <a th:text="${board.title}" th:href="@{/board/view(id=${board.id})}">제목입니다.</a>
              </td>
            </tr>
          </tbody>
      </table>
        <th:block th:each="page : ${#numbers.sequence(startPage, endPage)}">
          <a th:if="${page != nowPage}" th:href="@{/board/list(page = ${page-1})}" th:text="${page}"></a>
          <strong  th:if="${page == nowPage}" th:text="${page}" style="color : red"></strong>
          <!--현 페이지와 누르려는 페이지 같으면 href 작동 안해도 되게끔 if문-->
        </th:block>
        <!--th:block의 경우 굳이 tag로 감쌀필요 없는 태그를 th문법을 사용해 나타낼때 사용-->
    </div>
  </body>
</html>

boardlist.html 파일을 위와 같이 작성해주자. table 밑에 th:block을 만들어서 페이징 처리를 해준다.

이렇게 해서 실행해보면,

이렇게 페이지가 잘 나타나는 것을 볼 수 있다. 이렇게 하면 페이징 처리가 완료된 것이다.

 

이제, 게시판에서 글을 검색할 수 있는 검색창기능을 구현해보겠다.

이를 위해선 Repository에 메서드를 추가해주어야 한다.

Page<Board> findByTitleContaining(String searchKeyword, Pageable pageable);

findByTitleContaining을 추가해주고, Service로 이동한다.

public Page<Board> boardSearchList(String searchKeyword, Pageable pageable){
        return boardRepository.findByTitleContaining(searchKeyword, pageable);
    }

그리고 위와같이 boardSearchList 메서드를 추가해주자.

JPA에서 findBy(컬럼이름)Containing은 컬럼에서 키워드가 포함된 것을 찾는 것이고, findBy(칼럼이름)의 경우 컬럼에서 키워드와 동일한 것을 찾는 것이다.

그리고 Controller을 수정해줘야한다.

 @GetMapping("/board/list")
    public String boardList(Model model,
                            @PageableDefault(page = 0, size = 10, sort = "id", direction = Sort.Direction.DESC)
                            Pageable pageable,
                            String searchKeyword){ // 데이터를 담아 페이지로 보내기 위해 Model 자료형을 인자로 , 검색할 때 (searchKeyword가 있을 떄) 안할 때 구분해 if문 사용

        Page<Board> list = null;

        if(searchKeyword != null){
            list = boardService.boardSearchList(searchKeyword, pageable);
        } else {
            list = boardService.boardlist(pageable);
        }

        int nowPage = list.getPageable().getPageNumber() + 1; // 현재 페이지를 가져옴 , 0에서 시작하기에 처리를 위해 + 1
        int startPage = Math.max(nowPage - 4, 1); // Math.max(a, b) -- a 와 b 중 큰 값을 반환 --> 그냥 nowPAge - 4만 하면 nowpage가 1인 경우 -3도 가능하기에 이를 방지하기 위함
        int endPage = Math.min(nowPage + 5, list.getTotalPages()); // totalPage보다 크면 안되기에 두개 중 최소값 반환하는 Math.min을 사용

        model.addAttribute("list", list ); // boardService에서 생성한 boardlist메소드를 통해 list가 반환되는데 해당 list를 "list"라는 이름으로 넘겨주겠다는 것(html에 나올 수 있게)
        model.addAttribute("nowPage", nowPage);
        model.addAttribute("startPage", startPage);
        model.addAttribute("endPage", endPage);

        return "boardlist";
    }

searchKeyword가 들어오느냐, 마느냐에 따라 if문을 사용하여 분리해 처리할 수 있도록 설정해주었다. 만약 searchKeyword가 null이 아니면(검색을 하면) boardService.boardSearchList로 불러와 출력해주고 아닐 경우 기존의 list를 불러온다.

 

이렇게 하고 한번 실행해보겠다.

searchKeyword로 11을 넘겨주니 제목에 11이 들어가는 모두를 보여준다. 그런데 저기서 2페이지를 누르면 초기화되는 현상이 발생한다.

이런경우 url에 변수를 같이 넘겨주는 작업이 필요하다, 그렇기에 boardlist.html에서 thymeleaf로 이를 처리해주겠다.

searchKeyword = ${param.searchKeyword}를 추가하여 함께 url로 이동할 수 있도록 해주면 된다. param을 통해서 함께 넘길 수 있는 것이다. 그러면 검색하는 keyword가 있어도 다음 페이지를 눌러도 원래 list가 아닌 검색을 유지하며 이동한다.

검색창을 간단하게 추가하자.

<form th:action="@{/board/list}" method="get" >
          <input type="text" name="searchKeyword">
          <button type="submit">검색</button>
        </form>

이를 list페이지 밑에 추가해주고 실행해보면?

검색창에 11을 넣고 검색하게 되면,

get방식(url에 param으로 넘겨주는 방식)으로 잘 작동하는 것을 볼 수 있다..!

이렇게 해서 스프링 부트로 만드는 게시물 기초를 끝낼 수 있겠다.

아직 초라하고 기본적인 기능만 존재하지만 여기서 살을 붙여서 좋은 게시판을 만들 수 있을 것이다.

https://github.com/HanCoding/board_exam_project

 

GitHub - HanCoding/board_exam_project: 유튜브 영상을 위한 리포지토리

유튜브 영상을 위한 리포지토리. Contribute to HanCoding/board_exam_project development by creating an account on GitHub.

github.com

해당 게시판 만들기에 대한 원제작자 한코딩님의 github 프로젝트 주소를 올리며 마무리하도록 하겠다.