좋아요 기능 구현, Popular 페이지 렌더링을 연달아 구현할 예정입니다.
Popular 페이지란?
좋아요가 많은 순서대로 Image를 출력하는 페이지입니다.
따라서 그전에 좋아요 기능을 먼저 구현해 보도록 하겠습니다.
좋아요 기능 구현
모델 만들기 & 연관관계 매핑
엔티티명은 MYSQL에 LIKE 함수가 존재하므로 Likes로 만들었습니다.
😁 @Transient
엔티티 필드로는 필요한데, DB 칼럼으로 만들고 싶지 않을 때 사용하는 어노테이션
어떤 필드들이 필요할까?
우리가 SNS에서 어떤 로직으로 좋아요를 누르는지 생각해보면 됩니다.
즉, 어떤 이미지를 누가 좋아했는지 알아야 합니다.
또한 특정 유저는 한 이미지를 딱 한 번만 좋아요 누를 수 있습니다. (다중 칼럼 UNIQUE 제약 조건)
- 좋아요 누르고 다시 누르면 좋아요 취소되게끔 JS처리
Likes
@NoArgsConstructor
@Getter
@Entity
@Table(
uniqueConstraints = {
@UniqueConstraint(
name = "likes_uk",
columnNames = {"imageId", "userId"}
)
}
)
public class Likes extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@JoinColumn(name = "imageId")
@ManyToOne(fetch = FetchType.LAZY)
private Image image;
@JoinColumn(name = "userId")
@ManyToOne(fetch = FetchType.LAZY)
private User user;
}
모델과 Repository는 짝꿍으로 함께 만들어줍니다.
LikesRepository
public interface LikesRepository extends JpaRepository<Likes, Integer> {
}
이제 LikesRepository를 Service에 연결합니다.
그리고 Controller를 만들면 되는데,
어떤 Controller를 만들어야 할까요?
- Controller
- ApiController
우리는 API 호출을 통해 데이터를 받을 것이므로 ApiController, 그중에서도 이미 만든 이미지 컨트롤러를 사용할 것입니다. (재사용성)
만들 메서드
- 좋아요
- 좋아요 취소
해당 메서드 API 주소
- 이미지 / 어떤 이미지를 / 좋아요 한 건지
- @PostMapping("/api/image/{imageId}/likes")
- 이미지 / 어떤 이미지를 / 좋아요 취소 한 건지
- @DeleteMapping("/api/image/{imageId}/likes")
로직
- ApiController에서 서비스를 호출
- 어떤 이미지를, 누가 좋아하는지 알아야 하므로 파라미터에 ImageId, sessionId 넘김
- 서비스에서 리포지토리를 호출
- 리포지토리에서 nativeQuery 직접 구현
👏 이 정도 쿼리는 생성할 수 있도록 연습해야 한다.
좋아요
INSERT INTO likes(imageId, userId) VALUES(:imageId, :sessionId)
좋아요 취소
DELETE FROM likes WHERE imageId =:imageId AND userId =:sessionId
Postman 확인
로직을 잘 탔는지 Postman으로 확인해 보겠습니다. (로그인 : 2)
좋아요 기능 확인
좋아요 기능 취소 확인
브라우저에서 확인 (JS)
Postman으로 좋아요 기능과 취소기능을 확인했습니다.
이제 브라우저에서도 확인할 수 있게 html, js 처리를 해주면 됩니다.
목표
- 로그인하면 Feed페이지에 내가 좋아요 한 이미지만 빨간 하트처리
로직
- Feed페이지 랜더링 API를 호출할 때, images에 좋아요 정보를 추가로 담아야 합니다.
- 관련 API : ImageApiController의 @GetMapping("/api/feed")
- Image엔티티에 likes 양방향 연관관계 설정 (@OneToMany)
- 좋아요 카운트, 좋아요 상태를 담을 필드 추가 생성 (likesCount, likesState)
- DB에 굳이 컬럼을 생성 안 해도 되므로 상단 @Transient 기재
- 좋아요 정보 담기
👍 좋아요 정보를 어떻게 담을 건지 생각해 볼 것
- 상황 : 로그인(1번) 하면 Feed페이지에 내(1번)가 구독하는 사람들이 올린 이미지가 출력됨
- 목표 : 이미지 좋아요, 이미지 카운트 정보 뽑아서 images에 담기
- 어떻게 :
1. Feed에 있는 모든 이미지를 돌면서 (for 문)
2. 이미지의 좋아요 정보 뽑기
3. 그중 내가(1번) 좋아요 한 게 있다면 likesState=true 후 그 정보를 images에 넣기
4. 좋아요 카운트는 이미지의 좋아요 사이즈를 통해 세팅
이를 적용한 ImageService
@Transactional(readOnly = true)
public Page<Image> Feed(int sessionId, Pageable pageable) {
Page<Image> images = imageRepository.cFeed(sessionId, pageable);
images.forEach((image -> {
image.setLikesCount(image.getLikes().size());
image.getLikes().forEach((like) -> {
if(like.getUser().getId() == sessionId) {
image.setLikesState(true);
}
});
}));
return images;
}
😁 중간점검 😁
1. 추후 리팩토링 해야 할 부분 (Image 엔티티)
현 상황
@Data 사용을 지양하기 위해 엔티티 상단에 있던 것을 지우고 변경이 필요한 필드 위에만 @Setter를 걸어둠
리팩토링 방향
@Setter를 지우고 DTO를 새로 만들어서 처리
2. 기억할 것
@XToOne은 default가 EAGER이므로 → LAZY 수정
@XToMany default가 LAZY
3. 무한 순환참조 이슈가 있는지 항상 확인 (발생한 Case별로 나눠서 포스팅함)
이어서 진행해 보겠습니다. 이제 브라우저 상에서 인터렉티브 한 부분을 다룰 차례입니다. (JS)
목표
하트를 눌렀을 때 좋아요가 되면서 카운트되도록 하기
html에서 해당 코드 부분 (id 주목)
<span class="like"><b id="storyLikeCount-${image.id}">${image.likeCount}</b>likes</span>
JS로 와서 하트를 누르는 함수에 위의 id를 가져와서 적용해 주면 됩니다.
$.ajax({
type: "post",
url: `/api/image/${imageId}/likes`,
dataType: "json",
success: function(res) {
let likeCountStr = $(`#storyLikeCount-${imageId}`).text();
let likeCount = Number(likeCountStr) + 1;
$(`#storyLikeCount-${imageId}`).text(likeCount);
likeIcon.addClass("fas");
likeIcon.addClass("active");
likeIcon.removeClass("far");
},
- let likeCountStr = $(`#storyLikeCount-${image.id}`).text();
- 아이디 #storyLikeCount-${image.id}의 text를 가져와서 저장합니다.
- let likeCount = Number(likeCountStr) - 1;
- likeCountStr이 문자열이므로 Number로 캐스팅
- $(`#storyLikeCount-${image.id}`).text(likeCount);
- text 자리에 넣습니다.
- text 자리에 넣습니다.
원래 좋아요 기능, Popular 페이지 렌더링, 자잘한 부분을 전부 한 포스팅에 기재하려고 했습니다.
자세하게 적느라 너무 길어져 총 3편으로 나누게 되었고 전부 합쳐서 github에 올리겠습니다.
읽어주셔서 감사합니다😁
'Project > 토이 프로젝트' 카테고리의 다른 글
[Cinemagram] hover 효과 및 프로필 사진 등록 - (12) (1) | 2023.01.26 |
---|---|
[Cinemagram] Popular 페이지 렌더링 - (11) (0) | 2023.01.26 |
[Trouble Shooting] 무한 순환참조 이슈 (0) | 2023.01.18 |
[Cinemagram] 팔로우 기능 구현 - (9) (0) | 2022.12.26 |
[Trouble Shooting] JPA DTO Mapping - QLRM 라이브러리 사용 (0) | 2022.12.23 |