Files

17 KiB

추천 화면 AI 캐릭터 및 이벤트 배너 노출 정책 PRD

문서 정보

항목 내용
문서 상태 구현 및 검증 완료 · 운영 배포 제외 · 전체 빌드는 UTC 환경 기준
작성일 2026-10-31
최종 수정일 2026-10-31
대상 제품 홈 추천 및 콘텐츠 추천 API의 AI 캐릭터·이벤트 배너 노출
작성자·결정권자 사용자 결정 반영
관련 API Contract 별도 문서 없음. 기존 응답 계약 유지
관련 구현 계획 docs/20261031_추천화면AI캐릭터및이벤트배너노출정책/plan-task.md
관련 review 없음

요구사항 상태

사용자 정책은 확정이며 기술 범위 선택은 별도 Decision Log에 분리했다. 사용자 승인으로 오인하지 않는다. 미결 정책 질문 없음, 모호성 0.00.

1. 개요

홈 추천 aggregate 응답에서 AI 캐릭터 목록은 모든 조회자에게 빈 배열로 제공한다. 홈 및 오디오 추천의 기존 이벤트 배너는 배너 자체의 현재 노출 조건을 유지하면서 연결된 이벤트 상세 조회와 같은 대상자 정책을 만족할 때만 반환한다. 응답 구조와 비이벤트 추천 콘텐츠는 변경하지 않는다.

2. 문제 정의

  • 홈 추천 aggregate에 AI 캐릭터 목록이 포함되어 있어 해당 노출을 중단해도 응답 계약은 유지할 필요가 있다.
  • 이벤트 배너는 배너 자체 조건만으로는 연결된 이벤트의 활성 상태, 기간, 국가 기반 언어, 인증 성인 대상 정책을 모두 보장하지 못한다.
  • 이벤트 상세 화면에서 볼 수 없는 이벤트 배너가 추천 응답에 노출되지 않도록 두 조회 경로의 정책을 일치시킨다.

3. 목표

  • 홈 aggregate 응답에서 aiCharacters를 항상 []로 반환한다.
  • EVENT 유형이며 event FK가 있는 추천 배너는 기존 이벤트 상세 조회의 노출 정책을 만족할 때만 반환한다.
  • 기존 endpoint, response schema, 다른 배너 조건, subBanner 응답을 보존한다.

4. 비목표

  • AudioRecommendationsResponse에 aiCharacters 필드를 추가하지 않는다.
  • 전용 AI 캐릭터 endpoint의 정책이나 구현을 바꾸지 않는다.
  • AI 추천 snapshot, scheduler, 저장 데이터를 바꾸지 않는다.
  • LINK 유형 URL 파싱, 일반 배너, subBanners(event FK가 없는 데이터 포함)의 정책은 바꾸지 않는다.
  • 이벤트 상세 endpoint의 응답, 오류 정책, 보안 설정을 바꾸지 않는다.
  • legacy 배너 endpoint, DB schema, dependency, 전역 timezone 또는 formatter를 바꾸지 않는다.

5. 사용자와 권한

조회자 인증 판정 이벤트의 isAdult 노출 대상
익명 미인증 null, false
로그인했으나 AuthRepository 인증 기록 없음 미인증 null, false
인증 기록이 있는 회원 인증 null, true
ADMIN AuthRepository 인증 여부와 무관 이벤트 대상자 조건만 우회

인증 여부는 AuthRepository.getAuthIdByMemberId(memberId) != null로 판정한다. canViewAdultContent는 preference·country·auth 규칙에 따라 계산된 성인 노출 가능 여부이며, raw preference나 이벤트 인증 여부와 동일하지 않다. 두 판정은 다를 수 있으므로 이벤트의 성인 대상 정책은 반드시 인증 기록으로 판단한다.

6. 핵심 동작

  1. GET /api/v2/home/recommendations를 조회하면 기존 응답에 aiCharacters: []가 포함된다.
  2. 두 추천 endpoint에서 기존 배너 조건으로 후보 배너를 조회한다.
  3. EVENT 배너의 연결 이벤트가 존재하고 이벤트 상세 노출 정책을 통과하면 배너 후보를 유지한다.
  4. 정책에 맞지 않거나 연결 이벤트가 없는 EVENT 배너는 해당 배너만 제외한다. 응답의 나머지 영역은 정상 반환한다.
  5. 적격 배너 수가 0이면 banners: []를 반환한다.

7. 기능 요구사항

