Skip to content

feat: 채널 추천 결과 저장 API 구현 - #133

Open
1117mg wants to merge 5 commits into
devfrom
feat/128-save-recommendation-api
Open

feat: 채널 추천 결과 저장 API 구현#133
1117mg wants to merge 5 commits into
devfrom
feat/128-save-recommendation-api

Conversation

@1117mg

@1117mg 1117mg commented Aug 7, 2026

Copy link
Copy Markdown
Member

🔗 관련 이슈

Closes #128

📝 작업 내용

  • 채널 추천 결과 저장 API 추가: 추천을 다시 계산해 그 시점 값을 channel_recommendations에 스냅샷으로 저장 (순위 1~8)
  • 본인이 제출한 온보딩만 저장할 수 있고, 그 외에는 존재 여부를 노출하지 않도록 404 응답
  • 같은 온보딩으로 다시 저장하면 이전 행을 덮어씌움: (onboarding_id, channel_id) 유니크 제약
  • 추천 계산 결과를 스냅샷 타입으로 확장
  • 공개 경로를 메서드 단위로 분리: PUBLIC_GET_PATHS 추가
  • OpenAPI 응답 스키마 정합성 수정: 요청 스레드 - 성공 응답 래퍼의 data, 오류 응답 래퍼의 error를 required로 노출 / RecommendationItemResponse의 선택 필드가 null로 내려가던 것을 생략하도록 함
  • 마이그레이션: V15__create_channel_recommendations.sql / V16_add_channel_recommendation_rank_unique.sql 추가

✅ 체크리스트

  • 로컬에서 빌드 및 테스트가 통과했습니다.
  • 컨벤션(브랜치/커밋 메시지)을 준수했습니다.
  • 관련 문서를 수정했습니다. (필요한 경우)

💬 리뷰 참고 사항

RecommendationItemResponse@JsonInclude(NON_NULL)을 적용해 값이 없는 필드가 null 대신 키 자체가 빠집니다. 원래 스펙과 실제 응답이 어긋나 있는 부분도 수정했습니다.

Summary by CodeRabbit

  • 새 기능
    • 추천 결과를 온보딩별로 저장하고 다시 조회할 수 있습니다.
    • 저장된 추천 결과에 순위, 매칭 근거, 가격 및 예상 성과 정보가 포함됩니다.
    • 추천 조회는 인증 없이 이용할 수 있으며, 추천 저장은 인증이 필요합니다.
  • 개선
    • OpenAPI 문서에서 성공·오류 응답 필수 항목이 더욱 정확하게 표시됩니다.
    • 추천 응답에서 값이 없는 선택 항목은 표시되지 않습니다.
  • 버그 수정
    • 추천 결과 저장 시 중복 제출과 잘못된 온보딩 접근을 적절한 오류로 처리합니다.

@1117mg 1117mg self-assigned this Aug 7, 2026
@1117mg 1117mg added the feat label Aug 7, 2026
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@1117mg, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 21 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 25216833-b9be-4e08-930b-52a513391cc9

📥 Commits

Reviewing files that changed from the base of the PR and between 2ec534c and 51bfabb.

📒 Files selected for processing (6)
  • src/main/java/chaeso/zip/server/onboarding/domain/repository/OnboardingRepository.java
  • src/main/java/chaeso/zip/server/recommendation/application/RecommendationServiceImpl.java
  • src/main/java/chaeso/zip/server/recommendation/domain/entity/ChannelRecommendation.java
  • src/main/resources/db/migration/V16__add_channel_recommendation_rank_unique.sql
  • src/test/java/chaeso/zip/server/recommendation/application/RecommendationServiceImplTest.java
  • src/test/java/chaeso/zip/server/recommendation/domain/ChannelRecommendationRepositoryTest.java
📝 Walkthrough

Walkthrough

추천 계산 결과를 RecommendationSnapshot으로 통합하고, 온보딩별 채널 추천 저장 API를 추가했다. 저장 데이터베이스 구조, 인증 정책, OpenAPI 응답 계약과 관련 테스트도 갱신했다.

Changes

추천 결과 저장

