FireDrago

[js] 페이지네이션 만들기 본문

프로그래밍/JavaScript

[js] 페이지네이션 만들기

화이용 2024. 6. 18. 11:47

타임리프를 사용하여 레시피를 받아오는 페이지를 만들어 보자!

페이지네이션은 많은 양의 데이터를 나누어 표시할 때 유용하게 사용된다.

@GetMapping("/all")
public String readAllRecipes(@PageableDefault(size = 2) Pageable pageable,
                             Model model) {
    Page<Recipe> page = recipeService.findAllRecipes(pageable);
    List<Recipe> allRecipes = page.getContent();

    model.addAttribute("recipes", allRecipes);
    model.addAttribute("totalPages", page.getTotalPages());
    model.addAttribute("currentPage", page.getNumber() + 1);
    return "/recipe/allRecipe";
}
<body>
    <div>
        <form action="/recipe/search">
            <select name="type">
                <option value="ingredient">재료명</option>
                <option value="recipe">요리명</option>
            </select>
            <input type="text" name="q"  value="검색어를 입력하세요"/>
            <button type="submit">검색</button>
        </form>
    </div>

    <a th:each="recipe : ${recipes}" th:href="@{/recipe/{id}(id = ${recipe.id})}">
        <img th:src="|/file/${recipe.thumbnail.storedFileName}|" width="300" height="300">
        <h2 th:text="${recipe.title}"></h2>
        <p th:text="${recipe.instruction}"></p>
    </a>

    <div id="pagination" th:data-total-pages="${totalPages}" th:data-current-page="${currentPage}"></div>
</body>
 
 

th:data-변수명 : Thymeleaf 변수를 통해 전달된 서버 측 데이터를 기반으로 HTML 요소에 맞춤 데이터 속성을 동적으로 설정할 수 있다. 총페이지 수와 현재 페이지를 설정한다.

 

자바스크립트 이벤트 리스너를 작성한다.

document.addEventListener("DOMContentLoaded", function() {
    const totalPages = parseInt(document.getElementById('pagination').dataset.totalPages);
    const currentPage = parseInt(document.getElementById('pagination').dataset.currentPage);
    generatePagination(totalPages, currentPage);
});
 
 

"DomContentLoaded" : Dom (HTML 객체 구조) 가 완성된 후에 실행된다.

아직 img, css 같은 외부 소스가 적용되기 전이다. <==> "load" 는 img, css 모두 적용된 후에 실행된다.

 

document.getElementById('pagination').dataset.totalPages : 

타임리프에서 th:data-... 로 전달한 변수를 dataset 을 사용하여 받아온다.

 

이벤트 리스너는 Dom 구조가 완성된 후에 변수 파라미터와 함께 generatePagination 메서드를 호출한다. 

 

본격적인 페이지네이션 생성 코드를 만들어보자

function generatePagination(totalPages, currentPage) {
    const paginationContainer = document.getElementById('pagination');
    paginationContainer.innerHTML = '';

    // 첫번째 페이지 생성
    paginationContainer.innerHTML += createPageLink(1, currentPage === 1);

    if (totalPages > 1) {
        // 현재 페이지가 5 이상이면 ... 표시
        if (currentPage > 4) {
            paginationContainer.innerHTML += createEllipsis();
        }

        // 2페이지 ~ 마지막 바로앞 페이지 생성
        for (let i = Math.max(2, currentPage - 3); i <= Math.min(totalPages - 1, currentPage + 3); i++) {
            paginationContainer.innerHTML += createPageLink(i, currentPage === i);
        }

        // 현재 페이지가 총 페이지 -3 보다 작으면 ... 표시
        if (currentPage < totalPages - 3) {
            paginationContainer.innerHTML += createEllipsis();
        }
        
        // 마지막 페이지 표시
        paginationContainer.innerHTML += createPageLink(totalPages, totalPages === currentPage);
    }
}

function createPageLink(pageNum, isCurrent = false) {
    return `<a href='?page=${pageNum - 1}' class='${isCurrent ? "selected" : ""}'>${pageNum}</a>`
}

function createEllipsis() {
    return `<span>...</span>`
}
 
 

paginationContainer.innerHTML : HTML 태그를 넣어줄때 호출한다.

+) 자바스크립트에서 '==' : 타입 변경 후 비교 '===' 타입변경 없이 비교한다.

     '==' 은 문자열과 정수를 같다고 할 수 있다. '==='를 사용하자

 

  • 첫 번째 페이지는 항상 표시된다. 만약 현재 페이지가 1이라면 createPageLink 메서드에서 class="selected" 속성이 추가된다. CSS로 selected 속성을 스타일링하여 선택된 페이지를 강조한다.
  • 현재 페이지가 5 이상이면 첫 페이지 다음에 '...' 표시된다. 현재 페이지와 앞뒤 3페이지만 표시되고 나머지 페이지는 '...'으로 생략된다.
  • 반복문을 사용하여 2페이지부터 마지막 바로 앞 페이지까지 표시한다. 이때 2페이지와 현재 페이지 -3에서 시작할지, 마지막 바로 앞 페이지와 현재 페이지 +3에서 끝날지를 판단한다.
  • 마지막 페이지를 표시한다. 현재 페이지에 따라 selected 클래스가 추가될 수 있다.