쇼핑몰 웹사이트 만들어보기 - 찜하기 기능

Shiba·2024년 8월 15일
0

프로젝트 및 일기

목록 보기
18/29

이번 시간에는 위시리스트, 찜하기 기능을 추가하였다.

계속 기능을 추가하다보니 이제 챗지피티없이도 기능을 추가할 정도로 익숙해졌다.

그러면 코드를 살펴보자

먼저, 당연히 데이터베이스 테이블에 위시리스트를 저장할 테이블을 생성한다.

create table Wishlist (
	id int not null auto_increment,
    user_id varchar(50) not null,
    product_id int not null,
	number int not null default 0
    
    primary key(id),
    foreign key(user_id) references users(id)
			on delete restrict
            on update restrict,
	foreign key(product_id) references products(id)
			on delete restrict
            on update restrict			
)

다음으로 늘 하듯이 엔티티,리포지토리,서비스,컨트롤러,프론트코드 순으로 만들면 된다.

Wishlist

import jakarta.persistence.*;

@Entity
public class Wishlist {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String user_id;

    @ManyToOne
    @JoinColumn(name = "user_id", insertable = false, updatable = false)
    private Users user;

    private int product_id;
    @ManyToOne
    @JoinColumn(name = "product_id", insertable = false, updatable = false)
    private Products product;

    @Column(name = "count")
    private int count;

	//getter and setter

위시리스트의 엔티티이다. 늘 하듯이 열을 매핑시켜주면 되겠다.

WishlistRepository

package com.shoppingmall.repository;

import com.shoppingmall.domain.Products;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.TypedQuery;

import java.util.List;

public class MemoryProductRepository implements ProductRepository{
    @PersistenceContext
    public EntityManager em;

    public MemoryProductRepository(EntityManager em) {
        this.em = em;
    }

    @Override
    public Products save(Products products) {
        em.persist(products);
        return products;
    }

    public Products findById(int id){
        return em.find(Products.class, id);
    }

    @Override
    public List<Products> findByName(String name){
        TypedQuery<Products> query = em.createQuery("SELECT u FROM Products u WHERE u.product_name LIKE :name", Products.class);
        query.setParameter("name", "%" + name + "%");
        return query.getResultList();
    }

    @Override
    public List<Products> findByCategory(int category) {
        TypedQuery<Products> query = em.createQuery("SELECT u FROM Products u WHERE u.category = :category", Products.class);
        query.setParameter("category", category);
        return query.getResultList();
    }

    @Override
    public List<Products> findBySellerId(String seller_id) {
        TypedQuery<Products> query = em.createQuery("SELECT u FROM Products u WHERE u.seller_id like :seller_id", Products.class);
        query.setParameter("seller_id",  "%" + seller_id + "%");
        return query.getResultList();
    }

    @Override
    public boolean updatePrice(int id, int price) {
        Products product = em.find(Products.class, id);
        if (product != null) {
            product.setPrice(price);
            // `merge` 메서드를 사용하여 업데이트 수행
            em.merge(product);
            return true;
        }
        return false;
    }

    @Override
    public boolean discount(int id, int discount) {
        Products product = em.find(Products.class, id);
        if (product != null) {
            product.setDiscount(discount);
            // `merge` 메서드를 사용하여 업데이트 수행
            em.merge(product);
            return true;
        }
        return false;
    }

    @Override
    public boolean deleteProduct(int id) {
        Products product = em.find(Products.class, id);
        if (product != null) {
            em.remove(product); // 엔티티 삭제
            return true;
        }
        return false;
    }

    @Override
    public List<Products> getAllProduct() {
        String query = "SELECT c FROM Products c";
        return em.createQuery(query, Products.class).getResultList();
    }
}

위시리스트 리포지토리이다. 일단은 오늘 구현할 아주 기본적인 위시리스트 기능을 위한 함수들을 주로 추가해두었다.

WishlistService

package com.shoppingmall.service;

import com.shoppingmall.domain.Products;
import com.shoppingmall.domain.Users;
import com.shoppingmall.domain.Wishlist;
import com.shoppingmall.repository.WishlistRepository;
import jakarta.transaction.Transactional;

import java.util.List;

@Transactional
public class WishlistService {

    private final WishlistRepository wishlistRepository;

