Skip to content

[Feat] 상품 검색 기능#16

Merged
kangcheolung merged 3 commits intodevelopfrom
feature/15
Jan 10, 2026
Merged

[Feat] 상품 검색 기능#16
kangcheolung merged 3 commits intodevelopfrom
feature/15

Conversation

@kangcheolung
Copy link
Copy Markdown
Member

@kangcheolung kangcheolung commented Jan 10, 2026

🔍️ 작업 내용

  • Closes #
    상품 검색 기능

✨ 상세 설명

  • 상품 검색 기능 추가로 검색 범위는 브랜드명, 카테고리, 색상, 상품명으로 했습니다.

🛠️ 추후 리팩토링 및 고도화 계획

📸 스크린샷 (선택)

💬 리뷰 요구사항

Summary by CodeRabbit

  • 새로운 기능
    • 검색 기능: 상품명·브랜드·카테고리·색상 기반 키워드 검색 및 페이징(기본 페이지 크기 20) 제공, 입력 유효성 검사 적용(비어있지 않음·길이 1~50).
    • 특가 상품 조회: 할인율 기준 상위 10개 판매 중인 상품 조회.
    • 유사 상품 추천: 가격대가 유사한 판매 중인 상품 최대 6개 추천.
  • 수정
    • 요청 파라미터 유효성 위반에 대한 전역 예외 처리 추가(잘못된 입력 시 400 응답).

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 10, 2026

Walkthrough

상품 키워드 검색 기능이 Repository, Service, Controller에 추가되고, Controller에 입력 검증(@Validated) 및 GlobalExceptionHandler에 ConstraintViolationException 핸들러가 추가되었습니다. 기존 동작은 변경되지 않았습니다.

Changes

Cohort / File(s) 변경 사항
Product Repository
src/main/java/com/ongil/backend/domain/product/repository/ProductRepository.java
searchByKeyword(String keyword, Pageable pageable) 메서드 추가 — @EntityGraph 및 JPQL로 onSale 상품을 이름, 브랜드명, 카테고리명, colors에서 대소문자 무시 키워드 검색
Product Service
src/main/java/com/ongil/backend/product/service/ProductService.java
searchProducts(String keyword, Pageable pageable) 추가(키워드 trim/빈값 처리, 페이징 반환), getSpecialSaleProducts() 추가(할인율 상위 10개), getSimilarProducts(Long productId) 추가(가격범위 내 최대 6개). Ai description 판별에 AiMaterialCare null 체크 추가
Product Controller
src/main/java/com/ongil/backend/product/controller/ProductController.java
클래스에 @Validated 추가 및 GET /api/products/search 엔드포인트 추가 — keyword 검증(@NotBlank, @Size(1,50)), Pageable(디폴트 size=20) 수용 후 productService.searchProducts(...) 호출, DataResponse<Page<ProductSimpleResponse>> 반환; validation 관련 imports 추가
Global Exception Handler
src/main/java/com/ongil/backend/global/common/exception/GlobalExceptionHandler.java
ConstraintViolationException 처리용 @ExceptionHandler 메서드 추가(로그 기록, INVALID_PARAMETER 기반 ErrorResponse, HTTP 400 반환); 관련 import 추가

Sequence Diagram

sequenceDiagram
    actor Client
    participant Controller as ProductController
    participant Service as ProductService
    participant Repository as ProductRepository
    participant DB as Database

    Client->>Controller: GET /api/products/search?keyword=...&page=0
    Controller->>Service: searchProducts(keyword, pageable)
    Service->>Service: keyword 검증 (trim, null/empty 체크)
    Service->>Repository: searchByKeyword(keyword, pageable)
    Repository->>DB: JPQL 쿼리 실행 (name, brand.name, category.name, colors 검색)
    DB-->>Repository: Page<Product> 반환
    Repository-->>Service: Page<Product>
    Service->>Service: Product → ProductSimpleResponse 변환
    Service-->>Controller: Page<ProductSimpleResponse>
    Controller-->>Client: DataResponse<Page<ProductSimpleResponse>>
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • [Feat] 상품 검색 기능 #15: 상품명·브랜드·카테고리·색상 기반 키워드 검색 기능 구현 요청과 일치 — 이 PR이 해당 요구를 구현합니다.

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 변경의 핵심 내용인 상품 검색 기능 추가를 정확하게 설명하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/main/java/com/ongil/backend/product/service/ProductService.java (1)

