feat(home): 크리에이터 채널 홈 정책을 보강한다
This commit is contained in:
@@ -21,10 +21,30 @@
|
||||
- 스케줄 성인 노출 정책: repository query에서 조회자의 성인 노출 정책을 먼저 반영하고, service 최종 조합에서도 내부 스케줄 후보의 `isAdult`로 한 번 더 보정한다. 공개 스케줄 응답에는 `isAdult`를 노출하지 않는다.
|
||||
- 현재 라이브와 예약 라이브 스케줄은 기존 라이브 목록과 동일하게 성별 제한(`LiveRoom.genderRestriction`)과 크리에이터 입장 제한(`LiveRoom.isAvailableJoinCreator`)을 반영한다. application service는 조회자의 `Auth.gender`가 있으면 이를 우선하고, 없으면 `Member.gender`를 사용하는 `effectiveViewerGender`를 산출해 query port에 넘긴다.
|
||||
- 신규 오디오 콘텐츠와 오디오 목록은 중복 노출하지 않는다. `latestAudioContent`로 내려간 가장 최신 콘텐츠를 오디오 목록에서 제외한다.
|
||||
- `latestAudioContent`는 상단 고정 여부와 관계없이 공개 시각 최신순을 유지한다.
|
||||
- `audioContents`는 활성 `PinContent`를 `updatedAt desc`로 먼저 배치하고, 나머지는 `AudioContent.releaseDate desc`, `AudioContent.id desc`로 배치한 뒤 최대 9개를 내려준다.
|
||||
- 크리에이터별 활성 오디오 상단 고정 한도는 9개다. 10번째 고정은 기존처럼 `PinContent.updatedAt`이 가장 오래된 활성 고정을 교체한다.
|
||||
- 채널 후원 홈 섹션은 기존 채널 후원 목록과 동일하게 이번 달 기준 최신순 8개를 내려준다. 응답 메시지는 기본 문구를 조합하지 않고 후원자가 입력한 추가 메시지만 내려준다.
|
||||
- 오리지널 시리즈 여부는 `Series.isOriginal == true`로 판단한다.
|
||||
- 화보와 상단 탭별 전체보기 API는 이번 범위에서 제외한다.
|
||||
|
||||
### 0.1 2026-07-30 후속 변경 상태
|
||||
|
||||
| Phase | 상태 | 완료 Task | 활성/다음 Goal | 차단 또는 남은 조건 |
|
||||
|---:|---|---:|---|---|
|
||||
| 1~2 | 완료 | `3/3` | 없음 | 기존 검증 기록 유지 |
|
||||
| 3 | 완료 | `17/17` | 없음 | 없음 |
|
||||
| 4 | 완료 | `5/5` | 없음 | 없음 |
|
||||
| 5 | 완료 | `3/3` | 없음 | 없음 |
|
||||
| 6 | 완료 | `4/4` | 없음 | 없음 |
|
||||
| 7 | 완료 | `5/5` | 없음 | 없음 |
|
||||
|
||||
- 동시에 하나의 미완료 Goal만 진행한다.
|
||||
- 완료된 Task 체크박스와 기존 검증 기록은 되돌리지 않는다.
|
||||
- 2026-07-30 1차 정적 리뷰 후속 실행 순서 `P7-R1` → `P4-R1` → `P5-R1` → `P6-R1` 완료.
|
||||
- 2026-07-30 2차 정적 리뷰 후속 실행 순서 `P3-R1` → `P4-R2` → `P4-R3` → `P7-R2` 완료.
|
||||
- 2026-07-31 3차 정적 리뷰 후속 실행 순서 `P7-R3` 완료.
|
||||
|
||||
---
|
||||
|
||||
## 1. 파일 구조 계획
|
||||
@@ -54,6 +74,7 @@
|
||||
- Create: `src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/adapter/in/web/CreatorChannelHomeControllerTest.kt`
|
||||
|
||||
### 문서 산출물
|
||||
- Modify: `docs/20260612_크리에이터_채널_홈_API/prd.md`
|
||||
- Modify: `docs/20260612_크리에이터_채널_홈_API/plan-task.md`
|
||||
|
||||
---
|
||||
@@ -515,6 +536,20 @@ data class CreatorChannelSnsResponse(
|
||||
- REFACTOR: 좋아요/댓글/구매 여부 조회를 `leftJoin` 하나로 합치지 않고, 현재의 id 목록 기반 bulk 조회 구조를 유지한다.
|
||||
- 기대 결과: 구매자는 삭제된 유료 게시글도 기존 전체보기 의미와 동일하게 접근할 수 있고, 비구매자는 삭제된 게시글을 조회하지 못한다.
|
||||
|
||||
- [x] **Task 3.17: 홈 채널 후원자의 삭제 닉네임 prefix 제거**
|
||||
|
||||
**Goal 실행 `P3-R1`:** 홈 채널 후원도 기존 채널 후원 목록과 동일하게 삭제 회원 닉네임의 `deleted_` prefix를 공개 응답에서 제거한다.
|
||||
|
||||
- **시작 조건:** `REV-P3-001` 확정, Task 3.1~3.16 완료.
|
||||
- **완료 증거:** 삭제 회원 후원자 RED/GREEN, service focused test, 기존 후원 조회 회귀, 검증 기록 누적.
|
||||
- **범위 밖:** 비밀 후원 노출 정책, 후원 메시지, projection 컬럼, 공개 응답 스키마 변경.
|
||||
- Files:
|
||||
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/home/application/CreatorChannelHomeQueryService.kt`
|
||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/home/application/CreatorChannelHomeQueryServiceTest.kt`
|
||||
- [x] **RED:** `nickname = "deleted_donor"`인 후원 record가 홈 domain에서 `nickname = "donor"`로 조립되는 테스트를 추가한다.
|
||||
- [x] **GREEN:** 기존 채널 후원 목록과 전용 v2 후원 탭이 사용하는 `removeDeletedNicknamePrefix()`를 홈 후원 domain 변환 경계에도 적용한다.
|
||||
- [x] **REFACTOR/GATE:** 닉네임 변환 외 repository 조회·후원 메시지·JSON 계약은 변경하지 않고 후원 관련 focused test 결과를 누적한다.
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: application service 조립
|
||||
@@ -546,6 +581,50 @@ data class CreatorChannelSnsResponse(
|
||||
- REFACTOR: 차단 예외 메시지 조합에 `SodaMessageSource`가 필요하면 기존 `ExplorerService.getCreatorDetail` 패턴을 따른다.
|
||||
- 기대 결과: 신규 API 접근 정책이 구버전 채널 정책과 맞는다.
|
||||
|
||||
- [x] **Task 4.3: 조회자 콘텐츠 선호 조회를 1회로 통합**
|
||||
|
||||
**Goal 실행 `P4-R1`:** 홈 조회에서 한 번 가져온 `ViewerContentPreference`로 성인 노출 여부와 콘텐츠 타입을 모두 결정한다.
|
||||
|
||||
- **시작 조건:** `REV-P4-001` 확정, Task 4.1~4.2 완료.
|
||||
- **완료 증거:** service RED/GREEN, focused test, Phase 4 직접 영향 회귀, 검증 기록 누적.
|
||||
- **범위 밖:** 선호 기본값·국가별 성인 판정·쿼리 필터 정책 변경.
|
||||
- Files:
|
||||
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/home/application/CreatorChannelHomeQueryService.kt`
|
||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/home/application/CreatorChannelHomeQueryServiceTest.kt`
|
||||
- [x] **RED:** `getStoredPreference(viewer)`가 반환한 `isAdult`가 모든 성인 필터 인자에 전달되고, service가 `canViewAdultContent(viewer)`를 별도 호출하지 않는 것을 검증한다.
|
||||
- [x] **GREEN:** 이미 조회한 `preference.isAdult`를 `canViewAdultContent`로 사용해 중복 `REQUIRES_NEW` 조회를 제거한다.
|
||||
- [x] **REFACTOR/GATE:** 서비스 조립·성인 필터·`contentType` 회귀를 확인하고 `plan-task.md` 검증 기록에 결과를 누적한다.
|
||||
|
||||
- [x] **Task 4.4: 라이브 크리에이터 입장 제한에 조회자 role 전달**
|
||||
|
||||
**Goal 실행 `P4-R2`:** 조회 대상과의 동일인 여부가 아니라 조회자의 `MemberRole.CREATOR` 여부로 현재/예약 라이브의 크리에이터 입장 제한을 적용한다.
|
||||
|
||||
- **시작 조건:** `REV-P4-002` 확정, `P3-R1` 완료.
|
||||
- **완료 증거:** 다른 크리에이터 조회 RED/GREEN, 현재 라이브·예약 스케줄 service 회귀, 검증 기록 누적.
|
||||
- **범위 밖:** 성별·성인 필터, 자기 라이브 예외, query port의 공개 API 스키마 변경.
|
||||
- Files:
|
||||
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/home/application/CreatorChannelHomeQueryService.kt`
|
||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/home/application/CreatorChannelHomeQueryServiceTest.kt`
|
||||
- [x] **RED:** `viewer.role == CREATOR`이고 `viewer.id != creatorId`인 경우 `findCurrentLive`와 `findSchedules`에 `isViewerCreator = true`가 전달되는 테스트를 추가한다. 일반 회원은 `false`, 대상 본인인 크리에이터는 `true`인 기존 의미도 함께 고정한다.
|
||||
- [x] **GREEN:** `isViewerCreator`를 `viewer.role == MemberRole.CREATOR`로 계산해 기존 라이브 목록의 `isAvailableJoinCreator` 정책과 정렬한다.
|
||||
- [x] **REFACTOR/GATE:** repository의 자기 라이브 예외 조건은 유지하고 service focused test 결과를 누적한다.
|
||||
|
||||
- [x] **Task 4.5: 홈 조회 기본 시각을 UTC로 고정**
|
||||
|
||||
**Goal 실행 `P4-R3`:** JVM 기본 timezone과 무관하게 홈 조회의 공개/예약 경계와 KST 월 범위 계산에 UTC `LocalDateTime`을 전달한다.
|
||||
|
||||
- **시작 조건:** `REV-P4-003` 확정, `P4-R2` 완료.
|
||||
- **완료 증거:** 비 UTC JVM timezone RED/GREEN, facade→service `now` 전달 회귀, 검증 기록 누적.
|
||||
- **범위 밖:** DB 컬럼 타입, API 시간 문자열 형식, 클라이언트 timezone 파라미터 추가.
|
||||
- Files:
|
||||
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/creator/channel/home/application/CreatorChannelHomeFacade.kt`
|
||||
- Modify if needed: `src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/home/application/CreatorChannelHomeQueryService.kt`
|
||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/home/application/CreatorChannelHomeQueryServiceTest.kt`
|
||||
- Test if needed: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/creator/channel/home/CreatorChannelHomeEndToEndTest.kt`
|
||||
- [x] **RED:** JVM 기본 timezone을 `Asia/Seoul`로 바꿔도 인자 없는 홈 조회가 UTC 기준 `now`를 service/query port에 전달하는 테스트를 추가하고, 테스트 종료 시 원 timezone을 복구한다.
|
||||
- [x] **GREEN:** 기본 `now` 생성 지점을 `LocalDateTime.now(ZoneOffset.UTC)` 또는 같은 의미의 UTC clock으로 고정한다. 명시적으로 전달된 `now`는 그대로 사용한다.
|
||||
- [x] **REFACTOR/GATE:** 별도 시간 추상화가 필요하지 않으면 추가하지 않고, 공개/예약 비교와 KST 월 경계의 직접 영향 회귀만 확인한다.
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: web API와 응답 계약
|
||||
@@ -588,6 +667,21 @@ data class CreatorChannelSnsResponse(
|
||||
- REFACTOR: nullable 섹션은 단건이면 `null`, 목록이면 빈 배열로 일관되게 내려준다.
|
||||
- 기대 결과: 클라이언트가 사용할 JSON 스키마가 테스트로 고정된다.
|
||||
|
||||
- [x] **Task 5.3: 빈 홈 응답의 null/빈 배열 JSON 계약 고정**
|
||||
|
||||
**Goal 실행 `P5-R1`:** 데이터가 없는 홈 응답에서 단건 섹션은 `null`, 목록 섹션은 빈 배열로 직렬화되는 공개 계약을 회귀 테스트로 고정한다.
|
||||
|
||||
- **시작 조건:** `REV-P5-001` 확정, `P4-R1` 완료.
|
||||
- **완료 증거:** MockMvc RED/GREEN, controller focused test, 검증 기록 누적.
|
||||
- **범위 밖:** 공개 필드 이름·endpoint·`ApiResponse` 구조 변경.
|
||||
- Files:
|
||||
- Modify if needed: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/creator/channel/home/dto/CreatorChannelHomeResponse.kt`
|
||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/creator/channel/home/adapter/in/web/CreatorChannelHomeControllerTest.kt`
|
||||
- **TDD 예외 사유:** 확정 항목은 실행 결함이 아니라 빈 응답 회귀 증거 부재이므로, 신규 계약 테스트가 첫 실행에서 바로 통과할 수 있다.
|
||||
- [x] **계약 검증:** `currentLive`, `latestAudioContent`, `fanTalk.latestFanTalk`이 명시적 `null`이고, `channelDonations`, `notices`, `schedules`, `audioContents`, `series`, `communities`가 빈 배열인 fixture를 응답해 각 JSON 경로의 존재와 값을 검증한다.
|
||||
- [x] **GREEN:** 최초 계약 검증이 실제 직렬화 문제를 드러낼 때만 DTO mapping/annotation을 최소 수정한다.
|
||||
- [x] **REFACTOR/GATE:** 채워진 응답 계약 테스트와 빈 응답 계약 테스트를 같이 실행하고 결과를 누적한다.
|
||||
|
||||
---
|
||||
|
||||
### Phase 6: 통합 회귀와 문서 갱신
|
||||
@@ -636,6 +730,141 @@ data class CreatorChannelSnsResponse(
|
||||
- REFACTOR: 실패한 검증이 있으면 해당 phase/task로 돌아가 plan-task 체크박스를 완료 처리하지 않는다.
|
||||
- 기대 결과: 구현 완료 시 어떤 검증으로 완료 판단했는지 문서에 남는다.
|
||||
|
||||
- [x] **Task 6.4: 단일 HTTP 요청 기준 홈 전체 조립 통합 회귀 보강**
|
||||
|
||||
**Goal 실행 `P6-R1`:** 실제 controller→facade→service→repository/공용 서비스 경로가 한 번의 홈 API 요청에서 전체 섹션을 조립하는지 검증한다.
|
||||
|
||||
- **시작 조건:** `REV-P6-001` 확정, `P5-R1` 완료.
|
||||
- **완료 증거:** 통합 시나리오 검증, 실제 bean 경로의 전체 섹션 JSON assertion, Phase 6 직접 영향 회귀, 검증 기록 누적.
|
||||
- **범위 밖:** 홈 응답 스키마·조회 정책 변경, 테스트 편의용 운영 API 추가.
|
||||
- Files:
|
||||
- Create: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/creator/channel/home/CreatorChannelHomeEndToEndTest.kt`
|
||||
- **TDD 예외 사유:** 확정 항목은 계층 연결의 실행 결함이 아니라 단일 요청 통합 증거 부재이므로, 신규 E2E가 첫 실행에서 바로 통과할 수 있다.
|
||||
- [x] **통합 검증:** 현재 리포지토리 테스트의 각 조회 호출과 mock facade controller 테스트로는 증명되지 않는 단일 요청 시나리오를 작성한다. 최소한 creator/current live/latest audio/donation/notice/schedule/audio list/series/community/fan Talk/activity/SNS를 한 fixture에 구성하고 실제 bean과 JSON을 검증한다.
|
||||
- [x] **GREEN:** 통합 테스트가 드러낸 mapping·bean wiring·쿼리 누락만 최소 수정한다. 최초 통합 검증이 바로 통과하면 생산 코드는 변경하지 않는다.
|
||||
- [x] **REFACTOR/GATE:** fixture helper는 해당 테스트 범위에만 두고, 새 E2E와 기존 service/repository/controller focused test를 함께 실행해 결과를 누적한다.
|
||||
|
||||
---
|
||||
|
||||
### Phase 7: 오디오 상단 고정 한도와 홈 목록 정렬 보정
|
||||
|
||||
**Phase 결과:** 크리에이터가 오디오 콘텐츠를 최대 9개까지 상단 고정할 수 있고, 홈 `audioContents`에서 고정 콘텐츠가 최근 고정순으로 먼저 노출된다.
|
||||
|
||||
**선행조건:** PRD Feature D/H와 `DEC-001` 확정.
|
||||
|
||||
**Phase 완료 조건:** 기존 `P7-T1`, `P7-T2`, `P7-GATE`, `P7-R1`, `P7-R2` 완료 이력을 유지하고, 3차 리뷰 후속 `P7-R3`의 실제 검증 결과까지 누적.
|
||||
|
||||
- [x] **Task 7.1: 오디오 상단 고정 최대 개수를 9개로 보정**
|
||||
|
||||
**Goal 실행 `P7-T1`:** 상세 응답의 고정 가능 여부와 상단 고정 등록이 같은 9개 상한을 사용하도록 고정한다.
|
||||
|
||||
- **시작 조건:** PRD Feature H의 고정 상한과 초과 교체 정책 확정.
|
||||
- **완료 증거:** RED/GREEN/REFACTOR 체크박스 완료, `AudioContentServiceTest` focused test 통과, 검증 기록 누적.
|
||||
- **범위 밖:** 고정/해제 endpoint, 오류 키, `PinContent` 테이블 스키마 변경.
|
||||
- Files:
|
||||
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt`
|
||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/content/AudioContentServiceTest.kt`
|
||||
- Interfaces:
|
||||
- Consumes: `PinContentRepository.getPinContentList(memberId, active)`의 기존 오래된 고정 우선 반환 순서.
|
||||
- Produces: `MAX_PIN_CONTENT_COUNT = 9`, `isAvailablePin == (activePinCount < 9)`, 10번째 고정 시 가장 오래된 활성 고정 교체 동작.
|
||||
- [x] **RED:** 활성 고정이 8개일 때 상세 `isAvailablePin == true`, 9개일 때 `false`임을 검증하고, 9개 상태의 새 고정이 리포지토리 목록의 가장 오래된 항목을 재사용하는 테스트를 추가한다.
|
||||
- [x] **RED 확인:** `./gradlew test --tests kr.co.vividnext.sodalive.content.AudioContentServiceTest --no-daemon`을 실행해 기존 3개 상한 때문에 8개 상태의 `isAvailablePin` 또는 교체 대상 assertion이 실패함을 확인한다.
|
||||
- [x] **GREEN:** `AudioContentService`의 상세 고정 가능 판정과 `pinToTheTop`이 공통 `MAX_PIN_CONTENT_COUNT = 9`를 사용하도록 최소 수정한다.
|
||||
- [x] **GREEN 확인:** 동일 `AudioContentServiceTest` 명령을 다시 실행해 통과를 확인한다.
|
||||
- [x] **REFACTOR:** 상한 숫자 중복만 제거하고 고정 초과 시 최근 고정을 거부하는 새 예외·설정·DB 제약은 추가하지 않는다. focused test와 `./gradlew ktlintCheck --no-daemon`를 재실행한다.
|
||||
|
||||
- [x] **Task 7.2: 홈 오디오 목록을 활성 고정 우선으로 정렬**
|
||||
|
||||
**Goal 실행 `P7-T2`:** `latestAudioContent`의 최신 공개 정책을 유지하면서 `audioContents`만 활성 고정 우선순으로 반환한다.
|
||||
|
||||
- **시작 조건:** `P7-T1` 완료.
|
||||
- **완료 증거:** RED/GREEN/REFACTOR 체크박스 완료, `DefaultCreatorChannelHomeQueryRepositoryTest` focused test 통과, 검증 기록 누적.
|
||||
- **범위 밖:** `latestAudioContent` 선정 정책, `audioContents` 최대 9개, 공개 응답 DTO/스키마 변경.
|
||||
- Files:
|
||||
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/home/adapter/out/persistence/DefaultCreatorChannelHomeQueryRepository.kt`
|
||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/home/adapter/out/persistence/DefaultCreatorChannelHomeQueryRepositoryTest.kt`
|
||||
- Interfaces:
|
||||
- Consumes: `PinContent.member`, `PinContent.content`, `PinContent.isActive`, `PinContent.updatedAt` 및 기존 `findLatestAudioContent`/`findAudioContents` port 계약.
|
||||
- Produces: `findAudioContents`의 활성 고정 우선 정렬과 `findLatestAudioContent`의 기존 `releaseDate desc`, `id desc` 정렬 유지.
|
||||
- [x] **RED:** 최신 공개 콘텐츠, 최근에 고정한 이전 공개 콘텐츠, 먼저 고정한 이전 공개 콘텐츠, 비활성 고정, 일반 콘텐츠 fixture를 구성해 다음을 한 테스트에서 검증한다.
|
||||
- `latestAudioContent`는 상단 고정 여부와 관계없이 실제 최신 공개 콘텐츠다.
|
||||
- `audioContents`는 `PinContent.member.id == creatorId && isActive == true`인 콘텐츠를 `PinContent.updatedAt desc`로 먼저 반환한다.
|
||||
- 비활성 고정은 일반 콘텐츠로 취급하고, 고정 이후 나머지는 `releaseDate desc`, `id desc`다.
|
||||
- `latestAudioContent`는 `audioContents`에 중복되지 않고, 고정 우선 정렬 후 최대 9개만 반환한다.
|
||||
- [x] **RED 확인:** `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.home.adapter.out.persistence.DefaultCreatorChannelHomeQueryRepositoryTest --no-daemon`을 실행해 기존 최신순 전용 정렬 때문에 고정 우선 기대 순서 assertion이 실패함을 확인한다.
|
||||
- [x] **GREEN:** `findAudioContentRows`의 최신 단건/목록 용도를 구분해 목록 조회에만 크리에이터의 활성 `PinContent`를 left join하고 `PinContent` 존재 여부 desc, `PinContent.updatedAt desc`, `AudioContent.releaseDate desc`, `AudioContent.id desc`를 적용한다.
|
||||
- [x] **GREEN 확인:** 동일 repository focused test를 다시 실행해 통과를 확인한다.
|
||||
- [x] **REFACTOR:** 공개 조건·projection·bulk 조립은 기존 구조를 유지하고, 정렬 구분을 위한 최소 변경만 남긴다. repository focused test, `CreatorChannelHomeQueryServiceTest`, `./gradlew ktlintCheck --no-daemon`를 실행한다.
|
||||
|
||||
#### Phase 7 Gate
|
||||
|
||||
**Goal 실행 `P7-GATE`:** 고정 한도와 홈 오디오 정렬의 확정 요구사항, 직접 영향 회귀, 문서 정합성을 최종 판정한다.
|
||||
|
||||
- **시작 조건:** `P7-T1`, `P7-T2` 완료.
|
||||
- **완료 증거:** 아래 명령 전체 성공과 검증 기록 누적.
|
||||
- **범위 밖:** 전체 회귀 실패와 무관한 기존 문제 수정.
|
||||
|
||||
```bash
|
||||
./gradlew test --tests kr.co.vividnext.sodalive.content.AudioContentServiceTest --no-daemon
|
||||
./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.home.adapter.out.persistence.DefaultCreatorChannelHomeQueryRepositoryTest --tests kr.co.vividnext.sodalive.v2.creator.channel.home.application.CreatorChannelHomeQueryServiceTest --no-daemon
|
||||
./gradlew ktlintCheck --no-daemon
|
||||
./gradlew tasks --all --no-daemon
|
||||
git diff --check
|
||||
```
|
||||
|
||||
**Expected:** 활성 고정 상한 9개, 10번째 고정의 최고령 고정 교체, `latestAudioContent` 최신순 유지, `audioContents` 활성 고정 우선/일반 최신순/최대 9개가 검증되고 Kotlin 포맷과 문서 명령이 성공한다.
|
||||
|
||||
전체 `./gradlew test`는 공개 API 스키마·공통 인증·예외·설정을 변경하지 않고 두 focused test 범위로 직접 영향을 판정할 수 있으므로 기본 생략한다. Gate 실행 중 targeted test로 영향 범위를 판단할 수 없는 실패가 발생하면 전체 회귀로 확장한다.
|
||||
|
||||
**실행 순서:** `P7-T1` → `P7-T2` → `P7-GATE`(기존 완료) → `P7-R1`(기존 완료) → `P7-R2`(기존 완료) → `P7-R3`
|
||||
|
||||
- [x] **Task 7.3: 동시 상단 고정에서도 크리에이터별 활성 9개 상한 보장**
|
||||
|
||||
**Goal 실행 `P7-R1`:** 같은 크리에이터의 상단 고정 변경을 직렬화해 동시 요청에서도 활성 고정과 콘텐츠 중복이 생기지 않고 9개 상한을 지킨다.
|
||||
|
||||
- **시작 조건:** `REV-P7-001` 확정, 기존 `P7-GATE` 완료.
|
||||
- **완료 증거:** 동시성 RED/GREEN, 홈 목록 9개 상한 assertion, Phase 7 Gate 재실행, 검증 기록 누적.
|
||||
- **범위 밖:** endpoint·오류 키·노출 정렬 계약·DB DDL 변경.
|
||||
- Files:
|
||||
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt`
|
||||
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/member/MemberRepository.kt` if needed
|
||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/content/AudioContentServiceTest.kt`
|
||||
- Create: `src/test/kotlin/kr/co/vividnext/sodalive/content/AudioContentPinConcurrencyTest.kt`
|
||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/home/adapter/out/persistence/DefaultCreatorChannelHomeQueryRepositoryTest.kt`
|
||||
- [x] **RED:** `AiCharacterAdminCommunityPostConcurrencyTest` 패턴을 참고해 실제 transaction 2개를 사용한다. 활성 고정 8개 상태에서 같은 크리에이터의 서로 다른 콘텐츠 고정 요청을 동시 실행해 최종 활성 고정 수가 9를 넘지 않고 콘텐츠가 중복되지 않아야 함을 재현한다. 홈 오디오 fixture도 9개를 초과해 반환 개수 9를 명시적으로 검증한다.
|
||||
- [x] **GREEN:** 기존 `MemberRepository.findByIdForUpdate` 패턴 등 최소의 크리에이터 단위 pessimistic lock을 고정 상태 조회 전에 적용한다. 같은 콘텐츠의 재요청·9개 초과 교체 의미는 유지한다.
|
||||
- [x] **REFACTOR/GATE:** lock 범위를 고정 변경 transaction에만 두고, `AudioContentServiceTest`, home repository/service focused test, `ktlintCheck`, `git diff --check`의 결과를 누적한다.
|
||||
|
||||
- [x] **Task 7.4: 상단 고정 transaction의 첫 DB 조회에서 크리에이터 lock 획득**
|
||||
|
||||
**Goal 실행 `P7-R2`:** MySQL InnoDB 기본 `REPEATABLE READ`에서도 고정 상태를 읽기 전에 크리에이터 lock을 획득해 대기 transaction이 최신 고정 상태를 기준으로 판단하게 한다.
|
||||
|
||||
- **시작 조건:** `REV-P7-002` 확정, `P4-R3` 완료.
|
||||
- **완료 증거:** DB 호출 순서 RED/GREEN, 기존 동시성·9개 상한 회귀, Phase 7 직접 영향 Gate, 검증 기록 누적.
|
||||
- **범위 밖:** DB 격리수준 설정, `PinContent` DDL/unique constraint, 새 테스트 의존성, endpoint·오류 키 변경.
|
||||
- Files:
|
||||
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt`
|
||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/content/AudioContentServiceTest.kt`
|
||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/content/AudioContentPinConcurrencyTest.kt`
|
||||
- [x] **RED:** `memberRepository.findByIdForUpdate`가 `repository.findByIdAndCreatorId`, `findByContentIdAndMemberId`, `getPinContentList`보다 먼저 호출되는지를 하나의 `inOrder` 검증으로 고정한다. 현재 구현은 콘텐츠 일반 조회가 먼저라 실패해야 한다.
|
||||
- [x] **GREEN:** `pinToTheTop`의 첫 DB 접근에서 크리에이터 member row를 잠그고, 이후 콘텐츠와 활성 고정 상태를 조회한다. 필요하면 반환된 locked member를 후속 저장에 사용한다.
|
||||
- [x] **REFACTOR/GATE:** 기존 동시 요청 결과 검증과 10번째 교체·비활성 재활성화 회귀를 유지하고, 격리수준 변경이나 추가 lock/DDL 없이 최소 호출 순서 변경만 남긴다.
|
||||
|
||||
- [x] **Task 7.5: 상단 고정 해제도 크리에이터 lock으로 직렬화**
|
||||
|
||||
**Goal 실행 `P7-R3`:** 같은 크리에이터의 고정과 해제가 동일한 member row lock을 공유하게 해, 최고령 `PinContent` 행 재사용과 이전 콘텐츠 해제가 겹쳐 새 고정이 소실되지 않도록 한다.
|
||||
|
||||
- **시작 조건:** `REV-P7-003` 확정, `P7-R2` 완료.
|
||||
- **완료 증거:** 해제 lock 순서 RED/GREEN, 기존 동시 고정·10번째 교체·비활성 재활성화 회귀, Phase 7 직접 영향 Gate, 검증 기록 누적.
|
||||
- **범위 밖:** `PinContent` DDL/unique constraint, endpoint·오류 키·홈 정렬 계약, 고정 행 재사용 정책 변경.
|
||||
- Files:
|
||||
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt`
|
||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/content/AudioContentServiceTest.kt`
|
||||
- Test if needed: `src/test/kotlin/kr/co/vividnext/sodalive/content/AudioContentPinConcurrencyTest.kt`
|
||||
- [x] **RED:** `unpinAtTheTop`이 `memberRepository.findByIdForUpdate(member.id)`로 크리에이터 lock을 획득한 뒤 `findByContentIdAndMemberId`를 호출하는 순서를 `inOrder`로 먼저 고정한다. 현재 구현은 lock 호출이 없어 실패해야 한다.
|
||||
- [x] **GREEN:** `unpinAtTheTop`의 첫 DB 접근에서 `pinToTheTop`과 같은 member row lock을 획득하고, 잠긴 creator id로 해제할 `PinContent`를 조회해 비활성화한다.
|
||||
- [x] **REFACTOR/GATE:** 별도 lock·격리수준·DDL을 추가하지 않고 공용 member lock만 재사용한다. 해제 focused test와 기존 `AudioContentServiceTest`, `AudioContentPinConcurrencyTest`, 홈 repository/service 회귀, `ktlintCheck`, `git diff --check` 결과를 누적한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 중 주의사항
|
||||
@@ -645,6 +874,7 @@ data class CreatorChannelSnsResponse(
|
||||
- 공개 시간은 UTC ISO-8601 문자열로 내려주고, 앱 표시 포맷은 서버에서 조합하지 않는다.
|
||||
- 목록 섹션은 데이터가 없으면 빈 배열, 단건 섹션은 없으면 `null`로 내려준다.
|
||||
- 신규 API 공개 스키마 변경은 이 문서의 task 범위 안에서만 수행한다.
|
||||
- Phase 7은 공개 응답 DTO를 변경하지 않고, `PinContent` 고정 상한과 홈 `audioContents` 조회 순서만 보정한다.
|
||||
|
||||
---
|
||||
|
||||
@@ -703,3 +933,27 @@ data class CreatorChannelSnsResponse(
|
||||
- 2026-06-13: Phase 6 Task 6.1 통합 시나리오 검증 - `DefaultCreatorChannelHomeQueryRepositoryTest`에 현실적인 단일 크리에이터 fixture로 creator/currentLive/latestAudioContent/channelDonations/notices/schedules/audioContents/series/communities/fanTalk/activity/sns 후보 조회를 모두 검증하는 `shouldFindCreatorChannelHomeIntegratedSections`를 추가했다. 기존 구현에서 `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.adapter.out.persistence.DefaultCreatorChannelHomeQueryRepositoryTest --tests '*shouldFindCreatorChannelHomeIntegratedSections' --no-daemon` 통과. MockMvc 응답 표면은 `CreatorChannelHomeControllerTest`에 schedule 내부 `isAdult`와 channelDonation 내부 `donationId`/`memberId`/`isSecret` 비노출 assertion을 보강했고, `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.adapter.in.web.CreatorChannelHomeControllerTest --no-daemon` 통과.
|
||||
- 2026-06-13: Phase 6 Task 6.2 추천 페이지 enum rename 회귀 확인 - `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.HomeRecommendationQueryServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest --no-daemon` 통과. `rg -n "RecommendedActivityType" src/main/kotlin src/test/kotlin` 결과 없음.
|
||||
- 2026-06-13: Phase 6 Task 6.3 전체 검증 - `./gradlew test --tests kr.co.vividnext.sodalive.v2.common.domain.CreatorActivityTypeTest --tests kr.co.vividnext.sodalive.v2.creator.channel.domain.CreatorChannelHomeQueryPolicyTest --tests kr.co.vividnext.sodalive.v2.creator.channel.application.CreatorChannelHomeQueryServiceTest --tests kr.co.vividnext.sodalive.v2.creator.channel.adapter.out.persistence.DefaultCreatorChannelHomeQueryRepositoryTest --tests kr.co.vividnext.sodalive.v2.creator.channel.adapter.in.web.CreatorChannelHomeControllerTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.HomeRecommendationQueryServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest --no-daemon`, `./gradlew ktlintCheck --no-daemon`, `git diff --check`, `./gradlew test --no-daemon` 통과. 병렬 Gradle 실행 중 `build/snapshot/kotlin/kaptGenerateStubsTestKotlin` 삭제 경합이 한 번 발생했으나 동일 repository 테스트를 단독 재실행해 통과를 확인했다.
|
||||
- 2026-07-30: Phase 7 후속 변경 문서화 - 활성 오디오 상단 고정 한도 9개, 10번째 고정의 최고령 고정 교체, `latestAudioContent` 최신순 유지, `audioContents` 활성 고정 `PinContent.updatedAt desc` 우선·일반 콘텐츠 `releaseDate desc`, `id desc`·최대 9개 정책을 PRD Feature D/H와 `DEC-001`에 확정했다. 기존 완료 Task는 유지하고 `P7-T1` → `P7-T2` → `P7-GATE` TDD 계획을 추가했다. `git diff --check` 통과. `./gradlew tasks --all --no-daemon`은 최초 샌드박스의 `~/.gradle` lock 파일 접근 제한으로 실패했고, 승인된 Gradle 캐시 접근으로 동일 명령을 재실행해 `BUILD SUCCESSFUL`을 확인했다. 사용자 요청이 관련 문서 반영이므로 생산 코드와 테스트는 아직 변경·실행하지 않았다.
|
||||
- 2026-07-30: Phase 7 Task 7.1 RED 확인 - `AudioContentServiceTest`에 활성 고정 8개/9개 상세 `isAvailablePin` 계약과 10번째 고정의 최고령 활성 고정 재사용 테스트를 추가했다. `./gradlew test --tests kr.co.vividnext.sodalive.content.AudioContentServiceTest --no-daemon` 실행 시 기존 3개 상한 때문에 `shouldExposeAvailablePinByNineActivePinLimit`가 실패하는 것을 확인했다.
|
||||
- 2026-07-30: Phase 7 Task 7.1 GREEN/REFACTOR 확인 - `AudioContentService`의 상세 고정 가능 판정과 `pinToTheTop` 교체 기준을 `MAX_PIN_CONTENT_COUNT = 9`로 통일했다. `./gradlew test --tests kr.co.vividnext.sodalive.content.AudioContentServiceTest --no-daemon`, `./gradlew ktlintCheck --no-daemon` 통과.
|
||||
- 2026-07-30: Phase 7 Task 7.2 RED 확인 - `DefaultCreatorChannelHomeQueryRepositoryTest`에 최신 오디오, 최근/이전 활성 고정, 비활성 고정, 일반 오디오 fixture를 추가했다. `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.home.adapter.out.persistence.DefaultCreatorChannelHomeQueryRepositoryTest --no-daemon` 실행 시 기존 최신순 전용 정렬 때문에 `shouldSortHomeAudioContentsByActivePinBeforeReleaseDate`가 실패하는 것을 확인했다.
|
||||
- 2026-07-30: Phase 7 Task 7.2 GREEN/REFACTOR 확인 - `findAudioContentRows`를 최신 단건과 목록 용도로 구분하고, 목록 조회에만 활성 `PinContent` left join 및 `pinContent.isActive desc`, `pinContent.updatedAt desc`, `AudioContent.releaseDate desc`, `AudioContent.id desc` 정렬을 적용했다. `pinContent.id.isNotNull.desc()`는 HQL syntax 오류를 내 기존 repository 패턴인 `pinContent.isActive.desc()`로 보정했다. `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.home.adapter.out.persistence.DefaultCreatorChannelHomeQueryRepositoryTest --no-daemon`, `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.home.application.CreatorChannelHomeQueryServiceTest --no-daemon`, `./gradlew ktlintCheck --no-daemon` 통과.
|
||||
- 2026-07-30: Phase 7 Gate 확인 - `./gradlew test --tests kr.co.vividnext.sodalive.content.AudioContentServiceTest --no-daemon`, `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.home.adapter.out.persistence.DefaultCreatorChannelHomeQueryRepositoryTest --tests kr.co.vividnext.sodalive.v2.creator.channel.home.application.CreatorChannelHomeQueryServiceTest --no-daemon`, `./gradlew ktlintCheck --no-daemon`, `./gradlew tasks --all --no-daemon`, `git diff --check` 모두 통과.
|
||||
- 2026-07-30: Phase 7 reviewer gate 보정 RED/GREEN 확인 - 리뷰어가 `pinToTheTop`이 비활성 `PinContent`까지 포함한 전체 목록 기준으로 동작하면 `9 active + inactive` 상태에서 활성 고정이 10개가 될 수 있음을 차단 이슈로 지적했다. `shouldKeepNineActivePinsWhenReactivatingInactivePin` 테스트 추가 직후 `./gradlew test --tests kr.co.vividnext.sodalive.content.AudioContentServiceTest --no-daemon`에서 해당 테스트 실패를 확인했고, `pinToTheTop`이 `getPinContentList(memberId, active = true)` 기준으로 최고령 활성 고정을 내린 뒤 비활성 기존 고정을 재활성화하도록 보정했다. 보정 후 같은 `AudioContentServiceTest` 통과.
|
||||
- 2026-07-30: Phase 7 reviewer gate 보정 후 Gate 재확인 - `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.home.adapter.out.persistence.DefaultCreatorChannelHomeQueryRepositoryTest --tests kr.co.vividnext.sodalive.v2.creator.channel.home.application.CreatorChannelHomeQueryServiceTest --no-daemon`, `./gradlew ktlintCheck --no-daemon`, `./gradlew tasks --all --no-daemon`, `git diff --check` 통과.
|
||||
- 2026-07-30: Phase 7 reviewer gate 최종 확인 - 보정 delta 재검토 결과 차단 findings 없음.
|
||||
- 2026-07-30: Phase 1~7 정적 리뷰 - `docs/20260612_크리에이터_채널_홈_API/reviews/phase-1-review.md`~`phase-7-review.md`를 작성했다. Phase 1~3은 확정 발견 사항 없음으로 판정했고, `REV-P4-001`(선호 중복 조회), `REV-P5-001`(빈 응답 계약 테스트 누락), `REV-P6-001`(단일 요청 통합 증거 누락), `REV-P7-001`(동시 고정 상한/중복 경쟁)을 확정해 Task 4.3·5.3·6.4·7.3과 `P4-R1`·`P5-R1`·`P6-R1`·`P7-R1`로 전환했다. 사용자 지시에 따라 컴파일·테스트·ktlint은 실행하지 않았고, `rg`·`sed`·`git diff`·`git log`·`git status`를 사용한 정적 검토와 `git diff --check` 성공만 기록한다.
|
||||
- 2026-07-30: Phase 7 Task 7.3 RED/GREEN 확인 - `AudioContentServiceTest`에 `MemberRepository.findByIdForUpdate`가 `PinContent` 조회 전에 호출되는 순서를 먼저 고정했고, `AudioContentPinConcurrencyTest`에 활성 고정 8개 상태의 동시 고정 요청 최종 활성 9개/콘텐츠 중복 없음 통합 검증을 추가했다. RED는 production service 생성자에 lock 의존성이 없어 컴파일 실패하는 것으로 확인했고, `AudioContentService.pinToTheTop`이 고정 상태 조회 전 `MemberRepository.findByIdForUpdate(member.id!!)`를 호출하도록 보정했다. `DefaultCreatorChannelHomeQueryRepositoryTest`에는 활성 고정 후보가 9개를 넘어도 `findAudioContents(..., limit = 9)`가 9개만 반환하는 assertion을 추가했다. `./gradlew test --tests kr.co.vividnext.sodalive.content.AudioContentServiceTest --tests kr.co.vividnext.sodalive.content.AudioContentPinConcurrencyTest --no-daemon`, `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.home.adapter.out.persistence.DefaultCreatorChannelHomeQueryRepositoryTest --no-daemon` 통과.
|
||||
- 2026-07-30: Phase 4 Task 4.3 RED/GREEN 확인 - `CreatorChannelHomeQueryServiceTest`에 `getStoredPreference(viewer)` 결과의 `isAdult`가 service/커뮤니티 성인 필터에 전달되고 `canViewAdultContent(viewer)`가 별도 호출되지 않는 테스트를 추가했다. RED는 `NeverWantedButInvoked`로 확인했고, service가 `preference.isAdult`를 재사용하도록 보정했다. `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.home.application.CreatorChannelHomeQueryServiceTest --no-daemon` 통과.
|
||||
- 2026-07-30: Phase 5 Task 5.3 계약 검증 - `CreatorChannelHomeControllerTest`에 빈 홈 응답 fixture를 추가해 `currentLive`, `latestAudioContent`, `fanTalk.latestFanTalk`은 명시적 `null`, `channelDonations`, `notices`, `schedules`, `audioContents`, `series`, `communities`는 빈 배열로 직렬화됨을 검증했다. 문서상 TDD 예외 항목이며 생산 DTO 변경 없이 `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.creator.channel.home.adapter.in.web.CreatorChannelHomeControllerTest --no-daemon` 통과.
|
||||
- 2026-07-30: Phase 6 Task 6.4 통합 검증 - `CreatorChannelHomeEndToEndTest`를 추가해 실제 Spring bean과 DB fixture로 인증된 `GET /api/v2/creator-channels/{creatorId}/home` 단일 요청이 creator/current live/latest audio/donation/notice/schedule/audio list/series/community/fan Talk/activity/SNS 대표 필드를 조립하는지 검증했다. fixture 필수 series genre와 `audioContentCount` 기대값을 실제 정책에 맞게 보정한 뒤 `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.creator.channel.home.CreatorChannelHomeEndToEndTest --no-daemon` 통과.
|
||||
- 2026-07-30: 리뷰 후속 통합 Gate 확인 - `./gradlew test --tests kr.co.vividnext.sodalive.content.AudioContentServiceTest --tests kr.co.vividnext.sodalive.content.AudioContentPinConcurrencyTest --tests kr.co.vividnext.sodalive.v2.creator.channel.home.application.CreatorChannelHomeQueryServiceTest --tests kr.co.vividnext.sodalive.v2.api.creator.channel.home.adapter.in.web.CreatorChannelHomeControllerTest --tests kr.co.vividnext.sodalive.v2.api.creator.channel.home.CreatorChannelHomeEndToEndTest --tests kr.co.vividnext.sodalive.v2.creator.channel.home.adapter.out.persistence.DefaultCreatorChannelHomeQueryRepositoryTest --no-daemon`, `./gradlew ktlintCheck --no-daemon`, `git diff --check` 통과.
|
||||
- 2026-07-30: Phase 1~7 2차 정적 리뷰 - 기존 Phase별 리뷰 문서에 2차 리뷰 결과를 추가했다. Phase 1·2·5·6은 신규 확정 발견 사항 없음으로 판정했다. `REV-P3-001`(삭제 회원 후원자 닉네임 prefix), `REV-P4-002`(다른 크리에이터 조회 시 라이브 입장 제한 누락), `REV-P4-003`(기본 `now`의 JVM timezone 의존), `REV-P7-002`(member lock 전 일반 조회로 인한 MySQL snapshot 위험)를 확정해 Task 3.17·4.4·4.5·7.4와 `P3-R1`·`P4-R2`·`P4-R3`·`P7-R2`로 전환했다. 사용자 지시에 따라 컴파일·테스트·ktlint은 실행하지 않았으며 코드·테스트·문서와 MySQL 공식 격리수준 문서를 정적으로 대조했다. `git diff --check`, 리뷰 문서별 2차 섹션 단일 존재, finding ID와 미완료 Task 연결, trailing whitespace 부재를 정적 확인했다.
|
||||
- 2026-07-30: Phase 3 Task 3.17 RED/GREEN 확인 - `CreatorChannelHomeQueryServiceTest`에 삭제 회원 후원자 `deleted_donor`가 홈 domain에서 `donor`로 조립되는 테스트를 추가했다. RED는 `AssertionFailedError`로 확인했고, `CreatorChannelDonationRecord.toDomain()`에서 `removeDeletedNicknamePrefix()`를 적용한 뒤 `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.home.application.CreatorChannelHomeQueryServiceTest --tests '*shouldRemoveDeletedNicknamePrefixFromChannelDonation' --no-daemon` 통과.
|
||||
- 2026-07-30: Phase 4 Task 4.4 RED/GREEN 확인 - `viewer.role == CREATOR`이고 `viewer.id != creatorId`인 조회자의 `isViewerCreator` 전달 테스트를 추가해 RED를 확인했다. 이후 `CreatorChannelHomeQueryService`의 `isViewerCreator` 계산을 `viewer.role == MemberRole.CREATOR`로 보정하고 일반 회원 false, 대상 본인 크리에이터 true 회귀와 함께 `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.home.application.CreatorChannelHomeQueryServiceTest --tests '*shouldPassViewerCreatorFlagWhenViewerIsDifferentCreator' --tests '*shouldPassNonCreatorFlagWhenViewerIsUser' --tests '*shouldPassViewerCreatorFlagToLivePolicyQueries' --no-daemon` 통과.
|
||||
- 2026-07-30: Phase 4 Task 4.5 RED/GREEN 확인 - `CreatorChannelHomeFacadeTest`에 JVM 기본 timezone을 `Asia/Seoul`로 바꾼 상태에서 인자 없는 홈 조회가 UTC 기준 `now`를 service에 전달하는 테스트를 추가했다. RED는 UTC 범위 assertion 실패로 확인했고, facade와 service의 기본 `now`를 `LocalDateTime.now(ZoneOffset.UTC)`로 고정한 뒤 `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.creator.channel.home.application.CreatorChannelHomeFacadeTest --no-daemon` 통과.
|
||||
- 2026-07-30: Phase 7 Task 7.4 RED/GREEN 확인 - `AudioContentServiceTest`의 10번째 고정 테스트에 `memberRepository.findByIdForUpdate`가 `repository.findByIdAndCreatorId`와 `PinContent` 조회보다 먼저 호출되는 `inOrder` 검증을 추가했다. RED는 `VerificationInOrderFailure`로 확인했고, `AudioContentService.pinToTheTop`이 첫 DB 접근에서 locked creator를 가져와 이후 콘텐츠/고정 조회와 저장에 사용하도록 보정했다. `./gradlew test --tests kr.co.vividnext.sodalive.content.AudioContentServiceTest --tests '*shouldReuseOldestActivePinWhenPinningTenthContent' --tests '*shouldKeepNineActivePinsWhenReactivatingInactivePin' --no-daemon` 통과.
|
||||
- 2026-07-31: 2차 리뷰 후속 최종 Gate 확인 - Reviewer gate에서 `P7-R2` 순서 테스트가 `getPinContentList` 호출까지 고정하지 않은 blocker를 확인해 같은 `inOrder`에 추가했고, 재리뷰 PASS를 받았다. 전체 `./gradlew test --no-daemon`은 첫 실행에서 UTC 기본 `now` 변경 영향으로 `CreatorChannelHomeEndToEndTest`의 local-time fixture current live가 `null`이 되어 실패했고, fixture 기준 시각을 `LocalDateTime.now(ZoneOffset.UTC)`로 보정했다. 보정 후 `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.creator.channel.home.CreatorChannelHomeEndToEndTest --no-daemon`, `./gradlew test --no-daemon`, `./gradlew ktlintCheck --no-daemon`, `git diff --check` 통과.
|
||||
- 2026-07-31: Phase 1~7 3차 정적 리뷰 - 기존 Phase별 리뷰 문서에 3차 결과를 누적했다. Phase 1~6은 신규 확정 발견 사항 없음으로 판정했고, `REV-P7-003`(고정 해제가 creator lock을 공유하지 않아 최고령 `PinContent` 행 재사용과 겹치면 새 고정이 소실될 수 있음)을 확정해 Task 7.5 / `P7-R3`으로 전환했다. 사용자 지시에 따라 컴파일·테스트·ktlint은 실행하지 않았다. `rg -n "RecommendedActivityType" src/main/kotlin src/test/kotlin`과 문서 trailing whitespace 검색은 결과 없음, finding ID와 미완료 Task/Goal 연결 확인, `git diff --check -- docs/20260612_크리에이터_채널_홈_API/plan-task.md` 통과.
|
||||
- 2026-07-31: Phase 7 Task 7.5 RED/GREEN 확인 - `AudioContentServiceTest`에 `unpinAtTheTop`이 `memberRepository.findByIdForUpdate`로 크리에이터 lock을 획득한 뒤 `findByContentIdAndMemberId`를 호출하는 `inOrder` 테스트를 추가했다. RED는 `WantedButNotInvoked`로 확인했고, `AudioContentService.unpinAtTheTop`이 첫 DB 접근에서 creator member row lock을 획득한 뒤 잠긴 creator id로 `PinContent`를 조회하도록 최소 보정했다. `./gradlew test --tests kr.co.vividnext.sodalive.content.AudioContentServiceTest --tests '*shouldLockCreatorBeforeFindingPinWhenUnpinningContent' --no-daemon`, `./gradlew test --tests kr.co.vividnext.sodalive.content.AudioContentServiceTest --tests kr.co.vividnext.sodalive.content.AudioContentPinConcurrencyTest --no-daemon`, `./gradlew test --tests kr.co.vividnext.sodalive.v2.creator.channel.home.adapter.out.persistence.DefaultCreatorChannelHomeQueryRepositoryTest --tests kr.co.vividnext.sodalive.v2.creator.channel.home.application.CreatorChannelHomeQueryServiceTest --no-daemon`, `./gradlew ktlintCheck --no-daemon`, `git diff --check` 통과. 사용자 지시에 따라 전체 테스트는 실행하지 않았다.
|
||||
- 2026-07-31: Phase 1~7 4차 정적 리뷰 - 기존 Phase별 리뷰 문서에 4차 결과를 각각 누적했다. PRD·계획·구조 정렬 후속 계약과 현재 domain/port/repository/service/facade/controller, 기존 테스트를 정적 대조한 결과 모든 Phase에서 신규 확정 발견 사항이 없어 후속 Task/Goal은 추가하지 않았다. 사용자 지시에 따라 컴파일·테스트·ktlint은 실행하지 않았다. `rg`로 이전 enum 잔존 여부와 Phase별 4차 절의 단일 존재를 확인했고, trailing whitespace 검색과 `git diff --check`가 통과했다. 문서 가이드에 따른 `./gradlew tasks --all --no-daemon`은 최초 샌드박스의 Gradle lock 접근 제한으로 실패했으나 승인된 캐시 접근으로 재실행해 `BUILD SUCCESSFUL`을 확인했다.
|
||||
|
||||
Reference in New Issue
Block a user