From 6fa7044220aac7b9f636c24979331513ec69aebe Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 12 Jun 2026 17:23:16 +0900 Subject: [PATCH] =?UTF-8?q?docs(recommendation):=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A4=84=20=EC=84=B1=EC=9D=B8=20=EB=85=B8=EC=B6=9C=20=EC=A0=95?= =?UTF-8?q?=EC=B1=85=EC=9D=84=20=EB=B3=B4=EA=B0=95=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/20260612_크리에이터_채널_홈_API/plan-task.md | 13 ++++++++++--- docs/20260612_크리에이터_채널_홈_API/prd.md | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/20260612_크리에이터_채널_홈_API/plan-task.md b/docs/20260612_크리에이터_채널_홈_API/plan-task.md index cd96ba95..792f1e57 100644 --- a/docs/20260612_크리에이터_채널_홈_API/plan-task.md +++ b/docs/20260612_크리에이터_채널_홈_API/plan-task.md @@ -18,6 +18,7 @@ - 공용 활동 타입 enum: 기존 `RecommendedActivityType`을 `CreatorActivityType`으로 이름 변경하고 `kr.co.vividnext.sodalive.v2.common.domain` 하위로 이동한다. - 스케줄 타입: `LIVE`, `AUDIO`만 사용한다. 오디오 콘텐츠가 `다시보기` 카테고리여도 `AUDIO`로 내려준다. - 스케줄 정렬/개수: 현재 시각 이후 예약 중 오늘 날짜와 가장 근접한 3개, 예약 시각 오름차순, 같은 예약 시각이면 라이브 먼저 표시한다. +- 스케줄 성인 노출 정책: repository query에서 조회자의 성인 노출 정책을 먼저 반영하고, service 최종 조합에서도 내부 스케줄 후보의 `isAdult`로 한 번 더 보정한다. 공개 스케줄 응답에는 `isAdult`를 노출하지 않는다. - 신규 오디오 콘텐츠와 오디오 목록은 중복 노출하지 않는다. `latestAudioContent`로 내려간 가장 최신 콘텐츠를 오디오 목록에서 제외한다. - 채널 후원 홈 섹션은 기존 채널 후원 목록과 동일하게 이번 달 기준 최신순 8개를 내려준다. - 오리지널 시리즈 여부는 `Series.isOriginal == true`로 판단한다. @@ -208,6 +209,8 @@ data class CreatorChannelSnsResponse( ) ``` +> 스케줄 성인 여부는 service 최종 보정에 필요한 내부 domain/record 필드로만 유지하고, 위 공개 응답 DTO에는 포함하지 않는다. + --- ### Phase 1: 공용 활동 타입 정리 @@ -256,11 +259,13 @@ data class CreatorChannelSnsResponse( - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/domain/CreatorChannelHomeQueryPolicyTest.kt` - RED: 다음 정책 테스트를 작성한다. - 스케줄은 예약 시각 오름차순 최대 3개만 남긴다. + - 스케줄은 현재 시각 이후 예약만 남긴다. - 같은 예약 시각이면 `CreatorActivityType.LIVE`가 `AUDIO`보다 먼저 온다. + - 조회자의 성인 노출 정책이 false이면 성인 스케줄을 제외한다. - 오디오 목록에서는 `latestAudioContentId`와 같은 콘텐츠를 제외한다. - 오디오 콘텐츠의 첫 공개 콘텐츠 여부는 공개 시각 오름차순, 동일 시각이면 id 오름차순으로 판정한다. - 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.domain.CreatorChannelHomeQueryPolicyTest` - - GREEN: `limitSchedules`, `excludeLatestAudioContent`, `markFirstAudioContent` 같은 순수 함수를 구현한다. + - GREEN: `limitSchedules(schedules, now, canViewAdultContent)`, `excludeLatestAudioContent`, `markFirstAudioContent` 같은 순수 함수를 구현한다. - 통과 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.domain.CreatorChannelHomeQueryPolicyTest` - REFACTOR: DB 정렬과 application 보정이 중복되더라도 최종 응답 전 정책 함수가 한 번 더 보장하도록 service에서 재사용할 수 있게 둔다. - 기대 결과: 날짜/중복/첫 콘텐츠 정책이 DB fixture 없이 빠르게 검증된다. @@ -308,8 +313,9 @@ data class CreatorChannelSnsResponse( - 다시듣기 테마 예약 오디오도 스케줄 타입은 `AUDIO`다. - 같은 예약 시각이면 라이브가 오디오보다 먼저 온다. - 성인 라이브/오디오는 조회자의 성인 노출 정책이 false이면 제외된다. + - service 최종 보정을 위해 스케줄 후보 record에는 `isAdult`가 포함된다. - 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.adapter.out.persistence.DefaultCreatorChannelHomeQueryRepositoryTest` - - GREEN: `LiveRoom`, `AudioContent`, `AudioContentTheme` 조회를 구현하고 `CreatorActivityType.LIVE`/`AUDIO`를 record에 담는다. + - GREEN: `LiveRoom`, `AudioContent`, `AudioContentTheme` 조회를 구현하고 `CreatorActivityType.LIVE`/`AUDIO`와 `isAdult`를 record에 담는다. - 통과 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.adapter.out.persistence.DefaultCreatorChannelHomeQueryRepositoryTest` - REFACTOR: 최종 3개 제한은 repository query와 `CreatorChannelHomeQueryPolicy.limitSchedules` 양쪽 중복 방어를 허용하되, service에서 최종 보정한다. - 기대 결과: 스케줄 섹션이 PRD의 타입/정렬/개수 정책을 만족한다. @@ -374,7 +380,7 @@ data class CreatorChannelSnsResponse( - Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/application/CreatorChannelHomeQueryService.kt` - Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/domain/CreatorChannelHomeQueryPolicy.kt` - Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/application/CreatorChannelHomeQueryServiceTest.kt` - - RED: fake port를 사용해 모든 섹션 record를 넣고, service가 `CreatorChannelHome`으로 전체 섹션을 조립하는 테스트를 작성한다. `latestAudioContent`와 오디오 목록 중복 제거, 스케줄 최대 3개 제한, 같은 시각 라이브 우선 정렬도 service 테스트에서 검증한다. + - RED: fake port를 사용해 모든 섹션 record를 넣고, service가 `CreatorChannelHome`으로 전체 섹션을 조립하는 테스트를 작성한다. `latestAudioContent`와 오디오 목록 중복 제거, 스케줄 최대 3개 제한, 같은 시각 라이브 우선 정렬, 성인 스케줄 최종 제외도 service 테스트에서 검증한다. - 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.application.CreatorChannelHomeQueryServiceTest` - GREEN: service에서 creator 검증, 성인 노출 정책 입력, port 호출, policy 적용, URL 변환에 필요한 host 전달을 구현한다. - 통과 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.application.CreatorChannelHomeQueryServiceTest` @@ -508,3 +514,4 @@ data class CreatorChannelSnsResponse( - 2026-06-12: Phase 2 정리 확인 - `./gradlew ktlintCheck` 통과. - 2026-06-12: Phase 2 리뷰 보정 RED 확인 - 오디오 콘텐츠 `isAdult`와 스케줄 현재시각 필터 테스트 추가 후 `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.application.CreatorChannelHomeQueryServiceTest --tests kr.co.vividnext.sodalive.v2.creator.channel.domain.CreatorChannelHomeQueryPolicyTest` 실행 시 `Unresolved reference: isAdult`, `Too many arguments for limitSchedules` 컴파일 오류를 확인했다. - 2026-06-12: Phase 2 리뷰 보정 GREEN 확인 - `CreatorChannelAudioContent`/`CreatorChannelAudioContentResponse`에 `isAdult`를 추가하고 `CreatorChannelHomeQueryPolicy.limitSchedules(schedules, now)`가 `scheduledAt > now`만 남기도록 수정한 뒤 `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.application.CreatorChannelHomeQueryServiceTest --tests kr.co.vividnext.sodalive.v2.creator.channel.domain.CreatorChannelHomeQueryPolicyTest` 통과. +- 2026-06-12: 스케줄 성인 노출 정책 보강 - PRD와 plan-task에 repository query 1차 필터 + service 최종 보정 방식을 명시하고, 내부 `CreatorChannelSchedule.isAdult`와 `CreatorChannelHomeQueryPolicy.limitSchedules(schedules, now, canViewAdultContent)`를 반영했다. `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.application.CreatorChannelHomeQueryServiceTest --tests kr.co.vividnext.sodalive.v2.creator.channel.domain.CreatorChannelHomeQueryPolicyTest`, `./gradlew ktlintCheck` 통과. diff --git a/docs/20260612_크리에이터_채널_홈_API/prd.md b/docs/20260612_크리에이터_채널_홈_API/prd.md index c80cd20b..ebfa5c8a 100644 --- a/docs/20260612_크리에이터_채널_홈_API/prd.md +++ b/docs/20260612_크리에이터_채널_홈_API/prd.md @@ -158,6 +158,9 @@ - 오디오 콘텐츠가 `다시보기` 카테고리여도 스케줄 타입은 `LIVE_REPLAY`가 아니라 `AUDIO`로 내려준다. - 대상 ID는 타입이 `LIVE`이면 라이브 ID, `AUDIO`이면 오디오 콘텐츠 ID를 의미한다. - 정렬은 예약 날짜/시간 오름차순이다. 같은 예약 시간이면 라이브를 오디오보다 먼저 표시한다. +- 성인 예약 라이브/오디오는 조회자의 성인 노출 정책이 false이면 노출하지 않는다. +- 성인 노출 정책은 DB 조회 조건에 먼저 반영하고, 라이브/오디오 스케줄 후보를 service에서 합친 뒤에도 최종 응답 전 한 번 더 보정한다. +- service 최종 보정에 필요한 성인 여부는 내부 스케줄 후보 record/domain model에만 포함하고, 공개 스케줄 응답 필드에는 포함하지 않는다. #### Edge Cases - 예약 데이터가 없으면 빈 배열을 내려준다.