Skip to content

[URECA-78] Feat: 요약 및 전체 상품 추천 구현#51

Merged
Chaejy merged 2 commits intodevelopfrom
URECA-78/Feat/recommend
Jan 28, 2026
Merged

[URECA-78] Feat: 요약 및 전체 상품 추천 구현#51
Chaejy merged 2 commits intodevelopfrom
URECA-78/Feat/recommend

Conversation

@Chaejy
Copy link
Copy Markdown
Contributor

@Chaejy Chaejy commented Jan 28, 2026

Key Changes

  • category, recommend 부분 작업했습니다

작업 내역

💬 공유사항 to 리뷰어

비고

Summary by CodeRabbit

  • 새 기능
    • 요약 기반 맞춤형 상품 추천 기능 추가
    • 카테고리별 무작위 추천으로 다양한 상품 발견 지원
    • 추천 생성·저장·조회 워크플로 제공 (추천 목록, 순위 포함)
    • 추천 항목에 상품명, 이미지, 가격, 링크 등 상세 정보 포함
  • 안정성/오류 처리
    • 추천 관련 오류 처리 및 안정성 강화

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

@github-actions github-actions bot changed the title 요약 및 전체 상품 추천 구현 [URECA-78] Feat: 요약 및 전체 상품 추천 구현 Jan 28, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 28, 2026

📝 Walkthrough

Walkthrough

추천 도메인 기능이 신규 추가되었습니다. FastAPI 추천 서버 호출을 위한 RecommendClient, 추천 및 카테고리 DTO들, MyBatis 매퍼 인터페이스와 XML 매핑, 추천 로직을 수행하고 DB에 저장하는 RecommendService, 이를 노출하는 RecommendController가 포함됩니다. 또한 ErrorCode 열거형에 Lombok @Getter 적용 및 추천 관련 오류 코드들이 추가되었습니다.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes


상세 피드백

🎯 주요 강점

계층별 역할 분리가 명확합니다. DTO, Mapper, Service, Controller가 각각의 책임을 잘 수행하고 있으며, MyBatis XML과 Java 인터페이스의 매핑도 일관성 있게 구성되었습니다.

에러 처리 전략이 세밀합니다. RecommendService에서 FastAPI 호출 실패, DB 작업 실패, 빈 결과 등을 구분하여 처리하고 적절한 ErrorCode를 할당한 점은 디버깅과 클라이언트 대응을 수월하게 합니다.


⚠️ 검토 시 유의 사항

1. RecommendService의 트랜잭션 경계 재검토

// generateAndSave 메서드의 흐름
// 1. FastAPI 호출 (외부 서비스)
// 2. deleteItems (DB)
// 3. insertItems (DB)

개선 제안:

  • deleteItemsinsertItems@Transactional로 감싸되, FastAPI 호출은 트랜잭션 외부에 배치하세요. 외부 API는 예측 불가능하므로 트랜잭션 시간을 연장할 이유가 없습니다.
  • 스프링 공식 문서 Propagation and Isolation 참고하세요.

2. RecommendClient의 예외 처리

현재 RestTemplate 호출 시 HTTP 에러(4xx, 5xx)를 명시적으로 처리하지 않습니다.

개선 제안:

  • RestTemplatesetErrorHandler()를 통해 일관된 에러 응답 처리를 구현하거나, try-catchHttpServerErrorException, HttpClientErrorException을 포착하세요.
  • 또는 RestTemplate 대신 WebClient(비동기, 논블로킹)로 마이그레이션을 검토하세요.

3. RecommendResponse의 설계

summaryId가 모든 응답에 포함되는데, /me 엔드포인트(getRandomByCategory)에서는 summaryId = 0으로 설정됩니다.

개선 제안:

  • 명시성을 위해 응답 구조를 분리하거나, 필드 문서화(@ApiModelProperty 또는 Javadoc)로 0의 의미를 명확히 하세요.

4. RecommendMapper.xml의 JOIN 쿼리

selectBySummaryId에서 recommend, product, category를 조인합니다.

확인 사항:

  • 테이블 스키마상 외래키 관계가 명확한지 검토하세요.
  • 조인 조건과 필터 조건(WHERE)을 재확인하여 의도치 않은 행 필터링이 없는지 확인하세요.

5. 랭크 할당 로직

RecommendService.generateAndSave()에서 점수로 정렬 후 순차적으로 rankNo를 할당합니다.

// items.sort(...); // 점수 기준 내림차순
// for (int i = 0; i < items.size(); i++) {
//     items.get(i).setRankNo(i);
// }

개선 제안:

  • 점수가 동일한 경우 순서를 명확히 정의하세요 (예: 상품 ID 기준 부차 정렬).
  • 테스트 케이스에서 동점 시나리오를 포함하세요.

✅ 검토 시 확인 항목

  • FastAPI 서버의 응답 형식(RecommendResponse)이 실제 API와 정확히 일치하는지 확인
  • CategoryMapper.selectAll()의 쿼리 성능 (인덱스 여부)
  • RecommendMapper.selectRandomByCategory()LIMIT 5 하드코딩 필요성 재검토 (설정화 고려)
  • 에러 로그의 민감 정보 제외 여부 (외부 API 응답 전체 로깅 주의)
  • MyBatis XML namespace와 인터페이스 경로 일치 확인

