fix(home): 팔로잉 탭 후속 검증을 반영한다
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
# Phase 4 팔로잉 UI surface와 routing 리뷰
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 4 / Task 4.1~4.9 |
|
||||
| 기준 commit 또는 working tree | `f600d10e8b63b6fdc812b18a874b077dfe072ca8` + 2026-07-31 working tree |
|
||||
| 리뷰 일자 | 2026-07-31 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | 대상 `prd.md`, `plan-task.md`, 후속 pull-refresh/access 문서 |
|
||||
| 리뷰 상태 | 수정 검증 완료 |
|
||||
|
||||
## 2. 리뷰 목적과 범위
|
||||
|
||||
### 목적
|
||||
|
||||
- 팔로잉 surface 전환, section visibility, adapter click 연결과 상태 observer가 요구사항을 충족하는지 확인한다.
|
||||
- 후속 pull refresh와 공통 액션 변경을 포함한 현재 실행 경로에서 회귀가 없는지 확인한다.
|
||||
|
||||
### 포함 범위
|
||||
|
||||
- `HomeMainFragment.kt`, `fragment_v2_main_home.xml`
|
||||
- 팔로잉 adapter 5개, `HomeFollowingFragmentSourceTest`, `HomeMainFragmentLoginGuardSourceTest`
|
||||
|
||||
### 제외 범위
|
||||
|
||||
- chevron 실제 목적지와 로그인 유도 UI 디자인.
|
||||
|
||||
## 3. 판정 기준
|
||||
|
||||
- item 상세 이동 누락은 확정 기능 요구사항 위반으로 High, 제한된 로그인 만료/refresh 조건의 복구 문제는 Medium으로 판정했다.
|
||||
|
||||
## 4. 검토한 근거
|
||||
|
||||
### 문서와 코드
|
||||
|
||||
- PRD `On Air` item 터치 요구사항과 `이달의 스케줄` type/target 이동 요구사항
|
||||
- `HomeMainFragment.kt:115-122`, `397-419`, `657-687`
|
||||
- `HomeFollowingLiveAdapter.kt:49-54`, `HomeFollowingScheduleAdapter.kt:53-67`
|
||||
|
||||
### 실행 환경
|
||||
|
||||
```text
|
||||
OS: Darwin 25.0.0 x86_64
|
||||
JDK: OpenJDK 17.0.15
|
||||
Gradle wrapper: 8.14.3
|
||||
연결 기기: 없음
|
||||
```
|
||||
|
||||
### 실행한 검증
|
||||
|
||||
| 명령 | 결과 | 핵심 증거 |
|
||||
|---|---|---|
|
||||
| `./gradlew --no-daemon :app:testDebugUnitTest --tests "...HomeFollowingFragmentSourceTest" --tests "...HomeMainFragmentLoginGuardSourceTest"` | 성공 | source test 모두 PASS |
|
||||
| callback/observer 본문 수동 추적 | 문제 확인 | live/schedule callback은 `Unit`, LoginRequired는 refresh 종료 호출 없음 |
|
||||
| profile helper·Coil 2.7.0 source·저장소 `dispose()` 사용례 대조 | 문제 확인 | blank 분기가 drawable만 비우고 View 요청을 해제하지 않음 |
|
||||
| `HomeFollowingFragmentSourceTest` 이름과 테스트명 가이드 대조 | 문제 확인 | 완전한 영문 문장형 테스트명 11개 확인 |
|
||||
|
||||
## 5. 발견 사항 요약
|
||||
|
||||
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||||
|---|---|---|---|---|---|
|
||||
| `REV-P4-001` | High | 수정 완료 | On Air와 스케줄 item 터치가 no-op | Task 4.6 | `P4-R1` 완료 |
|
||||
| `REV-P4-002` | Medium | 수정 완료 | LoginRequired 응답에서 pull refresh가 종료되지 않음 | Task 4.7 | `P4-R2` 완료 |
|
||||
| `REV-P4-003` | Medium | 수정 완료 | blank 프로필 이미지 바인딩이 이전 Coil 요청을 해제하지 않음 | Task 4.8 | `P4-R3` 완료 |
|
||||
| `REV-P4-004` | Low | 수정 완료 | 공용 source test의 영문 문장형 테스트명이 한글 테스트명 규칙과 불일치 | Task 4.9 | `P4-R4` 완료 |
|
||||
|
||||
## 6. 발견 사항 상세
|
||||
|
||||
### REV-P4-001 — On Air와 스케줄 item 터치가 no-op
|
||||
|
||||
- **심각도:** High
|
||||
- **상태:** 수정 완료
|
||||
- **관련 요구사항:** PRD On Air `item 터치 시 해당 live room/detail`, 스케줄 `type/targetId 목적지`
|
||||
- **소유 Task:** Task 4.6
|
||||
|
||||
**관찰 내용**
|
||||
|
||||
adapter click listener는 Fragment callback까지 전달되지만 `onFollowingLiveClick()`과 `onFollowingScheduleClick()` 본문이 `Unit`이라 아무 이동도 발생하지 않는다.
|
||||
|
||||
**근거**
|
||||
|
||||
- 코드: `HomeMainFragment.kt:119-122`, `669-671`
|
||||
- 테스트: source test는 callback 존재만 확인하고 실제 액션을 요구하지 않음
|
||||
- 문서: Task 4.5 결과도 route 없는 skeleton임을 기록
|
||||
|
||||
**재현 또는 검증 절차**
|
||||
|
||||
1. On Air 또는 schedule item을 bind한다.
|
||||
2. item root를 터치하면 adapter callback이 Fragment로 전달된다.
|
||||
3. 실제 결과는 `Unit`; 요구 결과는 기존 공통 Live/Content/Community Action 이동이다.
|
||||
|
||||
**영향**
|
||||
|
||||
팔로잉 핵심 섹션 두 곳에서 사용자가 상세 콘텐츠로 진입할 수 없다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
On Air `liveId`는 `LiveActionCoordinator`, schedule `type/targetId`는 기존 최근 활동 route와 공통 액션을 재사용한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- 2026-07-30 — PRD와 실제 callback 본문을 대조해 확정.
|
||||
- 2026-07-30 — Task 4.6에서 공통 Live/Content/Community Action 연결과 회귀 검증을 완료.
|
||||
|
||||
### REV-P4-002 — LoginRequired 응답에서 pull refresh가 종료되지 않음
|
||||
|
||||
- **심각도:** Medium
|
||||
- **상태:** 수정 완료
|
||||
- **관련 요구사항:** 로그인 필요 상태 분기, 후속 홈 pull refresh 완료 정책
|
||||
- **소유 Task:** Task 4.7
|
||||
|
||||
**관찰 내용**
|
||||
|
||||
`Content`와 `Empty`는 `finishHomePullRefresh()`를 호출하지만 `LoginRequired`는 section만 숨긴다. 유효하지 않은 token 등으로 refresh API가 LoginRequired를 반환하면 SwipeRefresh spinner와 내부 refresh 추적 상태가 남는다.
|
||||
|
||||
**근거**
|
||||
|
||||
- 코드: `HomeMainFragment.kt:397-419`
|
||||
- 비교 경로: `Content` 406, `Empty` 411, `LoginRequired` 413
|
||||
- 테스트: LoginRequired의 section 숨김만 검사하고 refresh 종료는 검사하지 않음
|
||||
|
||||
**재현 또는 검증 절차**
|
||||
|
||||
1. 팔로잉 탭에서 pull refresh를 시작한다.
|
||||
2. API가 `isLoginRequired=true`를 반환한다.
|
||||
3. 실제 결과는 `bindHomeFollowingEmpty(false)`만 실행된다.
|
||||
4. 요구 결과는 section 숨김과 refresh 종료다.
|
||||
|
||||
**영향**
|
||||
|
||||
로그인 만료·서버 세션 불일치 조건에서 refresh UI가 계속 동작 중인 것처럼 남는다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
LoginRequired 분기에서 팔로잉 refresh 종료를 보장하는 최소 분기와 회귀 테스트를 추가한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- 2026-07-30 — observer 분기와 refresh 상태 함수를 추적해 확정.
|
||||
- 2026-07-30 — Task 4.7에서 LoginRequired refresh 종료 분기와 회귀 검증을 완료.
|
||||
|
||||
### REV-P4-003 — blank 프로필 이미지 바인딩이 이전 Coil 요청을 해제하지 않음
|
||||
|
||||
- **심각도:** Medium
|
||||
- **상태:** 수정 완료
|
||||
- **관련 요구사항:** PRD 팔로잉 크리에이터의 blank/실패 profile fallback, Phase 4 adapter image binding
|
||||
- **소유 Task:** Task 4.8
|
||||
|
||||
**관찰 내용**
|
||||
|
||||
팔로잉 creator/live/chat/schedule와 community profile이 재사용하는 `loadHomeCreatorProfileImage()`는 URL이 blank이면 drawable만 비운다. Coil 2.7.0은 View에 연결된 기존 요청을 별도로 유지하므로, 이전 item의 요청이 아직 진행 중이면 완료 시점에 재사용된 View에 이전 이미지가 다시 표시될 수 있다.
|
||||
|
||||
**근거**
|
||||
|
||||
- 코드: `HomeCreatorProfileImageLoader.kt:10-17`의 blank 분기에 `dispose()`가 없음
|
||||
- 호출부: `HomeFollowingCreatorAdapter`, `HomeFollowingLiveAdapter`, `HomeFollowingChatAdapter`, `HomeFollowingScheduleAdapter`, `HomeFollowingNewsAdapter`
|
||||
- 의존성: `app/build.gradle`의 Coil `2.7.0`; 로컬 Coil source의 `ViewTargetRequestManager.dispose()`는 현재 요청을 취소·분리함
|
||||
- 저장소 정상 사용례: `HomePopularCommunityAdapter.kt:67-70`은 blank image에서 `imageView.dispose()` 후 drawable을 비움
|
||||
|
||||
**재현 또는 검증 절차**
|
||||
|
||||
1. 재사용 가능한 profile `ImageView`에 완료 전 Coil 요청 A를 연결한다.
|
||||
2. 같은 ViewHolder에 blank profile URL item B를 바인딩한다.
|
||||
3. 현재 구현은 drawable만 비우고 요청 A를 유지한다.
|
||||
4. 요청 A가 뒤늦게 완료되면 item B에 A의 프로필이 표시될 수 있다. 요구 결과는 blank 바인딩 시 요청 A를 해제하고 빈 상태를 유지하는 것이다.
|
||||
|
||||
**영향**
|
||||
|
||||
빠른 스크롤이나 느린 이미지 응답 조건에서 creator/live/chat/schedule/news item에 다른 사용자의 프로필 이미지가 노출될 수 있다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
공통 `loadHomeCreatorProfileImage()`의 blank 분기에서 Coil 요청을 해제한 뒤 drawable을 비운다. 각 adapter를 중복 수정하지 않고 공통 helper 한 곳을 수정하며, Robolectric에서 View 요청 연결 해제를 검증한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- 2026-07-30 — 공통 helper, 모든 팔로잉 호출부, Coil 2.7.0 요청 manager와 저장소 내 `dispose()` 사용례를 대조해 확정.
|
||||
- 2026-07-30 — 구현 전 Phase 4 Task 4.8 / `P4-R3`로 전환.
|
||||
- 2026-07-30 — Task 4.8에서 `loadHomeCreatorProfileImage()` blank 분기에 `dispose()`를 추가하고, Robolectric focused test와 팔로잉 회귀 검증을 완료.
|
||||
|
||||
### REV-P4-004 — 공용 source test의 영문 문장형 테스트명이 규칙과 불일치함
|
||||
|
||||
- **심각도:** Low
|
||||
- **상태:** 수정 완료
|
||||
- **관련 요구사항:** `docs/agent-guides/build-test-style.md`, `docs/agent-guides/code-style.md`의 신규 Kotlin 테스트명 규칙
|
||||
- **소유 Task:** 신규 Task 4.9
|
||||
|
||||
**관찰 내용**
|
||||
|
||||
Phase 4에서 생성되고 후속 Phase가 함께 확장한 `HomeFollowingFragmentSourceTest`에 완전한 영문 문장형 backtick 테스트명 11개가 남아 있다. 테스트 작성 전부터 가이드가 신규 Kotlin 테스트명을 한글 문장으로 작성하도록 규정했지만 별도 한글 설명도 없다.
|
||||
|
||||
**근거**
|
||||
|
||||
- 테스트: `HomeFollowingFragmentSourceTest.kt`의 `home layout exposes...`부터 `following recent news adapter binds...`까지 영문 문장형 테스트명 11개
|
||||
- 가이드: `docs/agent-guides/build-test-style.md`와 `docs/agent-guides/code-style.md`의 신규 테스트명 한글 규칙
|
||||
- 이력: 테스트명 규칙은 `2026-05-28`, 대상 테스트는 `2026-06-25` 이후 추가됨
|
||||
|
||||
**재현 또는 검증 절차**
|
||||
|
||||
1. ``rg -n 'fun `[A-Za-z]' HomeFollowingFragmentSourceTest.kt``로 대상 이름을 확인한다.
|
||||
2. 클래스의 완전한 영문 문장형 이름 11개와 가이드의 한글 문장 규칙을 대조한다.
|
||||
3. 테스트 동작은 통과하지만 명명 규칙 불일치가 재현된다.
|
||||
|
||||
**영향**
|
||||
|
||||
runtime 동작에는 영향이 없지만 동일 기능 테스트의 언어 관례가 섞이고, 완료된 Phase가 저장소 테스트 규칙을 충족한다는 판정과 어긋난다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
assertion과 production 코드는 바꾸지 않고 해당 11개 테스트명만 검증 의도가 드러나는 한글 문장으로 변경한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- 2026-07-31 — 테스트 생성 이력, 현재 이름과 가이드를 대조해 확정하고 Phase 4 Task 4.9 / `P4-R4`로 전환.
|
||||
- 2026-07-31 — Task 4.9에서 대상 테스트명 11개를 한글 문장으로 변경하고 focused/팔로잉 회귀 검증을 완료.
|
||||
|
||||
## 7. 확정 항목의 plan·goal 전환
|
||||
|
||||
- Phase 4에 Task 4.6 / `P4-R1`, Task 4.7 / `P4-R2`를 추가해 완료했다.
|
||||
- 추가 재점검에서 Task 4.8 / `P4-R3`를 추가했으며 구현·검증을 완료했다.
|
||||
- 최종 재점검에서 Task 4.9 / `P4-R4`를 추가했으며 테스트명 정리를 완료했다.
|
||||
|
||||
## 8. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | UI→adapter→callback→공통 액션 경로 추적 |
|
||||
| 후보 항목 판정 완료 | 충족 | REV-P4-001~004 판정 완료 |
|
||||
| 확정 항목 plan 반영 | 충족 | Task 4.6~4.9 추가 |
|
||||
| 검증 명령과 결과 기록 | 충족 | focused/source test PASS와 수동 코드 추적 |
|
||||
|
||||
**최종 결론:** 확정 발견 사항 모두 수정 완료
|
||||
|
||||
**남은 항목:** 없음
|
||||
|
||||
## 9. 수정 후 검증 기록
|
||||
|
||||
- 2026-07-30: `REV-P4-001` 보완으로 팔로잉 On Air item은 `liveActionCoordinator.enterLiveRoom(item.liveId)`, 스케줄 item은 `CreatorActivityType`별 기존 Live/Content/Community Action을 호출하도록 연결했다. `targetId <= 0L`은 공통 action 호출 전 return하며 직접 `Intent` 또는 legacy extra 조립은 추가하지 않았다.
|
||||
- 2026-07-30: `REV-P4-002` 보완으로 `HomeFollowingUiState.LoginRequired` 분기에서 section 숨김 후 `finishHomePullRefresh(HOME_TAB_FOLLOWING)`을 호출하도록 변경했다. focused source test, 팔로잉 전체 회귀, `v2.live.action.*`, `v2.content.action.*`, `v2.community.action.*`, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check` PASS.
|
||||
- 2026-07-30: `REV-P4-003` 보완으로 `HomeCreatorProfileImageLoader.loadHomeCreatorProfileImage()`의 blank URL 분기에서 기존 Coil 요청을 `dispose()`로 해제한 뒤 drawable을 비우도록 변경했다. 구현 전 `HomeCreatorProfileImageLoaderTest` RED, 구현 후 focused GREEN, 팔로잉 전체 회귀와 신규 프로필 테스트, `:app:mergeDebugResources`, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check` PASS.
|
||||
- 2026-07-31 재점검: Fragment/source·공통 action·프로필 loader를 포함한 Phase 1~13 focused 회귀 113개 test, resource merge, Kotlin compile, ktlint PASS. 추가 확정 발견 사항 없음.
|
||||
- 2026-07-31 최종 재점검: `HomeFollowingFragmentSourceTest`의 영문 문장형 테스트명 11개를 `REV-P4-004`로 확정하고, production/test 동작을 변경하기 전에 Task 4.9 / `P4-R4`로 전환했다.
|
||||
- 2026-07-31 최종 검증: 통합 20개 suite·306개 test와 resource merge, Kotlin compile, ktlint가 PASS했다. 자동 검증 성공과 별개로 이름 규칙 위반 `REV-P4-004`는 Task 4.9의 수정 항목으로 유지했다.
|
||||
- 2026-07-31: `REV-P4-004` 보완으로 `HomeFollowingFragmentSourceTest`의 영문 문장형 테스트명 11개를 한글 문장으로 변경했다. `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest"`, 팔로잉 전체 회귀, `:app:compileDebugKotlin`, `:app:ktlintCheck` PASS.
|
||||
- 2026-07-31 이번 리뷰 재검증: surface 전환, section 상태, live/chat/schedule/news 공통 액션과 blank profile 요청 해제 경로를 다시 추적했다. 통합 16개 suite·120개 test, resource merge, Kotlin compile, ktlint가 PASS해 `REV-P4-001~004` 수정 완료 판정을 유지한다.
|
||||
Reference in New Issue
Block a user