이전글보기

https://aamoos.tistory.com/675

 

[Spring Jpa] 6. 게시판 만들기 - QueryDsl 설정, 게시판 페이징 구현

QueryDsl 적용 https://aamoos.tistory.com/454 [SpringBoot] Querydsl 적용 build.gradle plugins { id 'org.springframework.boot' version '2.5.3' id 'io.spring.dependency-management' version '1.0.11.RELE..

aamoos.tistory.com

 

목표

- 목록에서 검색기능, 페이징 검색기능을 개발해보겠습니다.

 

소스

 

BoardRepositoryImpl.java 수정

package jpa.board.repositoryImpl;

import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jpa.board.dto.BoardDto;
import jpa.board.dto.QBoardDto;
import jpa.board.repository.CustomBoardRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import java.util.List;
import static jpa.board.entity.QBoard.board;
import static jpa.board.entity.QMember.member;

/**
 * packageName    : jpa.board.repositoryImpl
 * fileName       : BoardRepositoryImpl
 * author         : 김재성
 * date           : 2022-08-02
 * description    :
 * ===========================================================
 * DATE              AUTHOR             NOTE
 * -----------------------------------------------------------
 * 2022-08-02        김재성       최초 생성
 */
@Repository
public class BoardRepositoryImpl implements CustomBoardRepository {
    private final JPAQueryFactory jpaQueryFactory;

    public BoardRepositoryImpl(JPAQueryFactory jpaQueryFactory) {
        this.jpaQueryFactory = jpaQueryFactory;
    }

    @Override
    public Page<BoardDto> selectBoardList(String searchVal, Pageable pageable) {
        List<BoardDto> content = getBoardMemberDtos(searchVal, pageable);
        Long count = getCount(searchVal);
        return new PageImpl<>(content, pageable, count);
    }

    private Long getCount(String searchVal){
        Long count = jpaQueryFactory
                .select(board.count())
                .from(board)
                .where(containsSearch(searchVal))
                //.leftJoin(board.member, member)   //검색조건 최적화
                .fetchOne();
        return count;
    }

    private List<BoardDto> getBoardMemberDtos(String searchVal, Pageable pageable){
        List<BoardDto> content = jpaQueryFactory
                .select(new QBoardDto(
                         board.id
                        ,board.title
                        ,board.content
                        ,board.regDate
                        ,board.uptDate
                        ,board.viewCount
                        ,member.username))
                .from(board)
                .leftJoin(board.member, member)
                .where(containsSearch(searchVal))
                .orderBy(board.id.desc())
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .fetch();
        return content;
    }

    /**
    * @methodName : containsSearch
    * @date : 2022-08-02 오후 5:28
    * @author : 김재성
    * @Description: %키워드% 조회
    **/
    private BooleanExpression containsSearch(String searchVal){
        return searchVal != null ? board.title.contains(searchVal) : null;
    }
}
querydsl BooleanExpression 함수를 만들어서 넘어온 keyword를 조회하는 구문을 추가하였습니다. null이 리턴될경우 where 절에서 null로 검색하지않고 넘어갑니다.

 

