ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 좋아요 버튼, 좋아요 수 구현
    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

    댓글

Designed by Tistory.