Skip to content

Commit bd8fb7b

Browse files
authored
Merge pull request #140 from Kimjipang/main
[volume-5] 인덱스 및 Redis 캐시 적용
2 parents 5c3904c + 3b8f531 commit bd8fb7b

15 files changed

Lines changed: 155 additions & 31 deletions

File tree

apps/commerce-api/src/main/java/com/loopers/CommerceApiApplication.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
import org.springframework.boot.SpringApplication;
55
import org.springframework.boot.autoconfigure.SpringBootApplication;
66
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
7+
import org.springframework.cache.annotation.EnableCaching;
8+
79
import java.util.TimeZone;
810

911
@ConfigurationPropertiesScan
1012
@SpringBootApplication
13+
@EnableCaching
1114
public class CommerceApiApplication {
1215

1316
@PostConstruct

apps/commerce-api/src/main/java/com/loopers/application/like/LikeFacade.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.loopers.domain.like.Like;
44
import com.loopers.domain.like.LikeRepository;
5+
import com.loopers.domain.product.Product;
56
import com.loopers.domain.product.ProductRepository;
67
import com.loopers.domain.user.UserRepository;
78
import com.loopers.interfaces.api.like.LikeV1Dto;
@@ -32,7 +33,7 @@ public LikeInfo doLike(LikeV1Dto.LikeRequest request) {
3233
() -> new CoreException(ErrorType.NOT_FOUND, "존재하지 않는 유저입니다.")
3334
);
3435

35-
productRepository.findById(productId).orElseThrow(
36+
Product product = productRepository.findById(productId).orElseThrow(
3637
() -> new CoreException(ErrorType.NOT_FOUND, "존재하지 않는 상품입니다.")
3738
);
3839

@@ -42,6 +43,8 @@ public LikeInfo doLike(LikeV1Dto.LikeRequest request) {
4243
Like newLike = request.toEntity();
4344
likeRepository.save(newLike);
4445

46+
product.addLikeCount();
47+
4548
return LikeInfo.from(newLike);
4649
});
4750
}
Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,87 @@
11
package com.loopers.application.product;
22

3+
import com.loopers.domain.brand.BrandRepository;
34
import com.loopers.domain.like.LikeRepository;
45
import com.loopers.domain.product.Product;
56
import com.loopers.domain.product.ProductRepository;
67
import com.loopers.interfaces.api.product.ProductV1Dto;
78
import com.loopers.support.error.CoreException;
89
import com.loopers.support.error.ErrorType;
910
import lombok.RequiredArgsConstructor;
11+
import org.springframework.cache.annotation.CacheEvict;
12+
import org.springframework.cache.annotation.Cacheable;
1013
import org.springframework.stereotype.Component;
1114
import org.springframework.transaction.annotation.Transactional;
1215

16+
import java.math.BigDecimal;
1317
import java.util.List;
1418

1519
@Component
1620
@RequiredArgsConstructor
1721
public class ProductFacade {
1822
private final ProductRepository productRepository;
1923
private final LikeRepository likeRepository;
24+
private final BrandRepository brandRepository;
2025

2126

2227
@Transactional
2328
public ProductInfo registerProduct(ProductV1Dto.ProductRequest request) {
29+
Long brandId = request.brandId();
30+
31+
brandRepository.findById(brandId).orElseThrow(
32+
() -> new CoreException(ErrorType.NOT_FOUND, "등록하고자 하는 상품의 브랜드가 존재하지 않습니다.")
33+
);
34+
2435
Product product = request.toEntity();
2536
productRepository.save(product);
2637

27-
return ProductInfo.from(product, 0);
38+
return ProductInfo.from(product);
2839
}
2940

3041
@Transactional(readOnly = true)
42+
@Cacheable(value = "products", key = "'all'")
3143
public List<ProductInfo> findAllProducts() {
3244
List<Product> products = productRepository.findAll();
3345

3446
return products.stream()
35-
.map(product -> {
36-
int likeCount = likeRepository.countByProductId(product.getId());
37-
return ProductInfo.from(product, likeCount);
38-
})
47+
.map(ProductInfo::from)
3948
.toList();
4049
}
4150

4251
@Transactional(readOnly = true)
52+
@Cacheable(value = "product", key = "#id")
4353
public ProductInfo findProductById(Long id) {
4454
Product product = productRepository.findById(id).orElseThrow(
4555
() -> new CoreException(ErrorType.NOT_FOUND, "찾고자 하는 상품이 존재하지 않습니다.")
4656
);
4757

48-
int likeCount = likeRepository.countByProductId(id);
49-
50-
return ProductInfo.from(product, likeCount);
58+
return ProductInfo.from(product);
5159
}
5260

5361
@Transactional(readOnly = true)
5462
public List<ProductInfo> searchProductsByCondition(ProductV1Dto.SearchProductRequest request) {
63+
request.filterCondition().conditionValidate();
5564
request.sortCondition().conditionValidate();
5665

5766
List<Product> products = productRepository.searchProductsByCondition(request);
5867

5968
return products.stream()
60-
.map(product -> {
61-
int likeCount = likeRepository.countByProductId(product.getId());
62-
return ProductInfo.from(product, likeCount);
63-
})
69+
.map(ProductInfo::from)
6470
.toList();
6571
}
72+
73+
@Transactional
74+
@CacheEvict(value = "product", key = "#request.id()")
75+
public ProductInfo changePrice(ProductV1Dto.ChangePriceRequest request) {
76+
Long id = request.id();
77+
BigDecimal newPrice = request.newPrice();
78+
79+
Product product = productRepository.findById(id).orElseThrow(
80+
() -> new CoreException(ErrorType.NOT_FOUND, "존재하지 않는 상품입니다.")
81+
);
82+
83+
Product changedProduct = product.changePrice(newPrice);
84+
85+
return ProductInfo.from(changedProduct);
86+
}
6687
}

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
import java.math.BigDecimal;
66

