From 04dc693d88b70800f521ae18fd8fd6b470e81a4e Mon Sep 17 00:00:00 2001 From: YongHwan Kim Date: Mon, 2 Oct 2023 16:45:22 +0900 Subject: [PATCH] =?UTF-8?q?[feat]=20=ED=8C=90=EB=82=B4=EB=82=B4=EC=97=AD?= =?UTF-8?q?=20=EC=83=81=ED=92=88=20=EC=A1=B0=ED=9A=8C=EC=8B=9C=20=EC=BB=AC?= =?UTF-8?q?=EB=9F=BC=20=EC=B6=94=EA=B0=80=20(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * #124 feat: 캐시를 위한 직렬화 인터페이스 구현 * #124 feat: 카테고리 관련 목록 조회 캐싱 * #124 feat: 판매내역 상품 조회시 컬럼 추가 --- .../app/api/category/CategoryQueryService.java | 2 ++ .../app/api/category/CategoryRestController.java | 3 +++ .../api/category/response/CategoryItemResponse.java | 4 +++- .../api/category/response/CategoryListResponse.java | 3 ++- .../main/java/codesquard/app/api/item/ItemService.java | 2 -- .../app/api/item/response/ItemDetailResponse.java | 3 ++- .../app/api/wishitem/WishItemController.java | 3 +++ .../codesquard/app/api/wishitem/WishItemService.java | 2 ++ .../wishitem/response/WishCategoryItemResponse.java | 4 +++- .../wishitem/response/WishCategoryListResponse.java | 3 ++- .../main/java/codesquard/app/config/CacheConfig.java | 9 +++++++++ .../src/main/java/codesquard/app/config/WebConfig.java | 10 ++++++++++ .../app/domain/sales/SalesPaginationRepository.java | 7 +++++-- .../codesquard/app/interceptor/CacheInterceptor.java | 5 +++++ 14 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 backend/src/main/java/codesquard/app/config/CacheConfig.java create mode 100644 backend/src/main/java/codesquard/app/interceptor/CacheInterceptor.java diff --git a/backend/src/main/java/codesquard/app/api/category/CategoryQueryService.java b/backend/src/main/java/codesquard/app/api/category/CategoryQueryService.java index d48cae82e..c0c29e748 100644 --- a/backend/src/main/java/codesquard/app/api/category/CategoryQueryService.java +++ b/backend/src/main/java/codesquard/app/api/category/CategoryQueryService.java @@ -2,6 +2,7 @@ import java.util.List; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -17,6 +18,7 @@ public class CategoryQueryService { private final CategoryRepository categoryRepository; + @Cacheable("categories") public CategoryListResponse findAll() { List categories = categoryRepository.findAll(); return new CategoryListResponse(categories); diff --git a/backend/src/main/java/codesquard/app/api/category/CategoryRestController.java b/backend/src/main/java/codesquard/app/api/category/CategoryRestController.java index 1b6175578..e6630f664 100644 --- a/backend/src/main/java/codesquard/app/api/category/CategoryRestController.java +++ b/backend/src/main/java/codesquard/app/api/category/CategoryRestController.java @@ -9,7 +9,9 @@ import codesquard.app.api.category.response.CategoryListResponse; import codesquard.app.api.response.ApiResponse; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +@Slf4j @RequestMapping("/api/categories") @RequiredArgsConstructor @RestController @@ -20,6 +22,7 @@ public class CategoryRestController { @ResponseStatus(HttpStatus.OK) @GetMapping public ApiResponse findAll() { + log.info("카테고리 목록 조회 요청"); return ApiResponse.of(HttpStatus.OK, "카테고리 조회에 성공하였습니다.", categoryQueryService.findAll()); } } diff --git a/backend/src/main/java/codesquard/app/api/category/response/CategoryItemResponse.java b/backend/src/main/java/codesquard/app/api/category/response/CategoryItemResponse.java index 74762f323..fad6c416c 100644 --- a/backend/src/main/java/codesquard/app/api/category/response/CategoryItemResponse.java +++ b/backend/src/main/java/codesquard/app/api/category/response/CategoryItemResponse.java @@ -1,5 +1,7 @@ package codesquard.app.api.category.response; +import java.io.Serializable; + import codesquard.app.domain.category.Category; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -9,7 +11,7 @@ @Getter @NoArgsConstructor(access = AccessLevel.PRIVATE) @AllArgsConstructor(access = AccessLevel.PRIVATE) -public class CategoryItemResponse { +public class CategoryItemResponse implements Serializable { private Long id; private String imageUrl; diff --git a/backend/src/main/java/codesquard/app/api/category/response/CategoryListResponse.java b/backend/src/main/java/codesquard/app/api/category/response/CategoryListResponse.java index 667c864c9..0d51f33fc 100644 --- a/backend/src/main/java/codesquard/app/api/category/response/CategoryListResponse.java +++ b/backend/src/main/java/codesquard/app/api/category/response/CategoryListResponse.java @@ -1,5 +1,6 @@ package codesquard.app.api.category.response; +import java.io.Serializable; import java.util.List; import java.util.stream.Collectors; @@ -10,7 +11,7 @@ @Getter @NoArgsConstructor(access = AccessLevel.PRIVATE) -public class CategoryListResponse { +public class CategoryListResponse implements Serializable { private List categories; diff --git a/backend/src/main/java/codesquard/app/api/item/ItemService.java b/backend/src/main/java/codesquard/app/api/item/ItemService.java index 4ebb597de..849477972 100644 --- a/backend/src/main/java/codesquard/app/api/item/ItemService.java +++ b/backend/src/main/java/codesquard/app/api/item/ItemService.java @@ -4,7 +4,6 @@ import java.util.Optional; import java.util.stream.Collectors; -import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -90,7 +89,6 @@ public void changeItemStatus(Long itemId, ItemStatus status) { log.info("상품 상태 변경 결과 : item={}", item); } - @Cacheable(cacheNames = "detailItem") public ItemDetailResponse findDetailItemBy(Long itemId, Long loginMemberId) { log.info("상품 상세 조회 서비스 요청, 상품 등록번호 : {}, 로그인 회원의 등록번호 : {}", itemId, loginMemberId); diff --git a/backend/src/main/java/codesquard/app/api/item/response/ItemDetailResponse.java b/backend/src/main/java/codesquard/app/api/item/response/ItemDetailResponse.java index c66019d95..65dc4fbea 100644 --- a/backend/src/main/java/codesquard/app/api/item/response/ItemDetailResponse.java +++ b/backend/src/main/java/codesquard/app/api/item/response/ItemDetailResponse.java @@ -1,5 +1,6 @@ package codesquard.app.api.item.response; +import java.io.Serializable; import java.time.LocalDateTime; import java.util.List; @@ -12,7 +13,7 @@ @Getter @NoArgsConstructor(access = AccessLevel.PRIVATE) -public class ItemDetailResponse { +public class ItemDetailResponse implements Serializable { private Boolean isSeller; private List imageUrls; diff --git a/backend/src/main/java/codesquard/app/api/wishitem/WishItemController.java b/backend/src/main/java/codesquard/app/api/wishitem/WishItemController.java index 6606df9fe..2df959b55 100644 --- a/backend/src/main/java/codesquard/app/api/wishitem/WishItemController.java +++ b/backend/src/main/java/codesquard/app/api/wishitem/WishItemController.java @@ -14,7 +14,9 @@ import codesquard.app.domain.oauth.support.Principal; import codesquard.app.domain.wish.WishStatus; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +@Slf4j @RestController @RequestMapping("/api/wishes") @RequiredArgsConstructor @@ -37,6 +39,7 @@ public ApiResponse findAll(@RequestParam(required = false) Long c @GetMapping("/categories") public ApiResponse readWishCategories(@AuthPrincipal Principal principal) { + log.info("관심 상품들의 카테고리 목록 요청 : {}", principal); return ApiResponse.ok("관심상품의 카테고리 목록 조회를 완료하였습니다.", wishItemService.readWishCategories(principal)); } } diff --git a/backend/src/main/java/codesquard/app/api/wishitem/WishItemService.java b/backend/src/main/java/codesquard/app/api/wishitem/WishItemService.java index 5f2ec66fb..ef19ac32f 100644 --- a/backend/src/main/java/codesquard/app/api/wishitem/WishItemService.java +++ b/backend/src/main/java/codesquard/app/api/wishitem/WishItemService.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.stream.Collectors; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -71,6 +72,7 @@ public ItemResponses findAll(Long categoryId, int size, Long cursor) { return PaginationUtils.getItemResponses(itemResponses); } + @Cacheable("wishCategories") public WishCategoryListResponse readWishCategories(Principal principal) { List wishes = wishRepository.findAllByMemberId(principal.getMemberId()); List categories = wishes.stream() diff --git a/backend/src/main/java/codesquard/app/api/wishitem/response/WishCategoryItemResponse.java b/backend/src/main/java/codesquard/app/api/wishitem/response/WishCategoryItemResponse.java index eea63a6e1..b667c4fb7 100644 --- a/backend/src/main/java/codesquard/app/api/wishitem/response/WishCategoryItemResponse.java +++ b/backend/src/main/java/codesquard/app/api/wishitem/response/WishCategoryItemResponse.java @@ -1,11 +1,13 @@ package codesquard.app.api.wishitem.response; +import java.io.Serializable; + import codesquard.app.domain.category.Category; import lombok.AccessLevel; import lombok.AllArgsConstructor; @AllArgsConstructor(access = AccessLevel.PRIVATE) -public class WishCategoryItemResponse { +public class WishCategoryItemResponse implements Serializable { private Long categoryId; private String categoryName; diff --git a/backend/src/main/java/codesquard/app/api/wishitem/response/WishCategoryListResponse.java b/backend/src/main/java/codesquard/app/api/wishitem/response/WishCategoryListResponse.java index dbeb6232e..dd5c859d6 100644 --- a/backend/src/main/java/codesquard/app/api/wishitem/response/WishCategoryListResponse.java +++ b/backend/src/main/java/codesquard/app/api/wishitem/response/WishCategoryListResponse.java @@ -1,5 +1,6 @@ package codesquard.app.api.wishitem.response; +import java.io.Serializable; import java.util.List; import java.util.stream.Collectors; @@ -8,7 +9,7 @@ import lombok.AllArgsConstructor; @AllArgsConstructor(access = AccessLevel.PRIVATE) -public class WishCategoryListResponse { +public class WishCategoryListResponse implements Serializable { private List categories; public static WishCategoryListResponse of(List categories) { diff --git a/backend/src/main/java/codesquard/app/config/CacheConfig.java b/backend/src/main/java/codesquard/app/config/CacheConfig.java new file mode 100644 index 000000000..b8926517e --- /dev/null +++ b/backend/src/main/java/codesquard/app/config/CacheConfig.java @@ -0,0 +1,9 @@ +package codesquard.app.config; + +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableCaching +public class CacheConfig { +} diff --git a/backend/src/main/java/codesquard/app/config/WebConfig.java b/backend/src/main/java/codesquard/app/config/WebConfig.java index aa6d4fe81..8e20a3117 100644 --- a/backend/src/main/java/codesquard/app/config/WebConfig.java +++ b/backend/src/main/java/codesquard/app/config/WebConfig.java @@ -1,14 +1,17 @@ package codesquard.app.config; import java.util.List; +import java.util.concurrent.TimeUnit; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; +import org.springframework.http.CacheControl; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.mvc.WebContentInterceptor; import com.fasterxml.jackson.databind.ObjectMapper; @@ -38,6 +41,13 @@ public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LogoutInterceptor()) .excludePathPatterns("/api/*") .addPathPatterns("/api/auth/logout"); + + WebContentInterceptor webCacheContentInterceptor = new WebContentInterceptor(); + webCacheContentInterceptor.addCacheMapping( + CacheControl.maxAge(3600, TimeUnit.SECONDS).noTransform().mustRevalidate(), + "/api/categories", "/api/wishes/categories"); + + registry.addInterceptor(webCacheContentInterceptor); } @Override diff --git a/backend/src/main/java/codesquard/app/domain/sales/SalesPaginationRepository.java b/backend/src/main/java/codesquard/app/domain/sales/SalesPaginationRepository.java index 1d5ba47c3..235e0b581 100644 --- a/backend/src/main/java/codesquard/app/domain/sales/SalesPaginationRepository.java +++ b/backend/src/main/java/codesquard/app/domain/sales/SalesPaginationRepository.java @@ -26,10 +26,13 @@ public Slice findAll(SalesStatus status, int size, Long cursor) { item.id.as("itemId"), item.thumbnailUrl, item.title, - item.region, + item.region.as("tradingRegion"), item.createdAt, item.price, - item.status)) + item.status, + item.wishCount, + item.chatCount, + item.member.loginId.as("sellerId"))) .from(item) .where(itemRepository.lessThanItemId(cursor), itemRepository.equalsStatus(status)) diff --git a/backend/src/main/java/codesquard/app/interceptor/CacheInterceptor.java b/backend/src/main/java/codesquard/app/interceptor/CacheInterceptor.java new file mode 100644 index 000000000..358234f57 --- /dev/null +++ b/backend/src/main/java/codesquard/app/interceptor/CacheInterceptor.java @@ -0,0 +1,5 @@ +package codesquard.app.interceptor; + +public class CacheInterceptor { + +}