95-114: 비슷한 상품 추천 로직이 합리적입니다.

기준 가격의 ±20% 범위와 동일 카테고리 조건으로 유사 상품을 찾는 로직이 적절합니다. 현재 상품 제외(IdNot) 처리와 최대 6개 제한도 명확합니다.

선택적 개선: 향후 추천 알고리즘 고도화 시 브랜드 선호도, 태그 유사도 등을 추가로 고려할 수 있습니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf8b2ab and f36ea05.

📒 Files selected for processing (3)
  • src/main/java/com/ongil/backend/domain/product/repository/ProductRepository.java
  • src/main/java/com/ongil/backend/product/controller/ProductController.java
  • src/main/java/com/ongil/backend/product/service/ProductService.java
🧰 Additional context used
📓 Path-based instructions (1)
src/main/java/**/*.java

⚙️ CodeRabbit configuration file

SOLID 원칙, 스프링 어노테이션, 의존성 주입 패턴, 예외 처리에 중점을 둔다

Files:

  • src/main/java/com/ongil/backend/product/controller/ProductController.java
  • src/main/java/com/ongil/backend/domain/product/repository/ProductRepository.java
  • src/main/java/com/ongil/backend/product/service/ProductService.java
🔇 Additional comments (3)
src/main/java/com/ongil/backend/product/service/ProductService.java (3)

116-124: 방어적 프로그래밍이 잘 구현되었습니다.

키워드의 null/empty 체크와 trim() 처리가 적절합니다. 컨트롤러에서 @RequestParam으로 필수 파라미터를 받지만, 서비스 계층에서도 방어 로직을 구현한 것은 좋은 관행입니다.

선택적 개선 사항: 컨트롤러에서 입력 검증을 추가하면(위 리뷰 참조) 더욱 견고해집니다.


85-93: 특가 상품 조회 로직 확인

특가 상품을 TOP 10으로 제한하고 할인율 기준 내림차순 정렬하는 로직이 명확합니다. ProductType.SPECIAL_SALE 필터링도 적절합니다.


126-130: AI 설명 생성 조건 개선

aiMaterialCare null 체크를 추가하여 AI 설명 생성 필요 여부를 더 정확히 판단합니다. 세 가지 필드(advantages, disadvantages, care) 중 하나라도 null이면 생성하는 로직이 일관성 있습니다.

Comment on lines +58 to +70
// 키워드 검색 (브랜드명, 카테고리명, 색상, 상품명)
@EntityGraph(attributePaths = {"brand", "category"})
@Query("""
SELECT p FROM Product p
WHERE p.onSale = true
AND (
LOWER(p.name) LIKE LOWER(CONCAT('%', :keyword, '%'))
OR LOWER(p.brand.name) LIKE LOWER(CONCAT('%', :keyword, '%'))
OR LOWER(p.category.name) LIKE LOWER(CONCAT('%', :keyword, '%'))
OR LOWER(p.colors) LIKE LOWER(CONCAT('%', :keyword, '%'))
)
""")
Page<Product> searchByKeyword(@Param("keyword") String keyword, Pageable pageable);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# ProductRepository와 Product 엔티티 찾기
fd -type f -name "ProductRepository.java" -o -name "Product.java" | head -20

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 237


🏁 Script executed:

# 스키마 및 마이그레이션 파일 찾기
fd -type f \( -name "*.sql" -o -name "*.xml" \) | grep -i -E "(schema|migration|flyway)" | head -20

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 237


🏁 Script executed:

# 전체 파일 구조 확인
git ls-files | grep -i product | head -30

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 1215


🏁 Script executed:

# ProductRepository 확인 (라인 수 먼저 확인)
wc -l src/main/java/com/ongil/backend/domain/product/repository/ProductRepository.java

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 150


🏁 Script executed:

# Product 엔티티 확인
wc -l src/main/java/com/ongil/backend/domain/product/entity/Product.java

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 137


