Skip to content

Commit eb5ba75

Browse files
committed
feat: Redis 캐시 연동을 위한 DAO 및 TTL 정책 클래스 추가
- 각 API 역할에 따라 캐시 DAO를 기능별로 분리 (ClusterCacheDao, DiaryCacheDao) - Redis 장애 시 fallback을 위한 DB 조회 및 예외 처리 적용
1 parent 05d6c7d commit eb5ba75

File tree

4 files changed

+169
-0
lines changed

4 files changed

+169
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.example.log4u.domain.map.cache;
2+
3+
public class CacheKeyGenerator {
4+
5+
private static final String CLUSTER_CACHE_KEY_FORMAT = "cluster:geohash:%s:level:%d";
6+
private static final String DIARY_KEY_PREFIX = "marker:diary:";
7+
private static final String GEOHASH_ID_SET_PREFIX = "marker:ids:geohash:";
8+
9+
public static String clusterCacheKey(String geohash, int level) {
10+
return String.format(CLUSTER_CACHE_KEY_FORMAT, geohash, level);
11+
}
12+
13+
public static String diaryKey(Long diaryId) {
14+
return DIARY_KEY_PREFIX + diaryId;
15+
}
16+
17+
public static String diaryIdSetKey(String geohash) {
18+
return GEOHASH_ID_SET_PREFIX + geohash;
19+
}
20+
}
21+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.example.log4u.domain.map.cache;
2+
3+
import java.time.Duration;
4+
5+
public class RedisTTLPolicy {
6+
public static final Duration DIARY_ID_SET_TTL = Duration.ofMinutes(60);
7+
public static final Duration DIARY_TTL = Duration.ofMinutes(60);
8+
public static final Duration CLUSTER_TTL = Duration.ofMinutes(60);
9+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.example.log4u.domain.map.cache.dao;
2+
3+
import java.time.Duration;
4+
import java.util.List;
5+
import java.util.Optional;
6+
7+
import org.springframework.data.redis.core.RedisTemplate;
8+
import org.springframework.stereotype.Component;
9+
10+
import com.example.log4u.domain.map.cache.CacheKeyGenerator;
11+
import com.example.log4u.domain.map.dto.response.DiaryClusterResponseDto;
12+
13+
import lombok.RequiredArgsConstructor;
14+
import lombok.extern.slf4j.Slf4j;
15+
16+
@Component
17+
@RequiredArgsConstructor
18+
@Slf4j
19+
public class ClusterCacheDao {
20+
21+
private final RedisTemplate<String, List<DiaryClusterResponseDto>> diaryClusterRedisTemplate;
22+
23+
public Optional<List<DiaryClusterResponseDto>> getDiaryCluster(String geohash, int level) {
24+
try {
25+
String key = CacheKeyGenerator.clusterCacheKey(geohash, level);
26+
List<DiaryClusterResponseDto> cached = diaryClusterRedisTemplate.opsForValue().get(key);
27+
return Optional.ofNullable(cached);
28+
} catch (Exception e) {
29+
log.warn("클러스터 캐시 조회 실패 (geo={}, level={})", geohash, level, e);
30+
return Optional.empty();
31+
}
32+
}
33+
34+
public void setDiaryCluster(String geohash, int level, List<DiaryClusterResponseDto> data, Duration ttl) {
35+
try {
36+
String key = CacheKeyGenerator.clusterCacheKey(geohash, level);
37+
diaryClusterRedisTemplate.opsForValue().set(key, data, ttl);
38+
} catch (Exception e) {
39+
log.warn("클러스터 캐시 저장 실패 (geo={}, level={})", geohash, level, e);
40+
}
41+
}
42+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package com.example.log4u.domain.map.cache.dao;
2+
3+
import java.util.Collections;
4+
import java.util.List;
5+
import java.util.Set;
6+
import java.util.stream.Collectors;
7+
8+
import org.springframework.data.redis.core.RedisTemplate;
9+
import org.springframework.stereotype.Component;
10+
11+
import com.example.log4u.domain.map.cache.CacheKeyGenerator;
12+
import com.example.log4u.domain.map.cache.RedisTTLPolicy;
13+
import com.example.log4u.domain.map.dto.response.DiaryMarkerResponseDto;
14+
import com.fasterxml.jackson.databind.ObjectMapper;
15+
16+
import lombok.RequiredArgsConstructor;
17+
import lombok.extern.slf4j.Slf4j;
18+
19+
@Component
20+
@RequiredArgsConstructor
21+
@Slf4j
22+
public class DiaryCacheDao {
23+
24+
private final RedisTemplate<String, Object> redisTemplate;
25+
private final ObjectMapper objectMapper;
26+
27+
public Set<Long> getDiaryIdSetFromCache(String geohash) {
28+
String key = CacheKeyGenerator.diaryIdSetKey(geohash);
29+
try {
30+
Set<Object> raw = redisTemplate.opsForSet().members(key);
31+
if (raw == null)
32+
return Collections.emptySet();
33+
34+
return raw.stream()
35+
.map(obj -> Long.parseLong(obj.toString()))
36+
.collect(Collectors.toSet());
37+
} catch (Exception e) {
38+
log.error("diaryId Set 조회 실패 (key: {})", key, e);
39+
return Collections.emptySet();
40+
}
41+
}
42+
43+
public void cacheDiaryIdSetByGeohash(String geohash, List<Long> diaryIds) {
44+
if (diaryIds == null || diaryIds.isEmpty())
45+
return;
46+
47+
String key = CacheKeyGenerator.diaryIdSetKey(geohash);
48+
try {
49+
redisTemplate.opsForSet().add(key, diaryIds.toArray());
50+
redisTemplate.expire(key, RedisTTLPolicy.DIARY_ID_SET_TTL);
51+
} catch (Exception e) {
52+
log.error("diaryId Set 저장 실패 (key: {})", key, e);
53+
}
54+
}
55+
56+
public DiaryMarkerResponseDto getDiaryFromCache(Long diaryId) {
57+
String key = CacheKeyGenerator.diaryKey(diaryId);
58+
try {
59+
Object raw = redisTemplate.opsForValue().get(key);
60+
if (raw == null) return null;
61+
62+
return objectMapper.readValue(raw.toString(), DiaryMarkerResponseDto.class);
63+
} catch (Exception e) {
64+
log.error("단건 diary 조회 실패 (key: {})", key, e);
65+
return null;
66+
}
67+
}
68+
69+
public void cacheDiary(Long diaryId, DiaryMarkerResponseDto dto) {
70+
String key = CacheKeyGenerator.diaryKey(diaryId);
71+
try {
72+
String json = objectMapper.writeValueAsString(dto);
73+
redisTemplate.opsForValue().set(key, json, RedisTTLPolicy.DIARY_TTL);
74+
} catch (Exception e) {
75+
log.error("단건 diary 캐시 저장 실패 (key: {})", key, e);
76+
}
77+
}
78+
79+
public void evictDiaryIdFromCache(String geohash, Long diaryId) {
80+
String key = CacheKeyGenerator.diaryIdSetKey(geohash);
81+
try {
82+
redisTemplate.opsForSet().remove(key, diaryId);
83+
} catch (Exception e) {
84+
log.error("diaryId 제거 실패 (key: {}, diaryId: {})", key, diaryId, e);
85+
}
86+
}
87+
88+
public void evictDiaryFromCache(Long diaryId) {
89+
String key = CacheKeyGenerator.diaryKey(diaryId);
90+
try {
91+
redisTemplate.delete(key);
92+
} catch (Exception e) {
93+
log.error("diary 캐시 삭제 실패 (key: {})", key, diaryId, e);
94+
}
95+
}
96+
97+
}

0 commit comments

Comments
 (0)