11package com .back .web7_9_codecrete_be .domain .concerts .repository ;
22
3+ import com .back .web7_9_codecrete_be .domain .concerts .dto .concert .ConcertDetailResponse ;
4+ import com .back .web7_9_codecrete_be .domain .concerts .dto .concert .ConcertItem ;
5+ import com .back .web7_9_codecrete_be .domain .concerts .dto .concert .ListSort ;
36import lombok .RequiredArgsConstructor ;
4- import org .springframework .data .redis .core .RedisTemplate ;
7+ import lombok .extern .slf4j .Slf4j ;
8+ import org .springframework .data .domain .Pageable ;
9+ import org .springframework .data .redis .core .*;
10+ import org .springframework .security .core .parameters .P ;
511import org .springframework .stereotype .Repository ;
612
13+ import java .util .*;
714import java .util .concurrent .TimeUnit ;
815
16+ @ Slf4j
917@ Repository
1018@ RequiredArgsConstructor
1119public class ConcertRedisRepository {
12- private final RedisTemplate <String ,String > redisTemplate ;
20+ private final RedisTemplate <String , String > redisTemplate ;
21+ private final RedisTemplate <String , Object > objectRedisTemplate ;
1322
1423 private static final String LOCK_FLAG_PREFIX = "initLoad: " ;
1524
25+ private static final String CONCERT_DETAIL_PREFIX = "concertDetail: " ;
26+
27+ private static final String CONCERT_LIST_PREFIX = "concertList: " ;
28+
29+ private static final String VIEW_COUNT_MAP = "viewCountMap" ;
30+
31+ private static final int HOUR = 3600 ;
32+
33+ // 최초 공연 로드 락
1634 public void lockSave (String key , String value ) {
1735 redisTemplate .opsForValue ().set (
1836 LOCK_FLAG_PREFIX + key ,
@@ -29,4 +47,118 @@ public void unlockSave(String key) {
2947 redisTemplate .delete (LOCK_FLAG_PREFIX + key );
3048 }
3149
50+ // 공연 목록 캐싱
51+ public void listSave (ListSort sort , Pageable pageable , List <ConcertItem > list ) {
52+ String key = CONCERT_LIST_PREFIX + sort .name () + pageable .getPageNumber ();
53+ objectRedisTemplate .opsForValue ().set (key , list , HOUR , TimeUnit .SECONDS );
54+ }
55+
56+ // 공연 목록 가져오기
57+ public List <ConcertItem > getConcertsList (Pageable pageable , ListSort sort ) {
58+ String key = CONCERT_LIST_PREFIX + sort .name () + pageable .getPageNumber ();
59+ Object object = objectRedisTemplate .opsForValue ().get (key );
60+ List <ConcertItem > list = (List <ConcertItem >) object ;
61+ if (list == null || list .isEmpty ()) return List .of (); // null 이 아닌 empty 값 반환
62+ return list ;
63+ }
64+
65+ // 캐싱된 모든 공연 목록 삭제
66+ public void deleteAllConcertsList () {
67+ deleteAllItemsByPREFIX (CONCERT_LIST_PREFIX );
68+ }
69+
70+ // 공연 상세 캐싱
71+ public void detailSave (long concertId , ConcertDetailResponse concertDetailResponse ) {
72+ objectRedisTemplate .opsForValue ().set (
73+ CONCERT_DETAIL_PREFIX + concertId ,
74+ concertDetailResponse ,
75+ HOUR ,
76+ TimeUnit .SECONDS
77+ );
78+ }
79+
80+ // todo : 객체 일부의 값만 바뀌는거니 해당 값만 바꿔서 저장하거나 Redis 내부의 값만 갱신할 수 있는 방법 찾기
81+ public ConcertDetailResponse getDetail (long concertId ) {
82+ ConcertDetailResponse concertDetailResponse = (ConcertDetailResponse ) objectRedisTemplate .opsForValue ().get (CONCERT_DETAIL_PREFIX + concertId );
83+ if (concertDetailResponse == null ) return null ;
84+ int viewCount = concertDetailResponse .getViewCount ();
85+ viewCountSet (concertId , viewCount + 1 );
86+ concertDetailResponse .setViewCount (viewCount + 1 );
87+ detailSave (concertId , concertDetailResponse );
88+ return concertDetailResponse ;
89+ }
90+
91+ // 공연 상세 삭제
92+ public void deleteDetail (String concertId ) {
93+ redisTemplate .delete (CONCERT_DETAIL_PREFIX + concertId );
94+ }
95+
96+ // 모든 공연 상세 삭제
97+ public void deleteAllConcertDetail () {
98+ deleteAllItemsByPREFIX (CONCERT_DETAIL_PREFIX );
99+ }
100+
101+ // 조회수 처리 -> 좀 지저분한데 개선 여지 찾아보기
102+ public int viewCountSet (long concertId , int viewCount ) {
103+ Map <String , Integer > rawMap = (Map <String , Integer >) objectRedisTemplate .opsForValue ().get (VIEW_COUNT_MAP );
104+
105+ if (rawMap == null ) {
106+ Map <Long , Integer > viewCountMap = new HashMap <>();
107+ viewCountMap .put (concertId , viewCount );
108+ objectRedisTemplate .opsForValue ().set (VIEW_COUNT_MAP , viewCountMap );
109+ } else {
110+ Map <Long , Integer > viewCountMap = convertViewCountMap (rawMap );
111+ viewCountMap .put (concertId , viewCount );
112+ objectRedisTemplate .opsForValue ().set (VIEW_COUNT_MAP , viewCountMap );
113+ log .info (viewCountMap .size () + "view count size." );
114+ }
115+ return viewCount ;
116+ }
117+
118+ // 조회수 맵 조회
119+ public Map <Long , Integer > getViewCountMap () {
120+ Map <String , Integer > rawMap = (Map <String , Integer >) objectRedisTemplate .opsForValue ().get (VIEW_COUNT_MAP );
121+ if (rawMap == null ) return null ;
122+ objectRedisTemplate .delete (VIEW_COUNT_MAP );
123+ return convertViewCountMap (rawMap );
124+ }
125+
126+ // 조회수 맵 삭제
127+ public void deleteViewCountMap () {
128+ objectRedisTemplate .delete (VIEW_COUNT_MAP );
129+ }
130+
131+ // String Integer 타입 맵을 Long Integer로 변환
132+ private Map <Long , Integer > convertViewCountMap (Map <String , Integer > rawMap ) {
133+ Map <Long , Integer > viewCountMap = new HashMap <>();
134+ for (Map .Entry <String , Integer > stringIntegerEntry : rawMap .entrySet ()) {
135+ viewCountMap .put (Long .parseLong (stringIntegerEntry .getKey ()), stringIntegerEntry .getValue ());
136+ }
137+ return viewCountMap ;
138+ }
139+
140+ // 해당 접두어의 모든 항목 삭제
141+ private void deleteAllItemsByPREFIX (String prefix ) {
142+ String pattern = CONCERT_LIST_PREFIX + "*" ;
143+ ScanOptions options = ScanOptions .scanOptions ().match (pattern ).count (100 ).build ();
144+ Set <String > keys = redisTemplate .execute ((RedisCallback <Set <String >>) connection -> {
145+ Set <String > keySet = new HashSet <>();
146+ try (Cursor <byte []> cursor = connection .scan (options )) {
147+ while (cursor .hasNext ()) {
148+ keySet .add (new String (cursor .next ()));
149+ }
150+ }
151+ return keySet ;
152+ }
153+ );
154+
155+ if (keys != null && !keys .isEmpty ()) {
156+ redisTemplate .delete (keys );
157+ log .info ("Successfully deleted %s items with prefix: %s" .formatted (keys .size () + "" , prefix ));
158+ } else {
159+ log .info ("no items with prefix: %s" .formatted (prefix ));
160+ }
161+ }
162+
163+
32164}
0 commit comments