list.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"  xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="layout/default_layout">
    <div layout:fragment="content" class="content">
        <form th:action th:object="${form}" method="get">
            <nav class="container">
                <br>
                <div class="input-group">
                    <input type="text" name="searchVal" th:value="${searchVal}" class="form-control" placeholder="제목을 입력해주세요.">
                    <button type="submit" class="btn btn-secondary">검색</button>
                </div>
                <br>
                <table class="table table-hover">
                    <colgroup>
                        <col width="2%" />
                        <col width="5%" />
                        <col width="20%" />
                        <col width="5%" />
                        <col width="5%" />
                        <col width="5%" />
                    </colgroup>
                    <thead>
                        <tr>
                            <th>
                                <label class="checkbox-inline">
                                <input type="checkbox" id="allCheckBox" class="chk">
                                </label>
                            </th>
                            <th>번호</th>
                            <th>제목</th>
                            <th>작성자</th>
                            <th>날짜</th>
                            <th>조회수</th>
                        </tr>
                    </thead>

                    <tbody>
                        <tr th:each="list, index : ${list}">
                            <td>
                                <label class="checkbox-inline">
                                    <input type="checkbox" class="chk" name="cchk" value="">
                                </label>
                            <td th:text="${totalCount - (size * number) - index.index}"></td>
                            <td><a th:text="${list.title}" href=""></a></td>
                            <td th:text="${list.username}"></td>
                            <td th:text="${#temporals.format(list.regDate, 'yyyy-MM-dd')}"></td>
                            <td th:text="${list.viewCount}"></td>
                        </tr>
                    </tbody>
                </table>
                <br>

                <div class="d-flex justify-content-end">
                    <a class="btn btn-danger">글삭제</a>
                    <a href="/write" class="btn btn-primary">글쓰기</a>
                </div>
                <br>
                <nav class="container d-flex align-items-center justify-content-center" aria-label="Page navigation example"
                     th:with="start=${(list.number/maxPage)*maxPage + 1},
                      end=(${(list.totalPages == 0) ? 1 : (start + (maxPage - 1) < list.totalPages ? start + (maxPage - 1) : list.totalPages)})">
                    <ul class="pagination">

                        <li th:if="${start > 1}" class="page-item">
                            <a th:href="@{/?(page=0, searchVal=${searchVal})}" class="page-link" href="#" aria-label="Previous">
                                <span aria-hidden="true">&laquo;&laquo;</span>
                            </a>
                        </li>

                        <li th:if="${start > 1}" class="page-item">
                            <a th:href="@{/?(page=${start - maxPage-1}, searchVal=${searchVal})}" class="page-link" href="#" aria-label="Previous">
                                <span aria-hidden="true">&laquo;</span>
                            </a>
                        </li>

                        <li th:each="page: ${#numbers.sequence(start, end)}" class="page-item" th:classappend="${list.number+1 == page} ? active">
                            <a th:href="@{/?(page=${page-1}, searchVal=${searchVal})}" th:text="${page}" class="page-link" href="#">1</a>
                        </li>

                        <li th:if="${end < list.totalPages}" class="page-item">
                            <a th:href="@{/?(page=${start + maxPage -1}, searchVal=${searchVal})}" class="page-link" href="#" aria-label="Next">
                                <span aria-hidden="true">&raquo;</span>
                            </a>
                        </li>

                        <li th:if="${end < list.totalPages}" class="page-item">
                            <a th:href="@{/?(page=${list.totalPages-1}, searchVal=${searchVal})}" class="page-link" href="#" aria-label="Next">
                                <span aria-hidden="true">&raquo;&raquo;</span>
                            </a>
                        </li>
                    </ul>
                </nav>
            </nav>
        </form>
    </div>
</html>

 

- 페이징 버튼을 클릭할때 원래는 pageNo만 넘겼으나, keyword도 같이 넘기게 추가하였습니다.

 

BoardController.java

package jpa.board.controller;

import jpa.board.dto.BoardDto;
import jpa.board.entity.Board;
import jpa.board.repository.CustomBoardRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * packageName    : jpa.board.controller
 * fileName       : BoardController
 * author         : 김재성
 * date           : 2022-08-01
 * description    :
 * ===========================================================
 * DATE              AUTHOR             NOTE
 * -----------------------------------------------------------
 * 2022-08-01        김재성       최초 생성
 */
@Controller
@RequiredArgsConstructor
public class BoardController {

    private final CustomBoardRepository customBoardRepository;

    /**
    * @methodName : list
    * @date : 2022-08-02 오후 2:07
    * @author : 김재성
    * @Description: 게시판 목록화면
    **/
    @GetMapping("/")
    public String list(String searchVal, Pageable pageable, Model model){
        Page<BoardDto> results = customBoardRepository.selectBoardList(searchVal, pageable);
        model.addAttribute("list", results);
        model.addAttribute("maxPage", 5);
        model.addAttribute("searchVal", searchVal);

        pageModelPut(results, model);
        return "board/list";
    }

    /**
    * @methodName : pageModelPut
    * @date : 2022-08-02 오후 4:36
    * @author : 김재성
    * @Description: pagenation 관련 값 model 넣기
    **/
    private void pageModelPut(Page<BoardDto> results, Model model){
        model.addAttribute("totalCount", results.getTotalElements());
        model.addAttribute("size",  results.getPageable().getPageSize());
        model.addAttribute("number",  results.getPageable().getPageNumber());
    }

    /**
    * @methodName : write
    * @date : 2022-08-02 오후 2:07
    * @author : 김재성
    * @Description: 게시판 글쓰기화면
    **/
    @GetMapping("/write")
    public String write(){
        return "board/write";
    }

    /**
    * @methodName : update
    * @date : 2022-08-02 오후 2:07
    * @author : 김재성
    * @Description: 게시판 수정화면
    **/
    @GetMapping("/update")
    public String update(){
        return "board/update";
    }
}
- 검색어를 모델에 담는것을 추가하였습니다. (searchVal)

 

결과화면

- 검색이 정상적으로 동작하고, 페이징 변경시 검색 키워드도 같이 파라미터로 넘어갑니다. 다음장에는 게시판 체크박스 이벤트를 개발해보겠습니다.

 

다음글보기

https://aamoos.tistory.com/677

 

[Spring Jpa] 8. 게시판 만들기 - 리스트 화면 체크박스 checked 제어 개발

목표 1. 최상단의 전체 체크 박스가 체크되었을때 하위 체크박스들이 전부 체크가 되고 체크가 안되면 하위 체크박스들이 체크가 안되게 수정 2. 하위 체크박스가 전부 체크되면 전체 체크박스 c

aamoos.tistory.com

 

복사했습니다!