    public WishlistService(WishlistRepository wishlistRepository) {
        this.wishlistRepository = wishlistRepository;
    }

    public Wishlist save(Users user, Products products){
        return wishlistRepository.save(user, products);
    }

    public List<Wishlist> getAllWishlistByUserId(String id){
        return wishlistRepository.getWishlistByUserId(id);
    }
    public List<Wishlist> getAllWishlistByProductId(int id){
        return wishlistRepository.getWishlistByProductId(id);
    }

    public Wishlist findByTwoId(String userId, int productId){
        List<Wishlist> wishlists = wishlistRepository.getWishlistByUserId(userId);
        for(Wishlist w : wishlists){
            Products p = w.getProduct();
            if(p.getId() == productId){
                return w;
            }
        }
        return null;
    }

    public boolean deleteWishlist(int id) { return wishlistRepository.deleteWishlist(id); }
}

위시리스트의 서비스 객체이다. 리포지토리로 만들어둔 함수를 여기서 제공해준다.

WishlistController

package com.shoppingmall.controller;

import com.shoppingmall.domain.Products;
import com.shoppingmall.domain.Users;
import com.shoppingmall.domain.Wishlist;
import com.shoppingmall.service.ProductService;
import com.shoppingmall.service.UserService;
import com.shoppingmall.service.WishlistService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class WishlistController {

    private final WishlistService wishlistService;

    private final UserService userService;

    private final ProductService productService;

    public WishlistController(WishlistService wishlistService, UserService userService, ProductService productService) {
        this.wishlistService = wishlistService;
        this.userService = userService;
        this.productService = productService;
    }

    @PostMapping("/addWishlist")
    public ResponseEntity<String> addToWishlist(@RequestParam("productId") int productId,
                                                @AuthenticationPrincipal UserDetails userDetails) {
        ResponseEntity response;
        try {
            String userId = userDetails.getUsername();
            Wishlist wishlist = wishlistService.findByTwoId(userId,productId);
            if(wishlist == null){
                Users user = userService.findById(userId);
                Products products = productService.findById(productId);
                wishlistService.save(user,products);
                response = ResponseEntity
                        .status(HttpStatus.CREATED)
                        .body("상품이 위시리스트에 추가되었습니다.");
            }
            else{
                response = ResponseEntity
                        .status(HttpStatus.CREATED)
                        .body("이미 찜한 상품");
            }
        } catch (Exception ex) {
            response = ResponseEntity
                    .status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("An exception occured due to " + ex.getMessage());
        }
        return response;
    }

    @PostMapping("/delWishlist")
    public ResponseEntity<String> delToWishlist(@RequestParam("productId") int productId,
                                                @AuthenticationPrincipal UserDetails userDetails) {
        ResponseEntity response;
        try {
            String userId = userDetails.getUsername();
            Wishlist wishlist = wishlistService.findByTwoId(userId,productId);
            if(wishlist != null){
                wishlistService.deleteWishlist(wishlist.getId());
                response = ResponseEntity
                        .status(HttpStatus.CREATED)
                        .body("상품이 삭제되었습니다.");
            }
            else{
                response = ResponseEntity
                        .status(HttpStatus.CREATED)
                        .body("제거실패");
            }
        } catch (Exception ex) {
            response = ResponseEntity
                    .status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("An exception occured due to " + ex.getMessage());
        }
        return response;
    }
}

위시리스트 컨트롤러이다 post명령을 받을 수 있도록 하여 위시리스트 추가,제거를 가능하도록 하였다

추가 수정한 코드들

ProductController

@GetMapping("/products/{id}")
    public String getProductDetail(@PathVariable("id") int id,
                                   @RequestParam(value = "query", required = false) String keyword,
                                   @RequestParam(value = "category", required = false) String category,
                                   @RequestParam(value = "sellerId", required = false) String sellerId,
                                   @AuthenticationPrincipal UserDetails userDetails,
                                   Model model) {
        Products products = productService.findById(id);
        int price = products.getPrice();
        int discountPrice = (int) (price * (1-products.getDiscount()/100));
        boolean isWishlist = false;
        if(userDetails !=null) {
            Wishlist wishlist = wishlistService.findByTwoId(userDetails.getUsername(), id);
            if (wishlist != null) isWishlist = true;
        }
        ProductDto productDtos = new ProductDto(products,discountPrice);
        model.addAttribute("productDto", productDtos);
        model.addAttribute("query", keyword); // 검색 쿼리를 모델에 추가
        model.addAttribute("category", category); // 검색 쿼리를 모델에 추가
        model.addAttribute("sellerId", sellerId); // 검색 쿼리를 모델에 추가
        model.addAttribute("isWishlist", isWishlist);
        return "product/productDetail"; // 상세 페이지의 템플릿 이름
    }

상품 상세페이지에서 위시리스트 추가가 가능하며, 이미 추가되어있다면 추가되어있음을 표시하도록 만들기 위해서 여기에서 위시리스트 또한 모델로 추가하여 사용할 수 있도록 했다.

