Skip to content

Commit dd93eed

Browse files
committed
Refactor: 인기글 레디스 적용
1 parent 0479af9 commit dd93eed

File tree

5 files changed

+54
-13
lines changed

5 files changed

+54
-13
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ dependencies {
9999
exclude(group = "org.yaml", module = "snakeyaml")
100100
}
101101
implementation("org.yaml:snakeyaml:2.0")
102+
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2")
102103

103104
}
104105

src/main/java/org/com/stocknote/config/RedisConfig.java

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
package org.com.stocknote.config;
22

3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.fasterxml.jackson.databind.SerializationFeature;
5+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
36
import org.springframework.beans.factory.annotation.Value;
47
import org.springframework.context.annotation.Bean;
58
import org.springframework.context.annotation.Configuration;
69
import org.springframework.data.redis.connection.RedisConnectionFactory;
710
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
811
import org.springframework.data.redis.core.RedisTemplate;
912
import org.springframework.data.redis.core.StringRedisTemplate;
13+
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
1014
import org.springframework.data.redis.serializer.GenericToStringSerializer;
15+
import org.springframework.data.redis.serializer.RedisSerializer;
1116
import org.springframework.data.redis.serializer.StringRedisSerializer;
1217

1318
@Configuration
@@ -17,15 +22,6 @@ public class RedisConfig {
1722
@Value("${spring.data.redis.port}")
1823
private int redisPort;
1924

20-
@Bean
21-
public RedisTemplate<String, Object> redisTemplate() {
22-
RedisTemplate<String, Object> template = new RedisTemplate<>();
23-
template.setConnectionFactory(redisConnectionFactory());
24-
template.setKeySerializer(new StringRedisSerializer());
25-
template.setValueSerializer(new GenericToStringSerializer<>(Object.class));
26-
return template;
27-
}
28-
2925
@Bean
3026
public RedisConnectionFactory redisConnectionFactory() {
3127
return new LettuceConnectionFactory(redisHost, redisPort);
@@ -44,4 +40,24 @@ public RedisTemplate<Object, Object> redisObjectTemplate() {
4440
redisTemplate.setValueSerializer(new StringRedisSerializer());
4541
return redisTemplate;
4642
}
43+
@Bean
44+
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
45+
RedisTemplate<String, Object> template = new RedisTemplate<>();
46+
template.setConnectionFactory(connectionFactory);
47+
48+
// 🔹 JSON 직렬화 설정
49+
ObjectMapper objectMapper = new ObjectMapper();
50+
objectMapper.registerModule(new JavaTimeModule()); // LocalDateTime 지원 추가
51+
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // ISO 8601 포맷 유지
52+
53+
RedisSerializer<Object> serializer = new GenericJackson2JsonRedisSerializer(objectMapper);
54+
55+
template.setKeySerializer(new StringRedisSerializer()); // 키 직렬화
56+
template.setValueSerializer(serializer); // 값 직렬화
57+
template.setHashKeySerializer(new StringRedisSerializer());
58+
template.setHashValueSerializer(serializer);
59+
60+
template.afterPropertiesSet();
61+
return template;
62+
}
4763
}

src/main/java/org/com/stocknote/domain/post/dto/PostResponseDto.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.com.stocknote.domain.post.dto;
22

3+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
34
import org.com.stocknote.domain.comment.dto.CommentDetailResponse;
45
import org.com.stocknote.domain.member.entity.Member;
56
import org.com.stocknote.domain.post.entity.Post;

src/main/java/org/com/stocknote/domain/post/service/PostService.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.com.stocknote.domain.post.service;
22

33
import lombok.RequiredArgsConstructor;
4+
import lombok.extern.slf4j.Slf4j;
45
import org.com.stocknote.domain.hashtag.entity.Hashtag;
56
import org.com.stocknote.domain.hashtag.service.HashtagService;
67
import org.com.stocknote.domain.like.repository.LikeRepository;
@@ -16,18 +17,21 @@
1617
import org.com.stocknote.domain.post.repository.PostRepository;
1718
import org.com.stocknote.domain.post.repository.PostSearchRepository;
1819
import org.springframework.beans.factory.annotation.Autowired;
20+
import org.springframework.beans.factory.annotation.Qualifier;
1921
import org.springframework.cache.annotation.Cacheable;
2022
import org.springframework.data.domain.*;
2123
import org.springframework.data.redis.core.RedisTemplate;
2224
import org.springframework.stereotype.Service;
2325
import org.springframework.transaction.annotation.Transactional;
2426

