docs(creator): 커뮤니티 후속 작업 기록을 갱신한다
This commit is contained in:
@@ -285,7 +285,694 @@
|
|||||||
- 검증 기록:
|
- 검증 기록:
|
||||||
- 2026-07-12: RED로 `CreatorChannelCommunityDetailViewModelTest.유료 미구매 게시물은 작성자가 아니면 reaction 현황과 댓글을 숨긴다`를 추가했고, production 변경 전 `showReaction` 미구현 컴파일 실패를 확인했다. 이후 상세 UI 모델/상태와 Activity binding에 잠금 조건을 반영해 focused 테스트 GREEN을 확인했다.
|
- 2026-07-12: RED로 `CreatorChannelCommunityDetailViewModelTest.유료 미구매 게시물은 작성자가 아니면 reaction 현황과 댓글을 숨긴다`를 추가했고, production 변경 전 `showReaction` 미구현 컴파일 실패를 확인했다. 이후 상세 UI 모델/상태와 Activity binding에 잠금 조건을 반영해 focused 테스트 GREEN을 확인했다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 15: 커뮤니티 구매 진입점·표시 상태·상세 메뉴 정리
|
||||||
|
|
||||||
|
**Phase 결과:** 홈과 커뮤니티 탭은 게시글 상태를 동일하게 표시하고 상세 이동만 수행하며, 구매와 권한별 관리/신고는 상세에서 수행한다.
|
||||||
|
|
||||||
|
**선행조건:** PRD `F0-20260810` 승인, 홈 Response의 `isPinned`/`isCommentAvailable` 서버 계약 확정.
|
||||||
|
|
||||||
|
**Phase 완료 조건:** `P15-T1`~`P15-T5`와 `P15-GATE` 완료, Task별 검증 기록 및 문서 최하단 `Verification Log` 누적.
|
||||||
|
|
||||||
|
**실행 제약:** 2026-08-10 사용자 지시에 따라 이번 계획 작성 단계에서는 구현과 Git 커밋을 수행하지 않는다. 이후 구현도 별도 사용자 요청 전에는 시작하지 않으며, 이 Phase에는 commit 단계를 두지 않는다.
|
||||||
|
|
||||||
|
#### Phase 15 파일 구조
|
||||||
|
|
||||||
|
- 홈 계약/표시:
|
||||||
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelHomeModels.kt`
|
||||||
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedItem.kt`
|
||||||
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedCommunityView.kt`
|
||||||
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt`
|
||||||
|
- `app/src/main/res/layout/view_feed_community.xml`
|
||||||
|
- 커뮤니티 탭:
|
||||||
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityFragment.kt`
|
||||||
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/model/CreatorChannelCommunityUiModels.kt`
|
||||||
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/model/CreatorChannelCommunityMappers.kt`
|
||||||
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/ui/CreatorChannelCommunityListAdapter.kt`
|
||||||
|
- `app/src/main/res/layout/item_creator_channel_community_list.xml`
|
||||||
|
- 확인만 수행: `app/src/main/res/layout/item_creator_channel_community_grid.xml`
|
||||||
|
- 상세/결과 전파:
|
||||||
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/detail/CreatorChannelCommunityDetailActivity.kt`
|
||||||
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/detail/CreatorChannelCommunityDetailViewModel.kt`
|
||||||
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt`
|
||||||
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
||||||
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/community/action/CommunityChange.kt`
|
||||||
|
- `app/src/main/res/layout/activity_creator_channel_community_detail.xml`
|
||||||
|
- 기존 테스트 수정:
|
||||||
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedItemTest.kt`
|
||||||
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeModelsTest.kt`
|
||||||
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityMapperTest.kt`
|
||||||
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityFragmentLayoutTest.kt`
|
||||||
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityDetailViewModelTest.kt`
|
||||||
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityDetailReplyUiContractTest.kt`
|
||||||
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt`
|
||||||
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/community/action/CommunityChangeTest.kt`
|
||||||
|
|
||||||
|
#### Task 15.1 홈 Response와 게시글 표시 정책
|
||||||
|
|
||||||
|
**Goal 실행 `P15-T1`:** 홈 커뮤니티 Response의 고정/댓글 가능 계약과 잠금·reaction 표시 조건을 순수 로직으로 고정한다.
|
||||||
|
|
||||||
|
- **시작 조건:** PRD `Home Response Contract`, `Home Community Requirements` 승인.
|
||||||
|
- **완료 증거:** 신규 DTO 필드 파싱 및 `FeedItem.Community` 표시 정책 focused test PASS.
|
||||||
|
- **범위 밖:** View binding, 커뮤니티 탭, 상세 메뉴.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelHomeModels.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedItem.kt`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeModelsTest.kt`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedItemTest.kt`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
|
||||||
|
- Consumes: 홈 JSON `isPinned: Boolean`, `isCommentAvailable: Boolean`, 기존 `price`, `existOrdered`.
|
||||||
|
- Produces:
|
||||||
|
|
||||||
|
`FeedItem.Community`에 추가할 member:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val isPinned: Boolean = false
|
||||||
|
val isCommentAvailable: Boolean = true
|
||||||
|
val hideReactionWhenLocked: Boolean = false
|
||||||
|
val isLocked: Boolean
|
||||||
|
val showReaction: Boolean
|
||||||
|
val showComment: Boolean
|
||||||
|
```
|
||||||
|
|
||||||
|
- `isLocked = price > 0 && !existOrdered`
|
||||||
|
- `showReaction = !hideReactionWhenLocked || !isLocked`
|
||||||
|
- `showComment = showReaction && isCommentAvailable`
|
||||||
|
|
||||||
|
- [x] **RED:** `CreatorChannelHomeModelsTest`의 홈 JSON에 `isPinned=true`, `isCommentAvailable=false` 커뮤니티를 넣고 두 필드 보존을 검증한다.
|
||||||
|
- [x] **RED:** `FeedItemTest`에 잠금 숨김 opt-in 유료 미구매는 reaction/comment 모두 false, 기본값 유료 미구매는 기존 reaction/comment 표시 유지, 댓글 불가 무료 글은 reaction true/comment false, 고정 상태 보존 케이스를 추가한다.
|
||||||
|
- [x] **RED 확인:** 아래 명령을 실행해 신규 DTO/property 미존재로 인한 컴파일 또는 assertion 실패를 확인한다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew :app:testDebugUnitTest \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeModelsTest" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedItemTest"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [x] **GREEN:** `CreatorChannelCommunityPostResponse`에 `@SerializedName("isPinned")`, `@SerializedName("isCommentAvailable")` 필드를 추가하고 `FeedItem.Community`에 다른 화면의 기존 표시를 유지하는 기본값 및 위 계산 property를 최소 구현한다.
|
||||||
|
- [x] **GREEN 확인:** 같은 focused test가 PASS하는지 확인한다.
|
||||||
|
- [x] **REFACTOR:** 신규 별도 presentation class를 만들지 않고 `FeedItem.Community` 계산 property만 유지한다.
|
||||||
|
|
||||||
|
- 검증 기록(2026-08-10): DTO/property 추가 전 focused test의 미존재 컴파일 RED를 확인했다. 구현 및 `P15-R3` 범위 보정 후 홈 JSON 신규 필드, 잠금 opt-in/default, 댓글 불가, 고정 상태 테스트가 최종 Gate에서 PASS했다.
|
||||||
|
|
||||||
|
#### Task 15.2 홈 커뮤니티 표시 연결
|
||||||
|
|
||||||
|
**Goal 실행 `P15-T2`:** 홈 커뮤니티 item이 잠금·댓글 가능·고정 상태를 표시하고 card 전체 상세 이동 외의 액션을 제공하지 않는다.
|
||||||
|
|
||||||
|
- **시작 조건:** `P15-T1` 완료.
|
||||||
|
- **완료 증거:** `P15-T1` 정책을 실제 bind에 사용하고 resource merge/Kotlin compile PASS.
|
||||||
|
- **범위 밖:** 커뮤니티 탭 item과 상세 메뉴.
|
||||||
|
- **TDD 예외 사유:** XML view 위치·visibility 자체를 직접 검증하는 테스트는 저장소 규칙상 작성하지 않는다. 표시 분기는 `P15-T1` 순수 로직 test로 검증한다.
|
||||||
|
- **대체 검증 방법:** View binding id 생성, resource merge, Kotlin compile, 기존 `FeedViewTest` 회귀를 실행한다.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedCommunityView.kt`
|
||||||
|
- Modify: `app/src/main/res/layout/view_feed_community.xml`
|
||||||
|
- Verify: `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedViewTest.kt`(수정 없이 기존 회귀만 실행)
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
|
||||||
|
- Consumes: `FeedItem.Community.isPinned`, `showReaction`, `showComment`.
|
||||||
|
- Produces: `FeedCommunityView.bind(item)`의 공지/reaction 상태와 기존 `FeedCommunityView` root click 계약.
|
||||||
|
|
||||||
|
- [x] `CreatorChannelCommunityPostResponse.toFeedCommunityItem()`이 `isPinned`, `isCommentAvailable`을 전달하도록 수정한다.
|
||||||
|
- [x] `view_feed_community.xml`에 커뮤니티 탭과 같은 공지 row와 reaction row/comment icon 식별 id를 추가한다.
|
||||||
|
- [x] `FeedCommunityView.bind()`가 공지 row, reaction row, 댓글 icon/count, 하트 icon/count를 `FeedItem.Community` 계산 property로 bind하도록 수정한다.
|
||||||
|
- [x] `CreatorChannelHomeSectionAdapter.bindCommunities()`의 `row.setOnClickListener { onCommunityClick(community.postId) }`만 item 액션으로 유지한다.
|
||||||
|
- [x] 아래 명령으로 focused 회귀 및 compile을 확인한다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew :app:testDebugUnitTest \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedItemTest" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest"
|
||||||
|
./gradlew :app:mergeDebugResources :app:compileDebugKotlin
|
||||||
|
```
|
||||||
|
|
||||||
|
- 검증 기록(2026-08-10): 홈 mapper/bind/root click을 정적 대조했고 `P15-R2`에서 reaction id 위치를 정정했다. `FeedItemTest`, `FeedViewTest`, resource merge, Kotlin compile이 PASS했다.
|
||||||
|
|
||||||
|
#### Task 15.3 커뮤니티 탭 상세 이동 전용화와 가격 UI 통일
|
||||||
|
|
||||||
|
**Goal 실행 `P15-T3`:** 커뮤니티 탭 리스트/그리드 item 액션을 상세 이동 하나로 제한하고 잠금 가격 UI를 홈/상세 구조로 통일한다.
|
||||||
|
|
||||||
|
- **시작 조건:** 기존 list/grid `onPostClick` 상세 routing 계약 확인.
|
||||||
|
- **완료 증거:** play/owner-more callback 제거, list/grid root 상세 이동 유지, mapper/routing focused test 및 compile PASS.
|
||||||
|
- **범위 밖:** 상세 화면 내부 구매·메뉴 동작.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityFragment.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/model/CreatorChannelCommunityUiModels.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/model/CreatorChannelCommunityMappers.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/ui/CreatorChannelCommunityListAdapter.kt`
|
||||||
|
- Modify: `app/src/main/res/layout/item_creator_channel_community_list.xml`
|
||||||
|
- Verify only: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/ui/CreatorChannelCommunityGridAdapter.kt`
|
||||||
|
- Verify only: `app/src/main/res/layout/item_creator_channel_community_grid.xml`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityMapperTest.kt`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityFragmentLayoutTest.kt`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
|
||||||
|
- Consumes: `CreatorChannelCommunityPostUiModel.postId`, `price`, `isLocked`, `showOwnerTopPrice`.
|
||||||
|
- Produces: list/grid adapter constructor의 단일 item callback `onPostClick: (CreatorChannelCommunityPostUiModel) -> Unit`.
|
||||||
|
|
||||||
|
- [x] **RED:** routing/source 계약 테스트에 Fragment/list adapter가 `onPlayClick`, `onOwnerMoreClick`, `toggleCommunityAudio`, `onCreatorChannelCommunityOwnerMoreClicked`를 갖지 않고 list/grid root가 `onPostClick`만 호출한다는 케이스를 추가한다.
|
||||||
|
- [x] **RED 확인:** 아래 명령으로 현재 하위 액션이 남아 있어 실패함을 확인한다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew :app:testDebugUnitTest \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityMapperTest" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [x] **GREEN:** Fragment의 media player lifecycle/재생 함수와 Host owner-more callback, list adapter의 play/owner-more callback 및 더보기 view를 제거한다.
|
||||||
|
- [x] **GREEN:** UI model/mapper에서 이번 변경으로 미사용이 된 `audioUrl`, `showPlayButton`, `showOwnerMore`만 제거하고 작성자 상단 가격인 `showOwnerTopPrice`는 유지한다.
|
||||||
|
- [x] **GREEN:** 기존 Fragment/layout/source test에서 제거된 play/owner-more의 positive assertion만 삭제하거나 상세 이동 단일 callback의 negative assertion으로 교체한다. View 크기·간격·constraint·visibility 속성 assertion은 새로 추가하지 않는다.
|
||||||
|
- [x] **GREEN:** 리스트 잠금 금액의 compound drawable `TextView`를 그리드/상세와 같은 별도 `ImageView(ic_bar_cash)` + 금액 `TextView` capsule 구조로 변경한다.
|
||||||
|
- [x] **GREEN:** 그리드는 이미 `lock icon + cash icon + 금액 capsule` 구조와 root 상세 이동만 사용하므로 신규 production 변경 없이 계약 일치만 확인한다.
|
||||||
|
- [x] **GREEN 확인:** focused test, `./gradlew :app:mergeDebugResources :app:compileDebugKotlin`을 실행한다.
|
||||||
|
- [x] **REFACTOR:** play/owner-more 제거로 생긴 import·binding·Host 구현 orphan만 삭제하고 다른 탭 코드는 정리하지 않는다.
|
||||||
|
|
||||||
|
- 검증 기록(2026-08-10): 하위 액션 제거 계약의 4개 RED 실패를 확인한 뒤 list/grid root `onPostClick`만 유지했다. mapper/layout focused test, resource merge, Kotlin compile과 최종 Gate가 PASS했다.
|
||||||
|
|
||||||
|
#### Task 15.4 상세 더보기 권한과 게시글 mutation
|
||||||
|
|
||||||
|
**Goal 실행 `P15-T4`:** 상세 작성자 영역 우측 끝에서 권한별 관리/신고 메뉴를 제공하고 성공/실패 상태를 정확히 전파한다.
|
||||||
|
|
||||||
|
- **시작 조건:** 상세 DTO의 `creatorId`, `price`, `existOrdered`, `isPinned` 계약과 기존 레거시 dialog/API 확인.
|
||||||
|
- **완료 증거:** 더보기 노출 4개 권한 케이스, 고정/삭제/신고 API와 event focused test PASS, resource merge/compile PASS.
|
||||||
|
- **범위 밖:** 홈/커뮤니티 탭 새로고침 실행.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/detail/CreatorChannelCommunityDetailViewModel.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/detail/CreatorChannelCommunityDetailActivity.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt`
|
||||||
|
- Modify: `app/src/main/res/layout/activity_creator_channel_community_detail.xml`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityDetailViewModelTest.kt`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityDetailReplyUiContractTest.kt`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
|
||||||
|
- Produces UI model fields:
|
||||||
|
|
||||||
|
`CreatorChannelCommunityPostDetailUiModel`에 추가할 member:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val isOwner: Boolean
|
||||||
|
val showMore: Boolean
|
||||||
|
```
|
||||||
|
|
||||||
|
- `isOwner = creatorId == SharedPreferenceManager.userId`
|
||||||
|
- `showMore = isOwner || price == 0 || existOrdered`
|
||||||
|
- Produces ViewModel methods/events:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun updateCommunityPostFixed()
|
||||||
|
fun deleteCommunityPost()
|
||||||
|
fun reportCommunityPost(reason: String)
|
||||||
|
val postDeletedEventLiveData: LiveData<Boolean>
|
||||||
|
```
|
||||||
|
|
||||||
|
- Produces repository method:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun reportCommunityPost(postId: Long, reason: String, token: String)
|
||||||
|
```
|
||||||
|
|
||||||
|
- [x] **RED:** 상세 ViewModel test에 작성자 유료 미구매=true, 일반 무료=true, 일반 유료 구매=true, 일반 유료 미구매=false인 `showMore` 케이스를 추가한다.
|
||||||
|
- [x] **RED:** 고정 성공은 `isPinned` 반전 요청 후 상세 재조회와 `postChangedEventLiveData=true`, 삭제 성공은 `postDeletedEventLiveData=true`, 신고는 `ReportType.COMMUNITY_POST` repository 호출만 수행하는 케이스를 추가한다.
|
||||||
|
- [x] **RED:** UI 계약 test에 `R.id.btn_creator_channel_community_detail_more` resource와 Activity의 modify launcher/기존 BottomSheet·신고 dialog 연결 계약을 추가한다. 위치·constraint·visibility 값은 test에서 직접 검증하지 않는다.
|
||||||
|
- [x] **RED 확인:** 아래 명령으로 신규 fields/methods 미존재 실패를 확인한다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew :app:testDebugUnitTest --tests \
|
||||||
|
"kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityDetailViewModelTest"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [x] **GREEN:** `CreatorChannelRepository.reportCommunityPost()`가 `ReportRequest(ReportType.COMMUNITY_POST, reason, communityPostId = postId)`를 기존 `ReportRepository`에 위임하도록 추가한다.
|
||||||
|
- [x] **GREEN:** DetailViewModel의 고정은 `updateCommunityPostFixed(postId, !content.post.isPinned, authToken())`, 삭제는 `ModifyCommunityPostRequest(creatorCommunityId = postId, isActive = false)`를 JSON `text/plain` RequestBody로 변환한 `modifyCommunityPost(postImage = null, ...)`, 신고는 신규 report method를 호출한다. 각 mutation의 중복 요청을 막고 실패 시 기존 Toast event만 내보낸다.
|
||||||
|
- [x] **GREEN:** 신고 성공은 API message 또는 기존 `character_comment_report_submitted` 문구를 Toast event로 전달하고 게시글 변경 event는 내보내지 않는다.
|
||||||
|
- [x] **GREEN:** 상세 layout에서 `layout_creator_channel_community_detail_price`와 `tv_creator_channel_community_detail_purchased`를 제거하고, 작성자 영역 우측 끝에 `@drawable/ic_seemore_vertical`, `@string/read_more` content description을 사용하는 `btn_creator_channel_community_detail_more`를 추가한다. 닉네임/작성 시간의 end constraint는 이 버튼 start에 연결한다.
|
||||||
|
- [x] **GREEN:** Activity는 `post.showMore`로 더보기 버튼 visibility를 bind한다.
|
||||||
|
- [x] **GREEN:** Activity에서 `CreatorCommunityPostMenuBottomSheetDialog`를 열어 작성자는 수정/삭제/고정, 일반 사용자는 신고를 연결한다. 수정은 기존 `CreatorCommunityModifyActivity`, 삭제 확인은 `V2ModalDialog`, 신고 입력은 `CreatorCommunityReportDialog`를 재사용한다.
|
||||||
|
- [x] **GREEN:** 상세 paywall의 `PurchaseCommunityPostDialog -> purchaseCommunityPost()` 연결은 유지하고 홈/커뮤니티 탭에는 구매 호출부가 없음을 확인한다.
|
||||||
|
- [x] **GREEN:** 수정 Activity `RESULT_OK`와 고정 성공 시 상세를 재조회하고 상위 `RESULT_OK`를 설정하며, 삭제 event 수신 시 `RESULT_OK` 설정 후 상세를 종료한다.
|
||||||
|
- [x] **GREEN 확인:** focused test, `./gradlew :app:mergeDebugResources :app:compileDebugKotlin`을 실행한다.
|
||||||
|
- [x] **REFACTOR:** 신규 menu abstraction 또는 별도 popup을 만들지 않고 기존 BottomSheet/Dialog를 직접 재사용한다.
|
||||||
|
|
||||||
|
- 검증 기록(2026-08-10): 신규 fields/methods/resource 미존재 RED를 확인한 뒤 권한 truth table, 고정·삭제·신고 성공/실패·중복 guard, 기존 dialog 재사용과 결과 전파를 구현했다. 커뮤니티 focused test와 resource/compile Gate가 PASS했다.
|
||||||
|
|
||||||
|
#### Task 15.5 상위 새로고침 전파와 목록 메뉴 orphan 제거
|
||||||
|
|
||||||
|
**Goal 실행 `P15-T5`:** 상세 변경 결과가 홈·커뮤니티 탭을 함께 갱신하고 목록에 남은 관리 메뉴 코드를 제거한다.
|
||||||
|
|
||||||
|
- **시작 조건:** `P15-T3`, `P15-T4` 완료.
|
||||||
|
- **완료 증거:** 상세 `RESULT_OK -> CommunityChange.Updated -> 홈+커뮤니티 refresh` routing test PASS, 제거된 callback 관련 compile PASS.
|
||||||
|
- **범위 밖:** 신고 후 새로고침, 다른 creator channel 탭.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/community/action/CommunityChange.kt`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/community/action/CommunityChangeTest.kt`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
|
||||||
|
- Consumes: `resolveCommunityActivityResult(CommunityActivityResultSource.Detail, RESULT_OK) == CommunityChange.Updated`.
|
||||||
|
- Produces: `CommunityChange.Updated` 처리 시 `homeActionDelegate?.refreshHome()`과 `refreshCreatorChannelCommunity()` 호출.
|
||||||
|
|
||||||
|
- [x] **RED:** `CreatorChannelActivitySourceTest`에 상세 `Updated` 처리 블록이 홈/커뮤니티 refresh를 모두 호출한다는 routing 계약을 추가한다.
|
||||||
|
- [x] **RED 확인:** 아래 명령으로 현재 `Updated`가 커뮤니티만 refresh하고 목록 전용 change가 남아 있어 실패함을 확인한다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew :app:testDebugUnitTest \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.community.action.CommunityChangeTest"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [x] **GREEN:** `handleCommunityChange(Updated)`가 홈과 커뮤니티를 함께 갱신하도록 변경한다.
|
||||||
|
- [x] **GREEN:** 목록 owner-more 제거로 미사용이 된 `communityPostModifyLauncher`, `onCreatorChannelCommunityOwnerMoreClicked`, 목록용 고정/삭제/dialog 함수와 관련 import/repository 의존을 `CreatorChannelActivity`에서 제거한다.
|
||||||
|
- [x] **GREEN:** 호출자가 사라진 `CommunityChange.Deleted`, `CommunityChange.PinChanged`를 제거하고 `CommunityChangeTest`의 삭제/고정 전용 케이스만 제거한다. `Write -> Created`, `Modify/Detail -> Updated`, 취소 -> `Ignored` 회귀는 유지한다.
|
||||||
|
- [x] **GREEN 확인:** focused test와 `./gradlew :app:compileDebugKotlin`을 실행한다.
|
||||||
|
- [x] **REFACTOR:** 다른 화면에서 사용 중인 `CommunityActionCommand.PostDetail`, `CommunityChange.Created/Updated`는 유지한다.
|
||||||
|
|
||||||
|
- 검증 기록(2026-08-10): routing RED 2건을 확인한 뒤 상세 `RESULT_OK`의 `Updated` 처리에서 홈·커뮤니티를 함께 갱신하고 목록 메뉴 orphan을 제거했다. Activity/action focused test와 Kotlin compile이 PASS했다.
|
||||||
|
|
||||||
|
#### Task 15.R1 목록 메뉴 제거 후 미사용 import 정리
|
||||||
|
|
||||||
|
**Goal 실행 `P15-R1`:** 확정 review 항목 `REV-P15-001`의 미사용 import를 제거해 Phase 15 style Gate를 회복한다.
|
||||||
|
|
||||||
|
- **시작 조건:** `REV-P15-001` 확정, `P15-T5` 목록 메뉴/orphan 제거 완료.
|
||||||
|
- **대상 파일:** `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
||||||
|
- **수정 범위:** `SharedPreferenceManager` 미사용 import 1줄 제거.
|
||||||
|
- **회귀 테스트:** `./gradlew :app:ktlintCheck`, Phase 15 Gate 전체.
|
||||||
|
- **완료 증거:** RED ktlint 정확한 1건 → import 제거 → ktlint/Gate PASS → review 보고서 수정 후 검증 누적.
|
||||||
|
- **범위 밖:** 다른 import 순서, 관련 없는 style 정리, `.editorconfig` deprecation 경고.
|
||||||
|
|
||||||
|
- [x] `./gradlew :app:ktlintCheck`에서 `CreatorChannelActivity.kt:29:1 Unused import`만 확정된 RED를 기록한다.
|
||||||
|
- [x] `SharedPreferenceManager` import 1줄만 제거한다.
|
||||||
|
- [x] `./gradlew :app:ktlintCheck`와 Phase 15 Gate를 재실행한다.
|
||||||
|
- [x] `plan-task.md` Verification Log와 `phase15-community-purchase-detail-menu-review.md`에 완료 증거를 누적한다.
|
||||||
|
|
||||||
|
- 검증 기록(2026-08-10): 최초 Gate에서 `CreatorChannelActivity.kt:29:1 Unused import` RED를 확인하고 해당 import 1줄만 제거했다. 최종 `ktlintCheck`와 Phase 15 Gate가 PASS했다.
|
||||||
|
|
||||||
|
#### Task 15.R2 홈 reaction row binding 대상 정정
|
||||||
|
|
||||||
|
**Goal 실행 `P15-R2`:** 확정 review 항목 `REV-P15-002`의 잘못된 `ll_feed_community_reaction` id 위치를 정정해 유료 미구매 홈 item의 reaction UI만 숨긴다.
|
||||||
|
|
||||||
|
- **시작 조건:** `REV-P15-002` 확정, `P15-T2` 표시 정책 구현 완료.
|
||||||
|
- **대상 파일:** `app/src/main/res/layout/view_feed_community.xml`
|
||||||
|
- **수정 범위:** `ll_feed_community_reaction` id를 작성자 row에서 하트·댓글 reaction row로 이동.
|
||||||
|
- **회귀 검증:** `FeedItemTest`, `FeedViewTest`, `:app:mergeDebugResources`, `:app:compileDebugKotlin`, Phase 15 Gate, XML tree 정적 재확인.
|
||||||
|
- **완료 증거:** 수정 전 XML tree에서 id가 프로필 row를 가리키는 근거 → id 1개 이동 → focused 회귀/Gate PASS → 리뷰 보고서 누적.
|
||||||
|
- **TDD 예외 사유:** 저장소 규칙상 View visibility·layout 속성을 직접 검증하는 신규 테스트를 추가하지 않는다. 표시 분기는 기존 `FeedItemTest`, binding/resource는 resource merge·compile·정적 XML tree 검토로 대체한다.
|
||||||
|
- **범위 밖:** 홈 card 간격·크기·제약, 다른 row 레이아웃.
|
||||||
|
|
||||||
|
- [x] 수정 전 `ll_feed_community_reaction` id가 프로필 row에 있고 실제 reaction row에는 id가 없는 근거를 기록한다.
|
||||||
|
- [x] id를 실제 reaction row로 이동한다.
|
||||||
|
- [x] focused 회귀, resource merge/compile, Phase 15 Gate를 재실행한다.
|
||||||
|
- [x] `plan-task.md` Verification Log와 review 보고서에 완료 증거를 누적한다.
|
||||||
|
|
||||||
|
- 검증 기록(2026-08-10): 수정 전 id가 프로필 row에 연결되고 실제 reaction row에는 없음을 XML tree로 확인했다. id 1개를 실제 reaction row로 이동한 뒤 focused test, resource merge, Kotlin compile과 최종 Gate가 PASS했다.
|
||||||
|
|
||||||
|
#### Task 15.R3 공유 FeedItem reaction 정책 범위 정정
|
||||||
|
|
||||||
|
**Goal 실행 `P15-R3`:** 확정 review 항목 `REV-P15-003`의 공유 모델 회귀를 제거해 크리에이터 채널 홈에서만 유료 미구매 reaction을 숨기고 다른 `FeedItem.Community` 호출부는 기존 표시 동작을 유지한다.
|
||||||
|
|
||||||
|
- **시작 조건:** `REV-P15-003` 확정, `P15-T1`~`P15-T2` 표시 정책 구현 완료.
|
||||||
|
- **대상 파일:** `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedItem.kt`, `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt`, `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedItemTest.kt`, `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLayoutTest.kt`
|
||||||
|
- **수정 범위:** `FeedItem.Community` 신규 정책은 기본값으로 기존 reaction을 유지하고, 크리에이터 채널 홈 mapper만 잠금 시 숨김을 명시하도록 제한.
|
||||||
|
- **회귀 테스트:** 잠금 item의 기본값은 reaction/comment 표시 유지, 크리에이터 채널 홈 opt-in은 reaction/comment 숨김, 기존 댓글 불가·고정 상태 테스트, Phase 15 Gate.
|
||||||
|
- **완료 증거:** 메인 홈 인기 커뮤니티 mapper와 공유 `FeedCommunityView` 호출 경로 확인 → 기본값 회귀 테스트 RED → 최소 opt-in 구현 → focused 회귀/Gate PASS → 리뷰 보고서 누적.
|
||||||
|
- **범위 밖:** 메인 홈 인기 커뮤니티의 이미지 잠금/가격 정책, 다른 feed UI 변경.
|
||||||
|
|
||||||
|
- [x] 메인 홈 인기 커뮤니티가 `price`, `existOrdered`를 전달하고 공유 `FeedCommunityView.bind()`를 사용해 기존 reaction까지 숨겨지는 호출 경로를 기록한다.
|
||||||
|
- [x] **RED:** 잠금 유료 `FeedItem.Community`가 신규 정책 기본값에서는 기존 reaction/comment 표시를 유지하고, opt-in에서는 둘 다 숨기는 회귀 테스트를 추가해 실패를 확인한다.
|
||||||
|
- [x] **GREEN:** 신규 정책 기본값은 기존 표시를 유지하고 `CreatorChannelHomeSectionAdapter`만 잠금 reaction 숨김을 선택하도록 최소 수정한다.
|
||||||
|
- [x] focused 회귀와 Phase 15 Gate를 재실행한다.
|
||||||
|
- [x] `plan-task.md` Verification Log와 review 보고서에 완료 증거를 누적한다.
|
||||||
|
|
||||||
|
- 검증 기록(2026-08-10): `HomeRecommendationMappers -> FeedItem.Community -> HomePopularCommunityAdapter -> FeedCommunityView.bind()` 호출 경로로 공유 정책 회귀를 확정했다. `hideReactionWhenLocked` 미존재 컴파일 RED 후 기본값 false, 크리에이터 채널 홈 true로 제한했고 `FeedItemTest`, 메인 홈 mapper 회귀 테스트와 최종 Gate가 PASS했다.
|
||||||
|
|
||||||
|
#### Task 15.R4 메인 홈 회귀 테스트 파일 미사용 import 정리
|
||||||
|
|
||||||
|
**Goal 실행 `P15-R4`:** 확정 review 항목 `REV-P15-004`의 미사용 import를 제거해 Phase 15 style Gate를 회복한다.
|
||||||
|
|
||||||
|
- **시작 조건:** `P15-R3` 메인 홈 회귀 테스트 추가 후 `ktlintCheck` 실패 확정.
|
||||||
|
- **대상 파일:** `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLayoutTest.kt`
|
||||||
|
- **수정 범위:** 실제 사용처가 없는 `ColorDrawable` import 1줄 제거.
|
||||||
|
- **회귀 검증:** `./gradlew :app:ktlintCheck`, Phase 15 Gate, `git diff --check`.
|
||||||
|
- **완료 증거:** ktlint 정확한 unused import 1건 → import 제거 → ktlint/Gate PASS → 리뷰 보고서 누적.
|
||||||
|
- **범위 밖:** 같은 테스트 파일의 기존 구조·deprecated API 정리.
|
||||||
|
|
||||||
|
- [x] `ktlintCheck`의 `HomeMainFragmentLayoutTest.kt:6:1 Unused import` RED를 기록한다.
|
||||||
|
- [x] `ColorDrawable` import 1줄만 제거한다.
|
||||||
|
- [x] `ktlintCheck`와 Phase 15 Gate를 재실행한다.
|
||||||
|
- [x] `plan-task.md` Verification Log와 review 보고서에 완료 증거를 누적한다.
|
||||||
|
|
||||||
|
- 검증 기록(2026-08-10): 메인 홈 회귀 테스트 추가 후 lint 대상이 된 파일에서 사용처 없는 `ColorDrawable` import 1건을 확인해 제거했다. 최종 `ktlintCheck`와 전체 focused test Gate가 PASS했다.
|
||||||
|
|
||||||
|
#### Phase 15 Gate
|
||||||
|
|
||||||
|
**Goal 실행 `P15-GATE`:** 승인된 홈/탭/상세 사용자 흐름과 회귀 방지를 최종 판정한다.
|
||||||
|
|
||||||
|
- **시작 조건:** `P15-T1`~`P15-T5` 완료.
|
||||||
|
- **완료 증거:** 아래 명령을 순차 실행해 모두 exit code 0을 확인하고 각 Task 검증 기록 및 `Verification Log`에 실제 결과를 누적한다.
|
||||||
|
- **범위 밖:** `app/src/androidTest`, `connectedDebugAndroidTest`, 기기·에뮬레이터 조작, 스크린샷·시각 QA, Git commit.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew :app:testDebugUnitTest \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedItemTest" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeModelsTest" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLayoutTest"
|
||||||
|
./gradlew :app:testDebugUnitTest \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.community.action.*"
|
||||||
|
./gradlew :app:testDebugUnitTest --tests \
|
||||||
|
"kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"
|
||||||
|
./gradlew :app:mergeDebugResources
|
||||||
|
./gradlew :app:compileDebugKotlin
|
||||||
|
./gradlew :app:ktlintCheck
|
||||||
|
git diff --check
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected:** 홈 Response 신규 필드와 reaction/pin 정책, list/grid 상세 이동 전용화, 상세 더보기 권한·구매/관리/신고, 상세 변경 후 홈+커뮤니티 refresh 관련 focused test PASS. 리소스 병합·Kotlin 컴파일·ktlint·diff whitespace 검증 PASS.
|
||||||
|
|
||||||
|
#### Phase 15 실행 순서
|
||||||
|
|
||||||
|
```text
|
||||||
|
P15-T1 → P15-T2 → P15-T3 → P15-T4 → P15-T5 → P15-R1 → P15-R2 → P15-R3 → P15-R4 → P15-GATE
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Phase 15 계획 기록 — 2026-08-10
|
||||||
|
|
||||||
|
- 상태: 계획 완료 / 구현 미시작
|
||||||
|
- 무엇을: 승인된 PRD `F0-20260810`을 `P15-T1`~`P15-T5`, `P15-GATE`로 분해했다.
|
||||||
|
- 왜: 홈·커뮤니티 탭의 표시/액션 일관성과 상세 구매·메뉴·결과 전파를 구현 전에 검증 가능한 단위로 고정하기 위해서다.
|
||||||
|
- 어떻게:
|
||||||
|
- `git diff --check` — 문서 변경 whitespace 검증 PASS.
|
||||||
|
- `./gradlew tasks --all` — exit code 0, `BUILD SUCCESSFUL`.
|
||||||
|
- 남은 항목: Phase 15 구현 전체. 사용자 지시에 따라 이번 작업에서는 수행하지 않는다.
|
||||||
|
- 다음 행동: 사용자가 구현을 별도로 요청하면 `P15-T1` RED부터 순차 실행한다.
|
||||||
|
|
||||||
|
#### Phase 15 구현·검증 기록 — 2026-08-10
|
||||||
|
|
||||||
|
- 상태: 구현 및 검증 완료 / Git commit 미수행
|
||||||
|
- 무엇을: `P15-T1`~`P15-T5`를 구현하고 리뷰 확정 항목 `P15-R1`~`P15-R4`를 수정했다.
|
||||||
|
- 왜: 미체크 항목이 구현 누락이었음을 코드·테스트 대조로 확인했고, 구현 후 독립 리뷰와 Gate에서 실제 회귀·style 문제를 확인했기 때문이다.
|
||||||
|
- 어떻게:
|
||||||
|
- 홈 Response에 `isPinned`, `isCommentAvailable`을 연결하고 크리에이터 채널 홈만 유료 미구매 reaction 숨김을 opt-in했다.
|
||||||
|
- 홈·커뮤니티 탭 item 액션을 상세 이동으로 제한하고 상세에 구매·권한별 더보기·변경 결과 전파를 모았다.
|
||||||
|
- `REV-P15-001`~`REV-P15-004`를 각각 회귀 Task로 전환한 뒤 최소 수정했다.
|
||||||
|
- 전체 focused local unit/source contract test, resource merge, Kotlin compile, ktlint를 최종 working tree에서 재실행해 `BUILD SUCCESSFUL`을 확인했고 `git diff --check`, `git diff HEAD --check`는 출력 없이 PASS했다.
|
||||||
|
- 제외: 저장소 규칙과 사용자 지시에 따라 `app/src/androidTest`, 기기·에뮬레이터 조작, 스크린샷·시각 QA, Git commit은 수행하지 않았다.
|
||||||
|
- 남은 항목: 실제 서버 Response 배포 상태와 기기 수동 UI 확인은 이번 자동 검증 범위 밖이다.
|
||||||
|
|
||||||
|
## Phase 16 — 홈·커뮤니티 리스트 권한별 더보기 메뉴
|
||||||
|
|
||||||
|
**Phase 결과:** 상세와 동일한 권한별 게시글 더보기 메뉴를 홈 커뮤니티 item과 커뮤니티 탭 리스트형에서 사용할 수 있고, 성공한 관리 작업은 두 목록에 함께 반영된다.
|
||||||
|
|
||||||
|
**선행조건:** 승인된 PRD `Home and Community List More Menu Requirements`, Phase 15 구현·검증 완료.
|
||||||
|
|
||||||
|
**Phase 완료 조건:** `P16-T1`~`P16-T3`, `P16-GATE` 완료와 Task별 검증 기록 및 `Verification Log` 누적.
|
||||||
|
|
||||||
|
### 범위
|
||||||
|
|
||||||
|
**포함**
|
||||||
|
|
||||||
|
- 홈 커뮤니티 item과 커뮤니티 탭 리스트형 작성자 영역 우측 끝 `ic_seemore_vertical` 버튼.
|
||||||
|
- 상세와 동일한 작성자/무료/구매 완료/유료 미구매 권한 truth table.
|
||||||
|
- 작성자 수정·삭제·고정/고정 해제, 일반 사용자 신고.
|
||||||
|
- 성공한 수정·삭제·고정/고정 해제 후 홈+커뮤니티 새로고침.
|
||||||
|
- card 본문 상세 이동과 더보기 메뉴 click 분리.
|
||||||
|
|
||||||
|
**제외**
|
||||||
|
|
||||||
|
- 커뮤니티 탭 썸네일형(그리드형) 더보기 버튼.
|
||||||
|
- 목록에서 유료 게시글 구매·오디오 재생.
|
||||||
|
- 상세 메뉴 구조 변경, 레거시 파일 수정, 신규 범용 coordinator/dependency.
|
||||||
|
- `app/src/androidTest`, 기기·에뮬레이터 조작, 스크린샷·시각 QA, Git commit.
|
||||||
|
|
||||||
|
### Task 16.1 공용 메뉴 권한 presentation 계약
|
||||||
|
|
||||||
|
**Goal 실행 `P16-T1`:** 홈과 커뮤니티 리스트가 동일한 메뉴 payload와 권한 계산을 사용하고 다른 공유 feed는 기존 표시를 유지한다.
|
||||||
|
|
||||||
|
- **시작 조건:** PRD 권한 truth table 승인.
|
||||||
|
- **완료 증거:** 메뉴 권한 4개 케이스와 홈/커뮤니티 mapper focused test PASS.
|
||||||
|
- **범위 밖:** 버튼 layout/click, Activity menu/API 실행.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
|
||||||
|
- Create: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/model/CreatorChannelCommunityMenuItem.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/model/CreatorChannelCommunityUiModels.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/model/CreatorChannelCommunityMappers.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/model/CreatorChannelHomeUiModels.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/model/CreatorChannelHomeMappers.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedItem.kt`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityMapperTest.kt`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeMapperTest.kt`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedItemTest.kt`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
data class CreatorChannelCommunityMenuItem(
|
||||||
|
val postId: Long,
|
||||||
|
val creatorId: Long,
|
||||||
|
val currentUserId: Long,
|
||||||
|
val price: Int,
|
||||||
|
val existOrdered: Boolean,
|
||||||
|
val isPinned: Boolean
|
||||||
|
) {
|
||||||
|
val isOwner: Boolean
|
||||||
|
val showMore: Boolean
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `isOwner = creatorId == currentUserId`
|
||||||
|
- `showMore = isOwner || price == 0 || existOrdered`
|
||||||
|
- `CreatorChannelCommunityPostUiModel`은 `menuItem: CreatorChannelCommunityMenuItem`을 제공한다.
|
||||||
|
- `CreatorChannelHomeSection.Communities`는 `currentMemberId: Long`을 함께 보존해 adapter가 같은 menu item을 만든다.
|
||||||
|
- `FeedItem.Community`는 `showMore: Boolean = false`를 추가해 다른 공유 feed 호출부의 기존 동작을 유지한다.
|
||||||
|
|
||||||
|
- [x] **RED:** `CreatorChannelCommunityMapperTest`에 작성자 유료 미구매=true, 일반 무료=true, 일반 유료 구매=true, 일반 유료 미구매=false인 `menuItem.showMore`와 `isPinned` 보존 케이스를 추가한다.
|
||||||
|
- [x] **RED:** `CreatorChannelHomeMapperTest`에 `Communities.currentMemberId` 보존, `FeedItemTest`에 `showMore` 기본값 false 회귀를 추가한다.
|
||||||
|
- [x] **RED 확인:** 아래 명령을 실행해 신규 type/property 미존재 컴파일 실패를 확인한다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew :app:testDebugUnitTest \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityMapperTest" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeMapperTest" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedItemTest"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [x] **GREEN:** 위 `CreatorChannelCommunityMenuItem` 계산 property를 추가하고 커뮤니티 mapper/UI model과 홈 `Communities.currentMemberId`를 연결한다.
|
||||||
|
- [x] **GREEN:** `FeedItem.Community.showMore`를 기본값 false로 추가한다.
|
||||||
|
- [x] **GREEN 확인:** 같은 focused test가 PASS하는지 확인한다.
|
||||||
|
- [x] **REFACTOR:** 별도 권한 service/factory를 만들지 않고 menu item 계산 property 하나만 공용 정책으로 유지한다.
|
||||||
|
|
||||||
|
- 검증 기록(2026-08-10): `menuItem`, `currentMemberId`, `showMore` 미존재로 `compileDebugUnitTestKotlin` RED 실패를 확인했다. `CreatorChannelCommunityMenuItem` 계산 property와 커뮤니티/홈 mapper 연결, `FeedItem.Community.showMore=false` 기본값을 최소 구현한 뒤 지정된 focused test 26개가 모두 PASS했다. 명세·코드 품질 독립 검토에서도 차단 이슈가 없음을 확인했다.
|
||||||
|
|
||||||
|
### Task 16.2 홈·커뮤니티 리스트 더보기 버튼과 callback
|
||||||
|
|
||||||
|
**Goal 실행 `P16-T2`:** 홈 커뮤니티 item과 커뮤니티 리스트형의 더보기 버튼이 card 상세 이동과 분리된 공용 menu callback을 호출한다.
|
||||||
|
|
||||||
|
- **시작 조건:** `P16-T1` 완료.
|
||||||
|
- **완료 증거:** 홈/리스트 callback routing focused test, resource merge, Kotlin compile PASS와 grid 무변경 확인.
|
||||||
|
- **범위 밖:** BottomSheet 표시와 mutation API.
|
||||||
|
- **TDD 예외 사유:** 버튼 위치·constraint·visibility 속성 자체는 저장소 규칙상 자동 테스트에서 직접 검증하지 않는다.
|
||||||
|
- **대체 검증 방법:** 권한 visibility는 `P16-T1` 순수 로직 test, callback은 click/source contract test, UI resource는 id 존재·resource merge·compile·정적 XML tree 대조로 검증한다.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
|
||||||
|
- Modify: `app/src/main/res/layout/view_feed_community.xml`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedCommunityView.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt`
|
||||||
|
- Modify: `app/src/main/res/layout/item_creator_channel_community_list.xml`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/ui/CreatorChannelCommunityListAdapter.kt`
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityFragment.kt`
|
||||||
|
- Verify only: `app/src/main/res/layout/item_creator_channel_community_grid.xml`
|
||||||
|
- Verify only: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/ui/CreatorChannelCommunityGridAdapter.kt`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedViewTest.kt`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityFragmentLayoutTest.kt`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun FeedCommunityView.setOnMoreClick(listener: ((FeedItem.Community) -> Unit)?)
|
||||||
|
|
||||||
|
CreatorChannelHomeSectionAdapter(
|
||||||
|
onCommunityClick: (Long) -> Unit,
|
||||||
|
onCommunityMoreClick: (CreatorChannelCommunityMenuItem) -> Unit
|
||||||
|
)
|
||||||
|
|
||||||
|
CreatorChannelCommunityListAdapter(
|
||||||
|
onPostClick: (CreatorChannelCommunityPostUiModel) -> Unit,
|
||||||
|
onMoreClick: (CreatorChannelCommunityMenuItem) -> Unit
|
||||||
|
)
|
||||||
|
|
||||||
|
fun onCreatorChannelCommunityMoreClicked(item: CreatorChannelCommunityMenuItem)
|
||||||
|
```
|
||||||
|
|
||||||
|
- [x] **RED:** `FeedViewTest`에 `btn_feed_community_more` resource와 `setOnMoreClick`이 bound item만 전달하는 callback 케이스를 추가한다.
|
||||||
|
- [x] **RED:** `CreatorChannelCommunityFragmentLayoutTest`와 `CreatorChannelActivitySourceTest`에 홈/리스트 adapter → Fragment Host의 동일 menu callback, list root 상세 click 유지, grid menu callback 부재 계약을 추가한다.
|
||||||
|
- [x] **RED 확인:** 아래 명령으로 신규 resource/API/callback 미존재 실패를 확인한다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew :app:testDebugUnitTest \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [x] **GREEN:** `view_feed_community.xml` 작성자 row를 전체 폭으로 유지하고 우측 끝에 42dp `btn_feed_community_more`(`ic_seemore_vertical`, `read_more`)를 추가한다. `FeedCommunityView.bind()`는 `item.showMore`를 bind하고 별도 more click을 적용한다.
|
||||||
|
- [x] **GREEN:** 홈 adapter는 `Communities.currentMemberId`로 menu item을 만들고 `FeedItem.showMore`, `setOnMoreClick`, `onCommunityMoreClick`을 연결한다. HomeFragment Host로 같은 menu item을 전달한다.
|
||||||
|
- [x] **GREEN:** 커뮤니티 리스트 layout의 기존 top actions 끝에 42dp more 버튼을 추가하고 wrapper는 `showOwnerTopPrice || menuItem.showMore`일 때 표시한다. list adapter/Fragment Host에 `onMoreClick`을 연결한다.
|
||||||
|
- [x] **GREEN:** root `onCommunityClick`/`onPostClick`은 상세 이동을 유지하고 more 버튼은 메뉴 callback만 호출한다.
|
||||||
|
- [x] **GREEN:** grid layout/adapter에는 more id/callback을 추가하지 않는다.
|
||||||
|
- [x] **GREEN 확인:** focused test와 `./gradlew :app:mergeDebugResources :app:compileDebugKotlin`이 PASS하는지 확인한다.
|
||||||
|
- [x] **REFACTOR:** 이번 Task가 만든 listener/import/binding 중복만 정리하고 공유 View의 다른 호출부 동작은 변경하지 않는다.
|
||||||
|
|
||||||
|
- 검증 기록(2026-08-10): `btn_feed_community_more`, `btn_creator_channel_community_list_more`, `setOnMoreClick` 미존재로 `compileDebugUnitTestKotlin` RED 실패를 확인했다. 두 42dp 버튼과 홈/리스트의 동일 `CreatorChannelCommunityMenuItem` Host callback을 최소 연결한 뒤 지정된 focused test 119개가 모두 PASS했다. `:app:mergeDebugResources :app:compileDebugKotlin`도 PASS했으며 grid XML/adapter에는 more id·callback이 없음을 정적 확인했다.
|
||||||
|
|
||||||
|
### Task 16.3 Activity 공용 메뉴와 mutation 결과 전파
|
||||||
|
|
||||||
|
**Goal 실행 `P16-T3`:** 홈과 커뮤니티 리스트의 공용 callback이 상세와 같은 BottomSheet/Dialog/API를 실행하고 성공한 관리 작업만 두 목록을 갱신한다.
|
||||||
|
|
||||||
|
- **시작 조건:** `P16-T2` 완료, Phase 15 상세 메뉴/API 계약 유지.
|
||||||
|
- **완료 증거:** Activity source/routing focused test와 전체 community 회귀, compile PASS.
|
||||||
|
- **범위 밖:** 상세 ViewModel/Activity 메뉴 변경, 신고 후 목록 새로고침.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
|
||||||
|
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
||||||
|
- Reuse: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt`
|
||||||
|
- Reuse: 레거시 `CreatorCommunityPostMenuBottomSheetDialog`, `CreatorCommunityReportDialog`, `CreatorCommunityModifyActivity`, `CreatorCommunityRepository`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt`
|
||||||
|
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/community/action/CommunityChangeTest.kt`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
override fun onCreatorChannelCommunityMoreClicked(item: CreatorChannelCommunityMenuItem)
|
||||||
|
|
||||||
|
private fun updateCreatorChannelCommunityPostFixed(item: CreatorChannelCommunityMenuItem)
|
||||||
|
private fun deleteCreatorChannelCommunityPost(item: CreatorChannelCommunityMenuItem)
|
||||||
|
private fun reportCreatorChannelCommunityPost(item: CreatorChannelCommunityMenuItem, reason: String)
|
||||||
|
```
|
||||||
|
|
||||||
|
- Activity는 `CreatorCommunityRepository`와 기존 `CreatorChannelRepository.reportCommunityPost()`를 주입해 재사용한다.
|
||||||
|
- 수정 launcher의 `RESULT_OK`, 삭제·고정 성공은 기존 `handleCommunityChange(CommunityChange.Updated)`로 홈+커뮤니티를 갱신한다.
|
||||||
|
- 신고 성공은 API message 또는 `character_comment_report_submitted` Toast만 표시하고 새로고침하지 않는다.
|
||||||
|
- `isCommunityPostMutating` 단일 guard로 고정·삭제·신고 중복 요청을 막고 성공/실패 모두 guard를 해제한다.
|
||||||
|
|
||||||
|
- [x] **RED:** `CreatorChannelActivitySourceTest`에 HomeFragment.Host와 CommunityFragment.Host의 동일 override 하나, 권한 guard, 기존 BottomSheet/Dialog/modify launcher 연결 계약을 추가한다.
|
||||||
|
- [x] **RED:** 같은 test에 고정 `!item.isPinned`, 삭제 `ModifyCommunityPostRequest(isActive=false)`, 신고 `reportCommunityPost`, 성공 `CommunityChange.Updated`, 신고 성공 no-refresh, 중복 guard 계약을 추가한다.
|
||||||
|
- [x] **RED 확인:** 아래 명령으로 Activity 공용 메뉴/mutation 연결 미존재 실패를 확인한다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew :app:testDebugUnitTest \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.community.action.CommunityChangeTest"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [x] **GREEN:** Activity에 modify launcher와 `CreatorCommunityPostMenuBottomSheetDialog`를 연결하고 `item.showMore`가 false면 menu를 시작하지 않는다.
|
||||||
|
- [x] **GREEN:** 작성자 menu는 수정·삭제·고정/고정 해제, 일반 사용자 menu는 `CreatorCommunityReportDialog` 신고만 연결한다. mutation method는 메뉴 역할과 `item.isOwner`를 다시 확인한다.
|
||||||
|
- [x] **GREEN:** 고정은 `updateCommunityPostFixed(item.postId, !item.isPinned, authToken())`, 삭제는 `ModifyCommunityPostRequest(creatorCommunityId = item.postId, isActive = false)` JSON `text/plain` 요청, 신고는 `CreatorChannelRepository.reportCommunityPost(item.postId, reason, authToken())`를 호출한다.
|
||||||
|
- [x] **GREEN:** 수정 복귀·고정·삭제 성공만 `CommunityChange.Updated`로 홈+커뮤니티를 갱신하고 실패/신고는 목록 상태를 변경하지 않는다.
|
||||||
|
- [x] **GREEN 확인:** focused test, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"`, `./gradlew :app:compileDebugKotlin`이 PASS하는지 확인한다.
|
||||||
|
- [x] **REFACTOR:** 상세 메뉴 코드를 옮기거나 신규 coordinator를 만들지 않고 Activity의 공용 목록 처리만 유지한다.
|
||||||
|
|
||||||
|
- 검증 기록(2026-08-10): Activity 공용 메뉴와 mutation source 계약 3개를 먼저 추가한 뒤 focused test에서 기존 100개는 통과하고 신규 3개가 `item.showMore` guard 및 mutation method/guard 미존재로 RED 실패함을 확인했다. 기존 BottomSheet/Dialog/modify launcher와 legacy/V2 repository를 Activity에 직접 연결한 뒤 focused test 103개, community wildcard 회귀 86개가 모두 PASS했다. `:app:compileDebugKotlin`도 PASS했으며 수정·고정·삭제 성공만 `CommunityChange.Updated`를 호출하고 신고 성공/모든 실패 경로에는 refresh가 없음을 source test로 확인했다.
|
||||||
|
- 리뷰 회귀 수정 검증 기록(2026-08-10): 게시물 작성자 권한은 채널 소유자 상태와 독립된 `item.isOwner`라는 PRD 계약에 맞춰 source test를 먼저 변경했고, 현재 `isCreatorChannelOwner()` 추가 조건 때문에 focused test 103개 중 권한 계약 3개가 RED 실패함을 확인했다. 수정·고정·삭제·신고 경로에서 채널 소유자 조건만 제거한 뒤 focused test 103개와 community wildcard 회귀 86개가 모두 PASS했고 `:app:compileDebugKotlin`도 PASS했다.
|
||||||
|
|
||||||
|
### Task 16.R1 Phase 완료 문서 정합성 복구
|
||||||
|
|
||||||
|
**Goal 실행 `P16-R1`:** 확정 review 항목 `REV-P16-001`에 따라 Phase 16 구현·Gate 완료 상태와 검증 증거를 문서에 누적한다.
|
||||||
|
|
||||||
|
- **시작 조건:** `P16-T1`~`P16-T3`, `P16-GATE` 실행 완료와 `REV-P16-001` 확정.
|
||||||
|
- **대상 파일:** `docs/20260708_커뮤니티_게시물_상세_댓글_답글/plan-task.md`, `reviews/phase16-home-community-list-more-menu-review.md`.
|
||||||
|
- **수정 범위:** 기존 계획 기록은 보존하고 구현·Gate 완료 기록, `Verification Log`, 리뷰 판정만 누적한다.
|
||||||
|
- **회귀 검증:** `git diff --check`, 최종 리뷰 재검토.
|
||||||
|
- **완료 증거:** 미시작 상태와 Gate 기록 누락 근거 → 완료 기록 누적 → diff check PASS → 5개 리뷰 lane 최종 판정.
|
||||||
|
- **범위 밖:** production/test 코드 변경, Gate 재실행을 위한 테스트 완화, Git commit.
|
||||||
|
|
||||||
|
- [x] Phase 16 구현·검증 완료 기록을 기존 계획 기록 뒤에 누적한다.
|
||||||
|
- [x] `Verification Log`에 focused test, resource merge, compile, ktlint, diff check 결과와 제외 범위를 누적한다.
|
||||||
|
- [x] Phase 16 리뷰 보고서에 `REV-P16-001` 수정 및 재검토 결과를 누적한다.
|
||||||
|
- [x] `git diff --check`와 최종 리뷰 재검토를 통과한다.
|
||||||
|
|
||||||
|
- 검증 기록(2026-08-10): Phase 16 완료 기록과 `Verification Log`를 누적한 뒤 `git diff --check`, `git diff HEAD --check`가 출력 없이 PASS했다. 최종 재검토에서 목표·실행 QA·코드 품질·보안·맥락 5개 lane이 모두 PASS했으며 추가 차단 이슈가 없음을 확인했다.
|
||||||
|
|
||||||
|
### Phase 16 Gate
|
||||||
|
|
||||||
|
**Goal 실행 `P16-GATE`:** 권한별 버튼·메뉴·mutation·새로고침과 grid 제외, 기존 상세/구매 회귀를 최종 판정한다.
|
||||||
|
|
||||||
|
- **시작 조건:** `P16-T1`~`P16-T3` 완료.
|
||||||
|
- **완료 증거:** 아래 명령의 exit code 0, 정적 권한/클릭 대조, Task별 검증 기록과 `Verification Log` 누적.
|
||||||
|
- **범위 밖:** Gate 통과를 위한 test 완화, 기기 UI 조작, Git commit.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew :app:testDebugUnitTest \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedItemTest" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeMapperTest" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"
|
||||||
|
./gradlew :app:testDebugUnitTest \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*" \
|
||||||
|
--tests "kr.co.vividnext.sodalive.v2.community.action.*"
|
||||||
|
./gradlew :app:mergeDebugResources
|
||||||
|
./gradlew :app:compileDebugKotlin
|
||||||
|
./gradlew :app:ktlintCheck
|
||||||
|
git diff --check
|
||||||
|
git diff HEAD --check
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected:** 홈·커뮤니티 리스트형은 동일 권한 메뉴를 제공하고 card root 상세 이동을 유지한다. grid에는 메뉴가 없고, 미구매 유료 일반 사용자는 버튼이 숨겨진다. 수정·삭제·고정 성공은 홈+커뮤니티를 갱신하며 신고/실패는 갱신하지 않는다. 기존 상세 구매·메뉴 focused 회귀와 resource/compile/style/whitespace Gate가 모두 PASS한다.
|
||||||
|
|
||||||
|
### Phase 16 실행 순서
|
||||||
|
|
||||||
|
```text
|
||||||
|
P16-T1 → P16-T2 → P16-T3 → P16-GATE
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 16 계획 기록 — 2026-08-10
|
||||||
|
|
||||||
|
- 상태: 계획 완료 / 구현 미시작
|
||||||
|
- 무엇을: 승인된 홈·커뮤니티 리스트형 권한별 더보기 후속 요구를 `P16-T1`~`P16-T3`, `P16-GATE`로 분해했다.
|
||||||
|
- 왜: 공용 권한 계산 → UI callback → Activity mutation 순으로 계약을 분리해 grid 제외와 상세 이동 회귀를 검증하기 위해서다.
|
||||||
|
- 남은 항목: 사용자 계획 검토 승인 후 `P16-T1` RED부터 순차 실행한다.
|
||||||
|
- Git commit: 사용자 지시에 따라 수행하지 않는다.
|
||||||
|
|
||||||
|
### Phase 16 구현·검증 기록 — 2026-08-10
|
||||||
|
|
||||||
|
- 상태: 구현 및 Gate 완료 / Git commit 미수행
|
||||||
|
- 무엇을: `P16-T1`~`P16-T3`를 구현하고 리뷰 확정 항목 `REV-P16-001`을 `P16-R1`로 수정했다.
|
||||||
|
- 왜: 홈과 커뮤니티 리스트형에서 상세과 동일한 권한별 메뉴를 제공하고, 성공한 관리 작업만 양쪽 목록에 반영하기 위해서다.
|
||||||
|
- 어떻게:
|
||||||
|
- 공용 `CreatorChannelCommunityMenuItem`으로 작성자/가격/구매 권한과 고정 상태를 전달했다.
|
||||||
|
- 홈·리스트의 더보기 click을 card 상세 click과 분리하고 grid는 기존 상세 이동만 유지했다.
|
||||||
|
- 기존 BottomSheet/Dialog/repository를 Activity에서 재사용하고 수정·삭제·고정 성공만 `CommunityChange.Updated`로 전파했다.
|
||||||
|
- 최초 독립 명세 검토에서 채널 소유자 조건이 작성자 권한을 제한하는 문제를 확인해 `item.isOwner` 계약으로 회귀 수정했다.
|
||||||
|
- 최종 Gate의 focused test, resource merge, Kotlin compile, ktlint, 두 diff check가 모두 통과했다.
|
||||||
|
- 제외: `app/src/androidTest`, 기기·에뮬레이터 조작, 스크린샷·시각 QA, 실제 서버 mutation, 별도 worktree, Git commit.
|
||||||
|
- 남은 항목: 없음.
|
||||||
|
|
||||||
## Verification Log
|
## Verification Log
|
||||||
|
- 2026-08-11: 사용자 요청에 따라 Phase 15·16 완료 체크를 PRD, production, test와 다시 역대조했다. 실제 Task 체크박스는 모두 완료 상태였고 체크 누락·미구현 항목은 없었다. Phase 15 홈 Response/공유 feed 회귀 test, Phase 16 홈·Feed·Activity focused test, community/action focused test와 필터 없는 전체 `:app:testDebugUnitTest`가 모두 `BUILD SUCCESSFUL`로 PASS했다. `:app:mergeDebugResources`, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check`, `git diff HEAD --check`도 PASS했다. 구매 호출은 상세에만 있고, 홈·리스트 root/more 분리와 grid 제외, 권한별 메뉴, 성공 관리 mutation의 홈+커뮤니티 갱신, 신고·실패 no-refresh, 공유 `FeedItem.Community` 기본값 보존을 정적으로 확인했다. 독립 재리뷰도 Critical/Important/Minor 이슈 없음과 승인 가능으로 판정했다. 새 확정 결함이 없어 억지로 회귀 수정 Task나 production/test 변경을 추가하지 않았고 Git commit도 수행하지 않았다.
|
||||||
|
- 2026-08-10: Phase 16 `P16-T1`~`P16-T3`와 `P16-GATE`를 완료했다. 최종 working tree에서 홈·Feed·Activity focused test와 community/action 회귀 test가 `BUILD SUCCESSFUL`로 PASS했고, `:app:mergeDebugResources`, `:app:compileDebugKotlin`, `:app:ktlintCheck`도 모두 PASS했다. `git diff --check`, `git diff HEAD --check`는 출력 없이 통과했다. 허용된 실행 QA 재검증에서도 P0 시나리오 11/11과 focused 128개, community/action 96개가 PASS했다. 리뷰에서 확인된 작성자 권한 추가 제한은 `item.isOwner` 기준으로 회귀 수정했고, 완료 문서 누락 `REV-P16-001`은 `P16-R1`로 누적했다. 저장소 규칙과 사용자 지시에 따라 `app/src/androidTest`, 기기·에뮬레이터 조작, 스크린샷·시각 QA, 실제 서버 mutation, 별도 worktree, Git commit은 수행하지 않았다. `.editorconfig disabled_rules` 및 Gradle deprecated feature 경고는 기존 비차단 경고로 유지했다.
|
||||||
|
- 2026-08-10: Phase 15 문서 대조·구현·독립 재리뷰를 완료했다. 미구현 체크리스트는 `P15-T1`~`P15-T5`로 구현했고, 리뷰에서 `REV-P15-001` 미사용 import, `REV-P15-002` reaction id 오배치, `REV-P15-003` 공유 피드 reaction 정책 범위 회귀, `REV-P15-004` 테스트 파일 미사용 import를 확정해 `P15-R1`~`P15-R4`로 수정했다. 최종 working tree에서 홈/공유 피드·커뮤니티 상세/action·상위 Activity focused test, `:app:mergeDebugResources`, `:app:compileDebugKotlin`, `:app:ktlintCheck`가 모두 `BUILD SUCCESSFUL`로 PASS했고 `git diff --check`, `git diff HEAD --check`는 출력 없이 PASS했다. 수정 후 독립 재리뷰에서도 새 Critical/Important/Minor 이슈가 없음을 확인했다. `.editorconfig disabled_rules` 및 기존 test deprecated API 경고는 실패가 아닌 기존 경고이며 범위 밖으로 유지했다. Git commit은 수행하지 않았다.
|
||||||
|
- 2026-08-10: 승인된 후속 PRD와 Phase 15 구현 계획만 작성했다. placeholder·요구사항 충돌을 자체 검토했고 `git diff --check`, `./gradlew tasks --all`이 통과했다. 사용자 지시에 따라 production/test 코드 구현과 Git commit은 수행하지 않았다.
|
||||||
- 2026-07-12: Phase 14 유료 미구매 상세 게시글 reaction/comment 숨김 변경을 완료했다. RED는 `CreatorChannelCommunityDetailViewModelTest.유료 미구매 게시물은 작성자가 아니면 reaction 현황과 댓글을 숨긴다`의 `showReaction` 미구현 컴파일 실패로 확인했고, 구현 후 focused 테스트, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"`, `./gradlew :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck`가 모두 `BUILD SUCCESSFUL`로 PASS했다. 실제 화면 직접 조작은 Activity가 내부 진입 경로와 데이터 계정에 의존해 이번 검증에서는 단위/리소스/컴파일/스타일 검증으로 대체했다.
|
- 2026-07-12: Phase 14 유료 미구매 상세 게시글 reaction/comment 숨김 변경을 완료했다. RED는 `CreatorChannelCommunityDetailViewModelTest.유료 미구매 게시물은 작성자가 아니면 reaction 현황과 댓글을 숨긴다`의 `showReaction` 미구현 컴파일 실패로 확인했고, 구현 후 focused 테스트, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"`, `./gradlew :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck`가 모두 `BUILD SUCCESSFUL`로 PASS했다. 실제 화면 직접 조작은 Activity가 내부 진입 경로와 데이터 계정에 의존해 이번 검증에서는 단위/리소스/컴파일/스타일 검증으로 대체했다.
|
||||||
|
|
||||||
- 구현 전 문서 작성 단계에서는 Gradle 검증을 실행하지 않았다.
|
- 구현 전 문서 작성 단계에서는 Gradle 검증을 실행하지 않았다.
|
||||||
|
|||||||
@@ -11,6 +11,10 @@
|
|||||||
- 사용자는 게시글에 좋아요를 누르고, 댓글과 답글을 작성/수정/삭제하고 싶어 한다.
|
- 사용자는 게시글에 좋아요를 누르고, 댓글과 답글을 작성/수정/삭제하고 싶어 한다.
|
||||||
- 댓글 작성이 불가능한 게시글에서는 댓글 영역과 입력 영역이 노출되면 안 된다.
|
- 댓글 작성이 불가능한 게시글에서는 댓글 영역과 입력 영역이 노출되면 안 된다.
|
||||||
- 조회용 신규 v2 API 계약과 Figma 기반 UI 요구사항이 별도 문서로 정의되어야 한다.
|
- 조회용 신규 v2 API 계약과 Figma 기반 UI 요구사항이 별도 문서로 정의되어야 한다.
|
||||||
|
- 2026-08-10 후속 확인에서 홈 커뮤니티 섹션의 유료 미구매 게시글에 하트·댓글 UI가 노출되고, 홈 Response에 고정/댓글 가능 여부가 없어 커뮤니티 탭·상세와 표시 상태가 달라지는 문제가 확인됐다.
|
||||||
|
- 커뮤니티 탭의 유료 미구매 가격 UI와 구매 진입점이 홈·상세와 일치하지 않고, 상세 구매 결과가 커뮤니티 탭에만 반영되어 홈 섹션은 구매 전 상태로 남는다.
|
||||||
|
- 게시글 관리/신고 메뉴가 상세에 없고 목록 item에 별도 액션이 남아 있어, 목록은 상세 이동만 담당하고 구매·관리 액션은 상세에 모으는 정리가 필요하다.
|
||||||
|
- 2026-08-10 구현 확인 후 상세에만 둔 권한별 더보기 메뉴를 홈 커뮤니티 item과 커뮤니티 탭 리스트형에서도 바로 사용할 수 있어야 한다는 후속 요구가 확정됐다.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -34,6 +38,11 @@
|
|||||||
- 하트 터치 시 좋아요 API를 호출하고, `isLiked`에 따라 `ic_feed_community_heart_fill`/`ic_feed_community_heart` icon을 표시한다.
|
- 하트 터치 시 좋아요 API를 호출하고, `isLiked`에 따라 `ic_feed_community_heart_fill`/`ic_feed_community_heart` icon을 표시한다.
|
||||||
- 댓글 작성이 불가능한 게시글(`isCommentAvailable == false`)이면 댓글 목록과 하단 입력 영역을 숨긴다.
|
- 댓글 작성이 불가능한 게시글(`isCommentAvailable == false`)이면 댓글 목록과 하단 입력 영역을 숨긴다.
|
||||||
- 하단 댓글/답글 입력 영역은 화면 하단에 고정하고 키보드 표시 여부에 따라 함께 올라오게 한다.
|
- 하단 댓글/답글 입력 영역은 화면 하단에 고정하고 키보드 표시 여부에 따라 함께 올라오게 한다.
|
||||||
|
- 크리에이터 채널 홈 Response의 커뮤니티 게시글에 `isPinned`, `isCommentAvailable`을 추가하고 홈 섹션의 고정·댓글 표시 상태에 반영한다.
|
||||||
|
- 홈과 커뮤니티 탭의 게시글 card 본문 터치는 상세 이동만 수행하고, 별도 더보기 버튼은 권한별 관리/신고 메뉴를 제공한다. 유료 게시글 구매는 상세에서만 시작한다.
|
||||||
|
- 커뮤니티 탭 리스트형·썸네일형의 유료 미구매 가격 UI를 홈·상세와 같은 잠금/가격 표현으로 통일한다.
|
||||||
|
- 상세·홈·커뮤니티 리스트형 게시글 작성자 영역 우측 끝에 `ic_seemore_vertical` 버튼을 표시하고 작성자/구매 권한에 따라 관리 또는 신고 메뉴를 제공한다.
|
||||||
|
- 상세 구매·수정·삭제·고정 결과가 크리에이터 채널 홈과 커뮤니티 탭에 모두 반영되도록 결과 전파 범위를 확장한다.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -45,6 +54,8 @@
|
|||||||
- 레거시 커뮤니티/댓글 관련 파일을 직접 수정하지 않는다.
|
- 레거시 커뮤니티/댓글 관련 파일을 직접 수정하지 않는다.
|
||||||
- 크리에이터 채널의 다른 탭 UI나 공통 스크롤 구조를 재설계하지 않는다.
|
- 크리에이터 채널의 다른 탭 UI나 공통 스크롤 구조를 재설계하지 않는다.
|
||||||
- v2 패키지 내부 popup dialog는 `kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog`를 사용한다.
|
- v2 패키지 내부 popup dialog는 `kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog`를 사용한다.
|
||||||
|
- 레거시 커뮤니티 게시글 목록의 구매 동작을 변경하지 않는다.
|
||||||
|
- 커뮤니티 게시글 화면 공통화를 위한 신규 framework, coordinator 또는 범용 abstraction을 추가하지 않는다.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -64,11 +75,96 @@
|
|||||||
- 사용자는 내가 쓴 댓글/답글을 수정하거나 삭제하고 싶다.
|
- 사용자는 내가 쓴 댓글/답글을 수정하거나 삭제하고 싶다.
|
||||||
- 채널 크리에이터는 자신의 채널 댓글/답글을 삭제해 관리하고 싶다.
|
- 채널 크리에이터는 자신의 채널 댓글/답글을 삭제해 관리하고 싶다.
|
||||||
- 사용자는 댓글이 불가능한 게시글에서는 입력창 없이 게시글만 보고 싶다.
|
- 사용자는 댓글이 불가능한 게시글에서는 입력창 없이 게시글만 보고 싶다.
|
||||||
|
- 사용자는 홈과 커뮤니티 탭에서 게시글을 터치하면 다른 중간 동작 없이 상세로 이동하고 싶다.
|
||||||
|
- 사용자는 미구매 유료 게시글의 하트·댓글 정보를 홈에서 보지 않고 상세에서 구매하고 싶다.
|
||||||
|
- 사용자는 고정 게시글을 홈 커뮤니티 섹션에서도 구분하고 싶다.
|
||||||
|
- 작성자는 상세에서 자신의 게시글을 수정·삭제·고정/고정 해제하고 싶다.
|
||||||
|
- 일반 사용자는 무료 또는 구매 완료 게시글 상세에서 게시글을 신고하고 싶다.
|
||||||
|
- 작성자는 홈 또는 커뮤니티 리스트에서 자신의 게시글을 수정·삭제·고정/고정 해제하고 싶다.
|
||||||
|
- 일반 사용자는 홈 또는 커뮤니티 리스트의 무료·구매 완료 게시글을 신고하고 싶다.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. Core Features
|
## 7. Core Features
|
||||||
|
|
||||||
|
### F0-20260810. 구매 진입점·표시 상태·권한별 메뉴 후속 정리
|
||||||
|
홈·커뮤니티 탭은 게시글 상태를 일관되게 표시하고 card 본문에서 상세로 이동한다. 구매는 상세에서만 수행하며, 권한별 게시글 관리/신고 메뉴는 상세·홈·커뮤니티 리스트형에서 제공한다.
|
||||||
|
|
||||||
|
#### Home Response Contract
|
||||||
|
크리에이터 채널 홈의 `communities`와 `notices`가 사용하는 게시글 Response에 상세와 같은 필드를 추가한다.
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
data class CreatorChannelCommunityPostResponse(
|
||||||
|
// 기존 필드 유지
|
||||||
|
@SerializedName("isPinned") val isPinned: Boolean,
|
||||||
|
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Home Community Requirements
|
||||||
|
- `isPinned == true`이면 커뮤니티 탭과 같은 고정/공지 표시를 노출한다.
|
||||||
|
- `price > 0 && existOrdered == false`인 유료 미구매 게시글은 하트 icon·좋아요 수·댓글 icon·댓글 수 reaction UI 전체를 숨긴다.
|
||||||
|
- 무료 또는 구매 완료 게시글은 하트 icon과 좋아요 수를 표시한다.
|
||||||
|
- 무료 또는 구매 완료 게시글에서 `isCommentAvailable == true`이면 댓글 icon과 댓글 수를 표시하고, `false`이면 댓글 icon과 댓글 수만 숨긴다.
|
||||||
|
- 게시글 card 본문 터치 액션은 상세 이동만 제공한다.
|
||||||
|
- 홈 커뮤니티 item에서 구매 dialog와 오디오 재생은 시작하지 않는다.
|
||||||
|
- 권한 조건을 만족하는 홈 커뮤니티 item은 작성자 영역 우측 끝의 더보기 버튼으로 관리/신고 메뉴를 시작한다.
|
||||||
|
|
||||||
|
#### Community Tab Requirements
|
||||||
|
- 리스트형과 썸네일형 card 본문 터치 액션은 상세 이동만 제공한다.
|
||||||
|
- 리스트형 item의 오디오 재생은 제거 상태를 유지하고, 권한 조건을 만족하는 item의 작성자 영역 우측 끝에는 더보기 버튼을 제공한다.
|
||||||
|
- 썸네일형(그리드형)은 작성자 영역이 없으므로 더보기 버튼을 제공하지 않고 상세 이동만 유지한다.
|
||||||
|
- 유료 미구매 게시글 item에서는 구매 dialog를 열지 않는다.
|
||||||
|
- 리스트형과 썸네일형의 유료 미구매 가격 UI를 홈/상세와 같은 `lock icon + cash icon + 금액 capsule` 구조로 통일한다.
|
||||||
|
- 리스트형·썸네일형의 기존 reaction 숨김, 댓글 가능 여부, 고정 표시 정책은 유지한다.
|
||||||
|
|
||||||
|
#### Detail Purchase and Badge Requirements
|
||||||
|
- 유료 미구매 게시글의 paywall을 터치한 경우에만 기존 `PurchaseCommunityPostDialog`를 표시한다.
|
||||||
|
- 상세 작성자 영역 우측 상단에 표시하던 유료 금액 badge를 제거한다.
|
||||||
|
- 유료 구매 완료 시 표시하던 구매 완료 badge를 제거한다.
|
||||||
|
- paywall 내부의 잠금/가격 표시는 구매 전 구매 진입점이므로 유지한다.
|
||||||
|
- 구매 성공 시 상세를 다시 조회하고 상위 화면에 변경 결과를 반환한다.
|
||||||
|
- 구매 실패 시 잠금 상태를 유지하고 기존 오류 Toast 정책을 따른다.
|
||||||
|
|
||||||
|
#### Detail More Menu Requirements
|
||||||
|
- `ic_seemore_vertical` 버튼은 title bar가 아니라 게시글 작성자 영역의 우측 끝에 표시한다.
|
||||||
|
- 작성자는 무료/유료 및 구매 여부와 관계없이 더보기 버튼을 표시한다.
|
||||||
|
- 일반 사용자는 무료 게시글 또는 구매 완료 유료 게시글에서 더보기 버튼을 표시한다.
|
||||||
|
- 일반 사용자의 유료 미구매 게시글에서는 더보기 버튼을 숨긴다.
|
||||||
|
- 작성자 메뉴는 기존 `CreatorCommunityPostMenuBottomSheetDialog`를 재사용해 수정, 삭제, 고정/고정 해제를 제공한다.
|
||||||
|
- 일반 사용자 메뉴는 같은 BottomSheet의 신고 메뉴를 제공하고 기존 `CreatorCommunityReportDialog`를 재사용한다.
|
||||||
|
- 수정은 기존 `CreatorCommunityModifyActivity`, 삭제/고정은 기존 `CreatorCommunityRepository` API를 호출한다.
|
||||||
|
- 신고는 기존 `ReportType.COMMUNITY_POST` 흐름을 재사용한다.
|
||||||
|
|
||||||
|
#### Home and Community List More Menu Requirements
|
||||||
|
- 홈 커뮤니티 item과 커뮤니티 탭 리스트형 item의 작성자 영역 우측 끝에 `ic_seemore_vertical` 버튼을 표시한다.
|
||||||
|
- 작성자 판정은 게시글 `creatorId == SharedPreferenceManager.userId`로 통일한다.
|
||||||
|
- 작성자는 무료/유료 및 구매 여부와 관계없이 더보기 버튼을 표시한다.
|
||||||
|
- 일반 사용자는 무료 게시글 또는 구매 완료 유료 게시글에서 더보기 버튼을 표시한다.
|
||||||
|
- 일반 사용자의 유료 미구매 게시글에서는 더보기 버튼을 숨긴다.
|
||||||
|
- 작성자 메뉴는 기존 `CreatorCommunityPostMenuBottomSheetDialog`를 재사용해 수정, 삭제, 고정/고정 해제를 제공한다.
|
||||||
|
- 일반 사용자 메뉴는 같은 BottomSheet의 신고 메뉴를 제공하고 기존 `CreatorCommunityReportDialog`를 재사용한다.
|
||||||
|
- 수정은 기존 `CreatorCommunityModifyActivity`, 삭제/고정은 기존 `CreatorCommunityRepository`, 신고는 기존 `ReportType.COMMUNITY_POST` 흐름을 재사용한다.
|
||||||
|
- 더보기 버튼 터치는 card의 상세 이동 click으로 전파하지 않고 메뉴만 표시한다.
|
||||||
|
- 커뮤니티 탭 썸네일형(그리드형)은 이번 후속 범위에서 제외한다.
|
||||||
|
|
||||||
|
#### Change Propagation Requirements
|
||||||
|
- 구매·수정·삭제·고정/고정 해제 성공 시 상세는 `RESULT_OK`를 반환한다.
|
||||||
|
- 수정 복귀와 고정/고정 해제 성공 시 상세를 다시 조회한다.
|
||||||
|
- 삭제 성공 시 `RESULT_OK`를 설정하고 상세를 종료한다.
|
||||||
|
- `CreatorChannelActivity`가 상세의 `RESULT_OK`를 받으면 홈 탭과 커뮤니티 탭을 모두 새로고침한다.
|
||||||
|
- 홈·커뮤니티 리스트 메뉴의 수정·삭제·고정/고정 해제 성공도 홈 탭과 커뮤니티 탭을 모두 새로고침한다.
|
||||||
|
- 신고 결과는 게시글 구매/내용/고정 상태를 바꾸지 않으므로 홈·커뮤니티 탭 새로고침을 발생시키지 않는다.
|
||||||
|
|
||||||
|
#### Edge Cases
|
||||||
|
- 작성자 응답의 `existOrdered == false`는 작성자의 관리 권한을 제한하지 않는다.
|
||||||
|
- 서버가 유료 미구매 홈 item에 reaction count를 내려줘도 UI에는 표시하지 않는다.
|
||||||
|
- `isCommentAvailable == false`인 무료/구매 완료 홈 item은 좋아요만 표시하고 댓글 UI는 숨긴다.
|
||||||
|
- 상세 메뉴 mutation이 실패하면 상세 상태를 임의 변경하거나 성공 결과를 반환하지 않는다.
|
||||||
|
- 홈·커뮤니티 리스트 메뉴 mutation이 실패하면 현재 목록 상태를 임의 변경하거나 성공 새로고침을 실행하지 않는다.
|
||||||
|
- 더보기 버튼이 숨겨진 유료 미구매 일반 사용자 item은 card 본문 상세 이동만 가능하다.
|
||||||
|
- 다른 화면에서 사용하는 `FeedItem.Community`는 신규 필드 기본값으로 기존 표시 동작을 유지한다.
|
||||||
|
|
||||||
### F0. 2026-07-12 후속 변경사항
|
### F0. 2026-07-12 후속 변경사항
|
||||||
크리에이터 커뮤니티 상세의 오디오/구매 동작을 기존 레거시 목록 UI와 동일한 사용성으로 보정한다.
|
크리에이터 커뮤니티 상세의 오디오/구매 동작을 기존 레거시 목록 UI와 동일한 사용성으로 보정한다.
|
||||||
|
|
||||||
@@ -107,7 +203,7 @@ Figma `290:9243`, `290:9266` 기준으로 게시글 본문과 좋아요를 표
|
|||||||
- 본문(`content`), 이미지(`imageUrl`), 오디오(`audioUrl`)를 표시한다.
|
- 본문(`content`), 이미지(`imageUrl`), 오디오(`audioUrl`)를 표시한다.
|
||||||
- 오디오 재생은 기존 `CreatorCommunityMediaPlayerManager`를 재사용한다.
|
- 오디오 재생은 기존 `CreatorCommunityMediaPlayerManager`를 재사용한다.
|
||||||
- 유료 게시글(`price > 0`)이고 미구매(`existOrdered == false`)이면 Figma paywall(잠금 + 가격) 표시를 따른다.
|
- 유료 게시글(`price > 0`)이고 미구매(`existOrdered == false`)이면 Figma paywall(잠금 + 가격) 표시를 따른다.
|
||||||
- 유료 게시글을 구매한 경우(`existOrdered == true`) Figma 기준 `구매완료` badge를 표시한다.
|
- 유료 게시글 구매 완료 badge는 2026-08-10 후속 요구사항에 따라 표시하지 않는다.
|
||||||
- 하트 터치 시 좋아요 API(`communityPostLike(postId)`)를 호출한다.
|
- 하트 터치 시 좋아요 API(`communityPostLike(postId)`)를 호출한다.
|
||||||
- `isLiked == true`이면 `ic_feed_community_heart_fill`, `false`이면 `ic_feed_community_heart`를 표시한다.
|
- `isLiked == true`이면 `ic_feed_community_heart_fill`, `false`이면 `ic_feed_community_heart`를 표시한다.
|
||||||
- 좋아요 수(`likeCount`)를 하트 옆에 표시한다.
|
- 좋아요 수(`likeCount`)를 하트 옆에 표시한다.
|
||||||
@@ -211,6 +307,9 @@ Figma `290:9266` 기준으로 댓글 불가 상태를 처리한다.
|
|||||||
|
|
||||||
## 8. UX / UI Expectations
|
## 8. UX / UI Expectations
|
||||||
- 게시글 item 터치부터 상세 진입까지 자연스럽게 이어진다.
|
- 게시글 item 터치부터 상세 진입까지 자연스럽게 이어진다.
|
||||||
|
- 홈과 커뮤니티 탭의 게시글 card 본문은 상세 이동만 수행하고, 별도 더보기 버튼은 권한별 관리/신고 메뉴만 제공한다. 구매·재생은 제공하지 않는다.
|
||||||
|
- 홈·커뮤니티 탭 리스트형·썸네일형·상세의 유료 미구매 가격은 동일한 잠금/가격 capsule 구조로 표시한다.
|
||||||
|
- 상세·홈·커뮤니티 리스트형 `ic_seemore_vertical` 버튼은 게시글 작성자 영역 우측 끝에 정렬하고 닉네임·작성 시간과 겹치지 않게 한다.
|
||||||
- 커뮤니티 상세 상단 게시글 영역은 목록 카드처럼 rounded 배경을 깔지 않고, 게시글과 댓글 영역 및 댓글 사이에는 Figma `290:9243` 기준 1dp border를 표시한다.
|
- 커뮤니티 상세 상단 게시글 영역은 목록 카드처럼 rounded 배경을 깔지 않고, 게시글과 댓글 영역 및 댓글 사이에는 Figma `290:9243` 기준 1dp border를 표시한다.
|
||||||
- 하트 상태와 좋아요 수는 터치 즉시 반응한다(낙관적 업데이트 후 실패 시 롤백).
|
- 하트 상태와 좋아요 수는 터치 즉시 반응한다(낙관적 업데이트 후 실패 시 롤백).
|
||||||
- 입력 영역은 키보드 있음/없음 상태를 기준으로 자연스럽게 대응한다.
|
- 입력 영역은 키보드 있음/없음 상태를 기준으로 자연스럽게 대응한다.
|
||||||
@@ -229,18 +328,30 @@ Figma `290:9266` 기준으로 댓글 불가 상태를 처리한다.
|
|||||||
- 커뮤니티 댓글/답글 신고는 현재 전용 신고 API/레거시 흐름이 없어 이번 구현 범위에서 제외하고, 기존 레거시 메뉴2의 실제 동작인 채널 크리에이터 삭제만 재사용한다.
|
- 커뮤니티 댓글/답글 신고는 현재 전용 신고 API/레거시 흐름이 없어 이번 구현 범위에서 제외하고, 기존 레거시 메뉴2의 실제 동작인 채널 크리에이터 삭제만 재사용한다.
|
||||||
- 신규 문자열은 `values`, `values-en`, `values-ja`에 추가하거나 기존 문자열을 재사용한다.
|
- 신규 문자열은 `values`, `values-en`, `values-ja`에 추가하거나 기존 문자열을 재사용한다.
|
||||||
- 오디오 재생은 기존 `CreatorCommunityMediaPlayerManager`를 재사용한다.
|
- 오디오 재생은 기존 `CreatorCommunityMediaPlayerManager`를 재사용한다.
|
||||||
|
- 홈 커뮤니티 표시는 기존 `FeedCommunityView`와 `FeedItem.Community`를 기본값 기반으로 최소 확장하며 다른 feed 호출부의 기존 동작을 유지한다.
|
||||||
|
- 상세·홈·커뮤니티 리스트형 게시글 메뉴는 기존 `CreatorCommunityPostMenuBottomSheetDialog`, `CreatorCommunityReportDialog`, `CreatorCommunityModifyActivity`, `CreatorCommunityRepository`를 호출해 사용하고 레거시 파일은 수정하지 않는다.
|
||||||
|
- 홈·커뮤니티 리스트형 메뉴 실행과 mutation은 `CreatorChannelActivity`의 공용 처리 흐름을 사용하고 화면별 중복 mutation 구현이나 신규 범용 coordinator를 추가하지 않는다.
|
||||||
|
- UI 자동 테스트는 Response/mapper/presentation/routing/result 전파 로직만 검증하고 View 크기·간격·constraint·visibility 속성을 직접 검증하지 않는다.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 10. Metrics
|
## 10. Metrics
|
||||||
- 별도 분석 이벤트 추가는 이번 범위에서 정의하지 않는다.
|
- 별도 분석 이벤트 추가는 이번 범위에서 정의하지 않는다.
|
||||||
- 기능 성공 기준은 게시글 상세 진입, 댓글/답글 조회, 좋아요/댓글/답글 작성·수정·삭제가 정상 동작하고 목록에 반영되는 것이다.
|
- 기능 성공 기준은 게시글 상세 진입, 댓글/답글 조회, 좋아요/댓글/답글 작성·수정·삭제가 정상 동작하고 목록에 반영되는 것이다.
|
||||||
|
- 2026-08-10 후속 성공 기준은 홈 Response 신규 필드가 고정/댓글 UI에 반영되고, 미구매 유료 홈 item reaction이 숨겨지며, card 본문은 상세 이동만 수행하고 권한별 더보기 메뉴가 상세·홈·커뮤니티 리스트형에 표시되며, 구매·메뉴 결과가 홈과 커뮤니티 탭에 반영되는 것이다.
|
||||||
- UI 완료 기준은 Figma `290:9243`, `290:9266`, `669:40462`, `669:40468`의 주요 layout 구조, 하트/전송 버튼 상태, 댓글 불가 처리, 입력 영역 동작이 구현 화면에 반영되었는지 확인되는 것이다.
|
- UI 완료 기준은 Figma `290:9243`, `290:9266`, `669:40462`, `669:40468`의 주요 layout 구조, 하트/전송 버튼 상태, 댓글 불가 처리, 입력 영역 동작이 구현 화면에 반영되었는지 확인되는 것이다.
|
||||||
- 자동 검증 기준은 조회 API 계약(page/size 기본값, 응답 매핑), 하트 icon/전송 버튼 상태 계약, 댓글 불가 게시글의 댓글/입력 영역 숨김 계약을 source/resource/ViewModel 테스트로 확인하는 것이다.
|
- 자동 검증 기준은 조회 API 계약(page/size 기본값, 응답 매핑), 하트 icon/전송 버튼 상태 계약, 댓글 불가 게시글의 댓글/입력 영역 숨김 계약과 홈 신규 Response 필드, 잠금·댓글·핀 표시 조건, 상세·홈·커뮤니티 리스트형 더보기 권한, 결과 전파를 local unit test로 확인하는 것이다.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 11. Open Questions & Decisions
|
## 11. Open Questions & Decisions
|
||||||
|
- 결정(2026-08-10, 목록 더보기 후속): 상세와 동일한 권한별 더보기 메뉴를 홈 커뮤니티 item과 커뮤니티 탭 리스트형에 적용한다. 작성자 영역이 없는 커뮤니티 그리드형은 제외한다.
|
||||||
|
- 결정(2026-08-10, 목록 click): card 본문은 상세 이동, 더보기 버튼은 메뉴 표시로 분리하며 더보기 touch는 상세 이동으로 전파하지 않는다.
|
||||||
|
- 결정(2026-08-10, 구현 구조): 홈·커뮤니티 리스트형은 `CreatorChannelActivity`의 공용 메뉴/mutation 흐름을 사용하고 기존 상세 메뉴 구현은 유지한다.
|
||||||
|
- 결정(2026-08-10, 더보기 권한): 작성자는 구매 여부와 관계없이 관리 메뉴를 표시하고, 일반 사용자는 무료 또는 구매 완료 게시글에서만 신고 메뉴를 표시한다.
|
||||||
|
- 결정(2026-08-10, 가격 UI): 커뮤니티 탭 리스트형과 썸네일형 모두 홈/상세의 잠금/가격 capsule 표현을 따른다.
|
||||||
|
- 결정(2026-08-10, 홈 고정 표시): 홈 Response의 `isPinned`를 홈 커뮤니티 item 고정 표시에 사용한다.
|
||||||
|
- 결정(2026-08-10, 문서 연속성): 상세 구매 후속 요구사항을 포함하는 기존 문서에 이번 홈/탭/상세 정리 범위를 이어서 기록한다.
|
||||||
- 결정(재사용 vs 복사): 답글 목록 화면 UI는 팬Talk 상세와 동일하지만 조회 API가 다르고, 현재 팬Talk 상세 화면은 v2에 미구현이다. 따라서 이번 범위에서는 **커뮤니티 전용 답글 화면으로 신규 구현하되, layout XML과 댓글/답글 item view(ViewHolder)만 데이터/콜백 주입형으로 설계해 재사용 가능하게 둔다.** ViewModel/Repository/데이터 모델/화면 컨트롤러는 커뮤니티 전용으로 분리한다.
|
- 결정(재사용 vs 복사): 답글 목록 화면 UI는 팬Talk 상세와 동일하지만 조회 API가 다르고, 현재 팬Talk 상세 화면은 v2에 미구현이다. 따라서 이번 범위에서는 **커뮤니티 전용 답글 화면으로 신규 구현하되, layout XML과 댓글/답글 item view(ViewHolder)만 데이터/콜백 주입형으로 설계해 재사용 가능하게 둔다.** ViewModel/Repository/데이터 모델/화면 컨트롤러는 커뮤니티 전용으로 분리한다.
|
||||||
- 근거: 아직 팬Talk 상세라는 두 번째 사용처가 없어 지금 데이터 계층까지 공통 추상화하면 단일 사용처를 위한 추측성 추상화가 된다. UI 리소스/item view만 공용화하면 추후 팬Talk 상세 구현 시 동일 UI를 그대로 재사용할 수 있다.
|
- 근거: 아직 팬Talk 상세라는 두 번째 사용처가 없어 지금 데이터 계층까지 공통 추상화하면 단일 사용처를 위한 추측성 추상화가 된다. UI 리소스/item view만 공용화하면 추후 팬Talk 상세 구현 시 동일 UI를 그대로 재사용할 수 있다.
|
||||||
- 남은 확인: 없음.
|
- 남은 확인: 없음.
|
||||||
|
|||||||
@@ -0,0 +1,242 @@
|
|||||||
|
# Phase 15 커뮤니티 구매·상세 메뉴 리뷰 보고서
|
||||||
|
|
||||||
|
## 1. 리뷰 정보
|
||||||
|
|
||||||
|
| 항목 | 내용 |
|
||||||
|
|---|---|
|
||||||
|
| 리뷰 대상 | Phase 15 / `P15-T1`~`P15-T5`, `P15-GATE` |
|
||||||
|
| 기준 commit 또는 working tree | `02cd583f` 기반 미커밋 working tree |
|
||||||
|
| 리뷰 일자 | 2026-08-10 |
|
||||||
|
| 리뷰어 | Codex, 독립 재검토 `phase15_review` |
|
||||||
|
| 기준 문서 | `prd.md` F0-20260810, `plan-task.md` Phase 15 |
|
||||||
|
| 리뷰 상태 | 수정 검증 완료 |
|
||||||
|
|
||||||
|
## 2. 리뷰 목적과 범위
|
||||||
|
|
||||||
|
### 목적
|
||||||
|
|
||||||
|
- Phase 15의 미체크 항목이 단순 체크 누락인지 미구현인지 확인한다.
|
||||||
|
- PRD의 홈 Response, 유료 미구매 표시, 상세 메뉴 권한, 상위 새로고침 요구사항을 코드·테스트·Gate와 대조한다.
|
||||||
|
|
||||||
|
### 포함 범위
|
||||||
|
|
||||||
|
- 코드: Phase 15 파일 구조에 나열된 홈, 커뮤니티 탭, 상세, 상위 Activity, action 파일
|
||||||
|
- 테스트: Phase 15 focused local unit/source contract 테스트
|
||||||
|
- 문서: `prd.md` F0-20260810, `plan-task.md` Phase 15
|
||||||
|
- 수동 검증: 코드/diff 정적 검토
|
||||||
|
|
||||||
|
### 제외 범위
|
||||||
|
|
||||||
|
- `app/src/androidTest`, 기기·에뮬레이터 UI 조작, 스크린샷·시각 QA
|
||||||
|
- 서버 Response 실제 배포 여부와 외부 API 운영 상태
|
||||||
|
|
||||||
|
## 3. 판정 기준
|
||||||
|
|
||||||
|
| 심각도 | 기준 |
|
||||||
|
|---|---|
|
||||||
|
| Blocker | 보안·데이터 손실, 핵심 흐름 불능 |
|
||||||
|
| High | 확정 PRD/API 계약 위반 또는 주요 회귀 |
|
||||||
|
| Medium | 제한 조건의 기능·접근성·복구 문제 |
|
||||||
|
| Low | 유지보수성, 문서 정합성, style Gate 문제 |
|
||||||
|
|
||||||
|
## 4. 검토한 근거
|
||||||
|
|
||||||
|
### 문서와 코드
|
||||||
|
|
||||||
|
- 요구사항: PRD `Home Response Contract`, `Home Community Requirements`, `Community Tab Requirements`, `Detail Purchase and Badge Requirements`, `Detail More Menu Requirements`, `Change Propagation Requirements`
|
||||||
|
- 계획: `P15-T1`~`P15-T5`, `P15-GATE`
|
||||||
|
- 코드/테스트: Phase 15 파일 구조와 각 Task에 명시된 파일
|
||||||
|
|
||||||
|
### 실행 환경
|
||||||
|
|
||||||
|
```text
|
||||||
|
OS: Darwin 25.0.0 x86_64
|
||||||
|
Java: OpenJDK 17.0.15
|
||||||
|
Android/Gradle: 저장소 wrapper 및 debug variant
|
||||||
|
민감 환경 값: 기록하지 않음
|
||||||
|
```
|
||||||
|
|
||||||
|
### 실행한 검증
|
||||||
|
|
||||||
|
| 명령 | 결과 | 핵심 증거 |
|
||||||
|
|---|---|---|
|
||||||
|
| 홈 focused unit test | 성공 | `BUILD SUCCESSFUL` |
|
||||||
|
| 커뮤니/action focused unit test | 성공 | `BUILD SUCCESSFUL` |
|
||||||
|
| `CreatorChannelActivitySourceTest` | 성공 | `BUILD SUCCESSFUL` |
|
||||||
|
| `./gradlew :app:mergeDebugResources` | 성공 | `BUILD SUCCESSFUL` |
|
||||||
|
| `./gradlew :app:compileDebugKotlin` | 성공 | `BUILD SUCCESSFUL` |
|
||||||
|
| `./gradlew :app:ktlintCheck` | 실패 | `CreatorChannelActivity.kt:29:1 Unused import` 1건 |
|
||||||
|
| `git diff --check` | 성공 | 출력 없음 |
|
||||||
|
| 수정 후 전체 focused test | 성공 | `BUILD SUCCESSFUL` |
|
||||||
|
| 수정 후 resource/compile/ktlint Gate | 성공 | `BUILD SUCCESSFUL` |
|
||||||
|
| 수정 후 `git diff --check`, `git diff HEAD --check` | 성공 | 출력 없음 |
|
||||||
|
|
||||||
|
## 5. 발견 사항 요약
|
||||||
|
|
||||||
|
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| `REV-P15-001` | Low | 수정 완료 | 목록 메뉴 제거 후 미사용 import가 style Gate를 막음 | `P15-R1` | 미사용 import 1줄 제거 및 Gate 재검증 |
|
||||||
|
| `REV-P15-002` | High | 수정 완료 | 홈 reaction id가 작성자 row에 연결됨 | `P15-R2` | reaction id를 실제 하트·댓글 row로 이동 및 Gate 재검증 |
|
||||||
|
| `REV-P15-003` | High | 수정 완료 | 공유 FeedItem 잠금 정책이 다른 홈 화면 reaction을 숨김 | `P15-R3` | 크리에이터 채널 홈만 잠금 숨김 정책을 선택하고 공유 기본값 회귀 검증 |
|
||||||
|
| `REV-P15-004` | Low | 수정 완료 | 메인 홈 회귀 테스트 파일의 미사용 import가 style Gate를 막음 | `P15-R4` | 미사용 import 1줄 제거 및 Gate 재검증 |
|
||||||
|
|
||||||
|
## 6. 발견 사항 상세
|
||||||
|
|
||||||
|
### REV-P15-001 — 목록 메뉴 제거 후 미사용 import가 style Gate를 막음
|
||||||
|
|
||||||
|
- **심각도:** Low
|
||||||
|
- **상태:** 수정 완료
|
||||||
|
- **관련 요구사항:** `P15-T5` orphan 제거, `P15-GATE`
|
||||||
|
- **소유 Task:** `P15-R1`
|
||||||
|
|
||||||
|
**관찰 내용**
|
||||||
|
|
||||||
|
`CreatorChannelActivity`의 목록 고정/삭제 로직을 제거한 후 `SharedPreferenceManager` import가 남아 `ktlintCheck`가 실패했다.
|
||||||
|
|
||||||
|
**근거**
|
||||||
|
|
||||||
|
- 코드: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt:29`
|
||||||
|
- 검증: `./gradlew :app:ktlintCheck` 결과 `Unused import (no-unused-imports)`
|
||||||
|
- 문서: `plan-task.md` `P15-T5`, `P15-GATE`
|
||||||
|
|
||||||
|
**영향**
|
||||||
|
|
||||||
|
기능 동작에는 영향이 없으나 Phase 15 style Gate를 통과할 수 없다.
|
||||||
|
|
||||||
|
**권장 조치**
|
||||||
|
|
||||||
|
`SharedPreferenceManager` import 1줄만 제거하고 `ktlintCheck`와 Phase 15 Gate를 재실행한다.
|
||||||
|
|
||||||
|
**판정 기록**
|
||||||
|
|
||||||
|
- 2026-08-10 — 기능 테스트·resource merge·compile은 통과했고 ktlint 정확한 1건으로 확정했다.
|
||||||
|
- 2026-08-10 — `SharedPreferenceManager` import 1줄 제거 후 최종 `ktlintCheck`와 Phase 15 Gate가 PASS했다.
|
||||||
|
|
||||||
|
### REV-P15-002 — 홈 reaction id가 작성자 row에 연결됨
|
||||||
|
|
||||||
|
- **심각도:** High
|
||||||
|
- **상태:** 수정 완료
|
||||||
|
- **관련 요구사항:** PRD `Home Community Requirements`, `P15-T2`
|
||||||
|
- **소유 Task:** `P15-R2`
|
||||||
|
|
||||||
|
**관찰 내용**
|
||||||
|
|
||||||
|
`FeedCommunityView.bind()`는 `ll_feed_community_reaction`에 `item.showReaction`을 적용하지만, XML의 해당 id가 프로필·닉네임·작성 시간 row에 붙어 있다. 실제 하트·댓글 row에는 id가 없다.
|
||||||
|
|
||||||
|
**근거**
|
||||||
|
|
||||||
|
- XML: `view_feed_community.xml:36-41`의 `ll_feed_community_reaction` 아래에 `iv_feed_community_profile`이 있음
|
||||||
|
- XML: `view_feed_community.xml:154-195`의 실제 reaction row에 id가 없음
|
||||||
|
- 코드: `FeedCommunityView.kt:51`, `FeedCommunityView.kt:81`
|
||||||
|
|
||||||
|
**재현 또는 검증 절차**
|
||||||
|
|
||||||
|
1. `price > 0 && existOrdered == false`인 홈 item의 `showReaction == false`를 확인한다.
|
||||||
|
2. `FeedCommunityView.bind()`가 `ll_feed_community_reaction.isVisible = false`를 적용한다.
|
||||||
|
3. XML tree에서 해당 id가 작성자 row를 가리키므로 작성자 row가 숨겨진다.
|
||||||
|
4. 실제 reaction row는 자신의 visibility binding을 받지 않아 하트·댓글 UI가 남는다.
|
||||||
|
|
||||||
|
**영향**
|
||||||
|
|
||||||
|
유료 미구매 홈 item이 PRD와 반대로 작성자 영역을 숨기고 reaction UI를 노출한다.
|
||||||
|
|
||||||
|
**권장 조치**
|
||||||
|
|
||||||
|
`ll_feed_community_reaction` id를 작성자 row에서 실제 하트·댓글 row로 이동한다.
|
||||||
|
|
||||||
|
**판정 기록**
|
||||||
|
|
||||||
|
- 2026-08-10 — XML tree와 binding 대상을 대조해 PRD 2번을 재현하는 실제 결함으로 확정했다.
|
||||||
|
- 2026-08-10 — id를 실제 하트·댓글 reaction row로 이동하고 focused/resource/compile 및 최종 Gate PASS를 확인했다.
|
||||||
|
|
||||||
|
### REV-P15-003 — 공유 FeedItem 잠금 정책이 다른 홈 화면 reaction을 숨김
|
||||||
|
|
||||||
|
- **심각도:** High
|
||||||
|
- **상태:** 수정 완료
|
||||||
|
- **관련 요구사항:** PRD `Edge Cases`, `Constraints & Dependencies`, `P15-T1`~`P15-T2`
|
||||||
|
- **소유 Task:** `P15-R3`
|
||||||
|
|
||||||
|
**관찰 내용**
|
||||||
|
|
||||||
|
`FeedItem.Community.showReaction = !isLocked`와 공유 `FeedCommunityView.bind()` 조합이 크리에이터 채널 홈뿐 아니라 메인 홈 인기 커뮤니티에도 적용된다. 메인 홈 mapper도 `price`, `existOrdered`를 전달하므로 유료 미구매 item의 기존 reaction 표시가 함께 사라진다.
|
||||||
|
|
||||||
|
**근거**
|
||||||
|
|
||||||
|
- 공유 정책: `FeedItem.kt`의 `showReaction`, `showComment`
|
||||||
|
- 다른 호출부: `HomeRecommendationMappers.kt:84-103`이 `price`, `existOrdered`를 전달
|
||||||
|
- 공유 View 사용: `HomePopularCommunityAdapter.kt:49`의 `view.bind(item.item)`
|
||||||
|
- 문서: PRD `다른 화면에서 사용하는 FeedItem.Community는 신규 필드 기본값으로 기존 표시 동작을 유지한다.`
|
||||||
|
|
||||||
|
**영향**
|
||||||
|
|
||||||
|
요구 범위 밖인 메인 홈 인기 커뮤니티의 유료 미구매 item 표시가 변경되어 기존 화면 회귀가 발생한다.
|
||||||
|
|
||||||
|
**권장 조치**
|
||||||
|
|
||||||
|
공유 모델의 신규 정책 기본값은 기존 reaction 표시를 유지하고, 크리에이터 채널 홈 mapper만 잠금 reaction 숨김을 명시적으로 선택한다.
|
||||||
|
|
||||||
|
**판정 기록**
|
||||||
|
|
||||||
|
- 2026-08-10 — 전체 `FeedItem.Community` production 생성자와 공유 View 호출부를 추적해 실제 범위 확장 결함으로 확정했다.
|
||||||
|
- 2026-08-10 — 기본값은 기존 reaction 표시를 유지하고 크리에이터 채널 홈만 opt-in하도록 수정했다. 공유 모델·메인 홈 mapper 회귀 테스트와 최종 Gate가 PASS했다.
|
||||||
|
|
||||||
|
### REV-P15-004 — 메인 홈 회귀 테스트 파일의 미사용 import가 style Gate를 막음
|
||||||
|
|
||||||
|
- **심각도:** Low
|
||||||
|
- **상태:** 수정 완료
|
||||||
|
- **관련 요구사항:** `P15-R3`, `P15-GATE`
|
||||||
|
- **소유 Task:** `P15-R4`
|
||||||
|
|
||||||
|
**관찰 내용**
|
||||||
|
|
||||||
|
메인 홈 mapper 회귀 테스트 추가로 `HomeMainFragmentLayoutTest`가 다시 lint 대상이 되면서 사용처가 없는 `ColorDrawable` import 1건이 확인됐다.
|
||||||
|
|
||||||
|
**근거**
|
||||||
|
|
||||||
|
- 코드: `HomeMainFragmentLayoutTest.kt:6`
|
||||||
|
- 검증: `./gradlew :app:ktlintCheck` 결과 `Unused import`
|
||||||
|
|
||||||
|
**영향**
|
||||||
|
|
||||||
|
기능 동작에는 영향이 없으나 Phase 15 style Gate가 실패한다.
|
||||||
|
|
||||||
|
**권장 조치**
|
||||||
|
|
||||||
|
사용처가 없는 `ColorDrawable` import 1줄만 제거한다.
|
||||||
|
|
||||||
|
**판정 기록**
|
||||||
|
|
||||||
|
- 2026-08-10 — `rg`로 import 외 사용처가 없음을 확인해 정확한 style Gate 결함으로 확정했다.
|
||||||
|
- 2026-08-10 — `ColorDrawable` import 1줄 제거 후 `ktlintCheck`와 최종 focused test Gate가 PASS했다.
|
||||||
|
|
||||||
|
## 7. 확정 항목의 plan·goal 전환
|
||||||
|
|
||||||
|
- `plan-task.md` Phase 15에 `Task 15.R1` / goal `P15-R1`, `Task 15.R2` / goal `P15-R2`, `Task 15.R3` / goal `P15-R3`, `Task 15.R4` / goal `P15-R4`를 추가했다.
|
||||||
|
- 각 항목의 대상 파일, 최소 수정 범위, 회귀 검증, 완료 증거를 명시했다.
|
||||||
|
|
||||||
|
## 8. 리뷰 종료 판정
|
||||||
|
|
||||||
|
| 판정 항목 | 결과 | 근거 |
|
||||||
|
|---|---|---|
|
||||||
|
| 리뷰 범위 전체 확인 | 충족 | PRD/plan·production/test/diff 대조 |
|
||||||
|
| 후보 항목 판정 완료 | 충족 | `REV-P15-001`~`REV-P15-004` 확정 |
|
||||||
|
| 확정 항목 plan 반영 | 충족 | `P15-R1`~`P15-R4` |
|
||||||
|
| 보류 항목 기록 | 해당 없음 | 없음 |
|
||||||
|
| 검증 명령과 결과 기록 | 충족 | `P15-R1`~`P15-R4` 수정 후 최종 Gate PASS |
|
||||||
|
|
||||||
|
**최종 결론:** 수정 검증 완료
|
||||||
|
|
||||||
|
**남은 항목:** 없음. 기기·에뮬레이터 수동 UI 확인과 실제 서버 Response 배포 상태는 리뷰 범위 밖이다.
|
||||||
|
|
||||||
|
## 9. 수정 후 검증 기록
|
||||||
|
|
||||||
|
- `REV-P15-001`: `CreatorChannelActivity`의 미사용 `SharedPreferenceManager` import 1줄을 제거했다.
|
||||||
|
- `REV-P15-002`: `ll_feed_community_reaction` id를 프로필 row에서 실제 하트·댓글 row로 이동했다.
|
||||||
|
- `REV-P15-003`: `FeedItem.Community.hideReactionWhenLocked` 기본값을 `false`로 두고 크리에이터 채널 홈 mapper만 `true`를 전달했다. 메인 홈 인기 커뮤니티 잠금 item의 기존 reaction 표시 회귀 테스트를 추가했다.
|
||||||
|
- `REV-P15-004`: `HomeMainFragmentLayoutTest`의 미사용 `ColorDrawable` import 1줄을 제거했다.
|
||||||
|
- 최종 전체 focused test: `BUILD SUCCESSFUL`.
|
||||||
|
- 최종 `:app:mergeDebugResources`, `:app:compileDebugKotlin`, `:app:ktlintCheck`: `BUILD SUCCESSFUL`.
|
||||||
|
- 최종 `git diff --check`, `git diff HEAD --check`: 출력 없이 PASS.
|
||||||
|
- 정적 재검토: 구매 호출은 상세에만 존재하고, 홈·커뮤니티 item은 상세 이동만 수행하며, 상세 배지 제거·권한별 메뉴·홈+커뮤니티 refresh 계약이 유지됨을 확인했다.
|
||||||
|
- 독립 재검토: Critical/Minor 없음, Important 1건(`REV-P15-003`)을 수정 반영했다.
|
||||||
|
- 수정 후 독립 최종 재검토: `REV-P15-001`~`REV-P15-004` 반영을 확인했고 새 Critical/Important/Minor 이슈 없음으로 판정했다.
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
# Phase 16 홈·커뮤니티 리스트 더보기 메뉴 리뷰 보고서
|
||||||
|
|
||||||
|
## 1. 리뷰 정보
|
||||||
|
|
||||||
|
| 항목 | 내용 |
|
||||||
|
|---|---|
|
||||||
|
| 리뷰 대상 | Phase 16 / `P16-T1`~`P16-T3`, `P16-GATE` |
|
||||||
|
| 기준 commit 또는 working tree | 현재 `main` 미커밋 working tree |
|
||||||
|
| 리뷰 일자 | 2026-08-10 |
|
||||||
|
| 리뷰어 | Sisyphus, 독립 목표·QA·품질·보안·맥락 검토 |
|
||||||
|
| 기준 문서 | `prd.md` F0-20260810, `plan-task.md` Phase 16 |
|
||||||
|
| 리뷰 상태 | 수정 검증 완료 |
|
||||||
|
|
||||||
|
## 2. 리뷰 목적과 범위
|
||||||
|
|
||||||
|
### 목적
|
||||||
|
|
||||||
|
- 홈과 커뮤니티 리스트형의 권한별 더보기 메뉴가 PRD와 일치하는지 확인한다.
|
||||||
|
- 완료 체크박스와 production/test/Gate/문서 기록이 일치하는지 확인한다.
|
||||||
|
|
||||||
|
### 포함 범위
|
||||||
|
|
||||||
|
- 코드: Phase 16 공용 권한 모델, 홈·리스트 callback, Activity 메뉴·mutation
|
||||||
|
- 테스트: Phase 16 focused local unit/source contract 테스트
|
||||||
|
- 문서: `prd.md` F0-20260810, `plan-task.md` Phase 16
|
||||||
|
- 수동 검증: 코드·XML·diff 정적 검토
|
||||||
|
|
||||||
|
### 제외 범위
|
||||||
|
|
||||||
|
- `app/src/androidTest`, 기기·에뮬레이터 조작, 스크린샷·시각 QA
|
||||||
|
- 실제 서버 Response와 API mutation, Git commit, 별도 worktree
|
||||||
|
|
||||||
|
## 3. 판정 기준
|
||||||
|
|
||||||
|
| 심각도 | 기준 |
|
||||||
|
|---|---|
|
||||||
|
| Blocker | 보안·데이터 손실, 핵심 흐름 불능 |
|
||||||
|
| High | 확정 PRD/API 계약 위반 또는 주요 회귀 |
|
||||||
|
| Medium | 제한 조건의 기능·접근성·복구 문제 |
|
||||||
|
| Low | 유지보수성, 문서 정합성, style Gate 문제 |
|
||||||
|
|
||||||
|
## 4. 검토한 근거
|
||||||
|
|
||||||
|
- 요구사항: PRD `Home and Community List More Menu Requirements`, `Change Propagation Requirements`
|
||||||
|
- 계획: `P16-T1`~`P16-T3`, `P16-GATE`
|
||||||
|
- 코드/테스트: Phase 16 Task에 명시된 파일과 focused test
|
||||||
|
- 실행 환경: macOS, 저장소 Gradle wrapper, debug variant
|
||||||
|
|
||||||
|
| 명령 또는 검증 | 결과 | 핵심 증거 |
|
||||||
|
|---|---|---|
|
||||||
|
| 홈·Feed·Activity focused test | 성공 | `BUILD SUCCESSFUL` |
|
||||||
|
| community/action focused test | 성공 | `BUILD SUCCESSFUL` |
|
||||||
|
| `./gradlew :app:mergeDebugResources` | 성공 | `BUILD SUCCESSFUL` |
|
||||||
|
| `./gradlew :app:compileDebugKotlin` | 성공 | `BUILD SUCCESSFUL` |
|
||||||
|
| `./gradlew :app:ktlintCheck` | 성공 | 기존 `disabled_rules` 경고만 존재 |
|
||||||
|
| `git diff --check`, `git diff HEAD --check` | 성공 | 출력 없음 |
|
||||||
|
|
||||||
|
## 5. 발견 사항 요약
|
||||||
|
|
||||||
|
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| `REV-P16-001` | Low | 수정 완료 | Phase 16 구현·Gate 완료 상태가 문서에 누락됨 | `P16-R1` | 완료 기록과 Verification Log 누적 |
|
||||||
|
|
||||||
|
## 6. 발견 사항 상세
|
||||||
|
|
||||||
|
### REV-P16-001 — Phase 16 구현·Gate 완료 상태가 문서에 누락됨
|
||||||
|
|
||||||
|
- **심각도:** Low
|
||||||
|
- **상태:** 수정 완료
|
||||||
|
- **관련 요구사항:** Phase 16 완료 조건, `P16-GATE`
|
||||||
|
- **소유 Task:** `P16-R1`
|
||||||
|
|
||||||
|
**관찰 내용**
|
||||||
|
|
||||||
|
모든 Phase 16 Task와 Gate가 통과했지만 계획 기록은 `구현 미시작`으로 남고 `Verification Log`에 Gate 결과가 없었다.
|
||||||
|
|
||||||
|
**근거**
|
||||||
|
|
||||||
|
- 문서: `plan-task.md` Phase 16 계획 기록과 `Verification Log`
|
||||||
|
- 검증: 최종 5개 리뷰 중 목표·코드 품질·맥락 검토가 동일 문서 불일치를 차단 이슈로 판정
|
||||||
|
|
||||||
|
**영향**
|
||||||
|
|
||||||
|
기능 동작에는 영향이 없지만 문서상 완료 조건을 충족하지 못해 Phase 완료 판정을 내릴 수 없다.
|
||||||
|
|
||||||
|
**권장 조치**
|
||||||
|
|
||||||
|
기존 계획 기록은 보존하고 구현·Gate 완료 기록과 `Verification Log`를 누적한 뒤 최종 리뷰를 재실행한다.
|
||||||
|
|
||||||
|
**판정 기록**
|
||||||
|
|
||||||
|
- 2026-08-10 — 코드와 Gate는 통과했으나 완료 문서가 실제 상태와 달라 확정했다.
|
||||||
|
- 2026-08-10 — 기존 계획 기록을 보존하고 Phase 16 구현·Gate 완료 기록과 `Verification Log`를 누적했다.
|
||||||
|
|
||||||
|
## 7. 확정 항목의 plan·goal 전환
|
||||||
|
|
||||||
|
- `plan-task.md`에 `Task 16.R1` / goal `P16-R1`을 추가했다.
|
||||||
|
|
||||||
|
## 8. 리뷰 종료 판정
|
||||||
|
|
||||||
|
| 판정 항목 | 결과 | 근거 |
|
||||||
|
|---|---|---|
|
||||||
|
| 리뷰 범위 전체 확인 | 충족 | PRD/plan·production/test/diff 대조 |
|
||||||
|
| 후보 항목 판정 완료 | 충족 | `REV-P16-001` 확정 |
|
||||||
|
| 확정 항목 plan 반영 | 충족 | `P16-R1` |
|
||||||
|
| 보류 항목 기록 | 해당 없음 | 없음 |
|
||||||
|
| 검증 명령과 결과 기록 | 충족 | Phase 16 구현 기록과 `Verification Log` 누적 |
|
||||||
|
|
||||||
|
**최종 결론:** 수정 검증 완료
|
||||||
|
|
||||||
|
**남은 항목:** 없음. 실제 서버 권한 검증과 기기 UI 확인은 리뷰 범위 밖이다.
|
||||||
|
|
||||||
|
## 9. 수정 후 검증 기록
|
||||||
|
|
||||||
|
- 수정 전: Phase 16 계획 기록은 구현 미시작이며 `Verification Log`에 Gate 결과가 없었다.
|
||||||
|
- 1차 수정: 기존 계획 기록 뒤에 구현·Gate 완료 기록을 추가하고 `Verification Log`에 최종 자동 검증과 제외 범위를 누적했다.
|
||||||
|
- 최종 검증: `git diff --check`, `git diff HEAD --check`가 출력 없이 PASS했다.
|
||||||
|
- 목표·제약 검토: PASS, 차단 이슈 없음.
|
||||||
|
- 실행 QA: PASS, 허용된 P0 시나리오 11/11과 focused 128개, community/action 96개 PASS.
|
||||||
|
- 코드 품질 검토: PASS, 차단 이슈 없음.
|
||||||
|
- 보안 검토: PASS, 클라이언트 범위의 Critical/High 이슈 없음. 서버 소유권·rate limit 검증은 저장소 밖이라 확인하지 못했다.
|
||||||
|
- 맥락 검토: PASS, 추가 요구사항·호출자·Git 이력 충돌 없음.
|
||||||
|
|
||||||
|
### 2차 독립 재점검 — 2026-08-11
|
||||||
|
|
||||||
|
- 문서 체크: `plan-task.md`의 실제 Task 체크박스는 모두 완료 상태이며, 체크 누락 또는 미구현 항목이 없음을 production/test와 역대조했다.
|
||||||
|
- 요구사항 대조: 더보기 권한 truth table, 홈·리스트 root/more click 분리, grid 제외, 작성자 관리/일반 사용자 신고, 성공 관리 작업의 홈+커뮤니티 갱신, 신고·실패 no-refresh가 PRD와 일치했다.
|
||||||
|
- 공유 회귀 대조: `FeedItem.Community.hideReactionWhenLocked=false`, `showMore=false` 기본값과 크리에이터 채널 홈의 명시적 opt-in을 확인해 다른 공유 feed의 기존 표시를 보존함을 확인했다.
|
||||||
|
- 자동 검증: Phase 15·16 focused test, community/action focused test, 전체 `:app:testDebugUnitTest`, resource merge, Kotlin compile, ktlint, 두 diff check가 모두 PASS했다.
|
||||||
|
- 독립 리뷰: Critical/Important/Minor 이슈 없음, 승인 가능 판정. 새 확정 항목이 없어 회귀 수정 Task와 production/test 수정은 추가하지 않았다.
|
||||||
Reference in New Issue
Block a user