diff --git a/pom.xml b/pom.xml index 81eaed6e..b1b4fef9 100644 --- a/pom.xml +++ b/pom.xml @@ -134,6 +134,11 @@ jjwt-jackson 0.11.5 + + + org.springframework.boot + spring-boot-starter-cache + diff --git a/src/main/java/com/t3t/frontserver/FrontServerApplication.java b/src/main/java/com/t3t/frontserver/FrontServerApplication.java index c5c725c5..41eaa7b8 100644 --- a/src/main/java/com/t3t/frontserver/FrontServerApplication.java +++ b/src/main/java/com/t3t/frontserver/FrontServerApplication.java @@ -3,7 +3,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; +import org.springframework.cache.annotation.EnableCaching; +@EnableCaching @ConfigurationPropertiesScan @SpringBootApplication public class FrontServerApplication { diff --git a/src/main/java/com/t3t/frontserver/book/controller/BookCategoryController.java b/src/main/java/com/t3t/frontserver/book/controller/BookCategoryController.java index 2583bc0d..9b9da814 100644 --- a/src/main/java/com/t3t/frontserver/book/controller/BookCategoryController.java +++ b/src/main/java/com/t3t/frontserver/book/controller/BookCategoryController.java @@ -5,6 +5,7 @@ import com.t3t.frontserver.book.model.response.BookDetailResponse; import com.t3t.frontserver.category.client.CategoryApiClient; import com.t3t.frontserver.category.response.CategoryTreeResponse; +import com.t3t.frontserver.category.service.CategoryService; import com.t3t.frontserver.model.response.BaseResponse; import com.t3t.frontserver.model.response.PageResponse; import lombok.RequiredArgsConstructor; @@ -23,13 +24,13 @@ @Controller public class BookCategoryController { private final BookCategoryApiClient bookCategoryApiClient; - private final CategoryApiClient categoryAdaptor; + private final CategoryService categoryService; @GetMapping("/category/{categoryId}/books") public String getBooksByCategoryId(Model model, @PathVariable Integer categoryId, @RequestParam(value = "pageNo", defaultValue = "0", required = false) int pageNo) { - List categoryList = getDataFromCategoryAdaptor(1, 2); + List categoryList = categoryService.getCategoryTreeByDepth(1, 2); PageResponse bookList = getDataFromBookCategoryAdaptor(categoryId, pageNo); if (bookList != null) { @@ -49,11 +50,6 @@ public String getBooksByCategoryId(Model model, @PathVariable Integer categoryId return "main/page/search"; } - private List getDataFromCategoryAdaptor(Integer startDepth, Integer maxDepth ) { - ResponseEntity>> categoriesResponse = categoryAdaptor.getCategoryTreeByDepth(startDepth, maxDepth); - return handleResponse(categoriesResponse); - } - private PageResponse getDataFromBookCategoryAdaptor(Integer categoryId, int pageNo) { ResponseEntity>> booksResponse = bookCategoryApiClient.getBooksByCategoryId(categoryId, pageNo); return handleResponse(booksResponse); diff --git a/src/main/java/com/t3t/frontserver/book/controller/BookController.java b/src/main/java/com/t3t/frontserver/book/controller/BookController.java index 39a02dd7..a2994ec8 100644 --- a/src/main/java/com/t3t/frontserver/book/controller/BookController.java +++ b/src/main/java/com/t3t/frontserver/book/controller/BookController.java @@ -4,6 +4,7 @@ import com.t3t.frontserver.book.model.response.BookDetailResponse; import com.t3t.frontserver.category.client.CategoryApiClient; import com.t3t.frontserver.category.response.CategoryTreeResponse; +import com.t3t.frontserver.category.service.CategoryService; import com.t3t.frontserver.index.OrderFormRequest; import com.t3t.frontserver.model.response.BaseResponse; import com.t3t.frontserver.model.response.PageResponse; @@ -24,15 +25,15 @@ @Controller @RequiredArgsConstructor public class BookController { - private final CategoryApiClient categoryAdaptor; private final BookApiClient bookApiClient; private final ReviewApiClient reviewApiClient; + private final CategoryService categoryService; @GetMapping("books/{bookId}") public String getBook(Model model, @PathVariable Long bookId, @RequestParam(value = "pageNo", defaultValue = "0", required = false) int pageNo) { - List categoryList = getDataFromCategoryAdaptor(1, 2); + List categoryList = categoryService.getCategoryTreeByDepth(1, 2); BookDetailResponse bookDetailList = getDataFromBookAdaptor(bookId); PageResponse reviewList = getDataFromReviewAdaptor(bookId, pageNo); @@ -58,11 +59,6 @@ public String getBook(Model model, @PathVariable Long bookId, return "main/page/detail"; } - private List getDataFromCategoryAdaptor(Integer startDepth, Integer maxDepth ) { - ResponseEntity>> categoriesResponse = categoryAdaptor.getCategoryTreeByDepth(startDepth, maxDepth); - return handleResponse(categoriesResponse); - } - private BookDetailResponse getDataFromBookAdaptor(Long bookId) { ResponseEntity> bookDetailResponse = bookApiClient.getBook(bookId); return handleResponse(bookDetailResponse); diff --git a/src/main/java/com/t3t/frontserver/category/response/CategoryTreeResponse.java b/src/main/java/com/t3t/frontserver/category/response/CategoryTreeResponse.java index 645b169e..098b4508 100644 --- a/src/main/java/com/t3t/frontserver/category/response/CategoryTreeResponse.java +++ b/src/main/java/com/t3t/frontserver/category/response/CategoryTreeResponse.java @@ -1,7 +1,6 @@ package com.t3t.frontserver.category.response; -import lombok.Builder; -import lombok.Data; +import lombok.*; import java.util.List; @@ -12,6 +11,8 @@ */ @Data @Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public class CategoryTreeResponse { private Integer categoryId; diff --git a/src/main/java/com/t3t/frontserver/category/service/CategoryService.java b/src/main/java/com/t3t/frontserver/category/service/CategoryService.java index f39e1c26..84512161 100644 --- a/src/main/java/com/t3t/frontserver/category/service/CategoryService.java +++ b/src/main/java/com/t3t/frontserver/category/service/CategoryService.java @@ -3,15 +3,26 @@ import com.t3t.frontserver.category.adaptor.CategoryAdaptor; import com.t3t.frontserver.category.response.CategoryTreeResponse; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.List; -@Service +@CacheConfig(cacheNames = "categories") @RequiredArgsConstructor +@Service public class CategoryService { private final CategoryAdaptor categoryAdaptor; + /** + * 카테고리 트리를 조회한다. + * 요청하는 startDepth 와 maxDepth 에 대해서 캐싱이 적용되어 조회된다. + * + * @author woody33545(구건모) + */ + @Cacheable(key = "'startDepth:' + #startDepth + 'maxDepth:' + #maxDepth", unless = "#result == null") public List getCategoryTreeByDepth(Integer startDepth, Integer maxDepth) { return categoryAdaptor.getCategoryTreeByDepth(startDepth, maxDepth); } diff --git a/src/main/java/com/t3t/frontserver/config/CacheConfig.java b/src/main/java/com/t3t/frontserver/config/CacheConfig.java new file mode 100644 index 00000000..187ad694 --- /dev/null +++ b/src/main/java/com/t3t/frontserver/config/CacheConfig.java @@ -0,0 +1,37 @@ +package com.t3t.frontserver.config; + +import org.springframework.cache.CacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.time.Duration; + +@Configuration +public class CacheConfig { + + /** + * Cache Manager 빈 등록 + * Global Cache 로 Redis 를 사용하므로 RedisCacheManager 로 설정한다. + * @author woody35545(구건모) + */ + @Bean + public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { + RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration + .defaultCacheConfig() + .disableCachingNullValues() + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) + .entryTtl(Duration.ofMinutes(60L)); + + return RedisCacheManager.RedisCacheManagerBuilder + .fromConnectionFactory(redisConnectionFactory) + .cacheDefaults(redisCacheConfiguration) + .build(); + } +} diff --git a/src/main/java/com/t3t/frontserver/main/controller/MainController.java b/src/main/java/com/t3t/frontserver/main/controller/MainController.java index 5422a24a..de68d4e5 100644 --- a/src/main/java/com/t3t/frontserver/main/controller/MainController.java +++ b/src/main/java/com/t3t/frontserver/main/controller/MainController.java @@ -2,6 +2,7 @@ import com.t3t.frontserver.category.client.CategoryApiClient; import com.t3t.frontserver.category.response.CategoryTreeResponse; +import com.t3t.frontserver.category.service.CategoryService; import com.t3t.frontserver.main.response.BookInfoBrief; import com.t3t.frontserver.model.response.BaseResponse; import com.t3t.frontserver.recommendation.client.RecommendationApiClient; @@ -21,7 +22,7 @@ @RequiredArgsConstructor public class MainController { private final RecommendationApiClient recommendationAdaptor; - private final CategoryApiClient categoryAdaptor; + private final CategoryService categoryService; @GetMapping("/") public String homeView(Model model) { @@ -32,7 +33,7 @@ public String homeView(Model model) { LocalDate currentDate = LocalDate.now(); //String formattedDate = currentDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); String formattedDate = "2024-04-06"; // 테스트를 위해 값을 고정, 실제 배포시에는 삭제 예정 - List categoryList = getDataFromCategoryAdaptor(1, 2); + List categoryList = categoryService.getCategoryTreeByDepth(1, 2); List recentlyPublishedBookList = getDataFromRecommendationAdaptor(() -> recommendationAdaptor.getRecentlyPublishedBooks(formattedDate, defaultMaxCount)); List mostLikeBookList = getDataFromRecommendationAdaptor(() -> recommendationAdaptor.getBooksByMostLikedAndHighAverageScore(defaultMaxCount)); List bestSellerBookList = getDataFromRecommendationAdaptor(() -> recommendationAdaptor.getBestSellerBooks(defaultMaxCount)); @@ -46,11 +47,6 @@ public String homeView(Model model) { return "main/page/home"; } - private List getDataFromCategoryAdaptor(Integer startDepth, Integer maxDepth ) { - ResponseEntity>> categoriesResponse = categoryAdaptor.getCategoryTreeByDepth(startDepth, maxDepth); - return handleResponse(categoriesResponse); - } - private List getDataFromRecommendationAdaptor(Supplier>>> requestSupplier) { ResponseEntity>> responseEntity = requestSupplier.get(); return handleResponse(responseEntity);