fix(creator): 후원 item locale 표시를 적용한다

This commit is contained in:
2026-08-26 13:27:20 +09:00
parent 3560d7bf89
commit 4f4ae62026
8 changed files with 260 additions and 14 deletions
@@ -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했다.