Skip to content

Commit 5e86216

Browse files
authored
Merge pull request #274 from prgrms-aibe-devcourse/perf/v2-redis-caching
perf: 캐싱 적용
2 parents 7eedda7 + 5d9a49d commit 5e86216

7 files changed

Lines changed: 73 additions & 3 deletions

File tree

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ dependencies {
7979
annotationProcessor "jakarta.annotation:jakarta.annotation-api" // Jakarta 어노테이션 API
8080
annotationProcessor "jakarta.persistence:jakarta.persistence-api" // Jakarta Persistence API
8181

82+
// Cache
83+
implementation 'org.springframework.boot:spring-boot-starter-cache'
84+
implementation 'com.github.ben-manes.caffeine:caffeine'
8285
}
8386

8487
// QueryDSL Q-Type 생성 위치 지정
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package store.lastdance.config;
2+
3+
import com.github.benmanes.caffeine.cache.Caffeine;
4+
import org.springframework.cache.CacheManager;
5+
import org.springframework.cache.annotation.EnableCaching;
6+
import org.springframework.cache.caffeine.CaffeineCacheManager;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.Configuration;
9+
10+
import java.util.concurrent.TimeUnit;
11+
12+
@Configuration
13+
@EnableCaching
14+
public class CacheConfig {
15+
16+
@Bean
17+
public CacheManager cacheManager() {
18+
CaffeineCacheManager manager = new CaffeineCacheManager();
19+
20+
manager.registerCustomCache("userProfile",
21+
Caffeine.newBuilder()
22+
.maximumSize(500)
23+
.expireAfterWrite(10, TimeUnit.MINUTES)
24+
.recordStats()
25+
.build()
26+
);
27+
28+
manager.registerCustomCache("nickname",
29+
Caffeine.newBuilder()
30+
.maximumSize(1000)
31+
.expireAfterWrite(5, TimeUnit.MINUTES)
32+
.build()
33+
);
34+
35+
manager.registerCustomCache("expenseTrend",
36+
Caffeine.newBuilder()
37+
.maximumSize(200)
38+
.expireAfterWrite(5, TimeUnit.MINUTES)
39+
.build()
40+
);
41+
42+
manager.registerCustomCache("groupExpenseTrend",
43+
Caffeine.newBuilder()
44+
.maximumSize(200)
45+
.expireAfterWrite(5, TimeUnit.MINUTES)
46+
.build()
47+
);
48+
49+
return manager;
50+
}
51+
}

src/main/java/store/lastdance/service/expense/ExpenseV2QueryServiceImpl.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import lombok.RequiredArgsConstructor;
44
import lombok.extern.slf4j.Slf4j;
5+
import org.springframework.cache.annotation.Cacheable;
56
import org.springframework.data.domain.Page;
67
import org.springframework.data.domain.PageImpl;
78
import org.springframework.data.domain.Pageable;
@@ -116,6 +117,7 @@ public String getReceiptImageUrl(Long expenseId, UUID userId) {
116117
}
117118

