Skip to content

Commit d370f35

Browse files
authored
Merge pull request #69 from prgrms-web-devcourse-final-project/refactor/map-cluster-marker
Redis 캐싱 구조 리팩토링
2 parents 75d4c6c + 6087d92 commit d370f35

File tree

20 files changed

+461
-523
lines changed

20 files changed

+461
-523
lines changed

.github/workflows/dev-build.yml

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,26 @@ jobs:
3535
key: ${{ runner.os }}-sonar
3636
restore-keys: ${{ runner.os }}-sonar
3737

38-
- name: S3에서 init.sql 다운로드
39-
env:
40-
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
41-
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
42-
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
43-
run: |
44-
mkdir -p docker
45-
aws s3 cp s3://devcos4-team08-bucket/db/init.sql ./docker/init.sql
38+
# - name: S3에서 init.sql 다운로드
39+
# env:
40+
# AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
41+
# AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
42+
# AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
43+
# run: |
44+
# mkdir -p docker
45+
# aws s3 cp s3://devcos4-team08-bucket/db/init.sql ./docker/init.sql
4646

4747

4848
- name: Docker Compose 설치
4949
run: |
5050
sudo curl -L "https://github.com/docker/compose/releases/download/v2.17.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
5151
sudo chmod +x /usr/local/bin/docker-compose
5252
53+
- name: CI용 빈 init.sql 생성
54+
run: |
55+
mkdir -p docker
56+
touch docker/init.sql
57+
5358
- name: Docker Compose 실행
5459
run: |
5560
chmod -R 755 ./docker
@@ -85,6 +90,7 @@ jobs:
8590
fi
8691
8792
- name: Test and analyze
93+
if: github.event_name != 'pull_request'
8894
env:
8995
#GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
9096
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
@@ -95,7 +101,7 @@ jobs:
95101
NAVER_DEV_CLIENT_SECRET: ${{ secrets.NAVER_DEV_CLIENT_SECRET }}
96102
NAVER_DEV_REDIRECT_URI: ${{ secrets.NAVER_DEV_REDIRECT_URI }}
97103
GOOGLE_DEV_CLIENT_ID: ${{ secrets.GOOGLE_DEV_CLIENT_ID }}
98-
GOOGLE_DEV_CLIENT_SECRET: ${{ secrets.GOOGLE_DEV_CLIENT_SECRET }}r
104+
GOOGLE_DEV_CLIENT_SECRET: ${{ secrets.GOOGLE_DEV_CLIENT_SECRET }}
99105
GOOGLE_DEV_REDIRECT_URI: ${{ secrets.GOOGLE_DEV_REDIRECT_URI }}
100106
KAKAO_DEV_CLIENT_ID: ${{ secrets.KAKAO_DEV_CLIENT_ID }}
101107
KAKAO_DEV_CLIENT_SECRET: ${{ secrets.KAKAO_DEV_CLIENT_SECRET }}

src/main/java/com/example/log4u/common/config/RedisConfig.java

