티스토리 뷰

사소해 보였지만 매우 사소하지 않았던 애를 많이 먹은 구현사항이였다.

 

참고서적 : 자바 웹개발 워크북

 

 

요구조건

 

게시글을 선택 후 상세조회 게시글에서 '목록' 버튼을 눌러 나올때 원래라면 초기화된 기본 1페이지로 나온다.

초기화 시키지 말고 전 페이지로 보내주자.

 

예시

만약 게시판의 7페이지 게시글을 선택해 상세조회를 했을시
현재는 구현을 완료했지만 원래는 '목록'버튼을 눌렀다면
초기화된 1페이지 즉, 페이지 데이터는 사라진 채로 get하게 된다.
이걸 다시 전 페이지로 돌아오게 해보자


 

PageRequestDTO
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PageRequestDTO {

    /**
     * 현재 페이지
     */
    @Builder.Default
    @Min(value = 1)
    @Positive
    private int page = 1;

    /**
     * 페이지당 조회 게시글 수
     */
    @Builder.Default
    @Positive
    private int size = 10;

    /**
     * 게시글 몇번째 부터 보여줄지(mybatis에서 사용)
     */
    public int getSkip() {
        return (page - 1) * 10;
    }

    /* 검색조건 */
    /**
     * 날짜 시작
     */
    private LocalDate startDate;

    /**
     * 날짜 종료
     */
    private LocalDate endDate;

    /**
     * 카테고리 검색
     */
    private String categoryBoard;

    /**
     * 검색어
     */
    private String keyword;

    /**
     * 페이지 이동 link
     */
    private String link;

    /**
     * link 값 파싱 후 전달
     * @return
     */
    public String getLink() {
        StringBuilder builder = new StringBuilder();
        builder.append("page=" + this.page);

        if (categoryBoard != null) {
            builder.append("&categoryBoard=" + URLEncoder.encode(categoryBoard, StandardCharsets.UTF_8));
        }

        if (startDate != null) {
            builder.append("&startDate=" + startDate);
        }

        if (endDate != null) {
            builder.append("&endDate=" + endDate);
        }

        if (keyword != null) {
            builder.append("&keyword=" + URLEncoder.encode(keyword, StandardCharsets.UTF_8));
        }

        return builder.toString();
    }
}

주목할 부분은 맨 아래의 private String link와 그 link를 getter한 getLink() 메서드 이다.

getLink() 메서드는 검색조건에 데이터가 있을시 페이지 정보와 같이 파라미터로 달고 다니며 활동할 녀석들이다.

기본적으로 'page'파라미터는 바로 url옆에 붙으며 정보를 전달하고

그 아래 if문들은 검색조건의 상황에 맞춰서 추가되면서 파라미터에 들어갈 것이다.

 


HTML - List
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/basic.html}">

<div layout:fragment="content">

    <div class="container">
        <form action="/free" method="get">
            <div class="row g-3" style="margin: auto">
                <input type="hidden" name="categoryBoard" value="FREE">

                <div class="col-auto">
                    <input type="date" class="form-control" name="startDate">
                </div>
                <div class="col-auto">
                    ~
                </div>
                <div class="col-auto">
                    <input type="date" class="form-control" name="endDate">
                </div>

                <div class="col-auto">
                    <input type="text" class="form-control" name="keyword" th:value="${pageRequestDTO.keyword}">
                </div>
                <div class="col-auto input-group-append">
                    <button class="btn btn-outline-secondary searchBtn" type="submit">검색</button>
                </div>
            </div>
        </form>

        <div class="row mt-3">
            <div class="col">
                <div class="card">
                    <div class="card-header">
                        자유게시판
                    </div>
                    <div class="card-body">
                        <table class="table text-center table-hover">
                            <thead class="table-light">
                            <tr>
                                <th style="width: 50%">제목</th>
                                <th>작성자</th>
                                <th>조회수</th>
                                <th>등록일시</th>
                                <th>수정일시</th>
                            </tr>
                            </thead>
                            <tbody th:with="link = ${pageRequestDTO.getLink()}">
                            <tr th:each="list : ${freeBoardList}">
                                <td><a th:href="|@{/free/{boardNo}(boardNo=${list.boardNo})}?${link}|">
                                    [[ ${list.boardTitle} ]]
                                </a></td>
                                <td>[[ ${list.boardWriter} ]]</td>
                                <td>[[ ${list.boardView} ]]</td>
                                <td>[[ ${list.boardRegDate} ]]</td>
                                <td th:text="${list.boardModDate == null ? '-' : list.boardModDate}"></td>
                            </tr>
                            </tbody>
                        </table>

                        <div>
                            <ul class="pagination justify-content-center">
                                <li class="page-item" th:if="${pagination.prev}">
                                    <a class="page-link" th:data-num="${pagination.startPage - 1}">이전</a>
                                </li>

                                <th:block th:each="i : ${#numbers.sequence(pagination.startPage, pagination.endPage)}">
                                    <li th:class="${pagination.page == i} ? 'page-item active' : 'page-item'">
                                        <a class="page-link" th:data-num="${i}">[[ ${i} ]]</a>
                                    </li>
                                </th:block>

                                <li class="page-item" th:if="${pagination.next}">
                                    <a class="page-link" th:data-num="${pagination.endPage + 1}">다음</a>
                                </li>
                            </ul>
                        </div>

                        <a href="/free/write" role="button" class="btn btn-primary float-end">글쓰기</a>
                    </div>
                </div>
            </div>
        </div>

    </div>
</div>