118119
@Override
120+
@Cacheable(value = "expenseTrend", key = "#userId + ':' + #searchDTO.year() + ':' + #searchDTO.month() + ':' + #searchDTO.months() + ':' + (#searchDTO.category() ?: 'ALL')")
119121
public MonthlyExpenseTrendResponseDTO getPersonalExpenseTrend(UUID userId, ExpenseSearchDTO searchDTO) {
120122
log.info("개인 지출 추이 조회: userId={}, year={}, month={}, months={}, category={}", userId, searchDTO.year(), searchDTO.month(), searchDTO.months(), searchDTO.category());
121123

@@ -130,6 +132,7 @@ public MonthlyExpenseTrendResponseDTO getPersonalExpenseTrend(UUID userId, Expen
130132
}
131133

132134
@Override
135+
@Cacheable(value = "expenseTrend", key = "#userId + ':' + #searchDTO.year() + ':' + #searchDTO.month() + ':' + #searchDTO.months() + ':' + (#searchDTO.category() ?: 'ALL')")
133136
public MonthlyExpenseTrendResponseDTO getGroupExpenseTrend(UUID userId, UUID groupId, ExpenseSearchDTO searchDTO) {
134137
User user = findUserById(userId);
135138
Group group = findGroupById(groupId);

src/main/java/store/lastdance/service/expense/ExpenseV2ServiceImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import lombok.RequiredArgsConstructor;
44
import lombok.extern.slf4j.Slf4j;
5+
import org.springframework.cache.annotation.CacheEvict;
56
import org.springframework.stereotype.Service;
67
import org.springframework.transaction.annotation.Transactional;
78
import org.springframework.web.multipart.MultipartFile;
@@ -58,6 +59,7 @@ private Group findGroupById(UUID groupId) {
5859
}
5960

6061
@Override
62+
@CacheEvict(value = {"expenseTrend", "groupExpenseTrend"}, allEntries = true)
6163
public ExpenseResponseDTO createPersonalExpense(UUID userId, CreatePersonalExpenseRequestDTO requestDTO, MultipartFile receiptFile) {
6264
Expense expense = createBaseExpense(userId, requestDTO, ExpenseType.PERSONAL, receiptFile);
6365

@@ -78,6 +80,7 @@ public ExpenseResponseDTO createPersonalExpense(UUID userId, CreatePersonalExpen
7880
}
7981

8082
@Override
83+
@CacheEvict(value = {"expenseTrend", "groupExpenseTrend"}, allEntries = true)
8184
public ExpenseResponseDTO createGroupExpense(UUID userId, CreateGroupExpenseRequestDTO requestDTO, MultipartFile receiptFile) {
8285

8386
Group group = findGroupById(requestDTO.groupId());
@@ -201,6 +204,7 @@ private void applySplitResultToDatabase(Expense original, Map<UUID, BigDecimal>
201204
}
202205

203206
@Override
207+
@CacheEvict(value = {"expenseTrend", "groupExpenseTrend"}, allEntries = true)
204208
public ExpenseResponseDTO updateExpense(UUID userId, Long expenseId, UpdateExpenseRequestDTO requestDTO, MultipartFile receiptFile) {
205209

206210
User user = findUserById(userId);
@@ -287,6 +291,7 @@ private List<GroupMember> validateAndGetGroupMembers(Expense expense) {
287291
}
288292

289293
@Override
294+
@CacheEvict(value = {"expenseTrend", "groupExpenseTrend"}, allEntries = true)
290295
public void deleteExpense(UUID userId, Long expenseId) {
291296

292297
User user = findUserById(userId);
@@ -312,6 +317,7 @@ public void deleteExpense(UUID userId, Long expenseId) {
312317
}
313318

314319
@Override
320+
@CacheEvict(value = {"expenseTrend", "groupExpenseTrend"}, allEntries = true)
315321
public void deleteReceiptImage(UUID userId, Long expenseId) {
316322

317323
User user = findUserById(userId);

src/main/java/store/lastdance/service/user/UserV2QueryServiceImpl.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import lombok.RequiredArgsConstructor;
44
import lombok.extern.slf4j.Slf4j;
5+
import org.springframework.cache.annotation.Cacheable;
56
import org.springframework.stereotype.Service;
67
import org.springframework.transaction.annotation.Transactional;
78
import store.lastdance.converter.user.UserConverter;
@@ -41,13 +42,15 @@ public User findByUserId(UUID userId) {
4142
}
4243

4344
@Override
45+
@Cacheable(value = "userProfile", key = "#userId")
4446
public UserResponseDTO getUserWithProfileImage(UUID userId) {
4547
User user = userRepository.findByIdWithProfileImage(userId)
4648
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
4749
return userConverter.toResponseDTO(user);
4850
}
4951

5052
@Override
53+
@Cacheable(value = "nickname", key = "#userId + ':' + #nickname")
5154
public boolean isNicknameAvailable(UUID userId, String nickname) {
5255
if (nickname == null || nickname.trim().isEmpty()) {
5356
return false;

src/main/java/store/lastdance/service/user/UserV2ServiceImpl.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import jakarta.servlet.http.HttpServletResponse;
55
import lombok.RequiredArgsConstructor;
66
import lombok.extern.slf4j.Slf4j;
7+
import org.springframework.cache.annotation.CacheEvict;
78
import org.springframework.context.ApplicationEventPublisher;
89
import org.springframework.stereotype.Service;
910
import org.springframework.transaction.annotation.Transactional;
@@ -34,6 +35,7 @@ public class UserV2ServiceImpl implements UserV2Service {
3435

3536
@Override
3637
@Transactional
38+
@CacheEvict(value = "nickname", allEntries = true)
3739
public User updateMyInfo(UUID userId, UserUpdateRequestDTO requestDTO) {
3840
User user = userV2QueryService.findByActiveUser(userId);
3941

@@ -56,6 +58,7 @@ public User updateMyInfo(UUID userId, UserUpdateRequestDTO requestDTO) {
5658

5759
@Override
5860
@Transactional
61+
@CacheEvict(value = "userProfile", key = "#userId")
5962
public UserResponseDTO updateProfileImage(UUID userId, MultipartFile file) {
6063
User user = userRepository.findByIdWithProfileImage(userId)
6164
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
@@ -90,8 +93,9 @@ public UserResponseDTO updateProfileImage(UUID userId, MultipartFile file) {
9093

9194
@Override
9295
@Transactional
93-
public UserResponseDTO deleteProfileImage(UUID userid) {
94-
User user = userRepository.findByIdWithProfileImage(userid).orElseThrow(
96+
@CacheEvict(value = "userProfile", key = "#userId")
97+
public UserResponseDTO deleteProfileImage(UUID userId) {
98+
User user = userRepository.findByIdWithProfileImage(userId).orElseThrow(
9599
() -> new CustomException(ErrorCode.USER_NOT_FOUND)
96100
);
97101

src/main/resources/application-prod.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ management:
183183
endpoints:
184184
web:
185185
exposure:
186-
include: health,info,prometheus # Prometheus 엔드포인트 노출
186+
include: health,info,prometheus,caches # Prometheus 엔드포인트 노출
187187
prometheus:
188188
metrics:
189189
export:

0 commit comments

Comments
 (0)