docs(content): 추천 오디오 노출 수 정책을 문서화한다

This commit is contained in:
2026-07-12 06:56:31 +09:00
parent 7c4353b55d
commit f570d0312c
2 changed files with 6 additions and 5 deletions

View File

@@ -25,7 +25,7 @@
- `freeAudios`: 최대 15개 랜덤 - `freeAudios`: 최대 15개 랜덤
- `pointAudios`: 최대 15개 랜덤 - `pointAudios`: 최대 15개 랜덤
- `mostCommentedAudios`: 최대 5개 - `mostCommentedAudios`: 최대 5개
- `recommendedAudios`: 최대 10개 - `recommendedAudios`: 최대 20개
- 공개 오디오 공통 조건: `AudioContent.isActive == true`, `AudioContent.duration != null`, `AudioContent.releaseDate != null`, `AudioContent.releaseDate <= now`, 크리에이터 회원 활성. - 공개 오디오 공통 조건: `AudioContent.isActive == true`, `AudioContent.duration != null`, `AudioContent.releaseDate != null`, `AudioContent.releaseDate <= now`, 크리에이터 회원 활성.
- 비회원과 19금 노출 불가 회원은 성인 콘텐츠를 제외하고 `SAFE` 스냅샷을 조회한다. - 비회원과 19금 노출 불가 회원은 성인 콘텐츠를 제외하고 `SAFE` 스냅샷을 조회한다.
- 19금 노출 가능 회원은 성인/비성인 콘텐츠를 모두 포함하는 `ALL` 스냅샷을 조회한다. - 19금 노출 가능 회원은 성인/비성인 콘텐츠를 모두 포함하는 `ALL` 스냅샷을 조회한다.
@@ -399,11 +399,11 @@ interface AudioRecommendationQueryPort {
- Files: - Files:
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/audio/recommendation/adapter/out/persistence/DefaultAudioRecommendationQueryRepository.kt` - Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/audio/recommendation/adapter/out/persistence/DefaultAudioRecommendationQueryRepository.kt`
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/audio/recommendation/adapter/out/persistence/DefaultAudioRecommendationQueryRepositoryTest.kt` - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/audio/recommendation/adapter/out/persistence/DefaultAudioRecommendationQueryRepositoryTest.kt`
- RED: 상세 조회수 45%, 좋아요 25%, 댓글 수 20%, 최신성 10% 점수를 계산하고 `SAFE/ALL` visibility별 최대 10개 후보를 반환하는 테스트를 작성한다. - RED: 상세 조회수 45%, 좋아요 25%, 댓글 수 20%, 최신성 10% 점수를 계산하고 `SAFE/ALL` visibility별 최대 20개 후보를 반환하는 테스트를 작성한다.
- 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.audio.recommendation.adapter.out.persistence.DefaultAudioRecommendationQueryRepositoryTest` - 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.audio.recommendation.adapter.out.persistence.DefaultAudioRecommendationQueryRepositoryTest`
- GREEN: `findRecommendedAudioSnapshots(...)`를 구현한다. 상세 조회수는 `creator_content_view_history` count를 사용하고 `AudioContent.playCount`를 사용하지 않는다. - GREEN: `findRecommendedAudioSnapshots(...)`를 구현한다. 상세 조회수는 `creator_content_view_history` count를 사용하고 `AudioContent.playCount`를 사용하지 않는다.
- REFACTOR: New & Hot과 공유 가능한 조회수/좋아요/댓글 aggregate CTE를 private SQL fragment 또는 QueryDSL helper로 정리한다. - REFACTOR: New & Hot과 공유 가능한 조회수/좋아요/댓글 aggregate CTE를 private SQL fragment 또는 QueryDSL helper로 정리한다.
- 기대 결과: `RECOMMENDED_AUDIO_SAFE/ALL`에 저장할 top 10 후보가 정확한 점수순으로 산출된다. - 기대 결과: `RECOMMENDED_AUDIO_SAFE/ALL`에 저장할 top 20 후보가 정확한 점수순으로 산출된다.
- [x] **Task 4.4: 스냅샷 refresh service와 lazy 보강 구현** - [x] **Task 4.4: 스냅샷 refresh service와 lazy 보강 구현**
- Files: - Files:
@@ -681,3 +681,4 @@ interface AudioRecommendationQueryPort {
- 2026-06-23 리뷰 보정 검증: `./gradlew ktlintCheck`: `BUILD SUCCESSFUL`, `git diff --check`: 출력 없음. - 2026-06-23 리뷰 보정 검증: `./gradlew ktlintCheck`: `BUILD SUCCESSFUL`, `git diff --check`: 출력 없음.
- 2026-06-25 후속 보정: `DefaultAudioRecommendationQueryRepositoryTest.shouldFindNewAndHotSnapshotsWithVisibility`의 score 비교 실패 원인은 repository native SQL의 `timestampdiff(day, c.release_date, :snapshotAt)` 최신성 계산이 DB 날짜 경계 기준에 의존해 `AudioRecommendationScorePolicy`의 24시간 경과 기준 `ChronoUnit.DAYS` 계산과 어긋날 수 있는 점으로 확인했다. `DefaultAudioRecommendationQueryRepository`의 New & Hot/추천 오디오 공개일 최신성 계산을 `floor(timestampdiff(hour, c.release_date, :snapshotAt) / 24)`로 변경해 Kotlin 정책과 일치시켰고, `SAFE` 성인 콘텐츠 제외 조건은 기존 `(:includeAdult = true or c.is_adult = false)` 구현이 올바른 것으로 확인했다. 검증은 `./gradlew test --rerun-tasks --tests 'kr.co.vividnext.sodalive.v2.content.recommendation.adapter.out.persistence.DefaultAudioRecommendationQueryRepositoryTest.shouldFindNewAndHotSnapshotsWithVisibility'`, `./gradlew test --rerun-tasks --tests 'kr.co.vividnext.sodalive.v2.content.recommendation.domain.AudioRecommendationScorePolicyTest'`, `./gradlew ktlintCheck` 모두 `BUILD SUCCESSFUL`로 완료했다. - 2026-06-25 후속 보정: `DefaultAudioRecommendationQueryRepositoryTest.shouldFindNewAndHotSnapshotsWithVisibility`의 score 비교 실패 원인은 repository native SQL의 `timestampdiff(day, c.release_date, :snapshotAt)` 최신성 계산이 DB 날짜 경계 기준에 의존해 `AudioRecommendationScorePolicy`의 24시간 경과 기준 `ChronoUnit.DAYS` 계산과 어긋날 수 있는 점으로 확인했다. `DefaultAudioRecommendationQueryRepository`의 New & Hot/추천 오디오 공개일 최신성 계산을 `floor(timestampdiff(hour, c.release_date, :snapshotAt) / 24)`로 변경해 Kotlin 정책과 일치시켰고, `SAFE` 성인 콘텐츠 제외 조건은 기존 `(:includeAdult = true or c.is_adult = false)` 구현이 올바른 것으로 확인했다. 검증은 `./gradlew test --rerun-tasks --tests 'kr.co.vividnext.sodalive.v2.content.recommendation.adapter.out.persistence.DefaultAudioRecommendationQueryRepositoryTest.shouldFindNewAndHotSnapshotsWithVisibility'`, `./gradlew test --rerun-tasks --tests 'kr.co.vividnext.sodalive.v2.content.recommendation.domain.AudioRecommendationScorePolicyTest'`, `./gradlew ktlintCheck` 모두 `BUILD SUCCESSFUL`로 완료했다.
- 2026-07-12 후속 정책 변경: 콘텐츠 추천 탭의 `latestAudios`, `freeAudios`, `pointAudios` 기본 노출 수를 각각 15개로 확정했다. PRD/plan-task의 섹션별 기본 노출 수와 관련 task 설명을 15개 정책으로 갱신했고, service 테스트는 `AudioRecommendationQueryService` 상수 기준으로 limit 전달을 검증하도록 정리했다. 검증은 `./gradlew --no-daemon test --tests kr.co.vividnext.sodalive.v2.content.recommendation.application.AudioRecommendationQueryServiceTest`, `./gradlew --no-daemon ktlintCheck`, `./gradlew --no-daemon tasks --all` 모두 `BUILD SUCCESSFUL`로 완료했다. 단, `tasks --all`은 일반 sandbox에서 `~/.gradle` wrapper lock 파일 접근 제한으로 실패해 승인 후 재실행했다. - 2026-07-12 후속 정책 변경: 콘텐츠 추천 탭의 `latestAudios`, `freeAudios`, `pointAudios` 기본 노출 수를 각각 15개로 확정했다. PRD/plan-task의 섹션별 기본 노출 수와 관련 task 설명을 15개 정책으로 갱신했고, service 테스트는 `AudioRecommendationQueryService` 상수 기준으로 limit 전달을 검증하도록 정리했다. 검증은 `./gradlew --no-daemon test --tests kr.co.vividnext.sodalive.v2.content.recommendation.application.AudioRecommendationQueryServiceTest`, `./gradlew --no-daemon ktlintCheck`, `./gradlew --no-daemon tasks --all` 모두 `BUILD SUCCESSFUL`로 완료했다. 단, `tasks --all`은 일반 sandbox에서 `~/.gradle` wrapper lock 파일 접근 제한으로 실패해 승인 후 재실행했다.
- 2026-07-12 후속 정책 변경: 콘텐츠 추천 탭의 `recommendedAudios``RECOMMENDED_AUDIO_SAFE/ALL` 스냅샷 후보 저장 수를 최대 20개로 확정했다. PRD의 섹션별 기본 노출 수와 추천 오디오 요구사항, plan-task의 개요와 Task 4.3 후보 산정 설명을 20개 정책으로 갱신했다. 해당 개수는 정책값으로 이후 변경될 수 있으므로 테스트는 `assertEquals(20, RECOMMENDED_AUDIO_LIMIT)`처럼 숫자 자체를 고정하지 않고, 기존 service/refresh 테스트에서 `RECOMMENDED_AUDIO_LIMIT`가 snapshot 조회와 후보 산정 port 호출에 전달되는 흐름을 검증하는 방식으로 유지했다. 검증은 이전 개수 정책 문구 잔존 검색 결과 없음, `./gradlew --no-daemon test --tests kr.co.vividnext.sodalive.v2.content.recommendation.application.AudioRecommendationQueryServiceTest --tests kr.co.vividnext.sodalive.v2.content.recommendation.application.AudioRecommendationSnapshotRefreshServiceTest` `BUILD SUCCESSFUL`, `git diff --check` 출력 없음, `./gradlew --no-daemon tasks --all` `BUILD SUCCESSFUL`로 완료했다. 단, `tasks --all`은 일반 sandbox에서 `~/.gradle` wrapper lock 파일 접근 제한으로 실패해 승인 후 재실행했다.

View File

@@ -70,7 +70,7 @@
- `freeAudios`: 최대 15개 랜덤 - `freeAudios`: 최대 15개 랜덤
- `pointAudios`: 최대 15개 랜덤 - `pointAudios`: 최대 15개 랜덤
- `mostCommentedAudios`: 최대 5개 - `mostCommentedAudios`: 최대 5개
- `recommendedAudios`: 최대 10개 - `recommendedAudios`: 최대 20개
- 특정 섹션 데이터가 부족하면 가능한 개수만 내려주고 전체 API는 성공 처리한다. - 특정 섹션 데이터가 부족하면 가능한 개수만 내려주고 전체 API는 성공 처리한다.
- 무료/포인트/추천 오디오 섹션 사이에는 같은 오디오가 중복 노출될 수 있다. - 무료/포인트/추천 오디오 섹션 사이에는 같은 오디오가 중복 노출될 수 있다.
@@ -155,7 +155,7 @@
### Feature I. 추천 오디오 ### Feature I. 추천 오디오
#### Requirements #### Requirements
- 최대 10개를 표시한다. - 최대 20개를 표시한다.
- KST 매일 00:00에 전날 23:59:59 KST까지의 데이터를 반영해 스냅샷을 갱신한다. - KST 매일 00:00에 전날 23:59:59 KST까지의 데이터를 반영해 스냅샷을 갱신한다.
- 추천 점수는 `조회수 45% + 좋아요 25% + 댓글 수 20% + 최신성 10%`로 계산한다. - 추천 점수는 `조회수 45% + 좋아요 25% + 댓글 수 20% + 최신성 10%`로 계산한다.
- 조회수는 `creator_content_view_history`의 상세 페이지 조회 이력을 기준으로 스냅샷 집계 기간 내 `content_id`별 count를 사용한다. - 조회수는 `creator_content_view_history`의 상세 페이지 조회 이력을 기준으로 스냅샷 집계 기간 내 `content_id`별 count를 사용한다.