Lines changed: 0 additions & 47 deletions
This file was deleted.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.example.log4u.common.config.redis;
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude;
4+
import com.fasterxml.jackson.core.JsonProcessingException;
5+
import com.fasterxml.jackson.core.type.TypeReference;
6+
import com.fasterxml.jackson.databind.DeserializationFeature;
7+
import com.fasterxml.jackson.databind.ObjectMapper;
8+
import com.fasterxml.jackson.databind.SerializationFeature;
9+
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
10+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
11+
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
12+
13+
public class ObjectMapperFactory {
14+
15+
public static ObjectMapper objectMapper = new ObjectMapper()
16+
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
17+
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
18+
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
19+
.registerModule(new JavaTimeModule())
20+
.registerModule(new ParameterNamesModule())
21+
.registerModule(new Jdk8Module());
22+
23+
public static String writeValueAsString(Object value) {
24+
try {
25+
return objectMapper.writeValueAsString(value);
26+
} catch (JsonProcessingException e) {
27+
throw new RuntimeException(e);
28+
}
29+
}
30+
31+
public static <T> T readValue(String value, TypeReference<T> typeReference) {
32+
try {
33+
return objectMapper.readValue(value, typeReference);
34+
} catch (JsonProcessingException e) {
35+
throw new RuntimeException(e);
36+
}
37+
}
38+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.example.log4u.common.config.redis;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.data.redis.connection.RedisConnectionFactory;
6+
import org.springframework.data.redis.core.RedisTemplate;
7+
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
8+
import org.springframework.data.redis.serializer.StringRedisSerializer;
9+
10+
import com.fasterxml.jackson.databind.ObjectMapper;
11+
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
12+
13+
import lombok.RequiredArgsConstructor;
14+
15+
@Configuration
16+
@RequiredArgsConstructor
17+
public class RedisConfig {
18+
19+
private final RedisConnectionFactory redisConnectionFactory;
20+
21+
@Bean
22+
public RedisTemplate<String, String> redisTemplate() {
23+
RedisTemplate<String, String> template = new RedisTemplate<>();
24+
template.setConnectionFactory(redisConnectionFactory);
25+
template.setKeySerializer(new StringRedisSerializer());
26+
template.setValueSerializer(new StringRedisSerializer());
27+
return template;
28+
}
29+
30+
@Bean
31+
public ObjectMapper objectMapper() {
32+
return ObjectMapperFactory.objectMapper;
33+
}
34+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.example.log4u.common.infra.cache;
2+
3+
import java.time.Duration;
4+
5+
public interface CacheManager {
6+
7+
void cache(String key, String value, Duration ttl);
8+
9+
String get(String key);
10+
11+
void evict(String key);
12+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.example.log4u.common.infra.cache;
2+
3+
import java.time.Duration;
4+
import java.util.List;
5+
import java.util.Objects;
6+
7+
import org.springframework.data.redis.core.RedisTemplate;
8+
import org.springframework.data.redis.core.script.DefaultRedisScript;
9+
import org.springframework.stereotype.Component;
10+
11+
import lombok.RequiredArgsConstructor;
12+
13+
@Component
14+
@RequiredArgsConstructor
15+
public class RedisCacheManagerImpl implements CacheManager {
16+
17+
private final RedisTemplate<String, String> redisTemplate;
18+
19+
public void cache(String key, String value, Duration ttl) {
20+
redisTemplate.opsForValue().set(key, value, ttl);
21+
}
22+
23+
public String get(String key) {
24+
return redisTemplate.opsForValue().get(key);
25+
}
26+
27+
public void evict(String key) {
28+
redisTemplate.delete(key);
29+
}
30+
}

src/main/java/com/example/log4u/domain/diary/facade/DiaryFacade.java

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,9 @@ public class DiaryFacade {
3838

3939
/**
4040
* 다이어리 생성 use case
41-
* <ul><li>호출 과정</li></ul>
42-
* 1. mediaService: 섬네일 이미지 url 생성<br>
43-
* 2. diaryService: 다이어리 생성<br>
44-
* 2. mediaService: 해당 다이어리의 이미지 저장<br>
41+
* 1. mediaService: 섬네일 이미지 url 생성
42+
* 2. diaryService: 다이어리 생성
43+
* 2. mediaService: 해당 다이어리의 이미지 저장
4544
* 3. mapService: 해당 구역 카운트 증가
4645
* 4. diaryGeohashService: 해당 다이어리 위치가 포함되어있는 geoHash 문자열 저장
4746
* */
@@ -60,30 +59,29 @@ public void createDiary(Long userId, DiaryRequestDto request) {
6059

6160
/**
6261
* 다이어리 삭제 use case
63-
* <ul><li>호출 과정</li></ul>
6462
* 1. diaryService: 다이어리 검증
65-
* 2. mediaService: 해당 다이어리 이미지 삭제<br>
66-
* 3. mapService: 해당 구역 카운트 감소
67-
* 4. diaryGeohashService: 캐싱 되어있는 id, 데이터 삭제
68-
* 5. diaryService: 다이어리 삭제<br>
69-
*
63+
* 2. mediaService: 해당 다이어리 이미지 삭제
64+
* 3. mapService: 시/도, 시/군/구 지역 다이어리 개수 감소 및 클러스터 캐싱 키 삭제
65+
* 4. diaryGeohashService: 다이어리 Goehash 문자열 및 마커 캐싱 키 삭제
66+
* 5. diaryService: 다이어리 삭제
7067
* */
7168
@Transactional
7269
public void deleteDiary(Long userId, Long diaryId) {
7370
Diary diary = diaryService.getDiaryAfterValidateOwnership(diaryId, userId);
7471
mediaService.deleteMediaByDiaryId(diaryId);
7572
hashtagService.deleteHashtagsByDiaryId(diaryId);
76-
mapService.decreaseRegionDiaryCount(diary.getLocation().getLatitude(), diary.getLocation().getLongitude());
7773

78-
diaryGeohashService.deleteGeohashAndCache(diaryId);
74+
double lat = diary.getLocation().getLatitude();
75+
double lon = diary.getLocation().getLongitude();
76+
mapService.decreaseRegionDiaryCount(lat, lon);
77+
diaryGeohashService.deleteGeohash(diaryId);
7978
diaryService.deleteDiary(diary);
8079
}
8180

8281
/**
8382
* 다이어리 수정 use case
84-
* <ul><li>호출 과정</li></ul>
85-
* 1. diaryService: 다이어리 검증<br>
86-
* 2. mediaService: 해당 다이어리 이미지 삭제<br>
83+
* 1. diaryService: 다이어리 검증
84+
* 2. mediaService: 해당 다이어리 이미지 삭제
8785
* 3. diaryService: 다이어리 수정
8886
* 4. mapService: 해당 구역 카운트 감소
8987
* 5. mapService: 해당 구역 카운트 증가
@@ -93,10 +91,13 @@ public void updateDiary(Long userId, Long diaryId, DiaryRequestDto request) {
9391
Diary diary = diaryService.getDiaryAfterValidateOwnership(diaryId, userId);
9492
mediaService.updateMediaByDiaryId(diary.getDiaryId(), request.mediaList());
9593
hashtagService.saveOrUpdateHashtag(diary.getDiaryId(), request.hashtagList());
96-
mapService.decreaseRegionDiaryCount(diary.getLocation().getLatitude(), diary.getLocation().getLongitude());
97-
mapService.increaseRegionDiaryCount(request.location().latitude(), request.location().longitude());
98-
9994
String newThumbnailUrl = mediaService.extractThumbnailUrl(request.mediaList());
95+
96+
double oldLat = diary.getLocation().getLatitude();
97+
double oldLon = diary.getLocation().getLongitude();
98+
double newLat = request.location().latitude();
99+
double newLon = request.location().longitude();
100+
mapService.updateRegionDiaryCount(oldLat, oldLon, newLat, newLon);
100101
diaryService.updateDiary(diary, request, newThumbnailUrl);
101102
}
102103

src/main/java/com/example/log4u/domain/diary/repository/DiaryGeoHashRepository.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,14 @@ public interface DiaryGeoHashRepository extends JpaRepository<DiaryGeoHash, Long
1515
List<Long> findDiaryIdByGeohash(@Param("geohash") String geohash);
1616

1717
DiaryGeoHash findByDiaryId(Long diaryId);
18+
19+
@Query(value = """
20+
SELECT g.*
21+
FROM diarygeohash g
22+
JOIN diary d ON d.diaryId = g.diaryId
23+
WHERE d.latitude = :lat
24+
AND d.longitude = :lon
25+
""",
26+
nativeQuery = true)
27+
DiaryGeoHash findByLatLon(@Param("lat") double lat, @Param("lon") double lon);
1828
}

src/main/java/com/example/log4u/domain/diary/service/DiaryGeohashService.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import com.example.log4u.domain.diary.entity.DiaryGeoHash;
99
import com.example.log4u.domain.diary.repository.DiaryGeoHashRepository;
10-
import com.example.log4u.domain.map.cache.dao.DiaryCacheDao;
10+
import com.example.log4u.domain.map.cache.dao.MarkerCacheDao;
1111

1212
import ch.hsr.geohash.GeoHash;
1313
import lombok.RequiredArgsConstructor;
@@ -19,7 +19,7 @@
1919
public class DiaryGeohashService {
2020

2121
private final DiaryGeoHashRepository diaryGeoHashRepository;
22-
private final DiaryCacheDao diaryCacheDao;
22+
private final MarkerCacheDao markerCacheDao;
2323

2424
@Transactional
2525
public void saveGeohash(Long diaryId, double lat, double lon) {
@@ -32,15 +32,17 @@ public void saveGeohash(Long diaryId, double lat, double lon) {
3232
}
3333

3434
@Transactional
35-
public void deleteGeohashAndCache(Long diaryId) {
35+
public void deleteGeohash(Long diaryId) {
3636
DiaryGeoHash geoHash = diaryGeoHashRepository.findByDiaryId(diaryId);
37-
diaryCacheDao.evictDiaryIdFromCache(geoHash.getGeohash(), diaryId);
38-
diaryCacheDao.evictDiaryFromCache(diaryId);
39-
37+
markerCacheDao.evict(geoHash.getGeohash());
4038
diaryGeoHashRepository.deleteById(diaryId);
4139
}
4240

4341
public List<Long> getDiaryIdsByGeohash(String geohash) {
4442
return diaryGeoHashRepository.findDiaryIdByGeohash(geohash);
4543
}
44+
45+
public DiaryGeoHash getGeohashByLatLon(double lat, double lon) {
46+
return diaryGeoHashRepository.findByLatLon(lat, lon);
47+
}
4648
}

src/main/java/com/example/log4u/domain/map/cache/CacheKeyGenerator.java

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,14 @@
22

33
public class CacheKeyGenerator {
44

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:";
5+
private static final String CLUSTER_CACHE_KEY = "cluster:geohash:%s:level:%d";
6+
private static final String MARKER_CACHE_KEY = "marker:geohash:";
87

98
public static String clusterCacheKey(String geohash, int level) {
10-
return String.format(CLUSTER_CACHE_KEY_FORMAT, geohash, level);
9+
return String.format(CLUSTER_CACHE_KEY, geohash, level);
1110
}
1211

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;
12+
public static String markerCacheKey(String geohash) {
13+
return MARKER_CACHE_KEY + geohash;
1914
}
2015
}
21-

0 commit comments

Comments
 (0)