Layer / File(s) Summary
추천 스냅샷과 영속성
src/main/java/chaeso/zip/server/recommendation/domain/..., src/main/resources/db/migration/..., src/test/java/chaeso/zip/server/recommendation/domain/...
추천 스냅샷과 ChannelRecommendation 엔티티를 추가했다. 온보딩별 조회·삭제 저장소와 channel_recommendations 테이블을 정의했다.
추천 계산과 저장 오케스트레이션
src/main/java/chaeso/zip/server/recommendation/application/..., src/test/java/chaeso/zip/server/recommendation/application/...
추천 계산 결과를 스냅샷으로 반환하고, 기존 데이터를 삭제한 뒤 순위별로 저장한다. 온보딩 소유권과 동시 저장 충돌을 처리한다.
추천 저장 API 계약과 엔드포인트
src/main/java/chaeso/zip/server/recommendation/presentation/..., src/test/java/chaeso/zip/server/recommendation/presentation/...
인증된 사용자가 onboardingId를 사용해 추천 결과를 저장하도록 POST API와 요청·응답 DTO를 추가했다. 성공, 검증 오류, 온보딩 오류, 동시성 오류를 문서화하고 테스트했다.
HTTP 메서드 보안과 OpenAPI 계약
src/main/java/chaeso/zip/server/auth/..., src/main/java/chaeso/zip/server/common/config/..., src/test/java/chaeso/zip/server/auth/..., src/test/java/chaeso/zip/server/docs/...
GET /api/v1/recommendations만 공개하고 동일 경로의 POST는 인증을 요구하도록 변경했다. 응답 래퍼의 dataerror required 규칙을 OpenAPI에 반영했다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant RecommendationController
  participant RecommendationServiceImpl
  participant ChannelRecommendationRepository
  participant Database
  Client->>RecommendationController: POST /api/v1/recommendations
  RecommendationController->>RecommendationServiceImpl: userId, onboardingId 전달
  RecommendationServiceImpl->>RecommendationServiceImpl: 추천 계산 및 스냅샷 생성
  RecommendationServiceImpl->>ChannelRecommendationRepository: 기존 추천 삭제 및 새 추천 저장
  ChannelRecommendationRepository->>Database: channel_recommendations 저장
  Database-->>ChannelRecommendationRepository: 저장 결과 반환
  RecommendationServiceImpl-->>RecommendationController: SavedRecommendationResponse 반환
  RecommendationController-->>Client: 201 CREATED
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.17% 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
Linked Issues check ✅ Passed POST /recommendations, 기존 추천 계산 재사용, 온보딩별 추천 저장, 저장 결과 응답 요구사항을 구현했습니다. [#128]
Out of Scope Changes check ✅ Passed 보안 경로 분리, OpenAPI required 보정, null 필드 생략은 PR 목표에 명시된 보완 작업이며 관련 테스트와 마이그레이션도 포함됩니다.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 채널 추천 결과 저장 API 구현이라는 변경의 핵심 내용을 명확하고 간결하게 설명합니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/128-save-recommendation-api

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.

@codecov-commenter

codecov-commenter commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.22034% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.83%. Comparing base (051ee55) to head (51bfabb).

Files with missing lines Patch % Lines
...mendation/domain/entity/ChannelRecommendation.java 78.57% 3 Missing and 3 partials ⚠️
...ndation/application/RecommendationServiceImpl.java 96.77% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##                dev     #133      +/-   ##
============================================
+ Coverage     88.44%   88.83%   +0.39%     
- Complexity      469      491      +22     
============================================
  Files           105      107       +2     
  Lines          1437     1541     +104     
  Branches        111      119       +8     
============================================
+ Hits           1271     1369      +98     
- Misses          122      125       +3     
- Partials         44       47       +3     
Files with missing lines Coverage Δ
...r/auth/infrastructure/security/SecurityConfig.java 100.00% <100.00%> (ø)
.../recommendation/domain/RecommendationSnapshot.java 100.00% <100.00%> (ø)
...ndation/presentation/RecommendationController.java 100.00% <100.00%> (ø)
...ndation/application/RecommendationServiceImpl.java 95.49% <96.77%> (+3.43%) ⬆️
...mendation/domain/entity/ChannelRecommendation.java 78.57% <78.57%> (ø)
Components Coverage Δ
auth 93.65% <100.00%> (+0.02%) ⬆️
channel 54.86% <ø> (ø)
onboarding 87.37% <ø> (ø)
estimation 93.08% <ø> (ø)
performance 88.00% <ø> (ø)
simulation 93.08% <ø> (ø)
user 89.65% <ø> (ø)

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/chaeso/zip/server/recommendation/application/RecommendationServiceImpl.java`:
- Around line 76-89: Serialize the onboarding save flow in save by acquiring a
pessimistic or equivalent onboarding-scoped lock before calculate, so
findOwnedOnboarding, deletion, calculation, and persistence cannot run
concurrently for the same onboarding. Add a unique constraint on (onboarding_id,
rank) to protect against conflicting saves, and add an integration test covering
concurrent creation of different channel sets for one onboarding on PostgreSQL.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 07bd6f10-b642-40fe-97d5-0e5c3a8ecb07

📥 Commits

Reviewing files that changed from the base of the PR and between 051ee55 and 2ec534c.

📒 Files selected for processing (20)
  • src/main/java/chaeso/zip/server/auth/infrastructure/security/SecurityConfig.java
  • src/main/java/chaeso/zip/server/common/config/ResponseWrapperSchemaCustomizer.java
  • src/main/java/chaeso/zip/server/recommendation/application/RecommendationService.java
  • src/main/java/chaeso/zip/server/recommendation/application/RecommendationServiceImpl.java
  • src/main/java/chaeso/zip/server/recommendation/application/dto/RecommendationItemResponse.java
  • src/main/java/chaeso/zip/server/recommendation/application/dto/SavedRecommendationResponse.java
  • src/main/java/chaeso/zip/server/recommendation/domain/RecommendationSnapshot.java
  • src/main/java/chaeso/zip/server/recommendation/domain/entity/ChannelRecommendation.java
  • src/main/java/chaeso/zip/server/recommendation/domain/repository/ChannelRecommendationRepository.java
  • src/main/java/chaeso/zip/server/recommendation/presentation/RecommendationApiDocs.java
  • src/main/java/chaeso/zip/server/recommendation/presentation/RecommendationController.java
  • src/main/java/chaeso/zip/server/recommendation/presentation/dto/SaveRecommendationRequest.java
  • src/main/resources/db/migration/V15__create_channel_recommendations.sql
  • src/test/java/chaeso/zip/server/auth/infrastructure/security/SecurityConfigIntegrationTest.java
  • src/test/java/chaeso/zip/server/docs/OpenApiContractTest.java
  • src/test/java/chaeso/zip/server/recommendation/application/RecommendationServiceImplTest.java
  • src/test/java/chaeso/zip/server/recommendation/domain/ChannelRecommendationRepositoryTest.java
  • src/test/java/chaeso/zip/server/recommendation/presentation/RecommendationControllerTest.java
  • src/test/java/chaeso/zip/server/support/ChannelCatalogFixture.java
  • src/test/java/chaeso/zip/server/support/OnboardingFixture.java

@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown

코드 리뷰 결과 (자동 리뷰)

계층 아키텍처(ArchUnit 규칙), 정확성, JPA/영속성, Flyway 마이그레이션, 보안(인가/소유권/동시성), API 계약, 테스트 커버리지를 확인했습니다.

반드시 수정해야 할 항목
없습니다. Blocker/Major 급 문제는 발견하지 못했습니다.

🟡 Minor

  • ChannelRecommendation 엔티티가 BaseEntity/BaseTimeEntity를 상속하지 않고 id/createdAt 로직을 직접 재구현합니다. 저장소 컨벤션(다른 엔티티는 모두 BaseEntity 상속)과 다릅니다. 인라인 코멘트에 상세 내용을 남겼습니다.

확인한 주요 포인트 (문제 없음)

  • 계층 의존 방향(presentation → application → domain), *Request/*Response/*Repository/@Entity 패키지 위치 모두 ArchUnit 규칙 준수
  • 소유권 검증(findOwnedOnboarding)이 타인의 온보딩과 존재하지 않는 온보딩을 동일하게 404(ONB-007)로 응답해 존재 여부를 노출하지 않음
  • delete 후 saveAll+flush로 유니크 제약 위반을 즉시 감지해 동시 저장 요청을 409(ONB-006)로 안전하게 처리 (ChannelRecommendationRepositoryTest.deleteThenInsertInSameTransaction로 검증됨)
  • N+1 없이 채널/상품/단가를 배치 조회 후 메모리에서 매칭
  • Flyway 마이그레이션은 신규 추가만 있으며 테이블/컬럼명이 기존 컨벤션과 일치, 인덱스/유니크 제약 적절
  • SecurityConfig의 PUBLIC_GET_PATHS 도입으로 GET만 공개하고 POST는 인증 요구 (SecurityConfigIntegrationTest로 검증)
  • RecommendationItemResponse의 @JsonInclude(NON_NULL)과 OpenAPI 계약 정합성(ResponseWrapperSchemaCustomizer, OpenApiContractTest)이 일치

@1117mg
1117mg requested a review from SinnoLn August 7, 2026 10:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants