Skip to content

Commit

Permalink
KKUMI-118 [FEATURE] #73 포스트 좋아요 개수 조회 + 좋아요 Redis 캐싱
Browse files Browse the repository at this point in the history
포스트에 좋아요 개수를 조회하는 부분 구현, 한번 조회됐던 좋아요개수는 30초간 캐싱되도록 함
흐름은 아래와 같습니다.
1. Redis에서 post의 좋아요 개수 조회
2-1. Redis에 존재하면 바로 리턴
2-2. Redis에 존재하지 않으면 DB에서 조회 후 PostLikeCountCacheMissEvent 발생시켜 Redis에 캐싱되도록 함
  • Loading branch information
eekrwl committed Nov 11, 2024
1 parent e550457 commit f3c77b8
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ public interface LikesRepository extends JpaRepository<Likes, Long> {
"LEFT JOIN likes l ON p.post_id = l.post_id AND l.user_id = :userId " +
"WHERE p.post_id IN :postIds", nativeQuery = true)
List<Map<String, Object>> findPostLikesStatus(List<Long> postIds, Long userId);

@Query(value = "SELECT COUNT(l) FROM Likes l " +
"WHERE l.post.id = :postId AND l.isDeleted = false")
Long findPostLikeCountByPostId(Long postId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

import com.swmarastro.mykkumiserver.global.exception.CommonException;
import com.swmarastro.mykkumiserver.global.exception.ErrorCode;
import com.swmarastro.mykkumiserver.global.util.RedisUtils;
import com.swmarastro.mykkumiserver.post.PostRepository;
import com.swmarastro.mykkumiserver.post.domain.Post;
import com.swmarastro.mykkumiserver.post.event.PostLikeCountCacheMissEvent;
import com.swmarastro.mykkumiserver.user.User;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -21,6 +24,11 @@ public class LikesService {

private final LikesRepository likeRepository;
private final PostRepository postRepository;
private final RedisUtils redisUtils;
private final ApplicationEventPublisher eventPublisher;

private static final String POST_LIKE_KEY_PREFIX = "post:";
private static final String POST_LIKE_KEY_SUFFIX = ":likes";

public Boolean like(User user, Long postId) {

Expand Down Expand Up @@ -50,7 +58,7 @@ public Boolean unlike(User user, Long postId) {
Optional<Likes> optionalLike = likeRepository.findByPostAndUser(post, user);

//없음 -> 좋아요를 취소할 수 없습니다.
if(optionalLike.isEmpty()) {
if (optionalLike.isEmpty()) {
//TODO 이게 not_found가 맞는지 뭘쓸지 모르겠다
throw new CommonException(ErrorCode.NOT_FOUND, "좋아요를 취소할 수 없습니다.", "좋아요를 취소할 수 없습니다.");
}
Expand All @@ -73,4 +81,21 @@ public List<Boolean> isLikedByUser(User user, List<Long> postIds) {
.map(postId -> likeStatusMap.getOrDefault(postId, false))
.collect(Collectors.toList());
}

public Long getPostLikeCount(Long postId) {
Long value = redisUtils.getLongValues(makePostLikeKey(postId));
if (value != null) {
return value;
}
//없으면 DB에서 조회
Long postLikeCount = likeRepository.findPostLikeCountByPostId(postId);
//redis 갱신하도록 이벤트 던지기
eventPublisher.publishEvent(new PostLikeCountCacheMissEvent(postId, postLikeCount));
return postLikeCount;
}

private String makePostLikeKey(Long postId) {
return POST_LIKE_KEY_PREFIX + postId + POST_LIKE_KEY_SUFFIX;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.swmarastro.mykkumiserver.post.event;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class PostLikeCountCacheMissEvent {

private final Long postId;
private final Long likeCount;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.swmarastro.mykkumiserver.post.event.listener;

import com.swmarastro.mykkumiserver.global.util.RedisUtils;
import com.swmarastro.mykkumiserver.post.event.PostLikeCountCacheMissEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.time.Duration;

@Slf4j
@Component
@RequiredArgsConstructor
public class PostLikeEventListener {

private final RedisUtils redisUtils;
private static final String POST_LIKE_KEY_PREFIX = "post:";
private static final String POST_LIKE_KEY_SUFFIX = ":likes";
private static final Duration POST_LIKE_DURATION = Duration.ofSeconds(30);

@Async
@EventListener
public void setPostLikeCountCache(PostLikeCountCacheMissEvent postLikeCountCacheMissEvent) {
String key = makePostLikeKey(postLikeCountCacheMissEvent.getPostId());
redisUtils.setValues(key, postLikeCountCacheMissEvent.getLikeCount(), POST_LIKE_DURATION);
}

private String makePostLikeKey(Long postId) {
return POST_LIKE_KEY_PREFIX + postId + POST_LIKE_KEY_SUFFIX;
}
}

0 comments on commit f3c77b8

Please sign in to comment.