TIL

[Spring] 좋아요 기능 추가

승무_ 2023. 1. 28. 14:11

Controller testCode

    @Test
    @WithMockUser
    void 좋아요기능() throws Exception {
        mockMvc.perform(post("/api/v1/posts/1/likes")
                        .contentType(MediaType.APPLICATION_JSON)
                ).andDo(print())
                .andExpect(status().isOk());
    }

    @Test
    @WithAnonymousUser
    void 좋아요클릭시_로그인하지_않은경우() throws Exception {
        mockMvc.perform(post("/api/v1/posts/1/likes")
                        .contentType(MediaType.APPLICATION_JSON)
                ).andDo(print())
                .andExpect(status().isUnauthorized());
    }

    @Test
    @WithMockUser
    void 좋아요클릭시_게시물이_없는경우() throws Exception {
        doThrow(new SimpleSnsApplicationException(ErrorCode.POST_NOT_FOUND)).when(postService).like(any(), any());
        mockMvc.perform(post("/api/v1/posts/1/likes")
                        .contentType(MediaType.APPLICATION_JSON)
                ).andDo(print())
                .andExpect(status().isNotFound());
    }

Controller- 좋아요 누르기

    @PostMapping("/{postId}/likes")
    public Response<Void> like(@PathVariable Integer postId, Authentication authentication) {
        postService.like(postId, authentication.getName());
        return Response.success();
    }

Service- 좋아요 누르기

    @Transactional
    public void like(Integer postId, String userName) {
        //post,user가 존재하는지 확인
        PostEntity postEntity = postEntityRepository.findById(postId).orElseThrow(() -> new SimpleSnsApplicationException(ErrorCode.POST_NOT_FOUND, String.format("postId is %d", postId)));
        UserEntity userEntity = userEntityRepository.findByUserName(userName)
                .orElseThrow(() -> new SimpleSnsApplicationException(ErrorCode.USER_NOT_FOUND, String.format("userName is %s", userName)));
        
        //이 user가 이미 좋아요를 눌렀는지 확인
        likeEntityRepository.findByUserAndPost(userEntity, postEntity).ifPresent(it -> {
            throw new SimpleSnsApplicationException(ErrorCode.ALREADY_LIKED_POST, String.format("userName %s already like the post %s", userName, postId));
        });

        likeEntityRepository.save(LikeEntity.of(postEntity, userEntity));
    }

Repository- 좋아요 누르기

@Repository
public interface LikeEntityRepository extends JpaRepository<LikeEntity, Integer> {

    Optional<LikeEntity> findByUserAndPost(UserEntity user, PostEntity post);
}

LikeEntity

@Setter
@Getter
@Entity
@Table(name = "\"like\"")
@SQLDelete(sql = "UPDATE \"like\" SET removed_at = NOW() WHERE id=?")
@Where(clause = "removed_at is NULL")
@NoArgsConstructor
public class LikeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id = null;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private UserEntity user;

    @ManyToOne
    @JoinColumn(name = "post_id")
    private PostEntity post;

    @Column(name = "registered_at")
    private Timestamp registeredAt;

    @Column(name = "updated_at")
    private Timestamp updatedAt;

    @Column(name = "removed_at")
    private Timestamp removedAt;


    @PrePersist
    void registeredAt() {
        this.registeredAt = Timestamp.from(Instant.now());
    }

    @PreUpdate
    void updatedAt() {
        this.updatedAt = Timestamp.from(Instant.now());
    }

    public static LikeEntity of(PostEntity post, UserEntity user) {
        LikeEntity entity = new LikeEntity();
        entity.setPost(post);
        entity.setUser(user);
        return entity;
    }
}

이 entity로 user가 어떤 post에 좋아요를 눌렀는지 표현할 수 있음

 

 

Controller- 좋아요 가져오기

    @GetMapping("/{postId}/likes")
    public Response<Integer> getLikes(@PathVariable Integer postId, Authentication authentication) {
        return Response.success(postService.getLikeCount(postId));
    }

Service- 좋아요 가져오기

public Integer getLikeCount(Integer postId) {
        PostEntity postEntity = postEntityRepository.findById(postId).orElseThrow(() -> new SimpleSnsApplicationException(ErrorCode.POST_NOT_FOUND, String.format("postId is %d", postId)));
        List<LikeEntity> likes = likeEntityRepository.findAllByPost(postEntity);
        return likes.size();
    }

Repository- 좋아요 가져오기

List<LikeEntity> findAllByPost(PostEntity post);

 

 

좋아요 가져오기 최적화

위 방식대로 진행하여도 post당 좋아요 갯수를 가져올 수 있다.

그러나 위 방식은 필요한건 갯수 뿐인데 entity전체를 db에서 가져온다.

List<LikeEntity> findAllByPost(PostEntity post);
//SELECT * FROM "like" where post_id = 2;

@Query(value = "SELECT COUNT(*) from LikeEntity entity WHERE entity.post = :post")
Integer countByPost(@Param("post") PostEntity post);

다음과 같이 sql을 짜주면 위 문제를 해결할 수 있고 주석과 비교하면 차이를 알 수 있다.

'TIL' 카테고리의 다른 글

[Spring] JWT와 session 기반 인증의 차이점  (0) 2023.02.08
[Spring] 알람 기능 추가  (0) 2023.02.01
[Spring] JPA deleteAll  (0) 2023.01.26
[Spring] Json 응답처리와 예외처리  (0) 2023.01.14
[Spring] cloudtype에 배포하기  (0) 2023.01.02