77
public record ProductInfo(Long id, Long brandId, String name, BigDecimal price, int stock, int likeCount) {
8-
public static ProductInfo from(Product product, int likeCount) {
8+
public static ProductInfo from(Product product) {
99
return new ProductInfo(
10-
product.getId(),
11-
product.getBrandId(),
12-
product.getName(),
13-
product.getPrice(),
14-
product.getStock(),
15-
likeCount
10+
product.getId(),
11+
product.getBrandId(),
12+
product.getName(),
13+
product.getPrice(),
14+
product.getStock(),
15+
product.getLikeCount()
1616
);
17+
1718
}
1819
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.loopers.domain.brand;
2+
3+
import java.util.Optional;
4+
5+
public interface BrandRepository {
6+
Optional<Brand> findById(Long id);
7+
}

apps/commerce-api/src/main/java/com/loopers/domain/product/Product.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,24 @@ public class Product extends BaseEntity {
2929
@Column(name = "stock", nullable = false)
3030
private int stock;
3131

32+
@Column(name = "like_count", nullable = false)
33+
private int likeCount;
3234

33-
public Product(Long brandId, String name, BigDecimal price, int stock) {
35+
36+
public Product(Long brandId, String name, BigDecimal price, int stock, int likeCount) {
3437
this.brandId = brandId;
3538
this.name = name;
3639
this.price = price;
3740
this.stock = stock;
41+
this.likeCount = likeCount;
3842
}
3943

4044
public Product changePrice(BigDecimal newPrice) {
4145
this.price = newPrice;
4246
return this;
4347
}
4448

45-
public Product decreaseStock(int amount) {
49+
public void decreaseStock(int amount) {
4650
if (amount <= 0) {
4751
throw new CoreException(ErrorType.BAD_REQUEST, "재고 감소량은 0보다 커야 합니다.");
4852
}
@@ -51,11 +55,14 @@ public Product decreaseStock(int amount) {
5155
}
5256
this.stock -= amount;
5357

54-
return this;
5558
}
5659

5760
public boolean isInStock(int quantity) {
5861
return this.stock >= quantity;
5962
}
6063

64+
public int addLikeCount() {
65+
return this.likeCount++;
66+
}
67+
6168
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.loopers.infrastructure.brand;
2+
3+
import com.loopers.domain.brand.Brand;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
6+
public interface BrandJpaRepository extends JpaRepository<Brand, Long> {
7+
8+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.loopers.infrastructure.brand;
2+
3+
import com.loopers.domain.brand.Brand;
4+
import com.loopers.domain.brand.BrandRepository;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.stereotype.Component;
7+
8+
import java.util.Optional;
9+
10+
@Component
11+
@RequiredArgsConstructor
12+
public class BrandRepositoryImpl implements BrandRepository {
13+
private final BrandJpaRepository brandJpaRepository;
14+
15+
@Override
16+
public Optional<Brand> findById(Long id) {
17+
return brandJpaRepository.findById(id);
18+
}
19+
}

apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public List<Product> findAll() {
4141

4242
@Override
4343
public List<Product> searchProductsByCondition(ProductV1Dto.SearchProductRequest request) {
44-
System.out.println("sortBy = " + request.sortCondition().sortBy());
44+
4545
boolean isAsc = request.sortCondition().order().equals("asc");
4646

4747
JPAQuery<Product> query = queryFactory
@@ -50,6 +50,10 @@ public List<Product> searchProductsByCondition(ProductV1Dto.SearchProductRequest
5050
.on(product.id.eq(like.productId))
5151
.groupBy(product.id);
5252

53+
switch (request.filterCondition().filterBy()) {
54+
case "brandId" -> query.where(product.brandId.eq(request.filterCondition().filterValue()));
55+
}
56+
5357

5458
switch (request.sortCondition().sortBy()) {
5559
case "price" -> query.orderBy(isAsc ? product.price.asc() : product.price.desc());

apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/ProductV1ApiSpec.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@
99
@Tag(name = "Product V1 API", description = "Product API 입니다.")
1010
public interface ProductV1ApiSpec {
1111
@Operation(summary = "상품 등록")
12-
ApiResponse<ProductV1Dto.ProductResponse> registerProduct (ProductV1Dto.ProductRequest request);
12+
ApiResponse<ProductV1Dto.ProductResponse> registerProduct(ProductV1Dto.ProductRequest request);
1313

1414
@Operation(summary = "상품 목록 조회")
15-
ApiResponse<List<ProductV1Dto.ProductResponse>> findAllProducts ();
15+
ApiResponse<List<ProductV1Dto.ProductResponse>> findAllProducts();
1616

1717
@Operation(summary = "상품 상세 조회")
18-
ApiResponse<ProductV1Dto.ProductResponse> findProductById (Long id);
18+
ApiResponse<ProductV1Dto.ProductResponse> findProductById(Long id);
1919

2020
@Operation(summary = "상품 정렬 조회")
21-
ApiResponse<List<ProductV1Dto.ProductResponse>> findProductsBySortCondition (ProductV1Dto.SearchProductRequest request);
21+
ApiResponse<List<ProductV1Dto.ProductResponse>> findProductsBySortCondition(ProductV1Dto.SearchProductRequest request);
22+
23+
@Operation(summary = "상품 가격 변경")
24+
ApiResponse<ProductV1Dto.ProductResponse> changePrice(ProductV1Dto.ChangePriceRequest request);
25+
2226
}

0 commit comments

Comments
 (0)