🏁 Script executed:

# 마이그레이션/스키마 파일 찾기
find . -type f \( -name "*.sql" -o -name "*.xml" \) | grep -v node_modules | grep -v ".gradle" | head -30

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 49


🏁 Script executed:

# ProductRepository.java 전체 읽기
cat -n src/main/java/com/ongil/backend/domain/product/repository/ProductRepository.java

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 3004


🏁 Script executed:

# Product.java 전체 읽기
cat -n src/main/java/com/ongil/backend/domain/product/entity/Product.java

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 4636


🏁 Script executed:

# resources 디렉토리에서 스키마 찾기
find src/main/resources -type f \( -name "*.sql" -o -name "*.xml" \) 2>/dev/null

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 49


🏁 Script executed:

# application 설정 파일 찾기
fd -type f -name "application*.yml" -o -name "application*.properties" | head -10

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 237


🏁 Script executed:

# ProductService에서 searchByKeyword 사용 패턴
cat -n src/main/java/com/ongil/backend/product/service/ProductService.java | head -100

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 4303


🏁 Script executed:

# 전체 ProductService 크기 확인
wc -l src/main/java/com/ongil/backend/product/service/ProductService.java

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 138


🏁 Script executed:

# ProductService의 나머지 부분 읽기 (키워드 검색 메서드 포함)
cat -n src/main/java/com/ongil/backend/product/service/ProductService.java | tail -80

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 3100


🏁 Script executed:

# application 설정 파일 찾기 (다시 수정된 쿼리)
find . -name "application*.yml" -o -name "application*.properties" 2>/dev/null | head -10

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 225


🏁 Script executed:

# application.yml 확인
cat src/main/resources/application.yml

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 912


🏁 Script executed:

# application-prod.yml 확인 (프로덕션 설정)
cat src/main/resources/application-prod.yml

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 495


🏁 Script executed:

# Product 엔티티의 @Table 어노테이션 다시 확인 (인덱스 포함 여부)
sed -n '16,20p' src/main/java/com/ongil/backend/domain/product/entity/Product.java

Repository: IT-Cotato/12th-OnGil-BE

Length of output: 201


LIKE 검색 성능 최적화를 우선적으로 검토하세요.

현재 searchByKeyword 메서드는 LIKE LOWER(CONCAT('%', :keyword, '%')) 패턴으로 4개 필드(name, brand.name, category.name, colors)를 동시에 검색합니다. 이 패턴은 인덱스를 활용할 수 없어 데이터 증가에 따라 성능 저하가 예상됩니다.

검토 항목:

  • 검색 컬럼에 데이터베이스 인덱스 설정 (Product 엔티티에 @Index 어노테이션 또는 DDL에 명시)
  • 대규모 데이터셋에서는 Full-Text Search 도입 검토 (MySQL FULLTEXT INDEX, Elasticsearch 등)
  • colors 필드를 별도 엔티티로 분리하면 정규화와 동시에 검색 최적화 가능

- 검증로직 추가
@kangcheolung kangcheolung added the ✨ Feature 기능 개발 label Jan 10, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/main/java/com/ongil/backend/product/controller/ProductController.java:
- Around line 76-87: GlobalExceptionHandler is missing a handler for
javax.validation.ConstraintViolationException which causes parameter validation
failures (from methods like ProductController.searchProducts using
@NotBlank/@Size) to return 500; add an
@ExceptionHandler(ConstraintViolationException.class) method in
GlobalExceptionHandler with signature matching (ConstraintViolationException e,
HttpServletRequest request) that logs the error and returns
ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ErrorResponse.of(ErrorCode.INVALID_PARAMETER,
request)); ensure you import ConstraintViolationException and use the same
ErrorResponse and ErrorCode types used elsewhere.
🧹 Nitpick comments (1)
src/main/java/com/ongil/backend/product/controller/ProductController.java (1)

80-81: 검증 제약 조건 중복 (선택사항)

@NotBlank는 이미 문자열을 trim한 후 비어있지 않음을 보장하므로, @Sizemin=1은 기술적으로 중복입니다. 하지만 명시적인 의도를 나타내므로 현재 상태로 유지해도 무방합니다.

