Skip to content

Commit fbb6081

Browse files
kangcheolungclaude
andcommitted
feat: 상품 필터링 기능 개선
- 브랜드 필터링을 단일 → 다중 선택으로 변경 (brandIds) - 사이즈 필터링을 단일 → 다중 선택으로 변경 (clothingSizes) - 가격 필터링 시 할인가(discountPrice) 기준으로 적용되도록 수정 - 사이즈 REGEXP 패턴으로 정확한 값 매칭 처리 (S가 XS에 매칭되는 버그 수정) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 6b16aaa commit fbb6081

File tree

4 files changed

+45
-26
lines changed

4 files changed

+45
-26
lines changed

src/main/java/com/ongil/backend/domain/product/controller/ProductController.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,18 @@ public DataResponse<List<ProductOptionResponse>> getProductOptions(
7373
public DataResponse<ProductSearchPageResDto> getProducts(
7474
@RequestParam(required = false) String query,
7575
@RequestParam(required = false) Long categoryId,
76-
@RequestParam(required = false) Long brandId,
76+
@RequestParam(required = false) List<Long> brandIds,
7777
@RequestParam(required = false) String priceRange,
78-
@RequestParam(required = false) String clothingSize,
78+
@RequestParam(required = false) List<String> clothingSizes,
7979
@RequestParam(required = false, defaultValue = "POPULAR") ProductSortType sortType,
8080
@PageableDefault(size = 20) Pageable pageable,
8181
@AuthenticationPrincipal Long userId
8282
) {
8383
ProductSearchCondition condition = ProductSearchCondition.builder()
8484
.categoryId(categoryId)
85-
.brandId(brandId)
85+
.brandIds(brandIds != null && !brandIds.isEmpty() ? brandIds : null)
8686
.priceRange(priceRange)
87-
.size(clothingSize)
87+
.sizes(clothingSizes != null && !clothingSizes.isEmpty() ? clothingSizes : null)
8888
.build();
8989

9090
ProductSearchPageResDto res = productService.getProducts(condition, sortType, pageable, query, userId);

src/main/java/com/ongil/backend/domain/product/dto/request/ProductSearchCondition.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.ongil.backend.domain.product.dto.request;
22

3+
import java.util.List;
4+
35
import io.swagger.v3.oas.annotations.media.Schema;
46
import lombok.Builder;
57
import lombok.Getter;
@@ -12,14 +14,23 @@ public class ProductSearchCondition {
1214
@Schema(description = "카테고리 ID", example = "1")
1315
private Long categoryId;
1416

15-
@Schema(description = "브랜드 ID", example = "5")
16-
private Long brandId;
17+
@Schema(description = "브랜드 ID 목록 (다중 선택)", example = "[1, 2, 3]")
18+
private List<Long> brandIds;
1719

1820
@Schema(description = "가격 범위 (형식: minPrice-maxPrice)", example = "50000-100000")
1921
private String priceRange;
2022

21-
@Schema(description = "사이즈 (예: XS, S, M, L, XL)", example = "M")
22-
private String size;
23+
@Schema(description = "사이즈 목록 (다중 선택, 예: XS, S, M, L, XL)", example = "[M, L]")
24+
private List<String> sizes;
25+
26+
// 사이즈 목록을 REGEXP 패턴으로 변환 (예: [M, L] → "(^|,)(M|L)(,|$)")
27+
public String buildSizesPattern() {
28+
if (sizes == null || sizes.isEmpty()) {
29+
return null;
30+
}
31+
String sizeGroup = String.join("|", sizes);
32+
return "(^|,)(" + sizeGroup + ")(,|$)";
33+
}
2334

2435
// 가격 범위 파싱
2536
public Integer[] parsePriceRange() {

src/main/java/com/ongil/backend/domain/product/repository/ProductRepository.java

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,23 @@ public interface ProductRepository extends JpaRepository<Product, Long> {
4444
SELECT p FROM Product p
4545
WHERE (:targetIds IS NULL OR p.id IN :targetIds)
4646
AND (:categoryId IS NULL OR p.category.id = :categoryId)
47-
AND (:brandId IS NULL OR p.brand.id = :brandId)
48-
AND (:minPrice IS NULL OR p.price >= :minPrice)
49-
AND (:maxPrice IS NULL OR p.price <= :maxPrice)
50-
AND (:size IS NULL OR p.sizes LIKE CONCAT('%', :size, '%'))
47+
AND (:brandIds IS NULL OR p.brand.id IN :brandIds)
48+
AND (:minPrice IS NULL OR
49+
(p.discountPrice IS NOT NULL AND p.discountPrice > 0 AND p.discountPrice >= :minPrice)
50+
OR ((p.discountPrice IS NULL OR p.discountPrice = 0) AND p.price >= :minPrice))
51+
AND (:maxPrice IS NULL OR
52+
(p.discountPrice IS NOT NULL AND p.discountPrice > 0 AND p.discountPrice <= :maxPrice)
53+
OR ((p.discountPrice IS NULL OR p.discountPrice = 0) AND p.price <= :maxPrice))
54+
AND (:sizesPattern IS NULL OR FUNCTION('REGEXP_LIKE', p.sizes, :sizesPattern) = true)
5155
AND p.onSale = true
5256
""")
5357
Page<Product> findAllByCondition(
5458
@Param("targetIds") List<Long> targetIds,
5559
@Param("categoryId") Long categoryId,
56-
@Param("brandId") Long brandId,
60+
@Param("brandIds") List<Long> brandIds,
5761
@Param("minPrice") Integer minPrice,
5862
@Param("maxPrice") Integer maxPrice,
59-
@Param("size") String size,
63+
@Param("sizesPattern") String sizesPattern,
6064
Pageable pageable
6165
);
6266

@@ -66,19 +70,23 @@ Page<Product> findAllByCondition(
6670
SELECT p FROM Product p
6771
WHERE (:targetIds IS NULL OR p.id IN :targetIds)
6872
AND p.category.parentCategory.id = :parentCategoryId
69-
AND (:brandId IS NULL OR p.brand.id = :brandId)
70-
AND (:minPrice IS NULL OR p.price >= :minPrice)
71-
AND (:maxPrice IS NULL OR p.price <= :maxPrice)
72-
AND (:size IS NULL OR p.sizes LIKE CONCAT('%', :size, '%'))
73+
AND (:brandIds IS NULL OR p.brand.id IN :brandIds)
74+
AND (:minPrice IS NULL OR
75+
(p.discountPrice IS NOT NULL AND p.discountPrice > 0 AND p.discountPrice >= :minPrice)
76+
OR ((p.discountPrice IS NULL OR p.discountPrice = 0) AND p.price >= :minPrice))
77+
AND (:maxPrice IS NULL OR
78+
(p.discountPrice IS NOT NULL AND p.discountPrice > 0 AND p.discountPrice <= :maxPrice)
79+
OR ((p.discountPrice IS NULL OR p.discountPrice = 0) AND p.price <= :maxPrice))
80+
AND (:sizesPattern IS NULL OR FUNCTION('REGEXP_LIKE', p.sizes, :sizesPattern) = true)
7381
AND p.onSale = true
7482
""")
7583
Page<Product> findAllByParentCategoryCondition(
7684
@Param("targetIds") List<Long> targetIds,
7785
@Param("parentCategoryId") Long parentCategoryId,
78-
@Param("brandId") Long brandId,
86+
@Param("brandIds") List<Long> brandIds,
7987
@Param("minPrice") Integer minPrice,
8088
@Param("maxPrice") Integer maxPrice,
81-
@Param("size") String size,
89+
@Param("sizesPattern") String sizesPattern,
8290
Pageable pageable
8391
);
8492

src/main/java/com/ongil/backend/domain/product/service/ProductService.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,21 +160,21 @@ public ProductSearchPageResDto getProducts(
160160
products = productRepository.findAllByParentCategoryCondition(
161161
targetIds,
162162
condition.getCategoryId(),
163-
condition.getBrandId(),
163+
condition.getBrandIds(),
164164
minPrice,
165165
maxPrice,
166-
condition.getSize(),
166+
condition.buildSizesPattern(),
167167
pageableWithSort
168168
);
169169
} else {
170170
// 하위 카테고리 → 해당 카테고리 상품만 조회
171171
products = productRepository.findAllByCondition(
172172
targetIds,
173173
condition.getCategoryId(),
174-
condition.getBrandId(),
174+
condition.getBrandIds(),
175175
minPrice,
176176
maxPrice,
177-
condition.getSize(),
177+
condition.buildSizesPattern(),
178178
pageableWithSort
179179
);
180180
}
@@ -183,10 +183,10 @@ public ProductSearchPageResDto getProducts(
183183
products = productRepository.findAllByCondition(
184184
targetIds,
185185
null,
186-
condition.getBrandId(),
186+
condition.getBrandIds(),
187187
minPrice,
188188
maxPrice,
189-
condition.getSize(),
189+
condition.buildSizesPattern(),
190190
pageableWithSort
191191
);
192192
}

0 commit comments

Comments
 (0)