💡 추가 학습 자료

짧고 굵게: 잘 정리된 층 구성입니다 — 트랜잭션과 외부 호출 경계만 깔끔히 다듬으면 완성!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.46% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 요약 및 전체 상품 추천 기능 구현이라는 주요 변경사항을 명확하게 설명하고 있으며, 이는 추가된 코드 변경사항과 일치합니다.
Linked Issues check ✅ Passed PR이 연결된 이슈 #50의 모든 요구사항을 충족합니다: 요약별 추천(RecommendController generate 메서드, RecommendService generateAndSave)과 전체 상품 추천(getAll 메서드, getRandomByCategory) 기능이 모두 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항은 추천 API 구현과 카테고리 데이터 접근이라는 정의된 범위 내에 있으며, 범위를 벗어난 변경사항은 없습니다.

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


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

@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

🤖 Fix all issues with AI agents
In `@src/main/java/com/ureca/unity/domain/recommend/client/RecommendClient.java`:
- Around line 16-17: Remove the hard-coded fallback by changing the `@Value` on
the baseUrl field in RecommendClient so it references the property without a
default (replace `@Value`("${python.recommend.base-url:http://localhost:8000}")
with `@Value`("${python.recommend.base-url}") ), and add a startup-time validation
in RecommendClient (e.g., in a constructor or `@PostConstruct`) that checks the
baseUrl field and throws a clear IllegalStateException if it's blank; this
ensures the missing python.recommend.base-url property fails application startup
rather than causing generateAndSave() to call a local default at runtime.

In `@src/main/resources/mapper/recommend/RecommendMapper.xml`:
- Around line 38-51: The current selectRandomByCategory SQL uses ORDER BY RAND()
which is a performance bottleneck when getRandomByCategory() loops per category;
replace it with a scalable sampling approach (pick one): 1) add a precomputed
random column (e.g., random_seed or random_key) on product and query using WHERE
category_id=#{categoryId} ORDER BY random_key LIMIT 5, ensuring the column is
updated periodically and indexed; or 2) implement offset sampling by selecting
MAX(product_id) per category and using a calculated random offset (e.g., LIMIT
rand_offset,5) or WHERE product_id >= FLOOR(rand()*max_id) with fallback, or 3)
fetch a small top-N candidate set per category (e.g., LIMIT 100) and perform
application-level random selection in getRandomByCategory(); update the
selectRandomByCategory mapping and corresponding
RecommendService.getRandomByCategory() logic to use the chosen method and add
necessary DB schema/index changes for the precomputed-key option.
🧹 Nitpick comments (4)
src/main/resources/mapper/category/CategoryMapper.xml (1)

7-9: 카테고리 목록 정렬을 명시하면 더 안전합니다.
Line 8에서 ORDER BY가 없어 결과 순서가 DB/플랜에 따라 달라질 수 있습니다. 후속 로직이 순서를 기대한다면 흔들릴 수 있으니 명시적 정렬을 권장합니다.

♻️ 제안 변경
-    SELECT category_id AS categoryId, name FROM category
+    SELECT category_id AS categoryId, name FROM category
+    ORDER BY category_id
src/main/java/com/ureca/unity/domain/recommend/controller/RecommendController.java (1)

45-49: /me 경로는 사용자 전용으로 오해될 수 있음
실제 기능은 전체 랜덤 추천이므로 /random 또는 /all 같은 경로가 더 직관적입니다. REST API 명명 가이드(공식 문서)와 함께 정리해 주세요.

src/main/java/com/ureca/unity/domain/recommend/service/RecommendService.java (2)

33-49: 요청/응답 summaryId 정합성 검증 추가 권장
응답 summaryId가 요청과 다르면 다른 요약의 추천이 저장될 위험이 있습니다. API 스펙(공식 계약 문서)을 기준으로 일치 여부를 확인하고 불일치 시 오류 처리하는 편이 안전합니다.

🔧 제안 수정
     try {
       response = recommendClient.recommend(req);
     } catch (Exception e) {
       throw new CustomException(ErrorCode.FASTAPI_CALL_FAILED);
     }
+
+    if (response != null && response.getSummaryId() != summaryId) {
+      throw new CustomException(ErrorCode.INTERNAL_SERVER_ERROR); // 필요 시 전용 ErrorCode 추가
+    }

112-135: 주석 처리된 메서드는 정리 권장
장기간 주석으로 남으면 유지보수 시 혼동을 줍니다. 필요하면 이슈로 남기고 코드에서는 제거하는 편이 깔끔합니다.

@Chaejy Chaejy merged commit b8b66ed into develop Jan 28, 2026
3 checks passed
@Chaejy Chaejy deleted the URECA-78/Feat/recommend branch January 28, 2026 13:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[URECA-78] Feat: 추천 로직 API

3 participants