14 KiB
오디오 콘텐츠 위젯 태그 배지 Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Figma 20:3840, 20:3843, 20:3815, 20:3814 기준으로 v2 패키지 아래 신규 위젯인 AudioContentCardView 썸네일 영역에 Original, First, Point, Free 태그 배지를 추가한다.
Architecture: v2 신규 위젯인 AudioContentCardView와 view_audio_content_card.xml만 수정해 썸네일 ImageView를 overlay 가능한 container 안으로 이동하고 상단/하단 tag row를 추가한다. 태그 타입과 정렬 순서는 순수 Kotlin contract로 분리해 단위 테스트하고, 실제 이미지 로딩은 기존 thumbnailView() API로 계속 호출부에 위임한다. 레거시 화면, 레거시 adapter, 레거시 XML에는 포함하지 않는다.
Tech Stack: Android XML Views, Kotlin custom View, Android resources, JUnit4 local unit test.
작업 목표
- 대상은 v2 신규 위젯 파일인
app/src/main/java/kr/co/vividnext/sodalive/v2/widget/AudioContentCardView.kt와app/src/main/res/layout/view_audio_content_card.xml로 한정한다. - 레거시 화면과 기존 화면 적용 작업은 제외한다.
- Original, First는 썸네일 왼쪽 상단에 표시한다.
- Original과 First가 함께 있으면 Original, First 순서로 표시한다.
- Point, Free는 썸네일 왼쪽 하단에 표시한다.
- Free 태그는 string resource 기반 다국어 텍스트로 생성한다.
- 기존
setContent,thumbnailView,setSizeAPI는 유지한다. - 구현 중 체크박스와 검증 기록은 이 문서에 누적한다.
파일 구조
- Read:
docs/prd/20260522_오디오콘텐츠위젯태그배지_prd.md - Modify:
app/src/main/java/kr/co/vividnext/sodalive/v2/widget/AudioContentCardView.kt- tag view 참조,
setTags(...), 상/하단 정렬 적용, size 적용 시 thumbnail container 크기 갱신을 처리한다.
- tag view 참조,
- Modify:
app/src/main/res/layout/view_audio_content_card.xml- thumbnail overlay container와 top/bottom tag row를 추가한다.
- Do not modify: 레거시 화면 XML, 레거시 adapter, 기존 API/DTO, 기존 화면 바인딩 코드
- 신규 위젯 자체 기능만 추가한다.
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/widget/AudioContentTag.ktOriginal,First,Point,Free태그 타입과 위치/정렬 계약을 정의한다.
- Create:
app/src/test/java/kr/co/vividnext/sodalive/v2/widget/AudioContentTagTest.kt- 태그 중복 제거, 상단/하단 분류, 고정 정렬 순서를 검증한다.
- Modify:
app/src/main/res/values/strings.xmlaudio_content_tag_free문자열을 추가한다.
- Modify:
app/src/main/res/values-en/strings.xmlaudio_content_tag_free영어 문자열을 추가한다.
- Modify:
app/src/main/res/values-ja/strings.xmlaudio_content_tag_free일본어 문자열을 추가한다.
- Add if missing:
app/src/main/res/drawable/ic_content_tag_original.png - Add if missing:
app/src/main/res/drawable/ic_content_tag_point.png - Add if missing:
app/src/main/res/drawable/ic_content_tag_first_star.png - Create if missing:
app/src/main/res/drawable/bg_audio_content_tag_first.xml#FF34B8solid background for the62dp x 24dpFirst badge.
- Create if missing:
app/src/main/res/drawable/bg_audio_content_tag_free.xml#052742solid background for the Free badge. The view uses height24dp, min width34dp, andwrap_contentwidth.
- Modify:
docs/plan-task/20260522_오디오콘텐츠위젯태그배지.md- 구현 중 체크박스와 검증 기록을 누적한다.
구현 계획
Task 1: 기존 위젯과 Figma 기준 확인
Files:
-
Read:
app/src/main/java/kr/co/vividnext/sodalive/v2/widget/AudioContentCardView.kt -
Read:
app/src/main/res/layout/view_audio_content_card.xml -
Read:
app/src/main/java/kr/co/vividnext/sodalive/v2/widget/AudioContentCardSize.kt -
Read:
docs/prd/20260519_오디오콘텐츠카드컴포넌트_prd.md -
Read:
docs/prd/20260522_오디오콘텐츠위젯태그배지_prd.md -
Step 1: 대상 위젯 확인
Run: rg -n "class AudioContentCardView|iv_audio_content_thumbnail|ll_audio_content_label|AudioContentCardSize|thumbnailView" app/src/main/java/kr/co/vividnext/sodalive/v2/widget app/src/main/res/layout/view_audio_content_card.xml
Expected: 변경 대상이 v2 신규 위젯인 AudioContentCardView.kt와 view_audio_content_card.xml이며, 기존 size/content/thumbnail API를 유지해야 함을 확인한다. 레거시 화면 파일은 변경 대상에서 제외한다.
- Step 2: Figma tag 기준 확인
Run tools:
Figma_get_design_context(20:3840)Figma_get_screenshot(20:3840)Figma_get_design_context(20:3843)Figma_get_screenshot(20:3843)Figma_get_design_context(20:3815)Figma_get_screenshot(20:3815)Figma_get_design_context(20:3814)Figma_get_screenshot(20:3814)
Expected: First, Free 단일 tag와 Free/Point + Original/First 조합의 위치, 크기, 색상, 순서를 확인한다.
- Step 3: 기존 리소스 확인
Run: rg -n "ic_content_tag_original|ic_content_tag_point|ic_content_tag_first_star|audio_content_tag_free|#ff34b8|#052742" app/src/main/res
Expected: 재사용 가능한 리소스가 있으면 재사용하고, 없으면 최소 신규 리소스를 추가한다.
Task 2: AudioContentTag contract TDD
Files:
-
Create:
app/src/test/java/kr/co/vividnext/sodalive/v2/widget/AudioContentTagTest.kt -
Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/widget/AudioContentTag.kt -
Step 1: RED - 태그 정렬 테스트 추가
Test cases:
-
전달 순서가
First,Original이어도 top row는Original,First가 된다. -
전달 순서가
Free,Point이어도 bottom row는Point,Free가 된다. -
중복 태그는 한 번만 표시된다.
-
빈 set은 top/bottom 모두 비어 있다.
-
Step 2: RED 실행
Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.AudioContentTagTest"
Expected: Unresolved reference 'AudioContentTag' 또는 Unresolved reference 'audioContentTopTags' / Unresolved reference 'audioContentBottomTags'로 실패한다.
- Step 3: GREEN - 최소 contract 추가
Implementation notes:
-
enum class AudioContentTag { Original, First, Point, Free }를 추가한다. -
fun Collection<AudioContentTag>.audioContentTopTags(): List<AudioContentTag>는[Original, First]순서를 적용한다. -
fun Collection<AudioContentTag>.audioContentBottomTags(): List<AudioContentTag>는[Point, Free]순서를 적용한다. -
Step 4: GREEN 실행
Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.AudioContentTagTest"
Expected: BUILD SUCCESSFUL
Task 3: XML overlay 구조와 리소스 추가
Files:
-
Modify:
app/src/main/res/layout/view_audio_content_card.xml -
Modify:
app/src/main/res/values/strings.xml -
Modify:
app/src/main/res/values-en/strings.xml -
Modify:
app/src/main/res/values-ja/strings.xml -
Add if missing: drawable resources for tags/backgrounds
-
Step 1: thumbnail overlay container 적용
Implementation notes:
-
기존
iv_audio_content_thumbnail는FrameLayoutoverlay container 내부로 이동한다. -
container id는 예:
fl_audio_content_thumbnail_container로 둔다. -
AudioContentCardView.setSize는 thumbnailImageView와 container 모두thumbnailSizeDp크기로 맞춘다. -
기존
thumbnailView()는 동일한ImageView를 반환한다. -
Step 2: top/bottom tag row 추가
Implementation notes:
-
top row id 예:
ll_audio_content_tag_top -
bottom row id 예:
ll_audio_content_tag_bottom -
top row: parent start/top 정렬, horizontal orientation
-
bottom row: parent start/bottom 정렬, horizontal orientation
-
row는 태그가 없을 때
gone처리한다. -
Step 3: Free 다국어 문자열 추가
Suggested strings:
-
values/strings.xml:무료 -
values-en/strings.xml:Free -
values-ja/strings.xml:無料 -
Step 4: resource merge 확인
Run: ./gradlew :app:assembleDebug
Expected: Android resource merge와 debug assemble이 성공한다.
Task 4: AudioContentCardView tag binding 구현
Files:
-
Modify:
app/src/main/java/kr/co/vividnext/sodalive/v2/widget/AudioContentCardView.kt -
Step 1: view 참조 추가
Implementation notes:
-
thumbnail container, top tag row, bottom tag row를
onFinishInflate()에서 찾는다. -
setSize()에서 thumbnail container와 thumbnail image 크기를 함께 갱신한다. -
Step 2: public tag API 추가
Implementation notes:
-
fun setTags(tags: Set<AudioContentTag>)API를 추가한다. -
top row는
tags.audioContentTopTags()결과를 렌더링한다. -
bottom row는
tags.audioContentBottomTags()결과를 렌더링한다. -
빈 row는
View.GONE, 표시 row는View.VISIBLE로 처리한다. -
기존 public API는 제거하거나 시그니처 변경하지 않는다.
-
Step 3: tag view 생성 로직 추가
Implementation notes:
- Original:
ImageView+ic_content_tag_original,24dp x 24dp - Point:
ImageView+ic_content_tag_point,24dp x 24dp - First:
LinearLayout+bg_audio_content_tag_first+ic_content_tag_first_star+FIRST텍스트,62dp x 24dp- star icon:
17dp x 17dp,marginStart=2dp,top=4dp,contentDescription=null - text:
Phosphate Solid,16sp, white,singleLine=true,includeFontPadding=false,marginStart=1dp,top=2dp
- star icon:
- Free:
TextView+bg_audio_content_tag_free+@string/audio_content_tag_free, height24dp, min width34dp, widthwrap_content - 장식 아이콘의
contentDescription은null로 둔다.
Task 5: 검증
Files:
-
All changed implementation/resources/tests
-
Modify:
docs/plan-task/20260522_오디오콘텐츠위젯태그배지.md -
Step 1: 단위 테스트 실행
Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.AudioContentTagTest"
Expected: BUILD SUCCESSFUL
- Step 2: 리소스/빌드 검증 실행
Run: ./gradlew :app:assembleDebug
Expected: BUILD SUCCESSFUL
- Step 3: LSP diagnostics 확인
Run tools:
lsp_diagnostics(app/src/main/java/kr/co/vividnext/sodalive/v2/widget/AudioContentCardView.kt)lsp_diagnostics(app/src/main/java/kr/co/vividnext/sodalive/v2/widget/AudioContentTag.kt)lsp_diagnostics(app/src/test/java/kr/co/vividnext/sodalive/v2/widget/AudioContentTagTest.kt)
Expected: 변경 파일에 신규 error가 없다.
- Step 4: 검증 기록 누적
이 문서 하단에 무엇/왜/어떻게, 실행 명령, 결과, 남은 이슈를 한국어로 누적한다.
- Step 5: 레거시 변경 없음 확인
Run: git diff --name-only | rg -v "^(docs/prd/20260522_오디오콘텐츠위젯태그배지_prd.md|docs/plan-task/20260522_오디오콘텐츠위젯태그배지.md|app/src/main/java/kr/co/vividnext/sodalive/v2/widget/AudioContentCardView.kt|app/src/main/java/kr/co/vividnext/sodalive/v2/widget/AudioContentTag.kt|app/src/test/java/kr/co/vividnext/sodalive/v2/widget/AudioContentTagTest.kt|app/src/main/res/layout/view_audio_content_card.xml|app/src/main/res/values/strings.xml|app/src/main/res/values-en/strings.xml|app/src/main/res/values-ja/strings.xml|app/src/main/res/drawable/)"
Expected: 출력이 없어야 한다. 출력이 있으면 레거시 화면 또는 범위 밖 파일을 변경한 것이므로 되돌리거나 계획을 갱신하기 전에 사용자 확인을 받는다.
검증 기록
2026-05-22 문서 생성
- 무엇/왜/어떻게: 사용자 요청에 따라 오디오 콘텐츠 위젯 태그 배지 추가 작업의 PRD와 구현 계획/TASK 문서를 작성했다. 대상 위젯은 v2 패키지 아래 신규 위젯인
AudioContentCardView.kt와view_audio_content_card.xml로 한정했고, Figma20:3840,20:3843,20:3815,20:3814기준 태그 종류/위치/정렬/다국어 요구사항을 반영했다. 레거시 화면에는 포함하지 않는다는 제약도 문서에 반영했다. - 실행 명령: 문서 작성만 수행했으므로 Gradle 빌드는 실행하지 않았다.
- 결과: 구현 전 기준 문서가 준비되었다.
2026-05-27 구현 및 검증
- 무엇/왜/어떻게:
AudioContentCardView썸네일을FrameLayoutoverlay container로 감싸고 top/bottom tag row를 추가했다.AudioContentTagenum과audioContentTopTags()/audioContentBottomTags()순수 Kotlin contract를 추가해 Original/First, Point/Free 정렬과 중복 제거를 테스트했다.setTags(tags: Set<AudioContentTag>)API를 추가해 Original, First, Point, Free 태그를 썸네일 내부에 렌더링하도록 구현했으며, 기존setContent,setSize,thumbnailViewAPI는 유지했다. Free 태그 문자열과 First/Free 배경 drawable도 추가했다. - 실행 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.AudioContentTagTest"를 RED/GREEN으로 실행했고,./gradlew :app:assembleDebug를 실행했다.git diff --name-only와 계획 문서의 범위 확인 명령으로 레거시 변경 여부를 확인했다. - 결과: RED 단계는
Unresolved reference 'AudioContentTag'및Unresolved reference 'audioContentTopTags'/audioContentBottomTags컴파일 실패로 확인했다. GREEN 단계의 targeted unit test와assembleDebug는 모두BUILD SUCCESSFUL로 통과했다. debug APK 산출물app/build/outputs/apk/debug/app-debug.apk도 생성되었다. - 남은 이슈: Figma MCP
Figma_get_design_context호출은 timeout으로 완료하지 못해, 태그 크기/위치/색상은 PRD와 이 계획 문서에 이미 정리된 Figma 기준 및 기존SeriesContentCardView패턴을 근거로 구현했다. Kotlin LSP는kotlin-lsp미설치로 diagnostics를 실행할 수 없었고, Gradle compile/test/build로 대체 검증했다.