ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 쇼핑몰 프로젝트 --페이지네이션, 유효성검사, 리뷰 정렬(화면구현)
    쇼핑몰 프로젝트 2024. 6. 28. 02:05

    itemList, itemCategoryList, categoryGet, itemReviewGet페이지네이션 추가 

     

     

    itemList페이지네이션 추가 

    itemRepository

    // 아이템 전체 총 개수 조회 (페이징 하기 위해)
    public int countAll(String keyword) {
        String queryString = "select count(i) from Item i";
        if (keyword != null && !keyword.isEmpty()) {
            queryString += " where i.itemName like :keyword";
        }
        TypedQuery<Long> query = em.createQuery(queryString, Long.class);
        if (keyword != null && !keyword.isEmpty()) {
            query.setParameter("keyword", "%" + keyword + "%");
        }
        return query.getSingleResult().intValue();
    }

     

     

    itemService

    //아이템 전체 페이징 개수 조회
    public int countAll(String keyword) {
        return itemRepository.countAll(keyword);
    }

     

     

    itemController

    //아이템 전체 조회
    @GetMapping("/ypjs/item/get")
    public String getAllItem(
                                     @RequestParam(value = "page",defaultValue = "0") int page,
                                     @RequestParam(value = "size",defaultValue = "3") int size,
                                     @RequestParam(value = "sortBy", defaultValue = "itemId") String sortBy,
                                     @RequestParam(value = "keyword", required = false) String keyword,
                                     Model model) {
    
        Pageable pageable = PageRequest.of(page, size);
    
        List<ItemListDto> items = itemService.findAllItem(keyword, pageable, sortBy);
    
        //총 페이지 수 계산
        int totalPages = Page.totalPages(itemService.countAll(keyword), size);
    
        model.addAttribute("items", items);
        model.addAttribute("sortBy", sortBy); // 정렬 옵션을 다시 모델에 추가
        model.addAttribute("keyword", keyword); //검색조건 유지
        model.addAttribute("page",page); //페이징
        model.addAttribute("size",size); //페이징
        model.addAttribute("totalPages", totalPages); //총 페이지 수
    
    
        return "item/itemList";
    
    
    }
    

     

    오늘 서비스, 레파지토리는 페이징 부분만 추가, 나머지 서비스, 레파지토리는 전에 적어논 글에 있음

     

     

    item/itemList 페이지네이션 부분만 추가 됨

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org" lang="en">
    
    <head th:replace="frame/header :: head">
        <title>Zay Shop - Product Listing Page</title>
    </head>
    
    <style>
        .fixed-size-img {
            width: 300px; /* 원하는 너비로 설정 */
            height: 300px; /* 원하는 높이로 설정 */
            object-fit: cover; /* 이미지를 크기에 맞게 조정 */
        }
    </style>
    
    <body>
    <!--header-->
    <header th:replace="frame/header :: header"></header>
    <script src="/js/item/itemRatingsGet.js"></script>
    
    <!-- Start Content -->
    <div class="container py-5">
        <div class="row">
    
    
            <div class="container-fluid mt-3">
                <div class="row">
                    <div class="col-md-12">
                        <ul class="list-inline shop-top-menu pb-3 pt-1">
                            <li class="list-inline-item">
                                <a class="h3 text-dark text-decoration-none mr-3" th:href="@{/ypjs/item/post}">상품 등록</a>
                            </li>
                            <li class="list-inline-item">
                                <a class="h3 text-dark text-decoration-none mr-3" th:href="@{/ypjs/category/post}">카테고리 등록</a>
                            </li>
                            <li class="list-inline-item">
                                <a class="h3 text-dark text-decoration-none mr-3" th:href="@{/ypjs/category/get}">카테고리 보기</a>
                            </li>
    
                        </ul>
                    </div>
                </div>
            </div>
    
    
    
            <div class="col-lg-3">
                <h1 class="h2 pb-4">Categories</h1>
                <ul class="list-unstyled templatemo-accordion">
                    <li class="pb-3">
                        <a class="collapsed d-flex justify-content-between h3 text-decoration-none" href="#">
                            Women
                            <i class="fa fa-fw fa-chevron-circle-down mt-1"></i>
                        </a>
                        <ul class="collapse show list-unstyled pl-3">
                            <li><a class="text-decoration-none" th:href="@{/ypjs/categoryItem/get/{categoryId}(categoryId=3)}">Outer</a></li>
                            <li><a class="text-decoration-none" th:href="@{/ypjs/categoryItem/get/{categoryId}(categoryId=4)}">Top</a></li>
                            <li><a class="text-decoration-none" th:href="@{/ypjs/categoryItem/get/{categoryId}(categoryId=5)}">Bottom</a></li>
                        </ul>
                    </li>
                    <li class="pb-3">
                        <a class="collapsed d-flex justify-content-between h3 text-decoration-none" href="#">
                            Man
                            <i class="pull-right fa fa-fw fa-chevron-circle-down mt-1"></i>
                        </a>
                        <ul id="collapseTwo" class="collapse list-unstyled pl-3">
                            <li><a class="text-decoration-none" th:href="@{/ypjs/categoryItem/get/{categoryId}(categoryId=6)}">Outer</a></li>
                            <li><a class="text-decoration-none" th:href="@{/ypjs/categoryItem/get/{categoryId}(categoryId=7)}">Top</a></li>
                            <li><a class="text-decoration-none" th:href="@{/ypjs/categoryItem/get/{categoryId}(categoryId=8)}">Bottom</a></li>
                        </ul>
                    </li>
    
                </ul>
            </div>
    
    
    
    
    
    
    
            <div class="col-lg-9">
                <div class="row">
                    <div class="col-md-6">
                        <ul class="list-inline shop-top-menu pb-3 pt-1">
                            <li class="list-inline-item">
                                <a class="h3 text-dark text-decoration-none mr-3" th:href="@{/ypjs/item/get}">All Items</a>
                            </li>
    
                        </ul>
                    </div>
    
    
    
    
                    <div class="col-lg-6">
                        <!-- 검색 폼 및 정렬 폼 통합 -->
                        <form th:action="@{/ypjs/item/get}" method="get" class="input-group">
                            <input type="text" id="keyword" name="keyword" class="form-control"
                                   placeholder="Search by item name" th:value="${keyword}">
                            <div class="input-group-append">
                                <button type="submit" class="btn btn-link text-dark">
                                    <i class="fa fa-search"></i>
                                </button>
                            </div>
                            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                            <div class="col-auto">
                                <select class="form-control" name="sortBy" id="sortBy" onchange="this.form.submit()">
                                    <option value="itemId" th:selected="${sortBy == 'itemId'}">최신순</option>
                                    <option value="itemRatings" th:selected="${sortBy == 'itemRatings'}">별점 높은 순</option>
                                    <option value="likeCount" th:selected="${sortBy == 'likeCount'}">찜많은순</option>
                                </select>
                            </div>
                        </form>
                    </div>
    
    
    
    
    
    <!--                반별 추가-->
    
    <!--                <div class="container-fluid mt-3">-->
    <!--                    <div class="row">-->
    <!--                        &lt;!&ndash; items 리스트의 각 항목을 반복 &ndash;&gt;-->
    <!--                        <div th:each="item : ${items}" class="col-md-4">-->
    <!--                            <div class="card mb-4 product-wap rounded-0">-->
    <!--                                &lt;!&ndash; 제품 이미지 &ndash;&gt;-->
    <!--                                <div class="card rounded-0">-->
    <!--                                    <a th:href="@{/ypjs/item/get/{itemId}(itemId=${item.itemId})}">-->
    <!--                                        <img class="card-img rounded-0 img-fluid fixed-size-img" th:src="${item.itemFilePath}" alt="product-item">-->
    <!--                                        &lt;!&ndash; 필요한 경우 오버레이나 추가 요소를 여기에 추가할 수 있습니다 &ndash;&gt;-->
    <!--                                        <div class="card-img-overlay rounded-0 product-overlay d-flex align-items-center justify-content-center">-->
    <!--                                            &lt;!&ndash; 추가적인 콘텐츠가 필요하다면 여기에 추가할 수 있습니다 &ndash;&gt;-->
    <!--                                            <ul class="list-unstyled">-->
    <!--                                                &lt;!&ndash; 리스트의 다른 요소들 &ndash;&gt;-->
    <!--                                            </ul>-->
    <!--                                        </div>-->
    <!--                                    </a>-->
    <!--                                </div>-->
    <!--                                &lt;!&ndash; 제품 정보 &ndash;&gt;-->
    <!--                                <div class="card-body">-->
    <!--                                    &lt;!&ndash; 제품 링크 &ndash;&gt;-->
    <!--                                    <a th:href="@{/ypjs/item/get/{itemId}(itemId=${item.itemId})}" id="itemName" class="font-weight-bold" th:text="${item.itemName}"></a>-->
    
    <!--                                    &lt;!&ndash; 별점 및 평점 표시 &ndash;&gt;-->
    <!--                                    <div class="d-flex justify-content-center mb-1 align-items-center">-->
    <!--                                        <ul class="list-unstyled d-flex mb-0" id="starRating" th:attr="data-rating=${item.itemRatings}">-->
    <!--                                            &lt;!&ndash; 별 아이콘은 JavaScript로 생성됩니다 &ndash;&gt;-->
    <!--                                        </ul>&nbsp;&nbsp;&nbsp;&nbsp;-->
    <!--                                        <span id="itemRatings" class="font-weight-bold ml-2" th:text="${item.itemRatings}"></span>-->
    <!--                                    </div>-->
    
    <!--                                    &lt;!&ndash; 가격 표시 &ndash;&gt;-->
    <!--                                    <span id="itemPrice" class="font-weight-bold" th:text="${item.itemPrice}"></span>-->
    <!--                                    <span class="font-weight-bold">원</span>-->
    <!--                                </div>-->
    <!--                            </div>-->
    <!--                        </div>-->
    <!--                    </div>-->
    <!--                </div>-->
    
    <!--                <script>-->
    <!--                    document.addEventListener("DOMContentLoaded", function() {-->
    <!--                        var starRatingContainers = document.querySelectorAll("#starRating");-->
    
    <!--                        starRatingContainers.forEach(function(starRatingContainer) {-->
    <!--                            var rating = parseFloat(starRatingContainer.getAttribute("data-rating"));-->
    <!--                            starRatingContainer.innerHTML = generateStarRating(rating);-->
    <!--                        });-->
    <!--                    });-->
    
    <!--                    function generateStarRating(rating) {-->
    <!--                        var starsHTML = '';-->
    <!--                        var fullStars = Math.floor(rating);  // 정수 부분의 별 개수-->
    <!--                        var halfStar = (rating % 1 >= 0.1 && rating % 1 <= 0.9) ? 1 : 0; // 반 별 조건-->
    
    <!--                        // 정수 부분의 별 추가-->
    <!--                        for (var i = 0; i < fullStars; i++) {-->
    <!--                            starsHTML += '<i class="text-warning fa fa-star"></i>';-->
    <!--                        }-->
    
    <!--                        // 반 별 추가-->
    <!--                        if (halfStar) {-->
    <!--                            starsHTML += '<i class="text-warning fa fa-star-half-alt"></i>';-->
    <!--                        }-->
    
    <!--                        // 남은 빈 별 추가-->
    <!--                        var emptyStars = 5 - fullStars - halfStar;-->
    <!--                        for (var i = 0; i < emptyStars; i++) {-->
    <!--                            starsHTML += '<i class="text-muted fa fa-star"></i>';-->
    <!--                        }-->
    
    <!--                        return starsHTML;-->
    <!--                    }-->
    <!--                </script>-->
    
    
    
    
    
    <!--                꽉찬 별만-->
                    <div class="container-fluid mt-3">
                        <div class="row">
                            <!-- items 리스트의 각 항목을 반복 -->
                            <div th:each="item : ${items}" class="col-md-4">
                                <div class="card mb-4 product-wap rounded-0">
                                    <!-- 제품 이미지 -->
                                    <div class="card rounded-0">
                                        <a th:href="@{/ypjs/item/get/{itemId}(itemId=${item.itemId})}">
                                            <img class="card-img rounded-0 img-fluid fixed-size-img" th:src="${item.itemFilePath}" alt="product-item">
                                            <!-- 필요한 경우 오버레이나 추가 요소를 여기에 추가할 수 있습니다 -->
                                            <div class="card-img-overlay rounded-0 product-overlay d-flex align-items-center justify-content-center">
                                                <!-- 추가적인 콘텐츠가 필요하다면 여기에 추가할 수 있습니다 -->
                                                <ul class="list-unstyled">
                                                    <!-- 리스트의 다른 요소들 -->
                                                </ul>
                                            </div>
                                        </a>
                                    </div>
                                    <!-- 제품 정보 -->
                                    <div class="card-body">
                                        <!-- 제품 링크 -->
                                        <a th:href="@{/ypjs/item/get/{itemId}(itemId=${item.itemId})}" id="itemName" class="font-weight-bold" th:text="${item.itemName}"></a>
    
                                        <!-- 별점 및 평점 표시 -->
                                        <div class="d-flex justify-content-center mb-1 align-items-center">
                                            <ul class="list-unstyled d-flex mb-0" id="starRating" th:attr="data-rating=${item.itemRatings}">
                                                <!-- 별 아이콘은 JavaScript로 생성됩니다 -->
                                            </ul> &nbsp;&nbsp;&nbsp;&nbsp;
                                            <span id="itemRatings" class="font-weight-bold ml-2" th:text="${item.itemRatings}"></span>
                                        </div>
    
                                        <!-- 가격 표시 -->
                                        <span id="itemPrice" class="font-weight-bold" th:text="${item.itemPrice}"></span>
                                        <span class="font-weight-bold">원</span>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
    
    
    
                </div>
            </div>
    
        </div>
    </div>
    
    
    
    <!-- 페이지네이션 시작-->
    <nav th:unless="${#lists.isEmpty(items)}" class="mt-4">
        <ul class="pagination justify-content-center">
            <li class="page-item" th:classappend="${page == 0} ? 'disabled'">
                <a class="page-link" th:href="@{'/ypjs/item/get?page=' + ${page - 1} + '&size=' + ${size} + '&sortBy=' + ${sortBy} + '&keyword=' + ${#strings.defaultString(keyword, '')}}">이전</a>
            </li>
            <li class="page-item" th:each="i : ${#numbers.sequence(0, totalPages - 1)}" th:classappend="${page == i} ? 'active'">
                <a class="page-link" th:href="@{'/ypjs/item/get?page=' + ${i} + '&size=' + ${size} + '&sortBy=' + ${sortBy} + '&keyword=' + ${#strings.defaultString(keyword, '')}}" th:text="${i + 1}">1</a>
            </li>
            <li class="page-item" th:classappend="${page == totalPages - 1} ? 'disabled'">
                <a class="page-link" th:href="@{'/ypjs/item/get?page=' + ${page + 1} + '&size=' + ${size} + '&sortBy=' + ${sortBy} + '&keyword=' + ${#strings.defaultString(keyword, '')}}">다음</a>
            </li>
        </ul>
    </nav>
    
    <!--페이지네이션 끝-->
    
    
    
    <!-- End Content -->
    
    <!-- Start Brands -->
    <section class="bg-light py-5">
        <div class="container my-4">
            <div class="row text-center py-3">
                <div class="col-lg-6 m-auto">
                    <h1 class="h1">Our Brands</h1>
                    <p>
                        Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
                        Lorem ipsum dolor sit amet.
                    </p>
                </div>
                <div class="col-lg-9 m-auto tempaltemo-carousel">
                    <div class="row d-flex flex-row">
                        <!--Controls-->
                        <div class="col-1 align-self-center">
                            <a class="h1" href="#multi-item-example" role="button" data-bs-slide="prev">
                                <i class="text-light fas fa-chevron-left"></i>
                            </a>
                        </div>
                        <!--End Controls-->
    
                        <!--Carousel Wrapper-->
                        <div class="col">
                            <div class="carousel slide carousel-multi-item pt-2 pt-md-0" id="multi-item-example" data-bs-ride="carousel">
                                <!--Slides-->
                                <div class="carousel-inner product-links-wap" role="listbox">
    
                                    <!--First slide-->
                                    <div class="carousel-item active">
                                        <div class="row">
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_01.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_02.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_03.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_04.png" alt="Brand Logo"></a>
                                            </div>
                                        </div>
                                    </div>
                                    <!--End First slide-->
    
                                    <!--Second slide-->
                                    <div class="carousel-item">
                                        <div class="row">
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_01.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_02.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_03.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_04.png" alt="Brand Logo"></a>
                                            </div>
                                        </div>
                                    </div>
                                    <!--End Second slide-->
    
                                    <!--Third slide-->
                                    <div class="carousel-item">
                                        <div class="row">
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_01.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_02.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_03.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_04.png" alt="Brand Logo"></a>
                                            </div>
                                        </div>
                                    </div>
                                    <!--End Third slide-->
    
                                </div>
                                <!--End Slides-->
                            </div>
                        </div>
                        <!--End Carousel Wrapper-->
    
                        <!--Controls-->
                        <div class="col-1 align-self-center">
                            <a class="h1" href="#multi-item-example" role="button" data-bs-slide="next">
                                <i class="text-light fas fa-chevron-right"></i>
                            </a>
                        </div>
                        <!--End Controls-->
                    </div>
                </div>
            </div>
        </div>
    </section>
    <!--End Brands-->
    
    
    <footer th:replace="frame/footer :: footer"></footer>
    
    </body>
    
    </html>

     

     

     

     

     

     

    itemCategoryList 페이지네이션 

     

    itemRepository

    //카테고리 별 아이템 총 개수 조회(페이징)
    public int countAllCategoryItem(String keyword, Long categoryId) {
        // 기본 쿼리 설정
        String queryString = "select count(distinct i) from Item i join i.category c where i.category.categoryId = :categoryId";
    
        // 검색 조건 추가
        if (keyword != null && !keyword.isEmpty()) {
            queryString += " and i.itemName like :keyword";
        }
    
        // 쿼리 생성
        TypedQuery<Long> query = em.createQuery(queryString, Long.class);
        query.setParameter("categoryId", categoryId);
    
        // 검색 키워드 파라미터 설정
        if (keyword != null && !keyword.isEmpty()) {
            query.setParameter("keyword", "%" + keyword + "%");
        }
    
        // 결과 반환
        return query.getSingleResult().intValue();
    }
    

     

     

    itemService

    //카테고리 별 아이템 페이징 개수 조회
    public int countAllCategoryItem(String keyword, Long categoryId) {
        return itemRepository.countAllCategoryItem(keyword,categoryId);
    }

     

     

     

    itemController

    //카테고리당 아이템 조회(정렬,검색,페이징(페이징받아서 페이징))
    @GetMapping("/ypjs/categoryItem/get/{categoryId}")
    public String getAllCategoryItem(@PathVariable("categoryId") Long categoryId,
                                     @RequestParam(value = "page",defaultValue = "0") int page,
                                     @RequestParam(value = "size",defaultValue = "3") int size,
                                     @RequestParam(value = "sortBy", defaultValue = "itemId") String sortBy,
                                     @RequestParam(value = "keyword", required = false) String keyword,
                             Model model) {
    
        Pageable pageable = PageRequest.of(page, size);
    
        Category category = categoryService.findOneCategory(categoryId);
    
        List<ItemListDto> items = itemService.finaAllItemPagingSortBy(categoryId, keyword, pageable, sortBy);
    
    
        //총 페이지 수 계산
        int totalPages = Page.totalPages(itemService.countAllCategoryItem(keyword, categoryId), size);
    
        model.addAttribute("items",items);
        model.addAttribute("category", category);
        model.addAttribute("sortBy", sortBy); // 정렬 옵션을 다시 모델에 추가
        model.addAttribute("keyword", keyword); //검색조건 유지
        model.addAttribute("page",page); //페이징
        model.addAttribute("size",size); //페이징
        model.addAttribute("totalPages", totalPages); //총 페이지 수
    
    
    
    
    
        return "item/itemCategoryList";
    
    
    }
    
    

     

     

    item/itemCategoryList

    페이지네이션 부분만 추가

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org" lang="en">
    
    <head th:replace="frame/header :: head">
        <title>Zay Shop - Product Listing Page</title>
    </head>
    
    <style>
        .fixed-size-img {
            width: 300px; /* 원하는 너비로 설정 */
            height: 300px; /* 원하는 높이로 설정 */
            object-fit: cover; /* 이미지를 크기에 맞게 조정 */
        }
    </style>
    
    <body>
    <!--header-->
    <header th:replace="frame/header :: header"></header>
    <script src="/js/item/itemRatingsGet.js"></script>
    
    <!-- Start Content -->
    <div class="container py-5">
        <div class="row">
    
            <div class="col-lg-3">
                <h1 class="h2 pb-4">Categories</h1>
                <ul class="list-unstyled templatemo-accordion">
                    <li class="pb-3">
                        <a class="collapsed d-flex justify-content-between h3 text-decoration-none" href="#">
                            Women
                            <i class="fa fa-fw fa-chevron-circle-down mt-1"></i>
                        </a>
                        <ul class="collapse show list-unstyled pl-3">
                            <li><a class="text-decoration-none" th:href="@{/ypjs/categoryItem/get/{categoryId}(categoryId=3)}">Outer</a></li>
                            <li><a class="text-decoration-none" th:href="@{/ypjs/categoryItem/get/{categoryId}(categoryId=4)}">Top</a></li>
                            <li><a class="text-decoration-none" th:href="@{/ypjs/categoryItem/get/{categoryId}(categoryId=5)}">Bottom</a></li>
                        </ul>
                    </li>
                    <li class="pb-3">
                        <a class="collapsed d-flex justify-content-between h3 text-decoration-none" href="#">
                            Man
                            <i class="pull-right fa fa-fw fa-chevron-circle-down mt-1"></i>
                        </a>
                        <ul id="collapseTwo" class="collapse list-unstyled pl-3">
                            <li><a class="text-decoration-none" th:href="@{/ypjs/categoryItem/get/{categoryId}(categoryId=6)}">Outer</a></li>
                            <li><a class="text-decoration-none" th:href="@{/ypjs/categoryItem/get/{categoryId}(categoryId=7)}">Top</a></li>
                            <li><a class="text-decoration-none" th:href="@{/ypjs/categoryItem/get/{categoryId}(categoryId=8)}">Bottom</a></li>
                        </ul>
                    </li>
    
                </ul>
            </div>
    
            <div class="col-lg-9">
                <div class="row">
                    <div class="col-md-6">
                        <ul class="list-inline shop-top-menu pb-3 pt-1">
                            <li class="list-inline-item">
                                <a class="h3 text-dark text-decoration-none mr-3" th:href="@{/ypjs/item/get}">All Items</a>
                            </li>
    
                        </ul>
                    </div>
    
    
    
    
                    <div class="col-lg-6">
                        <!-- 검색 폼 및 정렬 폼 통합 -->
                        <form th:action="@{/ypjs/categoryItem/get/{categoryId}(categoryId=${category.categoryId})}" method="get" class="input-group">
                            <input type="text" id="keyword" name="keyword" class="form-control"
                                   placeholder="Search by item name" th:value="${keyword}">
                            <div class="input-group-append">
                                <button type="submit" class="btn btn-link text-dark">
                                    <i class="fa fa-search"></i>
                                </button>
                            </div>
                            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                            <div class="col-auto">
                                <select class="form-control" name="sortBy" id="sortBy" onchange="this.form.submit()">
                                    <option value="itemId" th:selected="${sortBy == 'itemId'}">최신순</option>
                                    <option value="itemRatings" th:selected="${sortBy == 'itemRatings'}">별점 높은 순</option>
                                    <option value="likeCount" th:selected="${sortBy == 'likeCount'}">찜많은순</option>
                                </select>
                            </div>
                        </form>
                    </div>
    
    
    
    
    
    
                    <div class="container-fluid mt-3">
                    <div class="row">
                        <!-- items 리스트의 각 항목을 반복 -->
                        <div th:each="item : ${items}" class="col-md-4">
                            <div class="card mb-4 product-wap rounded-0">
                                <!-- 제품 이미지 -->
                                <div class="card rounded-0">
                                    <a th:href="@{/ypjs/item/get/{itemId}(itemId=${item.itemId})}">
                                        <img class="card-img rounded-0 img-fluid fixed-size-img" th:src="${item.itemFilePath}" alt="product-item">
                                        <!-- 필요한 경우 오버레이나 추가 요소를 여기에 추가할 수 있습니다 -->
                                        <div class="card-img-overlay rounded-0 product-overlay d-flex align-items-center justify-content-center">
                                            <!-- 추가적인 콘텐츠가 필요하다면 여기에 추가할 수 있습니다 -->
                                            <ul class="list-unstyled">
                                                <!-- 리스트의 다른 요소들 -->
                                            </ul>
                                        </div>
                                    </a>
                                </div>
                                <!-- 제품 정보 -->
                                <div class="card-body">
                                    <!-- 제품 링크 -->
                                    <a th:href="@{/ypjs/item/get/{itemId}(itemId=${item.itemId})}" id="itemName" class="font-weight-bold" th:text="${item.itemName}"></a>
    
                                    <!-- 별점 및 평점 표시 -->
                                    <div class="d-flex justify-content-center mb-1 align-items-center">
                                        <ul class="list-unstyled d-flex mb-0" id="starRating" th:attr="data-rating=${item.itemRatings}">
                                            <!-- 별 아이콘은 JavaScript로 생성됩니다 -->
                                        </ul> &nbsp;&nbsp;&nbsp;&nbsp;
                                        <span id="itemRatings" class="font-weight-bold ml-2" th:text="${item.itemRatings}"></span>
                                    </div>
    
                                    <!-- 가격 표시 -->
                                    <span id="itemPrice" class="font-weight-bold" th:text="${item.itemPrice}"></span>
                                    <span class="font-weight-bold">원</span>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
    
    
                </div>
        </div>
    
    </div>
    </div>
    
    <!-- 페이지네이션 시작-->
    <nav th:unless="${#lists.isEmpty(items)}" class="mt-4">
        <ul class="pagination justify-content-center">
            <li class="page-item" th:classappend="${page == 0} ? 'disabled'">
                <a class="page-link" th:href="@{'/ypjs/categoryItem/get/' + ${category.categoryId} + '?page=' + (${page - 1}) + '&size=' + ${size} + '&sortBy=' + ${sortBy} + '&keyword=' + ${#strings.defaultString(keyword, '')}}">이전</a>
            </li>
            <li class="page-item" th:each="i : ${#numbers.sequence(0, totalPages - 1)}" th:classappend="${page == i} ? 'active'">
                <a class="page-link" th:href="@{'/ypjs/categoryItem/get/' + ${category.categoryId} + '?page=' + ${i} + '&size=' + ${size} + '&sortBy=' + ${sortBy} + '&keyword=' + ${#strings.defaultString(keyword, '')}}" th:text="${i + 1}">1</a>
            </li>
            <li class="page-item" th:classappend="${page == totalPages - 1} ? 'disabled'">
                <a class="page-link" th:href="@{'/ypjs/categoryItem/get/' + ${category.categoryId} + '?page=' + (${page + 1}) + '&size=' + ${size} + '&sortBy=' + ${sortBy} + '&keyword=' + ${#strings.defaultString(keyword, '')}}">다음</a>
            </li>
        </ul>
    </nav>
    <!-- 페이지네이션 끝-->
    
    
    
    <!-- End Content -->
    
    <!-- Start Brands -->
    <section class="bg-light py-5">
        <div class="container my-4">
            <div class="row text-center py-3">
                <div class="col-lg-6 m-auto">
                    <h1 class="h1">Our Brands</h1>
                    <p>
                        Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
                        Lorem ipsum dolor sit amet.
                    </p>
                </div>
                <div class="col-lg-9 m-auto tempaltemo-carousel">
                    <div class="row d-flex flex-row">
                        <!--Controls-->
                        <div class="col-1 align-self-center">
                            <a class="h1" href="#multi-item-example" role="button" data-bs-slide="prev">
                                <i class="text-light fas fa-chevron-left"></i>
                            </a>
                        </div>
                        <!--End Controls-->
    
                        <!--Carousel Wrapper-->
                        <div class="col">
                            <div class="carousel slide carousel-multi-item pt-2 pt-md-0" id="multi-item-example" data-bs-ride="carousel">
                                <!--Slides-->
                                <div class="carousel-inner product-links-wap" role="listbox">
    
                                    <!--First slide-->
                                    <div class="carousel-item active">
                                        <div class="row">
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_01.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_02.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_03.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_04.png" alt="Brand Logo"></a>
                                            </div>
                                        </div>
                                    </div>
                                    <!--End First slide-->
    
                                    <!--Second slide-->
                                    <div class="carousel-item">
                                        <div class="row">
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_01.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_02.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_03.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_04.png" alt="Brand Logo"></a>
                                            </div>
                                        </div>
                                    </div>
                                    <!--End Second slide-->
    
                                    <!--Third slide-->
                                    <div class="carousel-item">
                                        <div class="row">
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_01.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_02.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_03.png" alt="Brand Logo"></a>
                                            </div>
                                            <div class="col-3 p-md-5">
                                                <a href="#"><img class="img-fluid brand-img" src="/img/brand_04.png" alt="Brand Logo"></a>
                                            </div>
                                        </div>
                                    </div>
                                    <!--End Third slide-->
    
                                </div>
                                <!--End Slides-->
                            </div>
                        </div>
                        <!--End Carousel Wrapper-->
    
                        <!--Controls-->
                        <div class="col-1 align-self-center">
                            <a class="h1" href="#multi-item-example" role="button" data-bs-slide="next">
                                <i class="text-light fas fa-chevron-right"></i>
                            </a>
                        </div>
                        <!--End Controls-->
                    </div>
                </div>
            </div>
        </div>
    </section>
    <!--End Brands-->
    
    
    <footer th:replace="frame/footer :: footer"></footer>
    
    </body>
    
    </html>

     

    사진은 itemList와 동일

     

     

     

    categoryList부분 검색, 페이징 없앰

     

    categoryRepository

    //category전체 조회
    public List<Category> findAll() {
        return em.createQuery(
                        "select distinct c from Category c", Category.class)
                .getResultList();
    }

     

     

    categoryService

    public List<CategoryListDto> findAllCategory() {
        
        List<Category> categories = categoryRepository.findAll();
    
    
        List<CategoryListDto> result = categories.stream()
                .map(c -> new CategoryListDto(c))
                .collect(Collectors.toList());
    
        return result;
    }

     

     

     

    categoryController

    //카테고리 리스트 보기
    @GetMapping("/ypjs/category/get")
    public String getCategoryList(Model model) {
        
        List<CategoryListDto> categories = categoryService.findAllCategory();
    
        model.addAttribute("categories", categories);
    
        return "category/categoryList";
    }

     

     

    화면은 전에 올린 글에 있음

     

     

     

    categoryGet에 검색, 페이지네이션 추가

     

     

    itemRepository, service

    위에 카테고리 별 아이템 조회 그대로 씀

     

     

    categoryController

    //category 1개 조회
    @GetMapping("/ypjs/category/get/{categoryId}")
    public String getOneCategory (@PathVariable("categoryId") Long categoryId,
                                   Model model,
                                  @RequestParam(value = "page", defaultValue = "0") int page,
                                  @RequestParam(value = "size", defaultValue = "3") int size,
                                  @RequestParam(value = "keyword", required = false) String keyword) {
    
    
        Pageable pageable = PageRequest.of(page, size);
    
        Category findCategory = categoryService.findOneCategory(categoryId);
    
        List<ItemListDto> items = itemService.findAllCategoryItem(categoryId, pageable, keyword);
    
        //총 페이지 수 계산
        int totalPages = Page.totalPages(itemService.countAllCategoryItem(keyword, categoryId), size);
    
    
        model.addAttribute("category", findCategory);
        model.addAttribute("items",items);
        model.addAttribute("keyword", keyword); //검색조건 유지
        model.addAttribute("page",page); //페이징
        model.addAttribute("size",size); //페이징
        model.addAttribute("totalPages", totalPages); //총 페이지 수
    
    
        return "category/categoryGet";
    
    
    }
    

     

     

    category/categoryGet 검색, 페이지네이션, 아이템 이름에 링크 추가 

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org" lang="en">
    
    <head th:replace="frame/header :: head">
        <title>Zay Shop - Product Listing Page</title>
    </head>
    
    <body>
    <!--header-->
    <header th:replace="frame/header :: header"></header>
    
    <!-- Start Content -->
    <div class="container py-5">
        카테고리 번호 당 아이템 리스트
    </div>
    <!-- End Content -->
    
    
    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    
    <!-- JS 파일 포함 -->
    <script src="/js/jquery-1.11.0.min.js"></script>
    <script src="/js/summernote-lite.js"></script>
    <script src="/js/summernote-ko-KR.js"></script>
    <script src="/js/item/category.js"></script>
    <script src="/js/item/item.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.18/summernote.min.js"></script>
    
    <!-- Start Contact -->
    <div class="container py-5">
        <div class="row py-5">
    
            <!-- 폼 요소들 -->
            <div class="row">
    
                <style>
                    .category-box {
                        border: 1px solid #ccc;
                        padding: 10px;
                        margin-bottom: 10px;
                        max-width: 1000px; /* 최대 너비 설정 */
                        margin-left: auto;
                        margin-right: auto;
                    }
    
                    .category-label {
                        font-weight: bold;
                    }
    
                    .item-box {
                        border: 1px solid #ccc;
                        padding: 10px;
                        margin-bottom: 10px;
                        max-width: 1000px; /* 최대 너비 설정 */
                        margin-left: auto;
                        margin-right: auto;
                    }
    
                    .item-info {
                        /* Optional: Add additional styles for item information */
                    }
    
                    .category-label {
                        font-weight: bold;
                    }
    
                     .h3-indent {
                        margin-left: 100px; /* 왼쪽 여백을 줌으로써 텍스트를 오른쪽으로 이동 */
                    }
                </style>
    
    
                <div class="category-box">
                    <div>
                        <span class="category-label">카테고리 번호:</span>
                        &nbsp;&nbsp;&nbsp;&nbsp; <span>:</span>&nbsp;&nbsp;&nbsp;&nbsp;
                        <span th:text="${category.categoryId}"></span>
                    </div>
                    <div>
                        <span class="category-label">카테고리 부모 번호:</span>
                        &nbsp;&nbsp;&nbsp;&nbsp; <span>:</span>&nbsp;&nbsp;&nbsp;&nbsp;
                        <span th:text="${category.categoryParent.categoryId}"></span>
                    </div>
                    <div>
                        <span class="category-label">카테고리 이름:</span>
                        &nbsp;&nbsp;&nbsp;&nbsp; <span>:</span>&nbsp;&nbsp;&nbsp;&nbsp;
                        <span th:text="${category.categoryName}"></span>
                    </div>
    
                    <br>
    
                    <div class="container">
                        <div class="row justify-content-end">
                            <div class="col-auto">
                                <a th:href="@{/ypjs/category/update/{categoryId}(categoryId=${category.categoryId})}" class="btn btn-warning">수정하기</a>
                            </div>
                            <input type="hidden" id="categoryId" th:value="${category.categoryId}">
                            <div class="col-auto">
                                <button type="button" id="btn-categoryDelete" class="btn btn-danger">삭제하기</button>
                            </div>
                        </div>
                    </div>
                </div>
    
    
                <div class="row">
                    <div class="col-lg-9">
                        <div class="row justify-content-start">
                            <div class="col-md-6 offset-md-9">
                                <br><br>
                                <!-- 검색 폼 -->
                                <form th:action="@{/ypjs/category/get/{categoryId}(categoryId=${category.categoryId})}" method="get" class="form-inline w-100">
                                    <div class="input-group mb-3">
                                        <input type="text" id="keyword" name="keyword" class="form-control" aria-label="Recipient's username" aria-describedby="button-addon2"
                                               placeholder="Search by item name" th:value="${keyword}">
                                        <button type="submit" class="btn btn-link text-dark mb-2 ml-2" id="button-addon2">
                                            <i class="fa fa-search"></i>
                                        </button>
                                    </div>
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
    
                <div th:if="${not #lists.isEmpty(items)}">
                    <br><br><br>
                    <h3 class="h3-indent">아이템 목록</h3>
                    <br>
                    <div th:each="item : ${items}" class="item-box">
                        <div class="item-info">
                            <div>
                                <span class="category-label">아이템 이름:</span>
                                &nbsp;&nbsp;&nbsp;&nbsp; <span>:</span>&nbsp;&nbsp;&nbsp;&nbsp;
                                <a th:href="@{/ypjs/item/get/{itemId}(itemId=${item.itemId})}" id="itemName" class="font-weight-bold" th:text="${item.itemName}"></a>
    
                            </div>
                            <div>
                                <span class="category-label">아이템 내용:</span>
                                &nbsp;&nbsp;&nbsp;&nbsp; <span>:</span>&nbsp;&nbsp;&nbsp;&nbsp;
                                <span th:utext="${item.itemContent}"></span>
                            </div>
                            <div>
                                <span class="category-label">아이템 가격:</span>
                                &nbsp;&nbsp;&nbsp;&nbsp; <span>:</span>&nbsp;&nbsp;&nbsp;&nbsp;
                                <span th:text="${item.itemPrice}"></span>
                            </div>
                            <div>
                                <span class="category-label">아이템 수량:</span>
                                &nbsp;&nbsp;&nbsp;&nbsp; <span>:</span>&nbsp;&nbsp;&nbsp;&nbsp;
                                <span th:text="${item.itemStock}"></span>
                            </div>
                            <!-- 필요한 다른 아이템 정보들을 추가 -->
                            <div class="container">
                                <div class="row justify-content-end">
                                    <div class="col-auto">
                                        <a th:href="@{/ypjs/item/update/{itemId}(itemId=${item.itemId})}"
                                           class="btn btn-warning"
                                           style="padding: 2px 10px; font-size: 12px; width: 90px; height: 40px; text-align: center; display: inline-block; line-height: 40px;">
                                            수정하기
                                        </a>
                                    </div>
                                    &nbsp;
                                    <button type="button"
                                            class="btn btn-danger btn-sm btn-delete-item"
                                            th:attr="data-itemId=${item.itemId}"
                                            style="padding: 2px 10px; font-size: 16px; width: 90px; height: 40px;">
                                        삭제하기
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    
    <!-- 페이지네이션 시작-->
    <nav th:unless="${#lists.isEmpty(items)}" class="mt-4">
        <ul class="pagination justify-content-center">
            <li class="page-item" th:classappend="${page == 0} ? 'disabled'">
                <a class="page-link" th:href="@{'/ypjs/category/get/' + ${category.categoryId} + '?page=' + (${page - 1}) + '&size=' + ${size} + '&keyword=' + ${#strings.defaultString(keyword, '')}}">이전</a>
            </li>
            <li class="page-item" th:each="i : ${#numbers.sequence(0, totalPages - 1)}" th:classappend="${page == i} ? 'active'">
                <a class="page-link" th:href="@{'/ypjs/category/get/' + ${category.categoryId} + '?page=' + ${i} + '&size=' + ${size} + '&keyword=' + ${#strings.defaultString(keyword, '')}}" th:text="${i + 1}">1</a>
            </li>
            <li class="page-item" th:classappend="${page == totalPages - 1} ? 'disabled'">
                <a class="page-link" th:href="@{'/ypjs/category/get/' + ${category.categoryId} + '?page=' + (${page + 1}) + '&size=' + ${size} + '&keyword=' + ${#strings.defaultString(keyword, '')}}">다음</a>
            </li>
        </ul>
    </nav>
    <!-- 페이지네이션 끝-->
    
    
    
    
    
    <!-- End Contact -->
    
    <footer th:replace="frame/footer :: footer"></footer>
    
    </body>
    
    </html>

     

     

     

    검색

     

     

     

    페이징

     

     

     

    itemReview페이징

     

     

    itemReviewRepository

    //아이템리뷰 총 개수
    public int countAllItemReview(Long itemId){
       return em.createQuery("select count(distinct ir) from ItemReview ir join ir.item i where i.itemId = :itemId", Long.class)
                .setParameter("itemId", itemId)
                .getSingleResult()
                .intValue();
    
    }

     

     

    itemReviewService

    
    //아이템리뷰 페이징 개수 조회
    public int countAllItemReview(Long itemId){
        return itemReviewRepository.countAllItemReview(itemId);
    }

     

     

    itmReviewController

    //아이템 당 리뷰조회
    @GetMapping("/ypjs/itemReview/get/{itemId}")
    public String getAllItemReview(@PathVariable(name = "itemId") Long itemId, Model model,
                                   @RequestParam(value = "page",defaultValue = "0") int page,
                                   @RequestParam(value = "size",defaultValue = "10") int size) {
    
        Pageable pageable = PageRequest.of(page, size);
    
        itemService.findOneItem(itemId);
    
        List<ItemReviewListDto> itemReviews = itemReviewService.findAllItemReview(itemId, pageable);
    
        //총 페이지 수 계산
        int totalPages = Page.totalPages(itemReviewService.countAllItemReview(itemId), size);
    
        model.addAttribute("itemReviews", itemReviews);
        model.addAttribute("page",page); //페이징
        model.addAttribute("size",size); //페이징
        model.addAttribute("totalPages", totalPages); //총 페이지 수
    
    
        return "itemreview/itemReviewGet";
    }
    

     

     

    itemreview/itemReviewGet 페이지네이션 추가

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org" lang="en">
    
    <head th:replace="frame/header :: head">
        <title>Zay Shop - Product Listing Page</title>
    </head>
    
    <script src="/js/item/itemRatingsGet.js"></script>
    
    <body>
    <!--header-->
    <header th:replace="frame/header :: header"></header>
    
    <style>
        .category-box {
            border: 1px solid #ccc;
            padding: 10px;
            margin-bottom: 10px;
        }
    
        .category-label {
            font-weight: bold;
        }
    
        .item-box {
    border: 1px solid #ccc;
    padding: 10px;
    margin-bottom: 10px;
    }
    
    .item-info {
    /* Optional: Add additional styles for item information */
    }
    
    .category-label {
    font-weight: bold;
    }
    
    
         #reviewContent img {
           display: block; /* 이미지가 한 줄에 나타나도록 설정 */
            margin: auto; /* 가운데 정렬 */
            max-width: 100%; /* 최대 너비 설정 */
            height: auto; /* 높이 자동으로 조정 */
            margin-bottom: 10px; /* 이미지 아래 여백 추가 */
       }
    
    </style>
    
    
    <!-- Start Content -->
    <div class="container py-5">
        아이템 리뷰
    </div>
    <!-- End Content -->
    
    
    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    
    
    <!-- JS 파일 포함 -->
    <script src="/js/jquery-1.11.0.min.js"></script>
    <script src="/js/summernote-lite.js"></script>
    <script src="/js/summernote-ko-KR.js"></script>
    <script src="/js/item/itemReview.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.18/summernote.min.js"></script>
    
    
    <!-- Start Contact -->
    <div class="container py-5">
        <div class="row py-5">
            <form class="col-md-9 m-auto" method="post">
                <!-- 폼 요소들 -->
                <div class="row">
    
    
                    <div th:if="${not #lists.isEmpty(itemReviews)}">
                        <br><br><br>
                        <h3>리뷰 목록</h3>
                        <br>
                        <div th:each="itemReview : ${itemReviews}" class="item-box">
                            <div class="item-info" style="text-align: right;">
                                <div>
                                    <span style="font-weight: bold;">작성일 : </span>
                                    <span id="itemReviewCreateDate"
                                          th:text="${#temporals.format(itemReview.itemReviewCreateDate, 'yyyy-MM-dd')}"></span>
                                </div>
                            </div>
    
    
                            <div class="item-info">
                                <div>
                                    <span class="category-label">제목&nbsp;&nbsp;:&nbsp;&nbsp;</span>
                                    <span th:text="${itemReview.itemReviewName}"></span>
                                </div>
    
                                <!-- 별점 및 평점 표시 -->
                                <div class="d-flex justify-content-left mb-1 align-items-left">
                                    <span class="category-label">별점&nbsp;&nbsp;:&nbsp;&nbsp;</span>
                                    <ul class="list-unstyled d-flex mb-0" id="starRating"
                                        th:attr="data-rating=${itemReview.itemScore}">
                                        <!-- 별 아이콘은 JavaScript로 생성됩니다 -->
                                    </ul>
    
                                </div>
    
    
                                <div>
                                    <span class="category-label">리뷰 내용&nbsp;&nbsp;:&nbsp;&nbsp;</span>
                                    <div style="text-align: left;">
                                        <!-- 썸머노트에서 입력한 리뷰 내용 -->
                                        <div id="reviewContent" th:utext="${itemReview.itemReviewContent}">
                                            <!-- 썸머노트에서 삽입된 이미지의 스타일 -->
                                        </div>
                                    </div>
                                </div>
    
    
                                <input type="hidden" id="itemId" th:value="${itemReview.itemId}">
                                <!-- 수정 및 삭제 버튼 -->
                                <div class="container">
                                    <div class="row justify-content-end">
                                        <div class="col-auto">
                                            <a th:href="@{/ypjs/itemReview/update/{itemReviewId}(itemReviewId=${itemReview.itemReviewId})}"
                                               class="btn btn-warning">수정하기</a>
                                        </div>
                                        <div class="col-auto">
    
                                            <button type="button"
                                                    class="btn btn-danger btn-sm btn-delete-itemReview"
                                                    th:data-itemReviewId="${itemReview.itemReviewId}"
                                                    style="padding: 2px 10px; font-size: 16px; width: 90px; height: 40px;">
                                                삭제하기
                                            </button>
                                        </div>
    
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
    
                    <!-- 페이지네이션 시작 -->
                    <nav th:unless="${#lists.isEmpty(itemReviews)}" class="mt-4">
                        <ul class="pagination justify-content-center">
                            <li class="page-item" th:classappend="${page == 0} ? 'disabled'">
                                <a class="page-link"
                                   th:href="@{/ypjs/itemReview/get/{itemId}(itemId=${itemReviews[0].itemId}, page=${page - 1}, size=${size})}">이전</a>
                            </li>
                            <li class="page-item" th:each="i : ${#numbers.sequence(0, totalPages - 1)}"
                                th:classappend="${page == i} ? 'active'">
                                <a class="page-link"
                                   th:href="@{/ypjs/itemReview/get/{itemId}(itemId=${itemReviews[0].itemId}, page=${i}, size=${size})}"
                                   th:text="${i + 1}"></a>
                            </li>
                            <li class="page-item" th:classappend="${page == totalPages - 1} ? 'disabled'">
                                <a class="page-link"
                                   th:href="@{/ypjs/itemReview/get/{itemId}(itemId=${itemReviews[0].itemId}, page=${page + 1}, size=${size})}">다음</a>
                            </li>
                        </ul>
                    </nav>
                    <!--페이지네이션 끝-->
    
    
                    <!-- End Contact -->
    
    
                </div>
            </form>
        </div>
    </div>
    
    
    <footer th:replace="frame/footer :: footer"></footer>
    
    </body>
    
    </html>

     

     

     

     

     

     

    itemReview js에 유효성 검사 

    itemReview.js 유효성 검사 부분 추가, summernote도 itemReviewPost.html에서 여기로 옮김

    
    let itemBoardObject = {
        init: function() {
            let _this = this;
    
            $("#btn-itemReviewPost").on("click", function() {
                _this.insert();
            }),
            $("#btn-itemReviewUpdate").on("click", function() {
    
                  _this.update();
                    }),
    
            $(document).on("click", ".btn-delete-itemReview", function() {
              // 클릭된 버튼의 데이터 속성에서 itemReviewId를 가져옴
              let itemReviewId = $(this).data("itemreviewid");
              // 아이템 리뷰 삭제 함수 호출
               _this.deleteItemReview(itemReviewId);
                 });
    
    
            // Summernote 초기화
              $("#itemReviewContent").summernote({
                height: 300,
                placeholder: '내용을 입력하세요...'
                });
    
    
    
        },
    
        insert: function() {
    
    
            let itemId = $("#itemId").val();  // URL에 사용할 itemId를 가져옴
            let itemScore = $("#itemScore").val(); // 별점 가져오기
            let itemReviewName = $("#itemReviewName").val(); // 제목 가져오기
            let itemReviewContent = $("#itemReviewContent").summernote('code'); //썸머노트 내용 가져오기
    
    
                    // 별점을 선택하지 않은 경우 처리
                    if (itemScore === "0") {
                        alert("별점을 선택하세요.");
                        return; // 리뷰 등록 중단
                    }
    
                    // 제목이 비어 있는지 확인
                     if (!itemReviewName || itemReviewName.trim().length === 0) {
                          alert("제목을 입력하세요.");
                          return; // 리뷰 등록 중단
                      }
    
                    // 썸머노트에서 가져온 HTML 태그 제거 및 내용 검사
                     let plainTextContent = stripTags(itemReviewContent).trim();
    
                        if (!plainTextContent || plainTextContent.length === 0) {
                            alert("내용을 입력하세요.");
                            return;
                        }
    
    
    
            let data = {
                itemId: itemId,  // itemId를 데이터에 포함
                memberId: $("#memberId").val(),  // 멤버 ID 추가
                itemReviewName: $("#itemReviewName").val(),
                itemScore: $("#itemScore").val(),
                itemReviewContent: $("#itemReviewContent").val(),
            };
    
    
    
    
    
            $.ajax({
                type: "POST",
                url: "/ypjs/itemReview/post/" + itemId,  // URL에 itemId를 추가
                data: JSON.stringify(data),
                contentType: "application/json; charset=utf-8",
                success: function(response) {
                    alert("리뷰가 등록되었습니다.");
                    window.location.href = "/ypjs/itemReview/get/" + itemId;  // 성공 후 리디렉션
                },
                error: function(error) {
                    alert("에러 발생: " + JSON.stringify(error));
                }
            });
        },
    
    
         update: function() {
    
            let itemReviewId = $("#itemReviewId").val();
            let itemScore = $("#itemScore").val(); // 별점 가져오기
            let itemReviewName = $("#itemReviewName").val(); // 제목 가져오기
            let itemReviewContent = $("#itemReviewContent").summernote('code'); //썸머노트 내용 가져오기
    
    
              // 별점을 선택하지 않은 경우 처리
               if (itemScore === "0") {
                   alert("별점을 선택하세요.");
                   return; // 리뷰 등록 중단
               }
    
             // 제목이 비어 있는지 확인
              if (!itemReviewName || itemReviewName.trim().length === 0) {
                   alert("제목을 입력하세요.");
                   return; // 리뷰 등록 중단
              }
    
              // 썸머노트에서 가져온 HTML 태그 제거 및 내용 검사
             let plainTextContent = stripTags(itemReviewContent).trim();
              if (!plainTextContent || plainTextContent.length === 0) {
                  alert("내용을 입력하세요.");
                   return;
                                }
    
    
    
                let updateData = {
    
                    itemReviewName: $("#itemReviewName").val(),
                    itemScore: $("#itemScore").val(),
                    itemReviewContent: $("#itemReviewContent").val(),
                };
    
                $.ajax({
                    type: "PUT",
    
                    url: "/ypjs/itemReview/update/" + itemReviewId,
                    data: JSON.stringify(updateData),
                    contentType: "application/json; charset=utf-8",
                    success: function(response) {
                    alert("리뷰가 수정되었습니다.");
                    window.location.href = "/ypjs/itemReview/get/" + response ;
    
                    },
                    error: function(error) {
                        alert("에러 발생: " + JSON.stringify(error));
                        // 에러 발생 시 적절히 처리하도록 수정이 필요할 수 있습니다.
                    }
                });
            },
    
    
    
    
             deleteItemReview: function(itemReviewId) {
    
                     let itemId = $("#itemId").val();
    
                     $.ajax({
                         type: "DELETE",
                         url: "/ypjs/itemReview/delete/" + itemReviewId,
                         success: function(response) {
                             alert("리뷰가 삭제되었습니다.");
    
                             window.location.href = "/ypjs/itemReview/get/" + itemId;
                         },
                         error: function(xhr, status, error) {
                             alert("에러 발생: " + error);
                         }
                     });
                 }
    
    
            };
    
    
    
    $(document).ready(function() {
        itemBoardObject.init();
    });
    
    
    
    // HTML 태그 제거 함수
    function stripTags(html) {
        let tmp = document.createElement("DIV");
        tmp.innerHTML = html;
        return tmp.textContent || tmp.innerText || "";
    }
    
    

     

     

     

    카테고리 유효성 검사

    category.js에 유효성 검사 추가

    
    let categoryBoardObject = {
        init: function() {
            let _this = this;
    
            $("#btn-categoryPost").on("click", function() {
    
                _this.insert();
            }),
    
            $("#btn-categoryUpdate").on("click", function() {
    
                _this.update();
            }),
    
            $("#btn-categoryDelete").on("click", function() {
    
                _this.delete(); // 수정된 부분: delete 함수 호출
            });
        },
    
        insert: function() {
    
           let categoryParent = $("#categoryParent").val();
           let categoryName = $("#categoryName").val();
    
           if (!categoryParent || categoryParent.trim().length === 0) {
               alert("카테고리 부모 번호를 입력하세요");
               return;
           }
    
           if(!categoryName || categoryName.trim().length === 0){
               alert("카테고리 이름을 입력하세요");
               return;
           }
    
            let data = {
                categoryParent: $("#categoryParent").val(),
                categoryName: $("#categoryName").val(),
            };
    
            $.ajax({
                type: "POST",
                url: "/ypjs/category/post",
                data: JSON.stringify(data),
                contentType: "application/json; charset=utf-8",
                success: function(response) {
                 alert("카테고리 저장되었습니다.");
                    window.location.href = "/ypjs/category/get";
                },
                error: function(error) {
                    window.location.href = "/ypjs/category/get";
                }
            });
        },
    
        update: function() {
            let categoryId = $("#categoryId").val();
            let categoryParent = $("#categoryParent").val();
            let categoryName = $("#categoryName").val();
    
    
             if (!categoryId || categoryId.trim().length === 0) {
                  alert("카테고리 번호를 입력하세요");
                  return;
              }
    
    
             if (!categoryParent || categoryParent.trim().length === 0) {
                alert("카테고리 부모 번호를 입력하세요");
                return;
              }
    
            if(!categoryName || categoryName.trim().length === 0){
               alert("카테고리 이름을 입력하세요");
               return;
            }
    
            let updateData = {
                categoryId: categoryId, // 수정된 부분: categoryId 추가
                categoryParent: $("#categoryParent").val(),
                categoryName: $("#categoryName").val(),
            };
    
            $.ajax({
                type: "PUT",
                url: "/ypjs/category/update/" + categoryId,
                data: JSON.stringify(updateData),
                contentType: "application/json; charset=utf-8",
                success: function(response) {
                alert("카테고리가 수정되었습니다.");
                    window.location.href = "/ypjs/category/get";
                },
                error: function(error) {
                    alert("에러 발생: " + JSON.stringify(error));
                }
            });
        },
    
        delete: function() {
            let categoryId = $("#categoryId").val(); // 삭제할 categoryId 가져오기
    
            $.ajax({
                type: "DELETE",
                url: "/ypjs/category/delete/" + categoryId,
                success: function(response) {
                      alert("카테고리가 삭제되었습니다.");
                    window.location.href = "/ypjs/category/get"; // 삭제 후 페이지 이동
                },
                error: function(xhr, status, error) {
                    alert("에러 발생: " + error);
                }
            });
        }
    };
    
    $(document).ready(function() {
        categoryBoardObject.init();
    });
    

     

     

     

    아이템 유효성 검사

    itemPost.html, itemUpdate.html은 form으로 넘겨서 따로 itemValidation.js만들어서 유효성 검사,

    썸머노트, 썸네일부분 script도 여기로 옮김

    $(document).ready(function () {
        // Summernote 초기화
        $("#itemContent").summernote({
            height: 500,
            placeholder: '상품 내용을 입력하세요...',
            callbacks: {
                onChange: function (contents, $editable) {
                    // 내용이 변경될 때 추가 로직
                }
            }
        });
    
        // 파일명 표시 함수
        function displayFileName(input) {
            if (input.files.length > 0) {
                var fileName = input.files[0].name;
                document.getElementById('file-name-display').innerText = '선택된 파일: ' + fileName;
            } else {
                document.getElementById('file-name-display').innerText = '';
            }
        }
    
        // 폼 제출 전 유효성 검사
        $('#itemForm').submit(function (event) {
            var categoryId = $("#categoryId").val();
            var itemName = $("#itemName").val();
            var itemPrice = $("#itemPrice").val();
            var itemStock = $("#itemStock").val();
            var itemContent = $("#itemContent").summernote('code');
    
            if (!categoryId || categoryId.trim().length === 0) {
                alert('카테고리 번호를 입력하세요.');
                event.preventDefault(); // 폼 제출 방지
                return;
            }
    
            if (!itemName || itemName.trim().length === 0) {
                alert('상품명을 입력하세요.');
                event.preventDefault(); // 폼 제출 방지
                return;
            }
    
            if (!itemPrice || itemPrice.trim().length === 0) {
                alert('상품가격을 입력하세요.');
                event.preventDefault(); // 폼 제출 방지
                return;
            }
    
            if (!itemStock || itemStock.trim().length === 0) {
                alert('상품수량을 입력하세요.');
                event.preventDefault(); // 폼 제출 방지
                return;
            }
    
             // 썸머노트에서 가져온 HTML 태그 제거 및 내용 검사
             let plainTextContent = stripTags(itemContent).trim();
    
            if (!plainTextContent || plainTextContent.trim().length === 0) {
                alert('상품내용을 입력하세요.');
                event.preventDefault(); // 폼 제출 방지
                return;
            }
    
            var fileInput = document.getElementById('file');
            if (fileInput.files.length === 0) {
                alert('썸네일 파일을 선택하세요.');
                event.preventDefault(); // 폼 제출 방지
            }
        });
    });
    
    
    // HTML 태그 제거 함수
    function stripTags(html) {
        let tmp = document.createElement("DIV");
        tmp.innerHTML = html;
        return tmp.textContent || tmp.innerText || "";
    }

     

     

     

     

    itemList.html, itemCategoryList.html, itemGet.html에 있던 별 script

    itemRatingsGet.js따로 만듦

    document.addEventListener("DOMContentLoaded", function() {
        var starRatingContainers = document.querySelectorAll("#starRating");
    
        starRatingContainers.forEach(function(starRatingContainer) {
            var rating = parseFloat(starRatingContainer.getAttribute("data-rating"));
            starRatingContainer.innerHTML = generateStarRating(rating);
        });
    
        // 별점을 생성하여 HTML 문자열 반환하는 함수 (순수 JavaScript)
        function generateStarRating(rating) {
            var starsHTML = '';
            var numStars;
    
            if (rating >= 0 && rating < 1) {
                numStars = 0;
            } else if (rating >= 1 && rating < 2) {
                numStars = 1;
            } else if (rating >= 2 && rating < 3) {
                numStars = 2;
            } else if (rating >= 3 && rating < 4) {
                numStars = 3;
            } else if (rating >= 4 && rating < 5) {
                numStars = 4;
            } else if (rating == 5) {
                numStars = 5;
            }
    
            for (var i = 0; i < numStars; i++) {
                starsHTML += '<i class="text-warning fa fa-star"></i>';
            }
    
            for (var i = numStars; i < 5; i++) {
                starsHTML += '<i class="text-muted fa fa-star"></i>';
            }
    
            return starsHTML;
        }
    });

     

     

    itemReviewPost.html, itemReviewUpdate.html에 있던 별 js파일 따로 만듦

    itemRatingsPost.js

    $(document).ready(function() {
        var initialScore = $('#itemScore').val(); // 기존의 itemScore 값 가져오기
        $('#starRating').html(generateStarRating(initialScore)); // 초기화: 기존의 별점으로 별 아이콘 표시
    
        // 별 아이콘 클릭 이벤트 리스너 추가
        $('#starRating').on('click', '.fa-star', function() {
            var score = $(this).data('score');
            $('#itemScore').val(score); // hidden 필드에 새로운 점수 설정
            $('#starRating').html(generateStarRating(score)); // 별 아이콘 업데이트
        });
    
        // 별 아이콘을 생성하여 HTML 문자열 반환하는 함수
        function generateStarRating(score) {
            var starsHTML = '';
            for (var i = 1; i <= 5; i++) {
                if (i <= score) {
                    starsHTML += '<i class="fas fa-star rating-stars filled-star" data-score="' + i + '"></i>';
                } else {
                    starsHTML += '<i class="fas fa-star rating-stars" data-score="' + i + '"></i>';
                }
            }
            return starsHTML;
        }
    });
    
    
    
    

     

    둘이 합치고 싶었지만 그러면 둘 중 하나만 적용 됨

     

     

    리뷰 갯수 추가

    //item1개 조회
    @GetMapping("/ypjs/item/get/{itemId}")
    public String getOneItem (@PathVariable("itemId") Long itemId,
                                  Model model) {
    
        Item findItem = itemService.findOneItem(itemId);
    
        ItemOneDto item = new ItemOneDto(findItem);
    
    
        model.addAttribute("item", item);
    
        //조회수
        itemService.increaseItemCnt(itemId);
    
        //리뷰 갯수
        int reviewCount = itemReviewService.countAllItemReview(itemId);
        model.addAttribute("reviewCount", reviewCount);
    
        return "item/itemGet";
    
    }
    

     

    서비스, 레파지토리는 위에 있음

     

    item/itemGet

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org" lang="en">
    
    <head th:replace="frame/header :: head">
        <title>Zay Shop - Product Listing Page</title>
    </head>
    
    <script src="/js/item/itemRatings.js"></script>
    
    <body>
    <!--header-->
    <header th:replace="frame/header :: header"></header>
    <script src="/js/item/itemRatingsGet.js"></script>
    <script src="/js/item/item.js"></script>
    
    <style>
        .bg-light {
            background-color: transparent !important; /* 배경색을 투명하게 설정 */
        }
    
        /* 요소의 우선순위를 높여서 Bootstrap의 font-weight-bold 클래스가 적용될 수 있도록 함 */
        h1.font-weight-bold, span.font-weight-bold {
            font-weight: bold !important;
        }
    
         .custom-mt {
            margin-top: -20px; /* 원하는 간격 값으로 설정 */
        }
    <!--    가운데 정렬-->
    
        .centered-content {
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
            }
            #itemContent {
                text-align: center;
            }
    
    
         /* #itemContent를 flexbox로 설정하여 가운데 정렬 */
        #itemContent {
            display: flex;
            flex-direction: column; /* 내부 요소들을 세로로 정렬하기 위해 */
            justify-content: center; /* 수직 가운데 정렬 */
            align-items: center; /* 수평 가운데 정렬 */
        }
    
        /* 썸머노트에서 삽입된 이미지의 스타일 */
        #itemContent img {
            max-width: 100%; /* 최대 너비 설정 */
            height: auto; /* 높이 자동으로 조정 */
            margin-bottom: 10px; /* 이미지 아래 여백 추가 */
        }
    
        /* 썸머노트에서 삽입된 텍스트의 스타일 */
        #itemContent p {
            text-align: center; /* 텍스트 가운데 정렬 */
            margin-bottom: 10px; /* 문단 아래 여백 추가 */
        }
    
    </style>
    
    
    
    
    
    
    <!-- Open Content -->
    
    
    
    
    
    <div class="card-body pt-4 mt-3"> <!-- pt-4를 mt-3으로 변경 -->
        <div class="row">
            <div class="col-lg-7 offset-lg-1">
                <h1 class="h2 mb-3 font-weight-bold" th:text="${item.itemName}"></h1>
                <span id="itemPrice" class="font-weight-bold" th:text="${item.itemPrice}"></span>
                <span class="font-weight-bold">원</span>
                <p class="h3 py-2"></p>
            </div>
        </div>
    
        <!-- 별점 및 평점 표시 -->
        <div class="row mt-3">
            <div class="col-lg-7 offset-lg-1">
                <div class="d-flex justify-content-start align-items-center">
                    <ul class="list-unstyled d-flex mb-0 mr-3 text-left" id="starRating" th:attr="data-rating=${item.itemRatings}">
                        <!-- 별 아이콘은 JavaScript로 생성됩니다 -->
                    </ul> &nbsp;&nbsp;&nbsp;&nbsp;
                    <span id="itemRatings"  th:text="${item.itemRatings}"></span> &nbsp;&nbsp;&nbsp;&nbsp;
                    <div>
                        <a th:href="@{/ypjs/itemReview/get/{itemId}(itemId=${item.itemId})}">
                            Reviews  &nbsp;(<span th:text="${reviewCount}"></span>)
                        </a>
                    </div>
                </div>
            </div>
        </div>
    
    
    
    
        <!-- 수정하기 및 삭제하기 버튼 -->
        <div class="row justify-content-end mt-3">
            <div class="col-auto">
                <a th:href="@{/ypjs/item/update/{itemId}(itemId=${item.itemId})}" class="btn btn-warning">수정하기</a>
            </div>
            <div class="col-auto">
                <input type="hidden" id="itemId" th:value="${item.itemId}">
                <button type="button" id="btn-itemDelete" class="btn btn-danger">삭제하기</button>
            </div>
        </div>
    
                <div class="row justify-content-end mt-3">
                    <div class="col-auto">
                        <span class="font-weight-bold"> 조회수 : </span>
                        <span id="itemCnt" class="font-weight-bold" th:text="${item.itemCnt}"></span>
                        <br>
                        <span class="font-weight-bold">작성일 : </span>
                        <span id="itemCreateDate" class="font-weight-bold" th:text="${#temporals.format(item.itemCreateDate, 'yyyy-MM-dd')}"></span>
                    </div>
                    <div class="col-auto">
    
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    <hr>
    
    
    
    
    
    <section class="bg-light">
        <div class="container pb-5">
            <div class="row justify-content-center align-items-center">
                <div class="col-lg-5 mt-5 text-center">
                    <div id="itemContent" th:utext="${item.itemContent}">
                        <!-- 썸머노트에서 입력된 내용 (글과 이미지) -->
                    </div>
                </div>
            </div>
        </div>
    </section>
    
    
    
    
    <div class="row pb-3">
        <div class="col-lg-6 offset-lg-5"> <!-- offset-lg-6 클래스 추가하여 오른쪽으로 옮김 -->
            <ul class="list-inline pb-3 text-right"> <!-- text-right 클래스 추가하여 내용 오른쪽 정렬 -->
                <li class="list-inline-item">
                    수량
                    <input type="hidden" name="product-quanity" id="product-quanity" value="1">
                </li>
                <li class="list-inline-item"><span class="btn btn-success" id="btn-minus">-</span></li>
                <li class="list-inline-item"><span class="badge bg-secondary" id="var-value">1</span></li>
                <li class="list-inline-item"><span class="btn btn-success" id="btn-plus">+</span></li>
            </ul>
        </div>
    </div>
    
    <div class="row pb-3">
        <div class="col d-grid">
            <button type="submit" class="btn btn-success btn-md" name="submit" value="buy">Buy</button>
        </div>
        <div class="col d-grid">
            <button type="submit" class="btn btn-success btn-md" name="submit" value="addtocard">Add To Cart</button>
        </div>
    </div>
    
    </form>
    
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </section>
    <!-- Close Content -->
    
    
    
    
    
    
    <footer th:replace="frame/footer :: footer"></footer>
    
    </body>
    
    </html>

     

    reviewCount부분 추가

     

     

     

     

    리뷰 갯수 링크에 같이 연결돼서 나옴

     

     

     

     

     

    리뷰 정렬

    기본 최신순, 별점 높은 순, 별점 낮은 순 정렬

     

    itemReviewRepository

    //아이템 당 리뷰 조회
    public List<ItemReview> findAllItemReview(Long itemId, Pageable pageable, String sortBy) {
        // 기본 쿼리 문자열
        String queryString = "select ir from ItemReview ir join fetch ir.item i where i.itemId = :itemId";
    
        // 정렬 조건 추가
        switch (sortBy) {
            case "highScore":
                queryString += " order by ir.itemScore desc, ir.itemReviewId desc"; // 'highScore'일 때 itemScore 기준 내림차순, 동일할 경우 itemReviewId 내림차순 정렬
                break;
            case "lowScore":
                queryString += " order by ir.itemScore asc, ir.itemReviewId desc"; // 'lowScore'일 때 itemScore 기준 오름차순, 동일할 경우 itemReviewId 내림차순 정렬
                break;
            default:
                queryString += " order by ir.itemReviewId desc"; // 기본 정렬: itemReviewId 기준 내림차순 정렬 (최신순)
                break;
        }
    
        return em.createQuery(queryString, ItemReview.class)
                .setParameter("itemId", itemId)
                .setFirstResult((int) pageable.getOffset())
                .setMaxResults(pageable.getPageSize())
                .getResultList();
    }
    
    

     

     

     

    itemReviewService

    //아이템 당 리뷰조회
    public List<ItemReviewListDto> findAllItemReview(Long itemId, Pageable pageable, String sortBy) {
        List<ItemReview> reviews = itemReviewRepository.findAllItemReview(itemId, pageable, sortBy);
    
        List<ItemReviewListDto> result = reviews.stream()
                .map(ItemReviewListDto::new)
                .collect(Collectors.toList());
    
        return result;
    }

     

     

    itemReviewController

    //아이템 당 리뷰조회
    @GetMapping("/ypjs/itemReview/get/{itemId}")
    public String getAllItemReview(@PathVariable(name = "itemId") Long itemId, Model model,
                                   @RequestParam(value = "page",defaultValue = "0") int page,
                                   @RequestParam(value = "sortBy", defaultValue = "itemReviewId") String sortBy,
                                   @RequestParam(value = "size",defaultValue = "10") int size) {
    
        Pageable pageable = PageRequest.of(page, size);
    
        itemService.findOneItem(itemId);
    
        List<ItemReviewListDto> itemReviews = itemReviewService.findAllItemReview(itemId, pageable, sortBy);
    
        //총 페이지 수 계산
        int totalPages = Page.totalPages(itemReviewService.countAllItemReview(itemId), size);
    
        model.addAttribute("itemReviews", itemReviews);
        model.addAttribute("sortBy", sortBy); // 정렬 옵션을 다시 모델에 추가
        model.addAttribute("page",page); //페이징
        model.addAttribute("size",size); //페이징
        model.addAttribute("totalPages", totalPages); //총 페이지 수
    
    
        return "itemreview/itemReviewGet";
    }
    

     

     

     

    itemreview/itemReviewGet

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org" lang="en">
    
    <head th:replace="frame/header :: head">
        <title>Zay Shop - Product Listing Page</title>
    </head>
    
    <script src="/js/item/itemRatingsGet.js"></script>
    
    <body>
    <!--header-->
    <header th:replace="frame/header :: header"></header>
    
    <style>
        .category-box {
            border: 1px solid #ccc;
            padding: 10px;
            margin-bottom: 10px;
        }
    
        .category-label {
            font-weight: bold;
        }
    
        .item-box {
                        border: 1px solid #ccc;
                        padding: 10px;
                        margin-bottom: 10px;
                        max-width: 1000px; /* 최대 너비 설정 */
                        margin-left: auto;
                        margin-right: auto;
                    }
    
    .item-info {
    /* Optional: Add additional styles for item information */
    }
    
    .category-label {
    font-weight: bold;
    }
    
     .h3-indent {
                        margin-left: 100px; /* 왼쪽 여백을 줌으로써 텍스트를 오른쪽으로 이동 */
                    }
    
    
    
         .custom-select-wrapper {
            border: 1px solid #f0f8ff; /* 테두리 두께와 색상 설정 */
            padding: 3px; /* 선택 영역의 여백 설정 */
            background-color: #f0f8ff; /* 연한 하늘색 배경색 */
            display: inline-block; /* 인라인 요소로 표시 */
            border-radius: 10px; /* 테두리 둥글기 설정 */
        }
    
        .form-control {
            border: 1px solid #f0f8ff; /* 내부 테두리 설정 */
            border-radius: 8px; /* 안쪽 테두리 둥글기 설정 */
        }
    
    </style>
    
    
    <!-- Start Content -->
    <div class="container py-5">
        아이템 리뷰
    </div>
    <!-- End Content -->
    
    
    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    
    <script src="/js/item/itemReview.js"></script>
    
    
    <!-- Start Contact -->
    <!--정렬 추가-->
    <form th:action="@{/ypjs/itemReview/get/{itemId}(itemId=${itemReviews[0]?.itemId})}" method="get" class="row">
        <div class="offset-sm-9 col-sm-1 mb-2 custom-select-wrapper" style="width: 8%;">
            <select class="form-control" name="sortBy" id="sortBy" onchange="this.form.submit()">
                <option th:value="itemReviewId" th:selected="${sortBy == 'itemReviewId'}">최신순</option>
                <option th:value="highScore" th:selected="${sortBy == 'highScore'}">별점 높은 순</option>
                <option th:value="lowScore" th:selected="${sortBy == 'lowScore'}">별점 낮은 순</option>
            </select>
        </div>
    </form>
    
    
    
    
    
    
    
    
        <div class="container py-2">
        <div class="row py-5">
                <!-- 폼 요소들 -->
                <div class="row">
                    <div th:if="${not #lists.isEmpty(itemReviews)}">
                        <h3 class="h3-indent">리뷰 목록</h3>
                        <br>
                        <div th:each="itemReview : ${itemReviews}" class="item-box">
                            <div class="item-info" style="text-align: right;">
                                <div>
                                    <span style="font-weight: bold;">작성일 : </span>
                                    <span id="itemReviewCreateDate"
                                          th:text="${#temporals.format(itemReview.itemReviewCreateDate, 'yyyy-MM-dd')}"></span>
                                </div>
                            </div>
    
    
                            <div class="item-info">
                                <div>
                                    <span class="category-label">제목&nbsp;&nbsp;:&nbsp;&nbsp;</span>
                                    <span th:text="${itemReview.itemReviewName}"></span>
                                </div>
    
                                <!-- 별점 및 평점 표시 -->
                                <div class="d-flex justify-content-left mb-1 align-items-left">
                                    <span class="category-label">별점&nbsp;&nbsp;:&nbsp;&nbsp;</span>
                                    <ul class="list-unstyled d-flex mb-0" id="starRating"
                                        th:attr="data-rating=${itemReview.itemScore}">
                                        <!-- 별 아이콘은 JavaScript로 생성됩니다 -->
                                    </ul>
    
                                </div>
    
    
                                <div>
                                    <span class="category-label">리뷰 내용&nbsp;&nbsp;:&nbsp;&nbsp;</span>
                                    <div style="text-align: left;">
                                        <span th:utext="${itemReview.itemReviewContent}"></span>
                                    </div>
                                </div>
    
    
                                <input type="hidden" id="itemId" th:value="${itemReview.itemId}">
                                <!-- 수정 및 삭제 버튼 -->
                                <div class="container">
                                    <div class="row justify-content-end">
                                        <div class="col-auto">
                                            <a th:href="@{/ypjs/itemReview/update/{itemReviewId}(itemReviewId=${itemReview.itemReviewId})}"
                                               class="btn btn-warning">수정하기</a>
                                        </div>
                                        <div class="col-auto">
    
                                            <button type="button"
                                                    class="btn btn-danger btn-sm btn-delete-itemReview"
                                                    th:data-itemReviewId="${itemReview.itemReviewId}"
                                                    style="padding: 2px 10px; font-size: 16px; width: 90px; height: 40px;">
                                                삭제하기
                                            </button>
                                        </div>
    
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
    
                    <!-- 페이지네이션 시작 -->
                    <nav th:unless="${#lists.isEmpty(itemReviews)}" class="mt-4">
                        <ul class="pagination justify-content-center">
                            <li class="page-item" th:classappend="${page == 0} ? 'disabled'">
                                <a class="page-link" th:href="@{/ypjs/itemReview/get/{itemId}(itemId=${itemReviews[0].itemId}, page=${(page > 0 ? page - 1 : 0)}, size=${size}, sortBy=${sortBy})}">이전</a>
                            </li>
                            <li class="page-item" th:each="i : ${#numbers.sequence(0, totalPages - 1)}" th:classappend="${page == i} ? 'active'">
                                <a class="page-link" th:href="@{/ypjs/itemReview/get/{itemId}(itemId=${itemReviews[0].itemId}, page=${i}, size=${size}, sortBy=${sortBy})}" th:text="${i + 1}"></a>
                            </li>
                            <li class="page-item" th:classappend="${page == totalPages - 1} ? 'disabled'">
                                <a class="page-link" th:href="@{/ypjs/itemReview/get/{itemId}(itemId=${itemReviews[0].itemId}, page=${(page < totalPages - 1 ? page + 1 : totalPages - 1)}, size=${size}, sortBy=${sortBy})}">다음</a>
                            </li>
                        </ul>
                    </nav>
    
    
                    <!--페이지네이션 끝-->
    
    
                    <!-- End Contact -->
    
    
                </div>
        </div>
    </div>
    
    
    <footer th:replace="frame/footer :: footer"></footer>
    
    </body>
    
    </html>
    

     

    스타일, 정렬부분 추가 

     

Designed by Tistory.