-
좋아요 버튼, 좋아요 수 구현Spring Boot 2021. 11. 16. 21:16
<jsp 파일>
//1) 좋아요 버튼 <button>`; if(image.likeState){ item += `<i class="fas fa-heart active" id="storyLikeIcon-${image.id}" onclick="toggleLike(${image.id})"></i>`; }else{ item += `<i class="far fa-heart" id="storyLikeIcon-${image.id}" onclick="toggleLike(${image.id})"></i>`; } item += ` </button> </div> //2) 좋아요 수 <span class="like"><b id="storyLikeCount-${image.id}">${image.likeCount} </b>likes</span>
좋아요 버튼, 좋아요 수 구현
<ajax - js 파일 구현>
// (3) 좋아요, 안좋아요 function toggleLike(imageId) { let likeIcon = $(`#storyLikeIcon-${imageId}`); if (likeIcon.hasClass("far")) { // 좋아요 $.ajax({ type: "post", url: `/api/image/${imageId}/likes`, dataType: "json" }).done(res=>{ let likeCountStr = $(`#storyLikeCount-${imageId}`).text(); let likeCount = Number(likeCountStr) + 1; $(`#storyLikeCount-${imageId}`).text(likeCount); likeIcon.addClass("fas"); likeIcon.addClass("active"); likeIcon.removeClass("far"); }).fail(error=>{ console.log("오류", error); }); } else { // 좋아요취소 $.ajax({ type: "delete", url: `/api/image/${imageId}/likes`, dataType: "json" }).done(res=>{ let likeCountStr = $(`#storyLikeCount-${imageId}`).text(); let likeCount = Number(likeCountStr) - 1; $(`#storyLikeCount-${imageId}`).text(likeCount); likeIcon.removeClass("fas"); likeIcon.removeClass("active"); likeIcon.addClass("far"); }).fail(error=>{ console.log("오류", error); }); } }
let likeCountStr = $(`#storyLikeCount-${imageId}`).text();
이 부분은, jsp파일에서,
id = storkyLikeCount - ${image.id} 의 text() 부분, 즉
<span class="like"><b id="storyLikeCount-${image.id}">${image.likeCount} </b>likes</span>
이 코드의
${image.likeCount}
이 값을 의미한다.
그렇다면, 만약
이미지 10번에 대해서 id =1인 회원이 좋아요 수를 눌러서 현재, 좋아요 수가 1이라고 하자,
즉, ${image.likeCount} =1 인 상태이다.
(현재 id=2 인 회원이 로그인 한 상태인데, 이 회원은 아직 좋아요를 누르지 않았기 때문에 하트가 빈칸이다)
그런데, id =2 인 회원이 이미지 10번에 좋아요를 누르면 좋아요 수가 2로 증가해야 한다.
그래서 코드에서 보면
let likeCountStr = $(`#storyLikeCount-${imageId}`).text();
let likeCount = Number(likeCountStr) + 1;이렇게 +1을 해줌으로써 좋아요 수를 증가시킨다.
** 좋아요 버튼이 빨갛게 변하는 부분은
https://happy-fun.tistory.com/142
참고
<ImageApiController.java>
@PostMapping("/api/image/{imageId}/likes") public ResponseEntity<?> likes(@PathVariable int imageId, @AuthenticationPrincipal PrincipalDetails principalDetails){ likesService.좋아요(imageId, principalDetails.getUser().getId()); return new ResponseEntity<>(new CMRespDto<>(1, "좋아요성공", null), HttpStatus.CREATED); } @DeleteMapping("/api/image/{imageId}/likes") public ResponseEntity<?> unLikes(@PathVariable int imageId, @AuthenticationPrincipal PrincipalDetails principalDetails){ likesService.좋아요취소(imageId, principalDetails.getUser().getId()); return new ResponseEntity<>(new CMRespDto<>(1, "좋아요취소성공", null), HttpStatus.OK); }
중요하지는 않으나, 이해를 위해 apicontroller.java의 return에 있는 <CMRespDto.java> 파일 첨부
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @AllArgsConstructor @NoArgsConstructor @Data public class CMRespDto<T> { private int code; // 1(성공), -1(실패) private String message; private T data; }
<LikesService.java> 파일
@RequiredArgsConstructor @Service public class LikesService { private final LikesRepository likesRepository; @Transactional public void 좋아요(int imageId, int principalId) { likesRepository.mLikes(imageId, principalId); } @Transactional public void 좋아요취소(int imageId, int principalId) { likesRepository.mUnLikes(imageId, principalId); }
그런데, 좋아요 수 구현은, LikesService.java가 아니라 ImageService.java에 구현했다.
<ImageService.java>
@Transactional(readOnly = true) public Page<Image> 이미지스토리(int principalId, Pageable pageable){ Page<Image> images = imageRepository.mStory(principalId, pageable); images.forEach((image)->{ image.setLikeCount(image.getLikes().size()); image.getLikes().forEach((like) -> { if(like.getUser().getId() == principalId) { image.setLikeState(true); } }); }); return images; }
여기서 보면, 이미지스토리 메서드에서 구현하고 있다.
그런데, ImageService.java나, LikesService.java나 같은 js파일에서 ajax를 구현해서 같은 jsp파일에 그 값들을 뿌려주기 때문에, 문제 없이 똑같은 화면에서 좋아요 상태, 좋아요 수를 나타내어줄 수 있다.
그래서 jsp파일에 위 코드의 images를 사용해서, 좋아요 수를 나타내면 된다.
그러면 먼저,
1) LikesService.java와 관련하여, 좋아요, 좋아요 취소가 구현되는 기능부터 살펴보겠다.
<Likes.java> 파일
@Builder @AllArgsConstructor @NoArgsConstructor @Data @Entity @Table( uniqueConstraints = { @UniqueConstraint( name="likes_uk", columnNames = {"imageId", "userId"} ) } ) public class Likes { // N @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; @JoinColumn(name = "imageId") @ManyToOne private Image image; // 1 @JsonIgnoreProperties({"images"}) @JoinColumn(name = "userId") @ManyToOne private User user; // 1 private LocalDateTime createDate; @PrePersist public void createDate() { this.createDate = LocalDateTime.now(); } }
@Table, @UniqueConstraint 관련해서는
https://happy-fun.tistory.com/140
여기 참조
일단, 좋아요를 할 이미지(image), 좋아요를 누른 사용자(user) 파악을 위해 변수들을 만들었다.
<LikesRepository.java>
public interface LikesRepository extends JpaRepository<Likes, Integer>{ //좋아요 @Modifying @Query(value = "INSERT INTO likes(imageId, userId, createDate) VALUES(:imageId, :principalId, now())", nativeQuery = true) int mLikes(int imageId, int principalId); //좋아요 취소 @Modifying @Query(value = "DELETE FROM likes WHERE imageId = :imageId AND userId = :principalId", nativeQuery = true) int mUnLikes(int imageId, int principalId); }
좋아요를 하는 이미지 번호(imageId), 좋아요 버튼을 누르는 로그인 한 사용자(principalId)를 이용해서
쿼리를 만들어, 좋아요, 좋아요 취소 구현
2) 두번째로, 좋아요 카운트 수를 구현하겠다.
<Image.java>
@Builder @AllArgsConstructor @NoArgsConstructor @Data @Entity public class Image { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; private String caption; private String postImageUrl; @JsonIgnoreProperties({"images"}) @JoinColumn(name = "userId") @ManyToOne(fetch = FetchType.EAGER) private User user; // 이미지 좋아요 @JsonIgnoreProperties({"image"}) @OneToMany(mappedBy = "image") private List<Likes> likes; @Transient // DB에 칼럼이 만들어지지 않는다. private boolean likeState; @Transient private int likeCount; private LocalDateTime createDate; @PrePersist public void createDate() { this.createDate = LocalDateTime.now(); } }
여기서 보면, @Transient 를 볼 수 있다.
likeState - true 이면 좋아요 상태, false 이면 좋아요 취소
likeCount - 좋아요 수(해당 이미지에 좋아요를 누른 수)
두 가지 변수는 db에 칼럼을 만들지 않는다.
위에 ImageService.java를 첨부했었는데, 설명하기 좋게 여기에 또 첨부하겠다.
<ImageService.java>
@Transactional(readOnly = true) public Page<Image> 이미지스토리(int principalId, Pageable pageable){ Page<Image> images = imageRepository.mStory(principalId, pageable); images.forEach((image)->{ image.setLikeCount(image.getLikes().size()); image.getLikes().forEach((like) -> { if(like.getUser().getId() == principalId) { image.setLikeState(true); } }); }); return images; }
여기에서 위의 두가지 변수인 likeState, likeCount가 쓰인다.
likeCount : 즉, image 모델에 likes 변수를 list 형태로 두어서 그 size, 즉 좋아요 수를 구한다.
likeState : 즉, image 모델의 likes 변수의 각 like 값에대해서,
좋아요 버튼을 누른 사용자와, 현재 로그인한 사용자가 같다면,
likeState = true가 된다.
위의 코드에서 imageRepository 관련한 코드가 있는데, 이는 사용자가 웹사이트에 올린 이미지를 db에서 가져오는 내용이다
이렇게 하면 좋아요 구현, 좋아요 수 구현이 완성된다.
참고자료 : 이지업 강의 사이트 "스프링부트 SNS프로젝트 - 포토그램 만들기"
'Spring Boot' 카테고리의 다른 글
좋아요 수 표시하기 (0) 2021.11.18 인기페이지 구현 (0) 2021.11.17 좋아요 버튼 만들기 (0) 2021.11.16 페이지 스크롤 (0) 2021.11.12 페이징 처리 (0) 2021.11.12