본문 바로가기
Spring/JPA + Security

[Spring Data JPA] 좋아요 구현하기 -3 좋아요 뷰 렌더링, 카운트 렌더링

by pyogowoon 2023. 1. 13.

좋아요의 뷰를 구현하고자 한다. 빈 하트를 클릭하면 빨갛게 채워진 하트가 되어야 하고, 새로고침이나 로그인 시

 이전에 좋아요 했던 인원들은 이미 빨갛게 채워진 하트가 되어있어야 한다.

즉 이전에 포토 스토리 뷰를 ajax로 렌더링 할 때,

 DB에서 좋아요의 현황도 가져와야 한다.

 

story.js

function storyLoad() {

    $.ajax({

    type:"get",
    url:`/api/image?page=${page}`,
    dataType:"json"

    }).done(res =>{
    console.log(res, "성공");
    res.data.content.forEach((image) =>{


    let storyItem = getStoryItem(image);
    $("#storyList").append(storyItem);

 });

    }).fail(error =>{
        console.log(error,"실패");
    });


}

 storyLoad();

 

이전에 구현한 ajax 에서 여기서 foreach 돌린 image 안에 있는 likes 정보를 가져와야 한다.

 

 

 

 

ImageService

@Transactional(readOnly = true)
public Page<Image> 이미지스토리(int principalId , Pageable pageable){
    imageRepository.findById(principalId).orElseThrow(()->{
        throw new CustomApiException("아이디를 찾을 수 없습니다.");
    });
    Page<Image> images = imageRepository.mStory(principalId, pageable);

     
        return images;
}

결국 이전에 만든 ImageService 에서 어떻게든 likes 정보를 가져와야 한다.

 그러나 Image 도메인에는 likes를 만든적이 없다, 

 그러므로 Image에 likes 양방향 맵핑을 만들어야 한다.

 

 

 

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
public class Image {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String caption;
    private String postImageUrl;  // 사진을 전송받아서 그 사진을 서버에
    // 폴더에 저장하게될것 - DB에는 경로를 인서트

    @JsonIgnoreProperties({"images"})
    @JoinColumn(name="userId") //foreign 키 이름 지정
    @ManyToOne // 한명이 많은 이미지를 올릴 수 있고 이미지는 하나의 이미지가 여럿일수없으니
    private User user; // db에 오브젝트 자체를 저장할 순 없고 이대로면 foreign key로 저장됨

    //이미지 좋아요
    // image를 셀렉트할떄 likes 정보를 같이 들고옴

    @JsonIgnoreProperties({"image"})
    @OneToMany(mappedBy="image") //likes의 image변수 이름
    private List<Likes> likes;
    //이미지 댓글

    private LocalDateTime createDate;

    @Transient // DB에 칼럼이 만들어지지않는다
    private boolean likeState;

    @Transient
    private int likeCount;

    @PrePersist
    public void createDate() {
        this.createDate = LocalDateTime.now();
    }
}

Image 도메인에 List<Likes> likes 를 추가해주었다, 하나의 이미지에 좋아요는 수백개 수천개 있을 수 있으니

List로 받았다.

Image 도메인에  likeState , likeCount 를 추가하였고

@OneToMany(mappedBy = "image") 를 걸어주었다,   여기서 Image는 Likes 의 필드인 image의 변수값이다.

@Transient 어노테이션을 통해 DB에 칼럼을 만들지 않고 참조만 하도록 설정 해주었고 (이친구 아주 유용)

 

스크롤 페이징에서 무한참조가 되는 오류가 발생하였기에

 

likes 컬럼에 @JsonIgnoreProperties 어노테이션을 추가.(양방향 맵핑시에 꼭 붙이자.)

 

 

imageService 에 이미지 스토리에 forEach문 추가.

ImageService

@Transactional(readOnly = true)
public Page<Image> 이미지스토리(int principalId , Pageable pageable){
    imageRepository.findById(principalId).orElseThrow(()->{
        throw new CustomApiException("아이디를 찾을 수 없습니다.");
    });
    Page<Image> images = imageRepository.mStory(principalId, pageable);

        images.forEach((image) ->{

            image.getLikes().forEach((like) ->{
                if(like.getUser().getId() == principalId){
                    image.setLikeState(true);
                }

            });

        });

        return images;
}

 

기존의 이미지스토리 로직에서 foreach문을 돌리는 로직을 추가하였다,

 forEach로 Likes의 정보를 모두 나열한 후 Likes의 id와 로그인한 아이디(PrincipalId) 가 같으면

 likeState를 true로 해주는 로직이다.

 

 

.
.
.
.
  
  <div class="sl__item__img">
                      <img src="/upload/${image.postImageUrl}" />
                  </div>

                  <div class="sl__item__contents">
                      <div class="sl__item__contents__icon">

                          <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>

 

기존에 있던 HTML 영역을 백틱으로 닫아주고  if~ 부터는 JS영역으로 최초 홈페이지 실행시 빈 하트가 뜰 수 있도록 처리한다.

 

 

 

이제 좋아요 상태에 따라 빈하트와 빨간하트가 잘 작동된다.

 

 이제 좋아요 카운트 뷰 렌더링만 하면 된다.

 

ImageService

@Transactional(readOnly = true)
public Page<Image> 이미지스토리(int principalId , Pageable pageable){
    imageRepository.findById(principalId).orElseThrow(()->{
        throw new CustomApiException("아이디를 찾을 수 없습니다.");
    });
    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;
}

카운트의 경우 간단하게 likes의 사이즈를 likeCount 에 set 해주면 된다.

 

story.js

<span class="like"><b id="storyLikeCount-${image.id}">${image.likeCount}</b>likes</span>

js에도 연결해주고

 

likes 의 갯수도 잘 나온다.

 

 하지만 아직 비동기 렌더링은 진행하지 않았다.

 당연히 좋아요를 누르면 카운트가 바로 올라가야 하는 로직을 구현해야만 한다.

 -4에서 계속...

댓글