Skip to content

Commit 62a6728

Browse files
committed
feat: 재고 소진 시 상품 캐시 자동 갱신
- OrderFacade: 재고 차감 후 재고가 0이 되면 캐시 삭제 - ProductCacheService: Spring Cache 삭제 메서드 추가 - 다음 조회 시 DB에서 최신 데이터(재고 0)를 가져와 캐시 갱신
1 parent d6fb171 commit 62a6728

2 files changed

Lines changed: 34 additions & 3 deletions

File tree

apps/commerce-api/src/main/java/com/loopers/application/order/OrderFacade.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.loopers.application.coupon.CouponService;
44
import com.loopers.application.order.OrderCommand.OrderItemRequest;
55
import com.loopers.application.outbox.OutboxEventService;
6+
import com.loopers.application.product.ProductCacheService;
67
import com.loopers.domain.coupon.UserCoupon;
78
import com.loopers.domain.order.Order;
89
import com.loopers.domain.order.OrderItem;
@@ -38,6 +39,7 @@ public class OrderFacade {
3839
private final PointService pointService;
3940
private final CouponService couponService;
4041
private final OutboxEventService outboxEventService;
42+
private final ProductCacheService productCacheService;
4143

4244
@Transactional
4345
public OrderInfo createOrder(String userId, OrderCommand.Create command) {
@@ -92,6 +94,12 @@ private Order createOrderWithItems(String userId, List<OrderItemRequest> orderIt
9294
Product product = productMap.get(request.productId());
9395
product.deductStock(request.quantity());
9496
order.addOrderItem(OrderItem.from(product, request.quantity()));
97+
98+
// 재고 소진 시 캐시 갱신
99+
if (product.getStock() == 0) {
100+
log.info("재고 소진 - 캐시 갱신 - productId: {}", product.getId());
101+
productCacheService.evictCache(product.getId());
102+
}
95103
}
96104

97105
order.calculateTotalAmount();

apps/commerce-api/src/main/java/com/loopers/application/product/ProductCacheService.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package com.loopers.application.product;
22

3+
import java.time.Duration;
4+
import java.util.Optional;
35
import lombok.RequiredArgsConstructor;
46
import lombok.extern.slf4j.Slf4j;
7+
import org.springframework.cache.annotation.CacheEvict;
58
import org.springframework.data.redis.core.RedisTemplate;
69
import org.springframework.stereotype.Service;
710

8-
import java.time.Duration;
9-
import java.util.Optional;
10-
1111
@Slf4j
1212
@Service
1313
@RequiredArgsConstructor
@@ -119,4 +119,27 @@ public CacheStats getCacheStats(Long productId) {
119119
}
120120

121121
public record CacheStats(boolean viewCountCached, boolean likeCountCached) {}
122+
123+
/**
124+
* 특정 상품의 Spring Cache를 삭제
125+
*
126+
* 사용 시나리오:
127+
* - 재고가 소진되었을 때 호출
128+
* - 다음 조회 시 DB에서 최신 데이터를 가져와 캐시에 저장
129+
*
130+
* @param productId 캐시를 삭제할 상품 ID
131+
*/
132+
@CacheEvict(value = "product", key = "#productId")
133+
public void evictCache(Long productId) {
134+
log.info("상품 캐시 삭제 (재고 소진) - productId: {}", productId);
135+
}
136+
137+
/**
138+
* 모든 상품 캐시를 삭제
139+
* - 대량 업데이트 등 특수한 경우에 사용
140+
*/
141+
@CacheEvict(value = "product", allEntries = true)
142+
public void evictAllCache() {
143+
log.info("전체 상품 캐시 삭제");
144+
}
122145
}

0 commit comments

Comments
 (0)