diff --git a/backend-board/src/main/java/com/backendboard/BackendBoardApplication.java b/backend-board/src/main/java/com/backendboard/BackendBoardApplication.java index 6e9a482..bf1dac0 100644 --- a/backend-board/src/main/java/com/backendboard/BackendBoardApplication.java +++ b/backend-board/src/main/java/com/backendboard/BackendBoardApplication.java @@ -3,7 +3,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.scheduling.annotation.EnableScheduling; +@EnableScheduling @EnableJpaAuditing @SpringBootApplication public class BackendBoardApplication { diff --git a/backend-board/src/main/java/com/backendboard/domain/post/scheduler/PostScheduler.java b/backend-board/src/main/java/com/backendboard/domain/post/scheduler/PostScheduler.java new file mode 100644 index 0000000..949b5cf --- /dev/null +++ b/backend-board/src/main/java/com/backendboard/domain/post/scheduler/PostScheduler.java @@ -0,0 +1,39 @@ +package com.backendboard.domain.post.scheduler; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import com.backendboard.domain.postlike.repository.PostLikeRedisRepository; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +@RequiredArgsConstructor +public class PostScheduler { + private static final String UPDATE_LIKE_COUNT_SQL = "UPDATE post SET like_count = ? WHERE id = ?"; + + private final PostLikeRedisRepository postLikeRedisRepository; + private final JdbcTemplate jdbcTemplate; + + @Scheduled(fixedDelay = 300_000) + public void syncLikeCount() { + Map entries = postLikeRedisRepository.getEntries(); + List batchArgs = new ArrayList<>(); + + for (Map.Entry entry : entries.entrySet()) { + String postId = (String)entry.getKey(); + long likeCount = ((Number)entry.getValue()).longValue(); + batchArgs.add(new Object[] {likeCount, Long.valueOf(postId)}); + } + + jdbcTemplate.batchUpdate(UPDATE_LIKE_COUNT_SQL, batchArgs); + postLikeRedisRepository.delete(); + } +} diff --git a/backend-board/src/main/java/com/backendboard/domain/postlike/repository/PostLikeRedisRepository.java b/backend-board/src/main/java/com/backendboard/domain/postlike/repository/PostLikeRedisRepository.java new file mode 100644 index 0000000..1505103 --- /dev/null +++ b/backend-board/src/main/java/com/backendboard/domain/postlike/repository/PostLikeRedisRepository.java @@ -0,0 +1,13 @@ +package com.backendboard.domain.postlike.repository; + +import java.util.Map; + +public interface PostLikeRedisRepository { + Long getCount(String postId); + + void save(String postId, Long likeCount); + + void delete(); + + Map getEntries(); +} diff --git a/backend-board/src/main/java/com/backendboard/domain/postlike/repository/PostLikeRedisRepositoryImpl.java b/backend-board/src/main/java/com/backendboard/domain/postlike/repository/PostLikeRedisRepositoryImpl.java new file mode 100644 index 0000000..0c22309 --- /dev/null +++ b/backend-board/src/main/java/com/backendboard/domain/postlike/repository/PostLikeRedisRepositoryImpl.java @@ -0,0 +1,41 @@ +package com.backendboard.domain.postlike.repository; + +import java.util.Map; +import java.util.Optional; + +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Repository; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Repository +@RequiredArgsConstructor +public class PostLikeRedisRepositoryImpl implements PostLikeRedisRepository { + private static final String KEY = "post:like"; + + private final RedisTemplate redisTemplate; + + @Override + public Long getCount(String postId) { + return Optional.ofNullable(redisTemplate.opsForHash().get(KEY, postId)) + .map(value -> ((Number)value).longValue()) + .orElse(null); + } + + @Override + public void save(String postId, Long likeCount) { + redisTemplate.opsForHash().put(KEY, postId, likeCount); + } + + @Override + public void delete() { + redisTemplate.delete(KEY); + } + + @Override + public Map getEntries() { + return redisTemplate.opsForHash().entries(KEY); + } +} diff --git a/backend-board/src/main/java/com/backendboard/domain/postlike/service/PostLikeServiceImpl.java b/backend-board/src/main/java/com/backendboard/domain/postlike/service/PostLikeServiceImpl.java index 1ecf49f..0f5d823 100644 --- a/backend-board/src/main/java/com/backendboard/domain/postlike/service/PostLikeServiceImpl.java +++ b/backend-board/src/main/java/com/backendboard/domain/postlike/service/PostLikeServiceImpl.java @@ -6,6 +6,7 @@ import com.backendboard.domain.postlike.dto.PostLikeCountResponse; import com.backendboard.domain.postlike.dto.PostLikeStatusResponse; import com.backendboard.domain.postlike.entity.PostLike; +import com.backendboard.domain.postlike.repository.PostLikeRedisRepository; import com.backendboard.domain.postlike.repository.PostLikeRepository; import com.backendboard.domain.user.entity.User; import com.backendboard.domain.user.repository.UserRepository; @@ -16,6 +17,7 @@ @Service @RequiredArgsConstructor public class PostLikeServiceImpl implements PostLikeService { + private final PostLikeRedisRepository postLikeRedisRepository; private final PostLikeRepository postLikeRepository; private final UserRepository userRepository; @@ -41,7 +43,13 @@ public PostLikeStatusResponse getLikeStatus(Long authUserId, Long postId) { @Override public PostLikeCountResponse getLikeCount(Long postId) { - Long count = postLikeRepository.countByPostId(postId); + String postIdKey = postId.toString(); + Long count = postLikeRedisRepository.getCount(postIdKey); + + if (count == null) { + count = postLikeRepository.countByPostId(postId); + postLikeRedisRepository.save(postIdKey, count); + } return PostLikeCountResponse.toDto(count); } } diff --git a/backend-board/src/main/java/com/backendboard/global/config/RedisConfig.java b/backend-board/src/main/java/com/backendboard/global/config/RedisConfig.java index 4a89cb8..753139b 100644 --- a/backend-board/src/main/java/com/backendboard/global/config/RedisConfig.java +++ b/backend-board/src/main/java/com/backendboard/global/config/RedisConfig.java @@ -25,9 +25,14 @@ public RedisConnectionFactory redisConnectionFactory() { @Bean public RedisTemplate redisTemplate() { RedisTemplate redisTemplate = new RedisTemplate<>(); + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); + GenericJackson2JsonRedisSerializer genericSerializer = new GenericJackson2JsonRedisSerializer(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); - redisTemplate.setKeySerializer(new StringRedisSerializer()); - redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + redisTemplate.setKeySerializer(stringRedisSerializer); + redisTemplate.setHashKeySerializer(stringRedisSerializer); + redisTemplate.setValueSerializer(genericSerializer); + redisTemplate.setHashValueSerializer(genericSerializer); return redisTemplate; } }