docs(banner): 배너 Phase 3과 4 검증을 기록한다

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-05-28 00:08:56 +09:00
parent fe509365e2
commit 31b4e93bed
2 changed files with 21 additions and 11 deletions

View File

@@ -140,21 +140,21 @@
### Phase 3: XML 리소스와 디자인 타임 미리보기
- [ ] **Task 3.1: counter 배경 drawable 추가**
- [x] **Task 3.1: counter 배경 drawable 추가**
- 생성 파일: `app/src/main/res/drawable/bg_banner_counter.xml`
- 요구사항:
- solid color는 `#B3000000` 또는 동등한 `rgba(0,0,0,0.7)` 표현을 사용한다.
- corners radius는 capsule 형태로 충분히 큰 값을 사용한다.
- 검증: resource merge 시 drawable이 정상 참조된다.
- [ ] **Task 3.2: preview placeholder drawable 추가 또는 재사용 결정**
- [x] **Task 3.2: preview placeholder drawable 추가 또는 재사용 결정**
- 생성 후보: `app/src/main/res/drawable/bg_banner_preview_placeholder.xml`
- 요구사항:
- XML layout editor에서 이미지 데이터 없이도 배너 영역이 보인다.
- 기존 적절한 placeholder drawable이 있으면 새 drawable을 만들지 않고 해당 리소스를 사용한다.
- 검증: `item_banner.xml``tools:src` 또는 `android:background`에서 참조 가능하다.
- [ ] **Task 3.3: preview attrs 추가**
- [x] **Task 3.3: preview attrs 추가**
- 생성 또는 수정 파일: `app/src/main/res/values/attrs.xml`
- 추가 속성:
- `bannerPreviewItemCount` format `integer`
@@ -162,7 +162,7 @@
- `bannerPreviewImage` format `reference`
- 검증: `BannerView`에서 `context.obtainStyledAttributes(attrs, R.styleable.BannerView)`로 읽을 수 있다.
- [ ] **Task 3.4: `item_banner.xml` 추가**
- [x] **Task 3.4: `item_banner.xml` 추가**
- 생성 파일: `app/src/main/res/layout/item_banner.xml`
- 요구사항:
- root는 `FrameLayout`.
@@ -170,7 +170,7 @@
- root와 image는 `14dp` radius clip 대상이 될 수 있어야 한다.
- 검증: XML preview에서 정사각형 item 내부 이미지 영역이 확인된다.
- [ ] **Task 3.5: `view_banner.xml` 추가**
- [x] **Task 3.5: `view_banner.xml` 추가**
- 생성 파일: `app/src/main/res/layout/view_banner.xml`
- 요구사항:
- root는 `kr.co.vividnext.sodalive.v2.widget.banner.BannerView`.
@@ -182,7 +182,7 @@
### Phase 4: Adapter와 View 구현
- [ ] **Task 4.1: `BannerAdapter` 구현**
- [x] **Task 4.1: `BannerAdapter` 구현**
- 생성 파일: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/banner/BannerAdapter.kt`
- 요구사항:
- `RecyclerView.Adapter`로 구현한다.
@@ -192,7 +192,7 @@
- item click은 현재 `BannerItem`을 호출부 callback으로 전달한다.
- 검증: adapter 단위 또는 view test에서 click callback이 현재 item을 전달한다.
- [ ] **Task 4.2: `BannerView` 기본 구조 구현**
- [x] **Task 4.2: `BannerView` 기본 구조 구현**
- 생성 파일: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/banner/BannerView.kt`
- 요구사항:
- `@JvmOverloads constructor(context, attrs, defStyleAttr)` 패턴을 사용한다.
@@ -202,7 +202,7 @@
- `setOnBindBannerImage(listener: ((ImageView, BannerItem) -> Unit)?)` 또는 동등한 이미지 바인딩 API를 제공한다.
- 검증: 빈 목록, 단일 목록, 복수 목록을 설정해 visibility와 counter 상태가 바뀐다.
- [ ] **Task 4.3: layout size와 peek 적용**
- [x] **Task 4.3: layout size와 peek 적용**
- 수정 파일: `BannerView.kt`, `BannerAdapter.kt`
- 요구사항:
- view width를 기준으로 item size를 `width - 40dp`로 계산한다.
@@ -211,7 +211,7 @@
- 단일 item은 peek 없이 가운데 정렬한다.
- 검증: `BannerLayoutCalculatorTest``BannerViewTest`에서 item size, spacing, counter visibility를 확인한다.
- [ ] **Task 4.4: counter 표시 구현**
- [x] **Task 4.4: counter 표시 구현**
- 수정 파일: `BannerView.kt`
- 요구사항:
- 0개/1개에서는 counter를 숨긴다.
@@ -220,7 +220,7 @@
- scroll settle 후 실제 item index에 맞춰 counter를 갱신한다.
- 검증: 빠른 scroll 후에도 counter index가 현재 item과 일치한다.
- [ ] **Task 4.5: radius clipping 구현**
- [x] **Task 4.5: radius clipping 구현**
- 수정 파일: `BannerView.kt` 또는 item view holder
- 요구사항:
- 배너 image/root에 `radius_14` outline provider를 적용한다.
@@ -334,3 +334,12 @@
- 2026-05-27 RED: `BannerLayoutCalculatorTest`, `BannerCounterFormatterTest`, `BannerStateTest`는 최초 실행 시 `compileDebugUnitTestKotlin`에서 `BannerLayoutCalculator`, `BannerCounterFormatter`, `BannerState`, `BannerDisplayMode` 미해결 참조로 실패했다.
- 2026-05-27 GREEN: 순수 Kotlin contract 구현 후 대상 순수 테스트가 통과했다.
- 2026-05-27 LSP 진단: `kotlin-lsp`가 설치되어 있지 않아 Phase 2 Kotlin 파일의 LSP diagnostics는 사용할 수 없었다.
- 2026-05-27 Phase 3 리소스 추가: `bg_banner_counter.xml`, `bg_banner_preview_placeholder.xml`, `attrs.xml`, `item_banner.xml`, `view_banner.xml`을 추가했다. 기존 `bg_placeholder.xml`은 큰 vector icon이라 배너 영역 배경으로 재사용하지 않고, `radius_14` 기반 preview placeholder를 별도 생성했다.
- 2026-05-27 Phase 3 counter/view 구성: counter 배경은 `#B3000000` capsule로 만들고, `view_banner.xml``BannerView` root, horizontal `RecyclerView`, 우상단 counter overlay를 포함하도록 구성했다. counter text는 현재 index `white`, separator/total `gray_400` TextView로 분리했다.
- 2026-05-27 Phase 3 warning 조치: `android:clipToOutline`은 minSdk 23 기준 API 31 warning 대상이라 `item_banner.xml`에서 사용하지 않았다. 실제 radius clipping은 계획된 Phase 4.5 Kotlin outline provider 구현에서 처리한다.
- 2026-05-27 Phase 4 RED: `BannerViewTest` 추가 후 최초 실행 시 `view_banner.xml`이 참조하는 `BannerView` production class가 없어 `compileDebugJavaWithJavac`에서 실패함을 확인했다.
- 2026-05-27 Phase 4 구현: `BannerAdapter`는 0/1개 실제 count, 2개 이상 virtual position remap, image bind callback, item click callback, image radius clipping을 구현했다. `BannerView`는 XML child 연결, `RecyclerView + PagerSnapHelper`, `setItems`, click/image bind API, visibility, layout size/padding/spacing, counter 표시를 구현했다.
- 2026-05-27 Phase 4 RED/GREEN: 단일 item spacing 회귀 테스트 `배너 view는 단일 item이면 item 간격을 적용하지 않는다`를 추가해 RED를 확인한 뒤, 단일 item에서는 spacing을 `0`, 복수 item에서는 `8dp`로 적용하도록 수정해 GREEN을 확인했다.
- 2026-05-27 Phase 4 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.banner.*"``./gradlew :app:assembleDebug`를 순차 실행해 모두 `BUILD SUCCESSFUL`을 확인했다. Kotlin LSP는 `kotlin-lsp`가 설치되어 있지 않아 diagnostics를 실행할 수 없었다.
- 2026-05-27 Phase 4 리뷰 수정: 리뷰에서 `BannerView(context)` 실제 사용 경로가 내부 layout을 inflate하지 않는 문제가 지적되어, `view_banner.xml``<merge>`로 변경하고 `BannerView` 생성 시 내부 layout을 inflate하도록 수정했다. `배너 view는 코드로 생성해도 내부 layout을 포함한다` 테스트로 RED를 확인한 뒤 GREEN을 확인했다.
- 2026-05-27 Phase 4 리뷰 수정 후 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.banner.*"``./gradlew :app:assembleDebug`를 다시 실행해 모두 `BUILD SUCCESSFUL`을 확인했다.