<script layout:fragment="script" th:inline="javascript">

    document.querySelector(".pagination").addEventListener("click", function (e) {
        e.preventDefault()
        e.stopPropagation()

        const target = e.target

        if(target.tagName !== 'A') {
            return
        }

        const num = target.getAttribute("data-num")
        const formObj = document.querySelector("form")

        formObj.innerHTML += `<input type='hidden' name='page' value='${num}'>`
        formObj.submit();

    },false)

</script>

매우 복잡하지만..(화면은 정말 초보자 수준이다..)

일단 유의하며 볼 부분은 4부분이다.

               
               //검색조건에 keyword부분
               <div class="col-auto">
                    <input type="text" class="form-control" name="keyword" th:value="${pageRequestDTO.keyword}">
                </div>
                
                ```
                            //테이블에 제목, 상세조회로 넘어가는 a태그
                            <tbody th:with="link = ${pageRequestDTO.getLink()}">
                            <tr th:each="list : ${freeBoardList}">
                                <td><a th:href="|@{/free/{boardNo}(boardNo=${list.boardNo})}?${link}|">
                                    [[ ${list.boardTitle} ]]
                                </a></td>
                                
                                ```

                        //페이지네이션
                        <div>
                            <ul class="pagination justify-content-center">
                                <li class="page-item" th:if="${pagination.prev}">
                                    <a class="page-link" th:data-num="${pagination.startPage - 1}">이전</a>
                                </li>

                                <th:block th:each="i : ${#numbers.sequence(pagination.startPage, pagination.endPage)}">
                                    <li th:class="${pagination.page == i} ? 'page-item active' : 'page-item'">
                                        <a class="page-link" th:data-num="${i}">[[ ${i} ]]</a>
                                    </li>
                                </th:block>

                                <li class="page-item" th:if="${pagination.next}">
                                    <a class="page-link" th:data-num="${pagination.endPage + 1}">다음</a>
                                </li>
                            </ul>
                        </div>
                        
                        
                        ```
                        
//스크립트 태그
<script layout:fragment="script" th:inline="javascript">

    document.querySelector(".pagination").addEventListener("click", function (e) {
        e.preventDefault()
        e.stopPropagation()

        const target = e.target

        if(target.tagName !== 'A') {
            return
        }

        const num = target.getAttribute("data-num")
        const formObj = document.querySelector("form")

        formObj.innerHTML += `<input type='hidden' name='page' value='${num}'>`
        formObj.submit();

    },false)

</script>

주석 스크립트 태그 부분은 페이지네이션을 구현하기 위해 나온것이다.

 

keyword의 경우 th:value를 사용하지 않으면 검색조건으로 검색해도 데이터가 가지 않는다.

테이블에 제목 부분은 th:with이 변수를 지정하는 타임리프 문법이고 pageRequestDTO의 getLink()메서드를 찾아서 데이터를 넣어준다.

마지막 페이지네이션은 target으로 지정된 a태그를 data-num안에 formObj를 입력하여 페이지를 이동할때마다 전달하는 것이다.

 

'자바 웹개발 워크북'의 코드를 참고하여 구현하였다.

 

 

HTML - View

게시판을 구현했다면 제목으로 들어간 상세조회 페이지에서도 전달받은 파라미터를 넘겨줘야한다.

                    <div class="my-4">
                        <div class="float-end" th:with="link = ${pageRequestDTO.getLink()}">
                            <a th:href="|@{/free}?${link}|" class="text-decoration-none">
                                <button type="button" class="btn btn-secondary">목록</button>
                            </a>
                            <a th:href="@{/free/modify/{boardNo}(boardNo=${dto.boardNo})}" class="text-decoration-none">
                                <button type="button" class="btn btn-primary">수정</button>
                            </a>
                        </div>
                    </div>

전체를 볼 필요없이 목록으로 돌아가는 버튼에 th:with 변수를 지정하여 link를 전달해주면 된다.

 

그리고 그 전에 Controller에 고쳐야 될 부분이 있다.

    /**
     * 자유게시판 특정게시글 조회
     * @param boardNo
     * @param model
     * @return
     */
    @GetMapping("/free/{boardNo}")
    public String getFreeView(@PathVariable Long boardNo,
                              /* 추가해야될 파라미터 */
                              PageRequestDTO pageRequestDTO,
                              Model model) {

        BoardDTO dto = freeService.getFreeViewArticle(boardNo);
        List<ReplyDTO> replyDtoList = replyService.getReply(boardNo);

        model.addAttribute("dto", dto);
        model.addAttribute("replyDto", replyDtoList);
        return "board/free/freeView";
    }

파라미터를 사용하진 않지만 pageRequestDTO 파라미터를 만들어 보내주니 Controller에선 이 데이터를 매개변수로 받아주어야 한다.

 

그 후 목록 버튼을 누른다면 검색조건이든 페이지데이터든 달고 다니며 사용할 수 있을것이다.

 


 

검색조건 + 페이징 을 달고다니는 문제는 궁금하여 다른 커뮤니티 사이트도 많이 참고했다.

그 결과 내가 확인한 거의 모든 사이트들은 url에 파라미터 값으로 전달하는 것을 알게되었다.

많이 더러워지는것 같다고 생각했지만 어쩔 수 없이 노가다로 하나하나 옮겨주어야 되는 문제였었다.

 


https://github.com/Mo-Greene/SpringBoot_Admin_MPA

 

GitHub - Mo-Greene/SpringBoot_Admin_MPA: MPA 관리자 게시판

MPA 관리자 게시판. Contribute to Mo-Greene/SpringBoot_Admin_MPA development by creating an account on GitHub.

github.com

글로 설명하기 어려운 부분이라 참고를 원한다면..

Comments