22
33import com .loopers .application .event .product .ProductViewedEvent ;
44import com .loopers .domain .like .service .LikeReadService ;
5- import com .loopers .domain .product .Product ;
6- import com .loopers .domain .product .repository .ProductRepository ;
75import com .loopers .domain .product .service .ProductReadService ;
86import com .loopers .domain .product .command .ProductSearchFilter ;
97import com .loopers .domain .product .enums .ProductSortCondition ;
108import com .loopers .infrastructure .cache .ProductDetailCache ;
119import com .loopers .infrastructure .cache .ProductListCache ;
10+ import com .loopers .infrastructure .cache .ProductRankingCache ;
1211import lombok .RequiredArgsConstructor ;
1312import org .springframework .context .ApplicationEventPublisher ;
1413import org .springframework .data .domain .Page ;
@@ -29,7 +28,7 @@ public class ProductFacade {
2928 private final LikeReadService likeReadService ;
3029 private final ProductDetailCache productDetailCache ;
3130 private final ProductListCache productListCache ;
32- private final ProductRepository productRepository ;
31+ private final ProductRankingCache productRankingCache ;
3332 private final ApplicationEventPublisher eventPublisher ;
3433
3534 @ Transactional (readOnly = true )
@@ -100,33 +99,33 @@ public ProductDetailInfo getProductDetail(Long productId, Long memberIdOrNull) {
10099 return result ;
101100 });
102101
103- // 2. Product 엔티티 조회 (brandId 획득용)
104- Product product = productRepository .findById (productId )
105- .orElseThrow (() -> new com .loopers .support .error .CoreException (
106- com .loopers .support .error .ErrorType .NOT_FOUND ,
107- "상품을 찾을 수 없습니다." ));
108-
109- // 3. isLikedByMember 동적 계산
102+ // 2. isLikedByMember 동적 계산
110103 boolean isLiked = memberIdOrNull != null && likeReadService .isLikedBy (memberIdOrNull , productId );
111104
112- // 4. isLikedByMember 필드만 교체해서 반환
105+ // 3. 순위 조회 (실시간)
106+ Integer ranking = productRankingCache .getRank (productId );
107+
108+ // 4. 동적 필드(isLikedByMember, ranking)를 교체해서 반환
113109 ProductDetailInfo result = ProductDetailInfo .builder ()
114110 .id (cachedInfo .getId ())
115111 .name (cachedInfo .getName ())
116112 .description (cachedInfo .getDescription ())
113+ .brandId (cachedInfo .getBrandId ())
117114 .brandName (cachedInfo .getBrandName ())
118115 .brandDescription (cachedInfo .getBrandDescription ())
119116 .price (cachedInfo .getPrice ())
120117 .stock (cachedInfo .getStock ())
121118 .likeCount (cachedInfo .getLikeCount ())
122- .isLikedByMember (isLiked ) // ⭐ 동적 계산
119+ .isLikedByMember (isLiked )
120+ .ranking (ranking )
123121 .build ();
124122
125123 // 5. ProductViewedEvent 발행 (조회수 집계)
124+ // brandId는 캐시된 정보에서 가져옴 (불필요한 DB 조회 제거)
126125 eventPublisher .publishEvent (new ProductViewedEvent (
127126 memberIdOrNull , // 비로그인 사용자는 null
128127 productId ,
129- product .getBrandId (),
128+ cachedInfo .getBrandId (),
130129 LocalDateTime .now ()
131130 ));
132131
0 commit comments