2023.06.02 89일차 Team Project
89일차
위시리스트 고르기
별 누르면 위시리스트에 추가하기
만약 되어있는 상태라면 삭제해야한다.
위시리스트(찜하기)
좋아요방식사용
로그인하면 눌러지게 즐겨찾기 누르면 누르기
실시간으로 변하는것 보이기
눌러져잇으면 색변하게 보이기
멤버 id별 좋아요 목록 보기
찜목록 삭제하기
상품 상세페이지로 가기
즐겨찾기 기능(목록 추가하기(ajax) 기능 등등)
로그인안하면 버튼 안눌리게
눌려잇으면 색변하게
1.좋아요 버튼클릭시
상품id와 멤버id얻기
일단 wish-icon클래스를 가진 별 모양이 반복적으로 생성되고 있다.
<i class="${starShape} fa-star fa-2x wish-icon" style="position: absolute; top: 0; right: 0;" data-productid="${product.productId}"></i>각 별에는 data-productId라는 속성을 가지고 있으며 값은 상품의 id이다.
data-productid는 HTML5의 데이터 속성(data attribute)이다.
데이터 속성은 HTML 요소에 추가적인 데이터를 저장하기 위해 사용된다.
이름은 data-로 시작하며, data- 다음에는 원하는 속성 이름을 지정할 수 있다.
JavaScript에서는 jQuery 라이브러리를 사용하여 데이터 속성에 접근할 수 있다.
jQuery의 data() 메서드를 사용하여 데이터 속성의 값을 가져오거나 설정할 수 있다.
$(selector).data('속성이름')을 사용하면 해당 속성의 값을 가져올 수 있다.
위 코드에서 data-productid="${product.productId}"는 data-productid라는 이름의 데이터 속성을 생성하고 그 값으로 product.productId를 설정하는 것이다. 그런 다음, 클릭 이벤트 핸들러 내부에서 $(this).data('productid')를 사용하여 해당 데이터 속성의 값을 가져오게 된다.
$('.wish-icon').click(function() {
let productId = $(this).data('productid');
const data = {
"productId": productId
};
const self = $(this);
$.ajax("/wish/like", {
method: "post",
contentType: "application/json",
data: JSON.stringify(data),
success: function(response) {
if (response.like) {
self.addClass("fa-solid");
self.removeClass("fa-regular");
} else {
self.removeClass("fa-solid");
self.addClass("fa-regular");
}
},
error: function(response) {
const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample);
$(".toast-body").text(response.responseJSON.message);
toastBootstrap.show();
}
});ajax요청을 통해서 상품id만 json으로 담아서 보냈다.
응답을 like가 ture인지 false인지를 받게 되는데 true라면 꽉찬별 아니라면 빈 별을 하게 된다.
error코드를 받아서 로그인되지 않았다면 로그인하라는 메시지를 받게 된다.
2.컨트롤러
ajax처리를 할때 멤버id를 페이지에서 얻어올 수도 있지만 굳이 그럴필요는 없다. Authentication객체를 파라미터로 받고 서비스에서 getName을 통해서 사용할 것이다. 응답상태를 두가지로 나누어야하기 때문에 ResponseEntity객체를 사용했다.
로그인 되어 있지 않다면 Authentication객체가 null이기 때문에 403에러(권한없음)을 제공하고 로그인 후 가능하다는 메시지를 반환한다.
아니라면 서비스에 넘겨서 처리를 하게 한다.
// 찜 처리
@PostMapping("like")
public ResponseEntity<Map<String, Object>> likeProc(@RequestBody WishList wishList, Authentication authentication) {
if (authentication == null) {
return ResponseEntity
.status(403)
.body(Map.of("message", "로그인 후 가능합니다."));
} else {
Map<String, Object> res = wishService.like(authentication, wishList);
return ResponseEntity.ok(res);
}
}3. 서비스
1.먼저 삭제를 한다 리턴값으로 몇개가 삭제되었는지 반환값을 받는다.
2.삭제한게잇다면(반환값이 있다면) 이미 전에 눌럿던것이니 추가하지 않는다.
3.삭제한게 없다면 처음 누르는 것이니 추가한다.
특정 상품과 특정 멤버를 삭제해야하기 때문에 Authentication객체로부터 멤버 정보를 얻고
WishList 도메인의 멤버 id값으로 설정해준다.
@Override
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> like(Authentication authentication, WishList wishList) {
Map<String, Object> result = new HashMap<>();
result.put("like", false);
wishList.setMemberId(authentication.getName());
Integer deleteCnt = wishMapper.deleteList(wishList);
if (deleteCnt != 1) {
Integer insertCnt = wishMapper.insert(wishList);
result.put("like", true);
}
return result;
}4. js 버튼클릭시
클릭 이벤트 핸들러 내에서 $(this)의 의미:
$(this)는 클릭 이벤트가 발생한 요소(여기서는 .wish-icon)를 가리킨다.
그러나 클릭 이벤트 핸들러 내에서 $(this)를 사용할 때 주의할 점은 해당 핸들러가 콜백 함수로 실행되기 때문에 $(this)가 의도한대로 동작하지 않을 수 있다.
이 문제를 해결하기 위해 클릭 이벤트 핸들러의 바깥에서 const self = $(this);와 같은 변수를 선언하고, 이 변수를 클릭 이벤트 핸들러 내에서 사용하여 원하는 동작을 수행할 수 있게 할 수 있다.
위의 코드에서 const self = $(this);를 추가하여 클릭 이벤트 핸들러 바깥에서 $(this)를 저장한다.
그런 다음, self 변수를 사용하여 $(this)를 대체하여 원하는 동작을 수행한다.
이렇게 하면 클릭 이벤트 핸들러 내에서 $(this)를 올바르게 참조할 수 있게 된다.
즉, self 변수를 클로저(closure)로 사용하여 클릭 이벤트 핸들러 내에서 $(this)의 값을 보존하고 사용할 수 있도록 한다.
이를 통해 $(this)가 의도한대로 동작하고 원하는 결과를 얻을 수 있다.
5. 좋아요한것일시
좋아요한것처럼보이게 해야한다.
목록으로 불러오기때문에 비효율적이지만 반복문을 사용해야한다.
상품 id목록을 불러오고 상품목록에서 그사람이 좋아요 한 목록이 있다면 Liked를 true로한다.
// 현재 로그인 한 사람이 좋아요한 목록 가져오기
if (authentication != null) {
List<Integer> likedProductIds = wishListMapper.getLikedProductId(authentication.getName());
for (ProductView product : productList) {
if (likedProductIds.contains(product.getProductId())) {
product.setLiked(true);
}
}
}set으로 변경했다.
if (authentication != null) {
Set<Integer> likedProductIds = wishListMapper.getLikedProductId(authentication.getName());
for (ProductView product : productList) {
if (likedProductIds.contains(product.getProductId())) {
product.setLiked(true);
}
}
}List에서 Set르로 변경을 햇는데 중복된 값이 없더라도 Set은 검색 성능이 더 우수하다고 해서 사용했다.
Set은 내부적으로 해시 테이블 또는 트리 구조를 사용하여 요소를 저장하므로 contains() 메소드를 사용하여 특정 요소를 검색할 때 빠르게 찾을 수 있다고 한다.
이는 Set이 요소의 해시 값을 기반으로 검색하기 때문이다.
반면에 List는 순차적으로 요소를 검색하므로, contains() 메소드를 사용할 때 List의 크기에 따라 선형적으로 검색 시간이 증가한다.
따라서 List에서 특정 요소를 찾는 데는 시간이 더 많이 소요될 수 있다.
하지만 중복된 값이 없고 데이터의 크기가 작은 경우에는 Set과 List의 검색 성능 차이가 크게 나타나지 않을 수 있다고 한다.
작은 데이터 크기에서는 List로도 충분히 빠른 검색이 가능할 것이다.
따라서, 현재 상황에서 중복된 값이 없다면 Set과 List 중 어느 것을 선택해도 큰 성능 차이는 나타나지 않지만 둘 중에서 개발자가 더 편한 방식을 선택하면 된다고 한다.
7. 메일보내기 설정!
https://terianp.tistory.com/119
메일보내기 설정들보고 설정하였다. 따로 정리하도록 하겠다.
컨트롤러 설정 서비스 컨피그 등등