Files
sodalive-android/docs/plan-task/20260522_오디오콘텐츠위젯태그배지.md

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 신규 위젯인 AudioContentCardViewview_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.ktapp/src/main/res/layout/view_audio_content_card.xml로 한정한다.
  • 레거시 화면과 기존 화면 적용 작업은 제외한다.
  • Original, First는 썸네일 왼쪽 상단에 표시한다.
  • Original과 First가 함께 있으면 Original, First 순서로 표시한다.
  • Point, Free는 썸네일 왼쪽 하단에 표시한다.
  • Free 태그는 string resource 기반 다국어 텍스트로 생성한다.
  • 기존 setContent, thumbnailView, setSize API는 유지한다.
  • 구현 중 체크박스와 검증 기록은 이 문서에 누적한다.

파일 구조

  • 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 크기 갱신을 처리한다.
  • 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.kt
    • Original, First, Point, Free 태그 타입과 위치/정렬 계약을 정의한다.
  • Create: app/src/test/java/kr/co/vividnext/sodalive/v2/widget/AudioContentTagTest.kt
    • 태그 중복 제거, 상단/하단 분류, 고정 정렬 순서를 검증한다.
  • Modify: app/src/main/res/values/strings.xml
    • audio_content_tag_free 문자열을 추가한다.
  • Modify: app/src/main/res/values-en/strings.xml
    • audio_content_tag_free 영어 문자열을 추가한다.
  • Modify: app/src/main/res/values-ja/strings.xml
    • audio_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
    • #FF34B8 solid background for the 62dp x 24dp First badge.
  • Create if missing: app/src/main/res/drawable/bg_audio_content_tag_free.xml
    • #052742 solid background for the Free badge. The view uses height 24dp, min width 34dp, and wrap_content width.
  • 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.ktview_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_thumbnailFrameLayout overlay container 내부로 이동한다.

  • container id는 예: fl_audio_content_thumbnail_container로 둔다.

  • AudioContentCardView.setSize는 thumbnail ImageView와 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
  • Free: TextView + bg_audio_content_tag_free + @string/audio_content_tag_free, height 24dp, min width 34dp, width wrap_content
  • 장식 아이콘의 contentDescriptionnull로 둔다.

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.ktview_audio_content_card.xml로 한정했고, Figma 20:3840, 20:3843, 20:3815, 20:3814 기준 태그 종류/위치/정렬/다국어 요구사항을 반영했다. 레거시 화면에는 포함하지 않는다는 제약도 문서에 반영했다.
  • 실행 명령: 문서 작성만 수행했으므로 Gradle 빌드는 실행하지 않았다.
  • 결과: 구현 전 기준 문서가 준비되었다.

2026-05-27 구현 및 검증

  • 무엇/왜/어떻게: AudioContentCardView 썸네일을 FrameLayout overlay container로 감싸고 top/bottom tag row를 추가했다. AudioContentTag enum과 audioContentTopTags() / audioContentBottomTags() 순수 Kotlin contract를 추가해 Original/First, Point/Free 정렬과 중복 제거를 테스트했다. setTags(tags: Set<AudioContentTag>) API를 추가해 Original, First, Point, Free 태그를 썸네일 내부에 렌더링하도록 구현했으며, 기존 setContent, setSize, thumbnailView API는 유지했다. 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로 대체 검증했다.