ID 상태 요구사항 수용 기준 계약/Goal 연결
REC-AI-001 확정 홈 aggregate에서 AI 캐릭터를 제공하지 않고 기존 목록 필드를 빈 배열로 유지한다. 익명, 일반 회원, ADMIN 요청 모두 data.aiCharacters가 존재하고 JSON 배열 []이며 null이 아니고 생략되지 않는다. aggregate 경로에서 AI 추천 query/fallback이 호출되지 않는다. GET /api/v2/home/recommendations, P1-T1
REC-AI-002 확정 오디오 추천 응답의 현재 schema를 유지한다. AudioRecommendationsResponse에 aiCharacters가 없으며 이 변경으로 필드가 추가되지 않는다. GET /api/v2/audio/recommendations, P1-T3, P1-GATE
REC-EVT-001 확정 두 추천 endpoint의 EVENT 배너는 연결 이벤트가 존재하고 활성일 때만 노출한다. 연결 이벤트 부재 또는 비활성 이벤트이면 배너 전체를 제외한다. 다른 배너와 API 처리는 계속된다. P1-T2, P1-T3
REC-EVT-002 확정 이벤트 시작·종료 시각 경계는 양끝을 포함한다. 비교 의미는 상세 조회와 동일하다. startDate == now 및 endDate == now는 적격, now가 범위 밖이면 제외한다. 검증은 시각 정밀도를 포함해 결정적으로 수행한다. P1-T2, P1-T3
REC-EVT-003 확정 연결 이벤트의 언어 적격성은 회원의 해석 국가로 판정한다. resolveCountryCode(member) == JP이면 연결 이벤트 Lang.JA, 그 외는 Lang.KO여야 한다. request LangContext가 결과를 덮어쓰지 않는다. P1-T2, P1-T3
REC-EVT-004 확정 이벤트 성인 대상은 인증 기록 여부로 판정한다. isAdult == null은 모두, true는 인증 회원만, false는 미인증 조회자만 적격이다. 익명은 미인증이다. P1-T2, P1-T3
REC-EVT-005 확정 ADMIN은 이벤트 대상자 제한만 우회한다. ADMIN도 이벤트 존재, 활성, 시간 범위, 국가 기반 언어 조건을 통과해야 한다. ADMIN 우회가 배너 자체 조건을 우회하지 않는다. P1-T2, P1-T3
REC-EVT-006 확정 이벤트 정책은 기존 배너 정책과 교집합으로 적용한다. banner-own 활성, tab, language, reservation, 성인 노출 가능 여부, creator-block 조건이 모두 기존대로 유지된다. 이벤트가 부적격이면 배너를 제외하고 기존 정렬 후 limit 20 적용 순서 및 동순위 random tie 동작을 유지한다. P1-T2, P1-T3
REC-EVT-007 확정 이벤트 정책 필터링은 개별 후보에 적용한다. 부적격 EVENT 배너 하나 때문에 전체 API가 실패하지 않는다. 모든 후보 제외 시 빈 배열을 반환한다. LINK 및 subBanner 결과는 이 필터로 변경되지 않는다. P1-T2, P1-T3

8. API 계약

요구사항 Method Path 응답 계약
REC-AI-001, REC-EVT-001~007 GET /api/v2/home/recommendations 기존 aggregate schema 유지. aiCharacters는 []; banners에는 기존 조건과 이벤트 정책을 모두 통과한 배너만 포함
REC-AI-002, REC-EVT-001~007 GET /api/v2/audio/recommendations 기존 AudioRecommendationsResponse schema 유지. aiCharacters 필드 없음. 기존 배너 중 이벤트 정책 부적격 배너만 제외

응답 envelope, 오류 계약, 인증 공개 범위는 변경하지 않는다. 이벤트 대상자 판단에 필요한 이벤트 정보가 없거나 정책을 만족하지 않으면 해당 배너를 노출하지 않는다. 기존 상세 조회 endpoint의 오류 응답을 추천 API 오류로 전파하지 않는다.

9. 데이터 및 정책 규칙

  • 배너별 기존 필터는 유지하며, 이벤트 필터는 EVENT 유형에만 적용한다.
  • 이벤트 상세 정책은 이벤트 존재, isActive, startDate <= now <= endDate, 해석 국가의 언어, 인증 상태에 따른 성인 대상 검사를 뜻한다.
  • 국가 해석은 MemberContentPreferenceService.resolveCountryCode(member)를 기준으로 한다. 요청 언어와 별개다.
  • 이벤트 isAdult == null은 인증 여부와 관계없이 허용한다. true와 false는 인증 기록의 존재 여부와 정확히 일치해야 한다.
  • 기존 배너 자체 필터에는 preference·country·auth 규칙으로 계산되는 canViewAdultContent 성인 노출 가능 여부를 그대로 사용한다. 이는 raw preference나 이벤트 인증 판정과 동일하지 않으며 이벤트 인증 검사를 대체하지 않는다.
  • 상세 조회와 같은 시간 비교 의미를 사용하되 전역 시간대·formatter 및 기존 banner reservation 시간 정밀도 의미는 변경하지 않는다.
  • 기존 배너 정렬과 무작위 동순위 처리를 보존하고 적격 결과에 현재 limit 20을 적용한다.

