2023. 1. 25. 16:47ㆍProject/시네마그램
좋아요 기능 구현, 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) (1) | 2022.12.26 |
[Trouble Shooting] JPA DTO Mapping - QLRM 라이브러리 사용 (0) | 2022.12.23 |