  document.addEventListener("DOMContentLoaded", function () {
            var isWishlist = [[${isWishlist}]];
            let product_id = [[${productDto.products.id}]];
            console.log(product_id);
            const wish = document.getElementById("wish_image");
            if (isWishlist) {
                wish.src = '/images/icons/wishlist-wish.png';
            }
            document.getElementById("wish").addEventListener("click", function () {
                if(wish.src.includes("wishlist-nowish")){
                    const url = new URL('http://localhost:8080/addWishlist');
                    url.searchParams.append('productId', product_id);

                    fetch(url, {
                        method: 'POST',
                        headers: {
                            'X-Requested-With': 'XMLHttpRequest'
                        }
                    })
                        .then(response => {
                            if (response.status === 401) {
                                // 401 상태 코드인 경우 로그인 창으로 이동할지 물어보는 prompt 표시
                                const shouldRedirect = confirm('로그인이 필요합니다. 로그인 페이지로 이동하시겠습니까?');
                                if (shouldRedirect) {
                                    window.location.href = '/login'; // 로그인 페이지로 이동
                                }
                                throw new Error('Unauthorized'); // 에러를 throw하여 나머지 then 블록이 실행되지 않도록 함
                            } else
                                return response.text();
                        })
                        .then(data => {
                            document.getElementById("wish_image").src = '/images/icons/wishlist-wish.png';
                        })
                        .catch(error => {
                            console.error('Error:', error);
                            if(error === 'Network response was not ok.')
                                alert('네트워크 오류.');
                        });
                } else {
                    const url = new URL('http://localhost:8080/delWishlist');
                    url.searchParams.append('productId', product_id);

                    fetch(url, {
                        method: 'POST',
                        headers: {
                            'X-Requested-With': 'XMLHttpRequest'
                        }
                    })
                        .then(response => {
                            if (response.status === 401) {
                                // 401 상태 코드인 경우 로그인 창으로 이동할지 물어보는 prompt 표시
                                const shouldRedirect = confirm('로그인이 필요합니다. 로그인 페이지로 이동하시겠습니까?');
                                if (shouldRedirect) {
                                    window.location.href = '/login'; // 로그인 페이지로 이동
                                }
                                throw new Error('Unauthorized'); // 에러를 throw하여 나머지 then 블록이 실행되지 않도록 함
                            } else
                                return response.text();
                        })
                        .then(data => {
                            document.getElementById("wish_image").src = '/images/icons/wishlist-nowish.png';
                        })
                        .catch(error => {
                            console.error('Error:', error);
                            if (error === 'Network response was not ok.')
                                alert('네트워크 오류.');
                        });
                }
            });
        })

위시리스트가 추가되어있었다면, isWishlist가 true일 것이므로 이미지를 변경하여 추가되어있음을 표시하도록 했다.
그리고, 이 이미지 이름을 통하여 추가,제거를 프론트에서 판단하고 각각을 post명령으로 보낼 수 있도록 하였다.

테스트 결과


초기 상태는 위 그림과 같이 색이 채워져있지않은 하트로 표시된다.
로그인을 하지않았을 때도 마찬가지로 표시된다.

로그인을 한 상태로 하트를 누르게되면

위 그림과 같이 색이 채워지며 위시리스트에 추가가 된다.

데이터베이스에도 해당 위시리스트와 관련된 정보들이 추가됨을 확인했다.


다음 시간에는 회원 등급 기능을 추가해보도록하자.
(위시리스트를 모아둔 페이지는 생각나는 기능들을 모두 추가해둔 후에 추가해두자.)

profile
모르는 것 정리하기

0개의 댓글