10. 보안 및 데이터 취급

새 인증 경로나 저장 값은 만들지 않는다. 이벤트 공개 판정은 기존 AuthRepository 기록을 사용한다. 익명 조회의 인증 상태는 false로 취급한다. 추천 응답에 이벤트 상세 오류나 다른 사용자의 민감 데이터를 추가하지 않는다.

11. 품질 및 검증 기준

  • 이벤트의 시작/종료 정확한 경계, 한쪽이라도 범위를 벗어난 경우를 결정적으로 검증한다.
  • 인증 상태와 성인 선호 설정이 서로 다른 경우를 포함해 대상자 정책을 검증한다. 예: 비인증 JP 회원이 성인 콘텐츠 선호를 켠 경우와 인증 회원이 이를 끈 경우에도 이벤트 인증 판정은 Auth 기록을 따른다.
  • JP/비JP와 request LangContext 불일치를 검증한다.
  • EVENT 부적격 배너가 제거되면서 정상 배너, 순서, limit 20, 빈 목록 동작이 유지되는지 확인한다.
  • 홈 aggregate AI query/fallback 비호출, 모든 viewer의 빈 배열, audio schema 불변을 검증한다.
  • 구현 시 focused 테스트와 영향 범위 회귀 테스트를 수행한다. 이 PRD 작성 자체는 문서 검증만 대상으로 한다.

12. 성공 기준

12.1 기능 수용 기준

  • REC-AI-001: 모든 viewer의 홈 응답에 aiCharacters: []가 존재하며 aggregate AI query/fallback은 호출되지 않는다.
  • REC-AI-002: 오디오 추천 schema에 AI 캐릭터 필드가 추가되지 않는다.
  • REC-EVT-001~005: 이벤트 존재, 활성, 포함 경계 시간, 국가 언어, 인증 대상자 규칙이 검증된다.
  • REC-EVT-006~007: 기존 배너 조건 교집합, limit/order 보존, 개별 제외 및 빈 배열이 검증된다.
  • 배너 자체 성인 선호와 이벤트 인증 여부가 다른 조합에서도 각자의 조건이 독립적으로 적용된다.

12.2 추적성 완료 기준

  • 모든 확정 요구사항이 plan-task.md의 구현 Task와 자동 검증 증거에 연결된다.
  • 기존 API schema, subBanner, LINK 및 상세 endpoint 비변경이 검증된다.
  • 문서 간 정합성과 diff 공백 검사가 완료된다.

13. Open Questions

없음. 확정 요구사항의 최종 모호성은 0.00이다.

요구사항 명확성 자체 평가: Goal 1.00 · Scope 1.00 · Constraints 1.00 · Success 1.00 · Context 1.00.

14. 요구사항 추적표

요구사항 범위 API 계획 Goal 자동 검증 수동 검증
REC-AI-001 홈 추천 P1-T1 aggregate 응답/호출 검증 홈 aggregate와 전용 목록 비교
REC-AI-002 오디오 추천 P1-T3, P1-GATE audio DTO 계약 검증 오디오 endpoint 응답에서 필드 부재 확인
REC-EVT-001~007 홈·오디오 추천 P1-T2, P1-T3, P1-GATE 양쪽 repository 및 API 정책 회귀 검증 viewer/국가/경계 조합별 응답 확인

15. Decision Log

15.1 사용자 결정

날짜 ID 상태 결정 근거 영향 요구사항
2026-10-31 UD-001 확정 홈 aggregate 응답의 aiCharacters는 빈 배열로 둔다. null로 만들거나 필드를 생략하지 않는다. 사용자 확인 REC-AI-001
2026-10-31 UD-002 확정 이벤트 배너에도 기존 이벤트 상세 조회의 노출 정책을 적용한다. 사용자 확인 REC-EVT-001~007

15.2 최소 기술 범위 선택

아래는 구현 범위 선택이며 사용자 승인으로 인용하지 않는다.

날짜 ID 상태 선택 근거 영향 요구사항·범위
2026-10-31 TD-001 범위 확정 AudioRecommendationsResponse에 현재 없는 AI 필드를 만들지 않는다. 두 추천 endpoint의 배너 정책만 다루고 기존 스키마를 유지한다. 현행 DTO와 API 계약 확인 REC-AI-002, REC-EVT-*
2026-10-31 TD-002 범위 확정 이벤트 조건은 각 추천 배너 후보의 필터로 적용하고 부적격 배너만 제외한다. 기존 배너 조건과 결합하며 정렬·limit 전 적격성을 판정한다. 기존 추천 쿼리의 배너 조건 및 limit 흐름 REC-EVT-006~007
2026-10-31 TD-003 제외 전용 AI endpoint, AI snapshot/scheduler/data, LINK URL 파싱, subBanner, legacy endpoint, 상세 endpoint 오류·응답, 보안 설정, DB 및 dependency 변경은 이 문서의 구현 대상으로 삼지 않는다. 요청 범위와 독립된 기존 동작 보호 Non-Goals
2026-10-31 TD-004 보존 이벤트 상세 시간 비교 의미만 따른다. 전역 timezone/formatter와 기존 배너 reservation 정밀도는 바꾸지 않는다. 상세 서비스 비교식 및 프로젝트 시간 규칙 REC-EVT-002, REC-EVT-006