♻️ 중복 제거 옵션 (선택사항)
-		@Size(min = 1, max = 50, message = "검색 키워드는 1-50자 이내여야 합니다")
+		@Size(max = 50, message = "검색 키워드는 50자 이내여야 합니다")
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f36ea05 and eb40dd7.

📒 Files selected for processing (1)
  • src/main/java/com/ongil/backend/product/controller/ProductController.java
🧰 Additional context used
📓 Path-based instructions (1)
src/main/java/**/*.java

⚙️ CodeRabbit configuration file

SOLID 원칙, 스프링 어노테이션, 의존성 주입 패턴, 예외 처리에 중점을 둔다

Files:

  • src/main/java/com/ongil/backend/product/controller/ProductController.java
🔇 Additional comments (2)
src/main/java/com/ongil/backend/product/controller/ProductController.java (2)

8-8: 검증 로직 추가 승인

커밋 메시지에서 언급한 대로 클래스 레벨에 @Validated 어노테이션과 필요한 제약 조건 import를 추가하여 메서드 파라미터 검증을 올바르게 활성화했습니다.

Also applies to: 20-21, 25-25


76-87: 검색 엔드포인트 구현 승인

새로운 검색 API가 Spring MVC 패턴을 올바르게 따르고 있으며, 입력 검증이 적절히 적용되었습니다:

  • 컨트롤러는 HTTP 계층 관심사만 처리하고 비즈니스 로직은 서비스에 위임 (SRP 준수)
  • 페이지네이션 기본값 설정으로 API 사용성 향상
  • Swagger 문서화로 API 명세 제공

ConstraintViolationException 핸들러 추가
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/main/java/com/ongil/backend/global/common/exception/GlobalExceptionHandler.java (2)

63-76: 요청 정보 로깅을 추가하여 일관성을 개선하세요.

handleAppExceptionhandleAllException은 에러 발생 지점(요청 메서드와 URI)을 별도로 로깅하지만, 이 핸들러는 해당 로깅이 누락되어 있습니다. 디버깅 시 요청 컨텍스트를 파악하기 어려울 수 있습니다.

♻️ 일관된 로깅 패턴 적용
 	@ExceptionHandler(ConstraintViolationException.class)
 	public ResponseEntity<ErrorResponse> handleConstraintViolationException(
 		ConstraintViolationException e,
 		HttpServletRequest request
 	) {
 		log.error("ConstraintViolation 에러 발생: {}", e.getMessage());
+		log.error("에러가 발생한 지점: {} {}", request.getMethod(), request.getRequestURI());
 		ErrorResponse errorResponse = ErrorResponse.of(
 			ErrorCode.INVALID_PARAMETER,
 			request
 		);
 		return ResponseEntity
 			.status(HttpStatus.BAD_REQUEST)
 			.body(errorResponse);
 	}

68-68: 검증 실패 상세 정보를 로깅하여 디버깅을 개선하세요.

e.getMessage()는 모든 제약 위반을 하나의 문자열로 연결하지만, e.getConstraintViolations()를 사용하면 각 위반 사항(속성 경로, 잘못된 값, 메시지)을 구조화된 형태로 확인할 수 있어 디버깅이 더 용이합니다.

♻️ 상세한 제약 위반 정보 로깅
-		log.error("ConstraintViolation 에러 발생: {}", e.getMessage());
+		log.error("ConstraintViolation 에러 발생: {}", e.getConstraintViolations());
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb40dd7 and 008f059.

📒 Files selected for processing (1)
  • src/main/java/com/ongil/backend/global/common/exception/GlobalExceptionHandler.java
🧰 Additional context used
📓 Path-based instructions (1)
src/main/java/**/*.java

⚙️ CodeRabbit configuration file

SOLID 원칙, 스프링 어노테이션, 의존성 주입 패턴, 예외 처리에 중점을 둔다

Files:

  • src/main/java/com/ongil/backend/global/common/exception/GlobalExceptionHandler.java

@kangcheolung kangcheolung changed the title feat: 상품 검색 기능 [Feat] 상품 검색 기능 Jan 10, 2026
@kangcheolung kangcheolung merged commit 3ce099f into develop Jan 10, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant