쇼핑몰 웹사이트 만들어보기 - 홈화면 추천 상품 추가

Shiba·2024년 8월 29일
0

프로젝트 및 일기

목록 보기
28/29

추천 상품 추가해보기

이번 시간에는 홈 화면에 할인하는 상품들과 로그인한 사용자의 연령,성별에 맞는 상품을 추천하는 기능을 추가해보았다.

1. 할인하는 상품 추가하기

할인하는 상품을 추가하는 것은 간단하다. 할인율이 존재하는 상품들만 뽑아내서 리스트에 추가하는 코드를 작성하고, 이를 컨트롤러에서 작동시키면 된다. 그 후에 타임리프로 해당 값들을 받아주면 정상적으로 상품들이 보여지게 된다.

백엔드 코드 작성

리포지토리에 리스트 추출 메소드 작성

@Override
    public List<Products> getDiscountProduct(){
        TypedQuery<Products> query = em.createQuery("SELECT u FROM Products u WHERE u.discount > 0", Products.class);
        return query.getResultList();
    }

이 메소드를 서비스 클래스에서 감싸서 컨트롤러에서 실행되게 해주면 된다.
그 후, 컨트롤러에서 해당 메소드를 사용해서 리스트를 모델에 추가해서 보내주면 된다.

컨트롤러 코드 작성

@GetMapping(value = "/")
    public String home(@AuthenticationPrincipal UserDetails userDetails,
                       Model model){

        //할인하는 상품 리스트 추출
        List<Products> products = productService.getDiscountProduct();
        if(products == null) products = new ArrayList<>();
        List<ProductDto> discountProductDtos = new ArrayList<>();
        Collections.sort(products, new Comparator<Products>() {
            @Override
            public int compare(Products o1, Products o2) {
                return (int) (o2.getDiscount() - o1.getDiscount()); // 역순 정렬
            }
        });
        for (Products product : products) {
            int price = product.getPrice();
            int discountPrice = (int) (price * (1 - product.getDiscount() / 100));
            boolean isWishlist = false;
            int wishlistCnt = wishlistService.getAllWishlistByProductId(product.getId()).size();
            if (userDetails != null) {
                Wishlist wishlist = wishlistService.findByTwoId(userDetails.getUsername(), product.getId());
                if (wishlist != null) isWishlist = true;
            }
            discountProductDtos.add(new ProductDto(product, (int) product.getDiscount(), discountPrice, isWishlist, wishlistCnt));
        }
        model.addAttribute("discountProductDtos", discountProductDtos);
   }

상품 리스트를 추출하고 할인율이 높은 순서대로 정렬한 후, 찜하기 기능도 사용할 수 있도록 이를 ProductDto타입으로 묶어서 모델에 넣어주었다.
이제 프론트에서 타임리프를 통해 상품을 하나씩 받아주면 된다.

프론트엔드 코드 작성

<div class="product_list" th:if="${discountProductDtos.size()} > 0">
                    <h2>할인하는 상품들</h2>
                <div id="discount_product_list">
                    <div class="product_container" id="discount_products">
                        <li th:each="product : ${discountProductDtos}"  th:data-product-id="${product.products.id}" class="product">
                            <img class="product_img" th:src="@{'/images/uploads/' + ${product.products.photo}}" alt="Product Image">
                            <div>
                                <span class="price" th:text="${product.products.price} + '원'"></span>
                                <span class="percent" th:text="${product.percent} + '%'"></span>
                                <span class="discount_price" th:text="${product.discountPrice} + '원'"></span>
                            </div>
                            <label th:text="${product.products.product_name}"></label>
                            <div class="prefer_box">
                                <!-- 별점 표시 -->
                                <div class="rating">
                                    <!-- 평균 평점 가져오기 -->
                                    <span th:each="i : ${#numbers.sequence(1, 5)}"
                                          th:classappend="${product.products.getAverageRating() >= i ? 'filled' : 'empty'}">&#9733;</span>
                                    <span class="average-rating" th:text="${product.products.getAverageRating()}">0.0</span>
                                </div>
                                <div class="wishlist">
                                    <!-- 위시리스트 이미지와 버튼, 각 제품의 ID를 사용하여 고유하게 설정 -->
                                    <img th:src="${product.isWishlist} ? '/images/icons/wishlist-wish.png' : '/images/icons/wishlist-nowish.png'"
                                         alt="Wishlist"
                                         class="wish"
                                         th:data-product-id="${product.products.id}"
                                         style="cursor: pointer;"
                                         width="14"
                                         height="14"/>
                                    <span class="wish_count"
                                          th:text="'(' + ${product.wishlistCnt} + ')'">
                                    </span>
                                </div>
                            </div>
                        </li>
                    </div>
                </div>
            </div>

테스트 결과


테스트 결과 원하는 대로 상품이 검색됨을 확인할 수 있었다.
상품의 인터페이스는 지마켓을 참고해서 만들었다. (디자인에 재능이 없는듯..)

2. 사용자의 연령,성별에 따라 상품 추천하기

사전준비

이 기능을 만들기 위해서 데이터베이스에 수정이 필요했다. (이래서 시작부터 어떤 기능들을 넣을건지 정하고 미리 데이터베이스 정규화를 잘 해놓으면 편하다는 것을 느꼈다..)

users 테이블에 sex(성) 테이블을 추가하고, birth열이 원래는 문자열이였다면 이를 Date타입으로 변경했다.

users 엔티티 변경

   @Column(name = "birth")
   private LocalDate birth;

   @Column(name = "sex")
    private String sex;

위의 값이 추가되면서 당연히 회원가입쪽 코드도 변경이 필요했다 (이 코드들은 생략)

백엔드 코드 작성

할인하는 상품을 추가했을 때처럼 리스트를 추출하는 메소드를 작성해주자.

리포지토리에 메소드 작성

@Override
    public List<Products> getProductsByUserInfo(int age, String sex){
        // 20년 전과 30년 전의 날짜를 계산하여 birth 조건으로 사용
        LocalDate startDate = LocalDate.now().minusYears(age+10); // age+10년 전
        LocalDate endDate = LocalDate.now().minusYears(age);   // age년 전

        TypedQuery<Object[]> query = em.createQuery(
                "SELECT p, COUNT(p) as cnt FROM Purchases r " +
                        "JOIN r.products p " +
                        "WHERE r.users.birth BETWEEN :startDate AND :endDate AND r.users.sex = :sex " +
                        "GROUP BY p " +
                        "ORDER BY cnt DESC",
                Object[].class
        );

        query.setParameter("startDate", startDate);
        query.setParameter("endDate", endDate);
        query.setParameter("sex", sex);

        List<Object[]> results = query.getResultList();

        // 가장 많이 등장한 순서대로 Products 리스트 추출
        List<Products> products = results.stream()
                .map(result -> (Products) result[0])
                .collect(Collectors.toList());
        return products;
    }

이 코드는 지피티의 도움을 받았다. 이 코드를 짜면서 느낀점은 sql언어를 100%로 사용할 수 있다면 거의 모든 코드를 아주 간단하게 작성할 수 있을 것 같다는 느낌이 들었다.
지금까지 백엔드에서 했던 것을 생각해보자. 백엔드의 최종 목표는 데이터베이스에서 데이터를 추출해서 이를 프론트엔드로 보내는 것이라 생각된다. 데이터베이스를 잘 사용할 수 있다면 당연히 코드를 작성하기 엄청 수월해질 것 같다는 생각이 많이 들었다. (JPA나 EntityManager도 완벽하게 알고 있다면 더 쉬웠을 것 같다...)

아무튼 코드를 설명해보자면 컨트롤러쪽에서 사용자의 생년월일을 이용해서 나이를 구해준다. 나이를 /10 *10을 하여 10,20,30과 같이 만들어 파라미터로 넣어서 해당 메소드에 보내준다.
메소드가 실행되면 받아온 값을 이용하여 사용자의 연령대 범위를 구해준다.
사용자의 성별과 연령대에 포함되는 결제정보들을 구하고, 결제정보중 가장 많이 나온 순서대로 줄을 세워 리스트를 만든다.
해당 리스트에서 가장 앞에 나온 상품부터 차례대로 상품타입 리스트에 넣어주고 리턴해준다.

이렇게 해주면 사용자의 연령대 근처의 같은 성별의 사람들이 많이 구매한 상품들이 리스트에 추가가 될 것이다.

이제 이 코드를 컨트롤러에서 사용해주면 되겠다.

//사용자 연령,성별에 맞는 상품 추천하기
        List<Products> products1 = null;
        int age = 0;
        String sex ="";
        if(userDetails != null){
            Users user = userService.findById(userDetails.getUsername());
            // 현재 날짜와의 차이를 계산하여 나이 구하기
            age = Period.between(user.getBirth(), LocalDate.now()).getYears();
            age = age/10 * 10;
            sex = user.getSex().equals("Male")? "남성":"여성";
            products1 = productService.getProductsByUserInfo(age, user.getSex());

        }
        if(products1 == null) products1 = new ArrayList<>();
        List<ProductDto> recommendProductDtos = new ArrayList<>();
        for (Products product : products1) {
            int price = product.getPrice();
            int discountPrice = (int) (price * (1 - product.getDiscount() / 100));
            boolean isWishlist = false;
            int wishlistCnt = wishlistService.getAllWishlistByProductId(product.getId()).size();
            if (userDetails != null) {
                Wishlist wishlist = wishlistService.findByTwoId(userDetails.getUsername(), product.getId());
                if (wishlist != null) isWishlist = true;
            }
            recommendProductDtos.add(new ProductDto(product, (int) product.getDiscount(), discountPrice, isWishlist, wishlistCnt));
        }
        model.addAttribute("recommendProductDtos", recommendProductDtos);
        model.addAttribute("age", age);
        model.addAttribute("sex",sex);

코드가 쪼금 길지만, 할인하는 상품을 추가하는 코드와 비슷하다. 필요한 것이 더 많을 뿐이다.

프론트 코드는 할인하는 상품과 같기때문에 생략하겠다.

테스트 결과

테스트 결과 정상 작동함을 확인했다. 나중에 데이터베이스에 더미 상품들을 더 추가한다면 리스트를 sublist를 통해 잘라서 보내주도록해주기만 하면 되겠다.


쇼핑몰 웹사이트 프로젝트가 곧 끝이 날 것 같다...! 다음이 어쩌면 마지막일지도 모르겠다.

profile
모르는 것 정리하기

0개의 댓글