16. 근거 및 관련 문서

16.1 저장소 근거

  • docs/sample/sample-prd.md, docs/agent-guides/작업절차.md, docs/agent-guides/문서유지보수.md: PRD 형식, 사전 문서 절차, 문서 전용 검증.
  • src/main/kotlin/kr/co/vividnext/sodalive/v2/api/event/application/EventDetailService.kt: 이벤트 존재, 활성·기간·언어, 관리자 대상 예외와 인증 기록 판정.
  • src/main/kotlin/kr/co/vividnext/sodalive/v2/api/home/application/HomeRecommendationFacade.kt, src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/HomeRecommendationQueryService.kt: aggregate 조립, AI query/fallback, 배너 limit 20.
  • src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/adapter/out/persistence/DefaultHomeRecommendationQueryRepository.kt, src/main/kotlin/kr/co/vividnext/sodalive/v2/content/recommendation/adapter/out/persistence/DefaultAudioRecommendationQueryRepository.kt: 기존 추천 배너 조건과 정렬/limit.
  • src/main/kotlin/kr/co/vividnext/sodalive/v2/api/home/dto/recommendation/HomeRecommendationResponse.kt, src/main/kotlin/kr/co/vividnext/sodalive/v2/api/content/recommendation/dto/AudioRecommendationsResponse.kt: 현재 응답 필드와 오디오 응답의 AI 필드 부재.
  • src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBanner.kt, src/main/kotlin/kr/co/vividnext/sodalive/member/auth/AuthRepository.kt, src/main/kotlin/kr/co/vividnext/sodalive/member/contentpreference/MemberContentPreferenceService.kt: 배너 속성, 인증 기록, 국가 해석 계약.

16.2 관련 작업 문서

  • docs/20261028_일반이벤트상세조회/prd.md
  • docs/20261028_일반이벤트상세조회/plan-task.md
  • docs/20261027_콘텐츠배너예약기간/prd.md
  • docs/20261027_콘텐츠배너예약기간/plan-task.md
  • docs/20260819_추천탭_배너_접속국가별_언어필터/prd.md
  • docs/20260819_추천탭_배너_접속국가별_언어필터/plan-task.md

17. 문서 검증 기록

  • 2026-10-31: PRD 샘플, 작업 절차, 문서 유지보수 가이드 및 관련 구현 근거를 대조했다.
  • PRD 독립 검토 결과: 중요한 미결정 사항 및 명확한 소스 모순 없음. 구현 계획은 별도 자체 검토로 요구사항·파일·테스트·Goal 연결을 대조했다.
  • 두 신규 문서를 각각 git diff --no-index --check /dev/null의 비교 대상으로 검사했고 공백 오류 출력이 없었다. 최종 재확인한 종료 코드는 신규 파일 차이를 뜻하는 1이며, 이전의 종료 코드 0 기록은 정정한다. staging 후 git diff --cached --check로 별도 확인한다.
  • 구현 테스트와 Gradle 빌드·테스트·lint는 실행하지 않았다. 문서 전용 변경이므로 실행 검증은 구현 단계에 남기며 기능 수용 기준은 미완료로 유지한다.

구현 후 검증 — 2026-10-31

  • 위 문서 전용 기록 이후 사용자 승인으로 구현했다. 제품 커밋은 4a6b2ee9, 1f24a4b7, 969f999a이며 개별 TDD·리뷰 기록은 plan-task.md를 따른다.
  • 실제 JWT 및 HTTP 검증은 http-qa.md: 104개 요청·216개 판정 모두 PASS.
  • JAVA_TOOL_OPTIONS=-Duser.timezone=UTC ./gradlew build는 16분 34초에 종료 코드 0. 전체 테스트 2891개, failures/errors/skipped 0이며 lint와 bootJar 산출물도 확인했다.
  • 최종 독립 구현 리뷰는 명세·품질 승인, 차단 사항 없음이다. 기본 KST의 월말 기존 후원 이름 테스트 실패는 수정 범위 밖으로 남겼고 UTC 검증을 기본 환경 성공으로 확대하지 않는다.
  • 운영 배포·운영 DB 정밀도 및 도구가 없는 LSP 진단은 수행하지 않았다. 운영/저장소 시간대 설정은 변경하지 않았다.