fix(creator): 후원 item locale 표시를 적용한다
This commit is contained in:
@@ -41,6 +41,10 @@
|
||||
- `rankings`는 서버가 항상 최대 8명까지만 내려준다.
|
||||
- 후원 랭킹 `전체보기` 버튼은 기존 `UserProfileDonationAllViewActivity`로 이동한다.
|
||||
- 현재 API는 비밀 후원 여부를 별도 필드로 내려주지 않으므로 비밀 후원 전용 UI/표시 분기는 구현하지 않는다.
|
||||
- 사용자가 후원 메시지를 입력하지 않으면 서버는 `message`를 빈 문자열로 반환한다.
|
||||
- 사용자가 직접 입력한 후원 `message`는 번역하지 않고 원문을 유지한다.
|
||||
- 시스템 기본 후원 문구, CAN 단위, 상대 시간은 기존 한국어·영어·일본어 resource 경로를 재사용한다.
|
||||
- 서버 기본 문구의 한국어 패턴을 Android에서 추정하는 보정 로직은 추가하지 않는다.
|
||||
- 본인 채널에서는 content/empty 상태 모두 floating 후원하기 버튼을 숨긴다.
|
||||
- 본인 채널 empty 상태에서는 중앙 `후원하기` button도 숨긴다.
|
||||
- empty UI는 별도 `minHeight` 없이 표시한다.
|
||||
@@ -73,6 +77,8 @@
|
||||
- 탭 연결, pagination, 후원 dialog 재사용, 전체보기 이동은 기존 코드 패턴 중심으로 검증한다.
|
||||
- Phase 7: 필수 참조
|
||||
- 최종 수동 화면 검증은 PRD의 Figma 노드와 실제 화면을 대조한다.
|
||||
- Phase 8: Figma 참조 불필요
|
||||
- 후원 item의 API `message` 의미 계약과 locale별 formatter/resource 출력만 검증한다.
|
||||
|
||||
---
|
||||
|
||||
@@ -552,8 +558,110 @@
|
||||
|
||||
---
|
||||
|
||||
### Phase 8: 후원 item 다국어 회귀 수정
|
||||
|
||||
**Phase 상태:** 완료. 2026-08-26 API 메시지 계약 확인, locale 회귀 테스트 추가, Phase 8 Gate 검증을 완료했다.
|
||||
|
||||
**Phase 결과:** 사용자가 입력하지 않은 시스템 기본 후원 문구, CAN 단위, 상대 시간이 한국어·영어·일본어로 표시되고 사용자 작성 메시지는 원문을 유지한다.
|
||||
|
||||
**선행조건:** `GET /api/v2/creator-channels/{creatorId}/donations`가 사용자 메시지 미입력 시 `message: ""`를 반환한다는 서버 계약과 확인 가능한 응답 증거가 제공되어야 한다.
|
||||
|
||||
**Phase 완료 조건:** `P8-T1`, `P8-T2`, `P8-GATE` 완료와 검증 기록 누적.
|
||||
|
||||
- [x] **Task 8.1: 미입력 후원 메시지 API 계약 확인**
|
||||
|
||||
**Goal 실행 `P8-T1`:** 시스템 기본 문구와 사용자 작성 메시지를 Android가 문구 패턴 추정 없이 구분할 수 있는 응답 계약을 확정한다.
|
||||
|
||||
- **시작 조건:** 서버 담당자가 후원 생성·조회 API의 메시지 미입력 정책을 확인할 수 있다.
|
||||
- **완료 증거:** 미입력 내역의 `message: ""` 응답과 입력 내역의 원문 `message` 응답이 계약 문서 또는 비민감 샘플 응답으로 확인된다.
|
||||
- **범위 밖:** Android에서 한국어 기본 문구 정규식·문구 목록 추가, 사용자 메시지 자동 번역, 서버 코드 변경.
|
||||
- **TDD 예외 사유:** Android 저장소 밖의 API 응답 의미 계약을 확인하는 외부 의존 Task다.
|
||||
- **대체 검증 방법:** 서버 계약 또는 비민감 샘플 응답에서 미입력/입력 각 1건의 `message` 값을 대조한다. 실제 사용자 정보, token, URL은 기록하지 않는다.
|
||||
|
||||
- [x] 메시지 미입력 후원 내역이 `message: ""`로 조회되는지 확인한다.
|
||||
- [x] 사용자 작성 메시지가 번역·기본 문구 치환 없이 원문으로 조회되는지 확인한다.
|
||||
- [x] 서버가 한국어 기본 문구를 채운 응답은 계약 위반임을 확인하고 Android 보정 없이 서버 계약 수정 대상으로 되돌린다.
|
||||
|
||||
- **검증 기록:**
|
||||
- 2026-08-26: 승인된 PRD의 API 요구사항과 Resolved Decisions를 계약 문서로 대조했다. 미입력 메시지는 `message: ""`, 입력 메시지는 원문 반환으로 확정되어 있으며, 서버가 한국어 기본 문구를 채우는 응답은 계약 위반으로 처리하고 Android 보정 로직을 추가하지 않는 경계를 확인했다. 실제 사용자 정보, token, URL은 기록하지 않았다.
|
||||
|
||||
- [x] **Task 8.2: 후원 item locale 표시 회귀 검증**
|
||||
|
||||
**Goal 실행 `P8-T2`:** 기존 Android resource·formatter 경로가 빈 기본 문구, CAN 단위, 상대 시간을 한국어·영어·일본어로 표시하고 사용자 메시지는 보존함을 자동 검증한다.
|
||||
|
||||
- **시작 조건:** `P8-T1` 완료.
|
||||
- **완료 증거:** locale별 mapper/resource 회귀 테스트와 Phase 8 Gate 명령이 PASS하고 실제 결과가 이 Task 아래에 기록된다.
|
||||
- **범위 밖:** 신규 번역 framework, donation 전용 상대 시간 formatter, 사용자 메시지 번역, layout 크기·간격·constraint 검증.
|
||||
- **TDD 예외 사유:** 빈 `message` fallback, CAN resource, 공통 상대 시간 formatter가 이미 구현되어 있어 기존 동작을 억지로 실패시키지 않는 특성화·회귀 테스트 Task다.
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationMapperTest.kt`
|
||||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationFragmentLayoutTest.kt`
|
||||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/common/RelativeTimeFormatterTest.kt`
|
||||
- Confirm only: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/model/CreatorChannelDonationMappers.kt`
|
||||
- Confirm only: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelDonationCardBinder.kt`
|
||||
- Confirm only: `app/src/main/res/values/strings.xml`
|
||||
- Confirm only: `app/src/main/res/values-en/strings.xml`
|
||||
- Confirm only: `app/src/main/res/values-ja/strings.xml`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes: `CreatorChannelDonationResponse.message`, `CreatorChannelDonationResponse.can`, `CreatorChannelDonationResponse.createdAtUtc`, `AndroidUtcRelativeTimeTextFormatter`.
|
||||
- Produces: locale별 `CreatorChannelDonationUiModel.message`, `createdAtText`, `creator_channel_donation_can_format` 표시 계약.
|
||||
|
||||
- [x] 한국어·영어·일본어 context에서 빈 `message`가 각 locale의 `creator_channel_donation_fallback_message`로 매핑되는 테스트를 추가한다.
|
||||
- [x] 비어 있지 않은 동일 사용자 `message`가 세 locale에서 원문 그대로 유지되는 테스트를 추가한다.
|
||||
- [x] `creator_channel_donation_can_format`이 한국어 `%1$s캔`, 영어 `%1$s cans`, 일본어 `%1$sCAN`으로 포맷되는 테스트를 추가한다.
|
||||
- [x] 상대 시간이 세 locale의 기존 `character_comment_time_*` resource 값으로 표시되는 테스트를 추가한다.
|
||||
- [x] 기존 production 경로로 모든 테스트가 통과하면 production 코드를 변경하지 않는다. 실패가 확인되면 실패 원인에 직접 대응하는 최소 Task를 이 Phase에 먼저 추가한다.
|
||||
|
||||
- **검증 기록:**
|
||||
- 2026-08-26: `CreatorChannelDonationMapperTest`에 빈 `message == ""`의 한국어·영어·일본어 fallback과 동일 사용자 메시지 원문 보존 테스트를 추가했다. `CreatorChannelDonationFragmentLayoutTest`에는 실제 공통 card binder를 통한 `1,000캔`, `1,000 cans`, `1,000CAN` 표시 테스트를 추가했고, `RelativeTimeFormatterTest`에는 실제 2분 경과 값이 locale별 `character_comment_time_minutes` resource로 표시되는 테스트를 추가했다. 기존 동작을 고정하는 특성화 테스트이므로 계획의 TDD 예외를 적용했으며 production 코드는 변경하지 않았다. 세 테스트 클래스 focused 실행은 18개 테스트가 모두 PASS했다.
|
||||
|
||||
#### Phase 8 Gate
|
||||
|
||||
**Goal 실행 `P8-GATE`:** 서버의 빈 `message` 계약과 Android의 locale별 표시·사용자 메시지 보존을 최종 판정한다.
|
||||
|
||||
- **시작 조건:** `P8-T1`, `P8-T2` 완료.
|
||||
- **완료 증거:** 아래 명령이 모두 PASS하고 Task 8.1의 API 계약 증거와 함께 검증 기록에 누적된다.
|
||||
- **범위 밖:** `app/src/androidTest`, `connectedDebugAndroidTest`, 기기·에뮬레이터 조작, 스크린샷·시각 QA.
|
||||
|
||||
```bash
|
||||
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationMapperTest"
|
||||
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.common.RelativeTimeFormatterTest"
|
||||
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*"
|
||||
./gradlew :app:mergeDebugResources
|
||||
./gradlew :app:compileDebugKotlin
|
||||
./gradlew :app:ktlintCheck
|
||||
git diff --check
|
||||
```
|
||||
|
||||
**Expected:** 한국어·영어·일본어의 기본 후원 문구, CAN 단위, 상대 시간 테스트와 사용자 메시지 원문 보존 테스트가 PASS하고, production 코드에 한국어 기본 문구 식별 로직이나 신규 번역 dependency가 추가되지 않는다.
|
||||
|
||||
**Gate 결과:** 2026-08-26 완료. 위 7개 명령이 모두 PASS했고 production 코드는 변경하지 않았다.
|
||||
|
||||
- [x] **Task 8.3: 후원 item runtime locale 적용 회귀 수정**
|
||||
|
||||
**Goal 실행 `P8-T3`:** 후원 item의 기본 문구와 상대 시간이 ViewModel 생성 시점의 application context가 아니라 실제 item view context의 locale로 표시되도록 수정한다.
|
||||
|
||||
- **수정 대상:** `CreatorChannelDonationUiModels.kt`, `CreatorChannelDonationMappers.kt`, `CreatorChannelDonationAdapter.kt`, 관련 unit/source test.
|
||||
- **범위 밖:** 후원 문구 내용 변경, 신규 locale 추가, 사용자 작성 메시지 번역, visual QA, 기기·에뮬레이터 확인.
|
||||
- **완료 증거:** RED 테스트가 기존 구조에서 실패하고, 최소 production 수정 후 focused 테스트와 Phase 8 Gate가 PASS한다.
|
||||
|
||||
- [x] 후원 item adapter가 빈 `message` fallback을 item view context로 포맷하도록 테스트를 추가한다.
|
||||
- [x] 후원 item adapter가 `createdAtUtc`를 item view context로 상대 시간 포맷하도록 테스트를 추가한다.
|
||||
- [x] 사용자 작성 `message`는 기존처럼 원문을 유지한다.
|
||||
- [x] production 수정은 raw API 값 보존과 bind 시점 locale 포맷에 필요한 최소 범위로 제한한다.
|
||||
|
||||
- **검증 기록:**
|
||||
- 2026-08-26: RED로 `CreatorChannelDonationFragmentLayoutTest`에 adapter가 item view locale context로 빈 `message` fallback과 2분 전 상대 시간을 표시해야 한다는 테스트를 추가했고, 기존 구현에서 `ComparisonFailure`로 FAIL하는 것을 확인했다. 원인은 `CreatorChannelDonationViewModel`/mapper 단계에서 application context 기반 문자열을 미리 저장해 화면 locale context가 적용될 기회가 없는 구조였다. GREEN으로 mapper는 API `message`와 `createdAtUtc` 원문을 UI model에 보존하고, `CreatorChannelDonationAdapter`가 bind 시점의 `root.context`로 `creator_channel_donation_fallback_message`와 `formatUtcRelativeTimeText()`를 적용하도록 수정했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*"` 실행 결과 PASS했다.
|
||||
|
||||
---
|
||||
|
||||
## Verification Log
|
||||
|
||||
- 2026-08-26: Phase 8 Gate로 mapper focused 테스트, 공통 상대 시간 focused 테스트, 후원 탭 전체 테스트, resource merge, Kotlin compile, ktlint, `git diff --check`를 순서대로 실행해 모두 PASS했다. 별도로 세 변경 테스트 클래스를 `--rerun-tasks`로 실행해 18개 테스트가 실제 수행되는 것을 확인했다. 명세 준수 재리뷰와 코드 품질 리뷰도 APPROVED였으며, 사용자 요청에 따라 worktree, 기기·에뮬레이터, screenshot, visual QA는 실행하지 않았다. 전체 미완료 체크 감사 결과 Phase 8 누락은 없고, Phase 7의 실제 기기/API 수동 확인 Task 7.4만 기존 미완료 상태로 남아 있다.
|
||||
- 2026-06-22: 후원 랭킹 item 순위 text 하단이 보이지 않는 원인을 재확인했다. rank `TextView`는 profile `1:1` 컨테이너 안에서 `0.63` 지점부터 아래로 그려져야 하는데, profile 컨테이너와 root item의 child drawing이 bounds 안으로 clipping되어 컨테이너 밖으로 내려오는 하단 영역이 보이지 않는 구조였다. 따라서 앞선 padding 접근은 제거하고, root와 profile 컨테이너에 `android:clipChildren="false"`를 적용해 rank text가 profile 하단 밖으로 overflow되어도 보이도록 수정했다. 순위 text 상단 위치는 사용자 확인 기준대로 `0.63` guideline에 맞췄다. RED로 `CreatorChannelDonationFragmentLayoutTest`가 기존 XML에서 FAIL하는 것을 확인했고, 수정 후 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*"`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `./gradlew :app:mergeDebugResources`, `git diff --check`가 PASS했다. `:app:mergeDebugResources`는 sandbox에서 Gradle wrapper lock 파일 접근 권한 문제로 1회 실패했고, 외부 권한으로 동일 명령을 재실행해 PASS를 확인했다.
|
||||
- 2026-06-22: 사용자 추가 확인에 따라 후원 랭킹 item 순위 text 하단 clipping과 위치를 재조정한다. 기존 `0.68` guideline은 눈으로 봤을 때 Figma보다 낮게 보이므로 `0.63`으로 올리고, `@font/pattaya_regular` + shadow가 `includeFontPadding=false` 상태에서 하단 여유 없이 그려져 잘릴 수 있어 rank text에 bottom padding을 추가한다. 구현 전 RED로 `CreatorChannelDonationFragmentLayoutTest`가 기존 XML에서 FAIL하는 것을 확인했다.
|
||||
- 2026-06-22: 후원 랭킹 item 순위 표시 위치를 Figma `290:9097` 기준으로 수정했다. `get_design_context`와 screenshot에서 `75dp` profile 기준 rank text `top=51dp`를 확인했고, profile 높이의 약 `68%` 지점을 기준으로 rank text가 시작되도록 profile 영역을 `1:1` nested `ConstraintLayout`으로 분리한 뒤 horizontal guideline `0.68`에 rank top을 연결했다. 기존 bottom constraint + `translationY` 방식은 제거했다. RED로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest"`가 기존 XML에서 FAIL하는 것을 확인했고, 수정 후 동일 focused 테스트가 PASS했다. 회귀 검증으로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Donation*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check`가 PASS했다. `:app:mergeDebugResources`는 sandbox에서 Gradle wrapper lock 파일 접근 권한 문제로 1회 실패했고, 외부 권한으로 동일 명령을 재실행해 PASS를 확인했다.
|
||||
@@ -571,3 +679,4 @@
|
||||
- 2026-07-08: 후원 탭 item과 홈 탭 후원 섹션 색상 변경 통합 검증으로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `git diff --check`가 PASS했다. `./gradlew :app:ktlintCheck`는 이번 변경 파일이 아닌 `HomeMainFragmentLoginGuardSourceTest.kt`의 기존 line length/argument wrapping 위반으로 실패해 별도 관찰사항으로 둔다.
|
||||
- 2026-07-08: 후원 탭 item과 홈 탭 후원 섹션의 카드 내부 UI/색상 정책 중복을 줄이기 위해 추천 방향대로 공통 후원 카드 바인더를 도입하는 리팩토링을 진행한다. 섹션/adapter 전체는 스크롤 방향과 컨테이너 구조가 달라 유지하고, 카드 내부 profile/nickname/time/can/message/header/text color binding만 공통화한다. XML 단일화는 홈 width/message maxLines/icon 차이로 인한 UI 회귀 위험이 있어 이번 리팩토링 범위에서 제외한다.
|
||||
- 2026-07-08: 공통 후원 카드 바인더 리팩토링을 완료했다. `CreatorChannelDonationCardBinder`를 추가해 header 배경, profile, nickname, time, can, message, `red_400`/기본 텍스트 색상 정책을 한 곳에서 처리하고, 홈 탭 후원 섹션과 후원 탭 item adapter는 각자 layout/scroll/icon/message line 정책을 유지한 채 해당 바인더를 호출하도록 변경했다. RED로 `CreatorChannelActivitySourceTest`, `CreatorChannelDonationFragmentLayoutTest`에 공통 바인더 계약을 추가해 기존 구현에서 FAIL하는 것을 확인했고, 수정 후 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Donation*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check`가 PASS했다.
|
||||
- 2026-08-26: 후원 item 문자열 흐름과 기존 문서를 대조했다. 빈 `message`는 `creator_channel_donation_fallback_message`, CAN 단위는 `creator_channel_donation_can_format`, `createdAtUtc`는 `AndroidUtcRelativeTimeTextFormatter`를 사용하고, 비어 있지 않은 `message`는 API 원문을 표시한다. 인터뷰에서 시스템 기본 문구와 CAN 단위·상대 시간을 한국어·영어·일본어로 처리하고 사용자 메시지는 원문 유지하며, 미입력 `message`는 서버가 빈 문자열로 반환하는 계약을 확정했다. 이번 실행은 PRD와 계획/TASK 문서만 갱신했으며 코드 변경·Gradle 빌드·기능 테스트는 수행하지 않았다. `./gradlew tasks --all`은 sandbox의 Gradle wrapper lock 접근 제한으로 1회 실패한 뒤 승인된 권한으로 재실행해 `BUILD SUCCESSFUL`을 확인했다.
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
## 2. Problem
|
||||
- 크리에이터 채널에는 `후원` 탭이 존재하지만 현재 상세 탭 구현은 placeholder 상태다.
|
||||
- 사용자는 크리에이터 채널 안에서 누가 많이 후원했는지와 최근 후원 내역을 한 화면에서 확인할 수 있어야 한다.
|
||||
- 후원 내역 item에 서버가 만든 한국어 기본 후원 문구가 `message`로 포함되면 영어·일본어 환경에서도 한국어가 그대로 표시될 수 있다.
|
||||
- 후원 내역 item의 CAN 단위와 상대 시간도 현재 앱 언어에 맞는 resource 경로를 일관되게 사용해야 한다.
|
||||
- 후원 내역은 길어질 수 있으므로 `hasNext == true`일 때 다음 페이지를 이어서 조회해야 한다.
|
||||
- 후원 탭에서 후원하기를 완료하면 방금 반영된 후원 내역과 랭킹이 화면에 갱신되어야 한다.
|
||||
- 홈 탭에 이미 연결된 채널 후원 플로우와 동일한 UX/API 동작을 재사용해야 한다.
|
||||
@@ -23,6 +25,9 @@
|
||||
- 후원 랭킹 섹션에는 API `rankings`를 응답 순서 기준으로 최대 8명까지 표시한다.
|
||||
- 후원 내역 item에는 후원자 프로필 이미지, 닉네임, 작성 시간, 후원 캔 수량, 메시지를 표시한다.
|
||||
- `createdAtUtc`는 크리에이터 채널 v2 공통 상대 시간 포맷을 따른다.
|
||||
- 사용자가 후원 메시지를 입력하지 않은 경우 서버는 `message`를 빈 문자열로 반환하고, 앱은 현재 locale의 기본 후원 문구를 표시한다.
|
||||
- 사용자가 직접 입력한 `message`는 번역하지 않고 서버 응답 원문을 표시한다.
|
||||
- 후원 기본 문구, CAN 단위, 상대 시간은 한국어·영어·일본어 resource로 표시한다.
|
||||
- 후원 내역 item의 header 색상과 캔 수량 badge는 기존 홈 탭 후원 카드 정책을 우선 재사용한다.
|
||||
- 랭킹 섹션의 `전체보기` 버튼은 기존 `UserProfileDonationAllViewActivity`로 이동한다.
|
||||
- 우측 하단 floating 후원하기 버튼은 홈 탭 후원하기 버튼 터치 액션과 동일하게 `LiveRoomDonationDialog` 기반 채널 후원 플로우를 호출한다.
|
||||
@@ -41,6 +46,9 @@
|
||||
- API schema를 임의 변경하거나 서버 응답 필드명을 클라이언트에서 새로 정의하지 않는다.
|
||||
- Sort-bar에 정렬 label, 정렬 icon, 정렬 popup을 추가하지 않는다.
|
||||
- Figma asset을 localhost URL 그대로 앱 코드에 직접 의존하지 않는다.
|
||||
- 사용자 작성 후원 메시지를 자동 번역하지 않는다.
|
||||
- 서버 기본 문구를 한국어 정규식이나 문구 목록으로 추정해 교체하지 않는다.
|
||||
- 한국어·영어·일본어 외 신규 locale을 추가하지 않는다.
|
||||
- 레거시 후원 구현 파일을 직접 수정하지 않는다. 필요한 경우 기존 후원 dialog/repository 흐름을 호출하거나 신규 wrapper/adapter를 추가해 사용한다.
|
||||
|
||||
---
|
||||
@@ -78,6 +86,8 @@
|
||||
- 중복 pagination 요청이 발생하지 않도록 loading 중 추가 요청을 막아야 한다.
|
||||
- 다음 페이지 성공 시 기존 `donations` 뒤에 append한다.
|
||||
- 후원 성공 후 갱신은 append가 아니라 `page=0`, `size=20` 최초 조회를 다시 수행한다.
|
||||
- 사용자가 후원 메시지를 입력하지 않은 내역은 `CreatorChannelDonationResponse.message == ""`로 반환한다.
|
||||
- 사용자가 후원 메시지를 입력한 내역은 입력 원문을 `CreatorChannelDonationResponse.message`에 반환한다.
|
||||
|
||||
#### Response Contract
|
||||
```kotlin
|
||||
@@ -115,6 +125,7 @@ data class CreatorChannelDonationResponse(
|
||||
- 서버 응답의 `page`, `size`가 요청 상태와 다를 경우 서버 응답 값을 기준으로 ViewModel page 상태를 동기화한다.
|
||||
- `profileImage` 또는 `profileImageUrl`이 비어 있거나 이미지 로딩에 실패하면 기존 프로필 이미지 placeholder 정책을 따른다.
|
||||
- `message`가 빈 문자열이면 기존 홈 탭 후원 카드와 동일하게 `%1$d캔을 후원하였습니다.` 형식의 fallback 문구를 표시한다.
|
||||
- 서버가 미입력 `message`를 한국어 기본 문구로 채워 반환하는 응답은 계약 위반으로 취급하며, Android에서 한국어 문구 패턴을 추정해 보정하지 않는다.
|
||||
|
||||
### Donation Ranking Section
|
||||
후원 랭킹 섹션은 Figma `290:9097` 기준의 카드 UI로 상위 후원자를 표시한다.
|
||||
@@ -160,10 +171,19 @@ Sort-bar는 전체 후원 수만 표시한다.
|
||||
- 각 item은 `gray/900` 배경, radius 14dp 카드로 표시한다.
|
||||
- item header에는 후원자 프로필 이미지, 닉네임, 작성 시간, 후원 캔 수량 badge를 표시한다.
|
||||
- header 배경색은 기존 홈 탭 후원 카드의 `calculateCreatorChannelDonationHeaderColorRes(can)` 정책을 우선 재사용한다.
|
||||
- 후원 캔 수량은 can icon과 `%d캔` 텍스트로 표시한다.
|
||||
- 후원 캔 수량은 can icon과 현재 locale의 `creator_channel_donation_can_format` 텍스트로 표시한다.
|
||||
- `createdAtUtc`는 크리에이터 채널 v2 공통 상대 시간 포맷을 따른다.
|
||||
- 메시지는 header 아래에 16sp regular white text로 표시하고 긴 내용은 여러 줄로 확장한다.
|
||||
- `message`가 빈 문자열이면 기존 홈 탭 후원 카드 fallback 문구를 표시한다.
|
||||
- `message`가 빈 문자열이면 현재 locale의 `creator_channel_donation_fallback_message`를 `can` 값으로 포맷해 표시한다.
|
||||
- `message`가 비어 있지 않으면 사용자 작성 내용으로 간주하고 번역·치환 없이 원문을 표시한다.
|
||||
- 한국어·영어·일본어의 기본 후원 문구와 CAN 단위는 아래 값을 사용한다.
|
||||
|
||||
| 표시 내용 | 한국어 | 영어 | 일본어 |
|
||||
|---|---|---|---|
|
||||
| 기본 후원 문구 | `%1$d캔을 후원하였습니다.` | `Supported with %1$d cans.` | `%1$dCANを応援しました。` |
|
||||
| CAN 단위 | `%1$s캔` | `%1$s cans` | `%1$sCAN` |
|
||||
|
||||
- 상대 시간은 기존 `character_comment_time_just_now`, `character_comment_time_minutes`, `character_comment_time_hours`, `character_comment_time_days`, `character_comment_time_months`, `character_comment_time_years` resource를 재사용한다.
|
||||
- 현재 후원 탭 API는 비밀 후원 여부를 별도로 내려주지 않으므로 비밀 후원 전용 아이콘, 안내 문구, 익명 처리 분기를 구현하지 않는다.
|
||||
|
||||
#### Edge Cases
|
||||
@@ -171,6 +191,8 @@ Sort-bar는 전체 후원 수만 표시한다.
|
||||
- `createdAtUtc` 파싱 실패 시 기존 날짜 표시 fallback 정책을 따른다.
|
||||
- `can <= 0` 값이 내려와도 앱에서 크래시가 발생하지 않아야 하며, 서버 응답 값을 그대로 표시한다.
|
||||
- 메시지가 매우 긴 경우 item 높이는 자연스럽게 확장하되 인접 item과 겹치지 않아야 한다.
|
||||
- locale 변경 후 빈 `message`, CAN 단위, 상대 시간이 선택한 언어의 resource 값으로 표시되어야 한다.
|
||||
- locale 변경과 관계없이 비어 있지 않은 사용자 작성 `message`는 동일한 원문을 유지해야 한다.
|
||||
|
||||
### Floating Donation Button
|
||||
우측 하단 floating 후원하기 버튼은 홈 탭 후원하기 액션과 동일하게 동작한다.
|
||||
@@ -256,6 +278,8 @@ Sort-bar는 전체 후원 수만 표시한다.
|
||||
- 기존 `CreatorChannelPagerAdapter`의 `CreatorChannelTab.Donation` placeholder를 신규 `CreatorChannelDonationFragment`로 교체한다.
|
||||
- 기존 홈 탭 후원 API 호출 흐름인 `CreatorChannelRepository.postChannelDonation()`을 재사용한다.
|
||||
- 기존 홈 탭 후원 item의 색상 계산, fallback message, 상대 시간 formatter는 가능한 범위에서 재사용한다.
|
||||
- 후원 기본 문구는 `creator_channel_donation_fallback_message`, CAN 단위는 `creator_channel_donation_can_format`, 상대 시간은 기존 `AndroidUtcRelativeTimeTextFormatter`를 재사용한다.
|
||||
- 서버가 미입력 `message`를 빈 문자열로 반환하기 전에는 한국어 기본 문구의 안전한 식별 기준이 없으므로 후원 기본 문구 다국어 요구사항을 완료로 판정하지 않는다.
|
||||
- 랭킹 `전체보기` 이동은 기존 `UserProfileDonationAllViewActivity`를 재사용하고 신규 전체보기 화면을 만들지 않는다.
|
||||
- 기존 `UserProfileDonationAllViewActivity`는 `Constants.EXTRA_USER_ID`를 요구하므로, 구현 계획에서 크리에이터 채널에서 전달할 식별자가 기존 Activity 계약과 맞는지 확인한다.
|
||||
- 레거시 후원 dialog 또는 repository 파일은 직접 수정하지 않는다.
|
||||
@@ -279,6 +303,9 @@ Sort-bar는 전체 후원 수만 표시한다.
|
||||
- 후원 내역 empty 상태는 Figma `290:9008`/`290:9009`를 기준으로 구현하고, 랭킹 섹션이 있으면 그 아래에 표시한다.
|
||||
- 본인 채널에서는 empty 상태의 하단 `후원하기` button과 floating 후원하기 버튼을 모두 숨긴다.
|
||||
- 현재 API는 비밀 후원 여부를 별도로 내려주지 않으므로 이번 범위에서는 비밀 후원 관련 UI/표시 분기를 구현하지 않는다.
|
||||
- 2026-08-26: 후원 item의 시스템 기본 문구, CAN 단위, 상대 시간을 한국어·영어·일본어로 표시한다.
|
||||
- 2026-08-26: 사용자 작성 후원 메시지는 자동 번역하지 않고 원문을 유지한다.
|
||||
- 2026-08-26: 사용자가 메시지를 입력하지 않으면 서버가 `message`를 빈 문자열로 반환하고, Android는 기존 locale별 fallback을 사용한다. 한국어 기본 문구 패턴을 클라이언트에서 추정하는 보정은 추가하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
@@ -300,3 +327,5 @@ Sort-bar는 전체 후원 수만 표시한다.
|
||||
- 2026-06-22: 기존 홈 탭 PRD와 `CreatorChannelActivity.onCreatorChannelDonationClicked()`, `CreatorChannelHomeViewModel.postChannelDonation()` 흐름을 확인해 후원 탭 후원하기 액션 재사용 및 성공 후 갱신 요구를 PRD에 반영했다.
|
||||
- 2026-06-22: 사용자 확인사항을 반영해 랭킹 `전체보기`는 기존 `UserProfileDonationAllViewActivity` 이동으로 확정하고, `rankings` 최대 8명 서버 보장, Figma `290:9008` empty 상태, 본인 채널 후원하기 버튼 숨김, 비밀 후원 표시 제외 정책을 PRD에 보강했다.
|
||||
- 2026-06-22: 사용자 후속 요청을 반영해 empty UI는 Figma `290:9009` 컨텐츠 기준으로 별도 `minHeight` 없이 표시하고, 후원 랭킹 섹션이 있으면 그 아래에 배치하며, 랭킹 존재 여부와 관계없이 채널 후원이 없으면 항상 표시하도록 보강했다.
|
||||
- 2026-08-26: 후원 item 표시 경로를 대조했다. 빈 `message`는 `creator_channel_donation_fallback_message`, CAN 단위는 `creator_channel_donation_can_format`, `createdAtUtc`는 `AndroidUtcRelativeTimeTextFormatter`를 통해 한국어·영어·일본어 resource를 사용하지만, 비어 있지 않은 API `message`는 원문을 표시한다는 현재 경계를 확인했다.
|
||||
- 2026-08-26: 인터뷰에서 시스템 기본 문구와 CAN 단위·상대 시간을 모두 다국어 대상으로 확정하고, 사용자 작성 메시지는 원문 유지, 미입력 `message`는 서버가 빈 문자열로 반환하는 계약을 선택했다. 이번 단계는 문서만 갱신했으며 코드 구현·빌드·기능 테스트는 수행하지 않았다. 문서 명령 유효성 확인을 위한 `./gradlew tasks --all`은 PASS했다.
|
||||
|
||||
Reference in New Issue
Block a user