27+
import java.time.Duration;
2528
import java.time.LocalDateTime;
2629
import java.util.List;
2730
import java.util.stream.Collectors;
2831

2932
@Service
3033
@RequiredArgsConstructor
34+
@Slf4j
3135
public class PostService {
3236
private final PostRepository postRepository;
3337
private final HashtagService hashtagService;
@@ -113,11 +117,22 @@ public void deletePost(Long id) {
113117
//댓글은 CASCADE로 삭제됨
114118
postRepository.delete(post);
115119
}
116-
@Cacheable(value = POPULAR_POSTS_CACHE_KEY, key = "#pageable.pageNumber", cacheManager = "cacheManager")
120+
117121
@Transactional(readOnly = true)
118122
public Page<PostResponseDto> getPopularPosts(Pageable pageable) {
119-
LocalDateTime threeDaysAgo = LocalDateTime.now().minusDays(3);
123+
log.info("🔥 캐시 확인: getPopularPosts 실행 (page = {})", pageable.getPageNumber());
124+
125+
String cacheKey = POPULAR_POSTS_CACHE_KEY + ":" + pageable.getPageNumber();
126+
127+
// 🔹 Redis에서 캐시된 데이터 조회
128+
Object cachedData = redisTemplate.opsForValue().get(cacheKey);
129+
if (cachedData instanceof Page) {
130+
log.info("✅ 캐시된 데이터 반환 (page = {})", pageable.getPageNumber());
131+
return (Page<PostResponseDto>) cachedData;
132+
}
120133

134+
// 🔹 캐시된 데이터가 없으면 DB에서 조회
135+
LocalDateTime threeDaysAgo = LocalDateTime.now().minusDays(3);
121136
Page<Post> popularPosts = postRepository.findPopularPosts(threeDaysAgo, pageable);
122137

123138
List<PostResponseDto> sortedPosts = popularPosts.stream()
@@ -130,10 +145,16 @@ public Page<PostResponseDto> getPopularPosts(Pageable pageable) {
130145
})
131146
.collect(Collectors.toList());
132147

133-
return new PageImpl<>(sortedPosts, pageable, popularPosts.getTotalElements());
148+
Page<PostResponseDto> response = new PageImpl<>(sortedPosts, pageable, popularPosts.getTotalElements());
149+
150+
// 🔹 Redis에 캐싱 (TTL: 5분 설정)
151+
redisTemplate.opsForValue().set(cacheKey, response, Duration.ofMinutes(5));
152+
log.info("🚀 새로운 데이터 Redis에 캐싱 완료 (key = {})", cacheKey);
153+
154+
return response;
134155
}
135156

136-
// 좋아요 순 조회
157+
// 좋아요 순 조회
137158
@Transactional(readOnly = true)
138159
public Page<PostResponseDto> getPopularPostsByLikes(Pageable pageable) {
139160
LocalDateTime sevenDaysAgo = LocalDateTime.now().minusDays(7);

src/main/java/org/com/stocknote/global/cache/service/CacheService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package org.com.stocknote.global.cache.service;
22

33
import lombok.RequiredArgsConstructor;
4+
import org.springframework.beans.factory.annotation.Qualifier;
45
import org.springframework.data.redis.core.RedisTemplate;
56
import org.springframework.stereotype.Service;
67

78
@Service
89
@RequiredArgsConstructor
910
public class CacheService {
11+
@Qualifier("redisTemplate")
1012
private final RedisTemplate<String, Object> redisTemplate;
1113
private static final String POPULAR_POSTS_CACHE_KEY = "popularPosts";
1214

0 commit comments

Comments
 (0)