Files
sodalive-android/docs/plan-task/20260519_타이틀바및탭텍스트바컴포넌트.md

17 KiB

타이틀바 및 탭 텍스트바 컴포넌트

작업 목표

  • XML 레이아웃 기반 재사용 가능한 Title Bar Component를 개발한다.
  • XML 레이아웃 기반 재사용 가능한 Tab-Text Bar Component를 개발한다.
  • 구현 범위는 컴포넌트 추가로 한정하고 기존 화면 일괄 적용은 제외한다.

구현 계획

Task 1: 기존 리소스 및 유사 UI 확인

Files:

  • Read: app/src/main/res/layout/toolbar_audio_content_main.xml

  • Read: app/src/main/res/layout/activity_audio_content_main.xml

  • Read: app/src/main/res/layout/activity_can_status.xml

  • Read: app/src/main/res/values/colors.xml

  • Read: app/src/main/res/values/typography.xml

  • Step 1: 기존 타이틀 바 패턴 확인

Run: rg -n "toolbar|Toolbar|title|Title" app/src/main/res/layout app/src/main/java/kr/co/vividnext/sodalive

Expected: 기존 화면에서 include 또는 개별 toolbar 사용 패턴을 확인한다.

  • Step 2: 기존 탭 바 패턴 확인

Run: rg -n "TabLayout|tabText|ContentMainTabText|color_tabbar" app/src/main/res app/src/main/java/kr/co/vividnext/sodalive

Expected: 기존 TabLayout 및 탭 텍스트 스타일 사용 패턴을 확인한다.

  • Step 3: 필수 리소스 확인

Run: rg -n "img_text_logo_v2|gray_600|Typography\.Heading3|<color name=\"white\"|<color name=\"black\"" app/src/main/res

Expected: gray_600, Typography.Heading3, black/white 색상과 로고 drawable 존재 여부를 확인한다.

Task 2: Title Bar Component 추가

Files:

  • Create: app/src/main/res/layout/view_title_bar_home.xml

  • Create: app/src/main/res/layout/view_title_bar_default.xml

  • Step 1: Home Title Bar XML 추가

view_title_bar_home.xml에 높이 60dp, 전체 너비, 검정 배경, 세로 중앙 정렬을 갖는 레이아웃을 추가한다. 좌측에는 img_text_logo_v2, 중앙에는 가변 빈 영역, 우측에는 교체 가능한 메뉴 아이콘 ImageView를 둔다.

  • Step 2: Default Title Bar XML 추가

view_title_bar_default.xml에 높이 60dp, 전체 너비, 검정 배경, 세로 중앙 정렬을 갖는 레이아웃을 추가한다. 좌측에는 화면명 TextView, 중앙에는 가변 빈 영역, 우측에는 교체 가능한 메뉴 아이콘 ImageView를 둔다.

  • Step 3: 호출부 커스터마이징 지점 명확화

각 레이아웃의 화면명 TextView와 메뉴 ImageView에는 ViewBinding에서 접근 가능한 명확한 id를 부여한다.

Task 3: Tab-Text Bar Component 추가

Files:

  • Create: app/src/main/res/layout/view_text_tab_bar.xml

  • Create or Modify: 필요한 경우 탭 텍스트 색상 selector 리소스

  • Step 1: Tab-Text Bar XML 추가

view_text_tab_bar.xml에 높이 52dp, 전체 너비, 검정 배경, 세로 중앙 정렬을 갖는 텍스트 탭 바 레이아웃을 추가한다.

  • Step 2: Typography와 색상 적용

각 탭 텍스트는 Typography.Heading3을 적용하고, selected는 white, normal은 gray_600으로 표시되도록 color selector 또는 selected 상태 처리를 구성한다.

  • Step 3: 메뉴 개수 및 단일 선택 상태 처리 방식 구현

Kotlin helper 또는 커스텀 View를 추가하는 경우 메뉴 개수는 1~3개만 허용하고, 선택 변경 시 반드시 하나의 메뉴만 selected가 되도록 구현한다. XML include만으로 충분하지 않으면 최소 범위의 Kotlin 클래스를 추가한다.

Task 4: 검증 및 문서 기록

Files:

  • Modify: docs/plan-task/20260519_타이틀바및탭텍스트바컴포넌트.md

  • Step 1: XML 및 Kotlin 진단 실행

Run: lsp_diagnostics on modified XML/Kotlin files

Expected: 새 오류가 없다. XML LSP가 환경에 없으면 그 사실을 검증 기록에 남긴다.

  • Step 2: 디버그 빌드 실행

Run: ./gradlew :app:assembleDebug

Expected: BUILD SUCCESSFUL

  • Step 3: 수동 QA 수행

컴포넌트를 임시 또는 대상 화면에서 inflate 가능한지 확인한다. 실제 화면 적용이 이번 범위에 포함되지 않으면 리소스 빌드 성공과 ViewBinding 접근 id 생성 여부를 확인한다.

  • Step 4: 검증 기록 누적

문서 하단 검증 기록에 실행한 명령, 결과, 빌드 성공 여부를 한국어로 기록한다.

체크리스트

  • AC1: Home Title Bar는 width match_parent, height 60dp, black background, vertical center alignment를 가진다.
    • QA: view_title_bar_home.xml 속성 확인
  • AC2: Home Title Bar는 좌측 img_text_logo_v2, 중앙 가변 빈 영역, 우측 메뉴 아이콘 구조를 가진다.
    • QA: view_title_bar_home.xml 구조 및 id 확인
  • AC3: Default Title Bar는 width match_parent, height 60dp, black background, vertical center alignment를 가진다.
    • QA: view_title_bar_default.xml 속성 확인
  • AC4: Default Title Bar는 화면명 텍스트와 우측 메뉴 아이콘을 호출 화면에서 변경할 수 있다.
    • QA: ViewBinding 접근 가능한 id 확인
  • AC5: Tab-Text Bar는 width match_parent, height 52dp, black background, vertical center alignment를 가진다.
    • QA: view_text_tab_bar.xml 속성 확인
  • AC6: Tab-Text Bar 텍스트는 Typography.Heading3, normal gray_600, selected white를 사용한다.
    • QA: XML style/color selector 또는 Kotlin 상태 처리 확인
  • AC7: Tab-Text Bar 메뉴 개수는 최대 3개이며, 항상 하나만 selected 상태다.
    • QA: 선택 상태 변경 로직 또는 사용 계약 확인
  • AC8: 기존 화면에는 요청 없이 일괄 적용하지 않는다.
    • QA: 변경 파일 목록에서 기존 화면 레이아웃 변경 여부 확인
  • AC9: 리소스 병합 및 디버그 빌드가 성공한다.
    • QA: ./gradlew :app:assembleDebug

검증 기록

  • 2026-05-19
    • 무엇/왜/어떻게: 사용자 요청에 따라 구현 없이 PRD와 구현 계획/TASK 문서만 작성했다. 기존 프로젝트가 XML Views + ViewBinding 중심이며, 구현 전 문서가 필요하다는 저장소 규칙을 반영했다.
    • 실행 명령/도구:
      • read(docs/prd/sample-prd.md)
      • read(docs/agent-guides/workflow-docs-commits.md)
      • read(docs/agent-guides/code-style.md)
      • read(docs/agent-guides/build-test-style.md)
      • read(docs/prd/20260515_design_token_xml_resource_prd.md)
      • read(docs/prd/20260515_typography_xml_style_prd.md)
      • explore agent result for existing UI patterns
      • Figma_get_design_context(20:3575, 20:3576, 20:3585)
    • 결과:
      • PRD 문서는 docs/prd/20260519_타이틀바및탭텍스트바컴포넌트_prd.md에 작성했다.
      • 계획/TASK 문서는 docs/plan-task/20260519_타이틀바및탭텍스트바컴포넌트.md에 작성했다.
      • Figma 조회는 타임아웃되어 제공된 텍스트 요구사항을 기준으로 문서화했다.
      • 코드, 리소스, 레이아웃 구현 파일은 변경하지 않았다.
    • 무엇/왜/어떻게: 계획에 따라 재사용 가능한 XML Title Bar 2종, Text Tab Bar, 전용 텍스트 색상 selector, TextTabSelectionState 최소 상태 helper와 이를 사용하는 TextTabBarView를 추가했다. 기존 화면에는 적용하지 않았다.
    • 실행 명령/도구:
      • read(docs/plan-task/20260519_타이틀바및탭텍스트바컴포넌트.md)
      • read(app/src/main/res/layout/toolbar_audio_content_main.xml)
      • read(app/src/main/res/layout/activity_audio_content_main.xml)
      • read(app/src/main/res/layout/activity_can_status.xml)
      • read(app/src/main/res/values/colors.xml)
      • read(app/src/main/res/values/typography.xml)
      • rg -n "toolbar|Toolbar|title|Title" app/src/main/res/layout app/src/main/java/kr/co/vividnext/sodalive
      • rg -n "TabLayout|tabText|ContentMainTabText|color_tabbar" app/src/main/res app/src/main/java/kr/co/vividnext/sodalive
      • rg -n "img_text_logo_v2|gray_600|Typography\.Heading3|<color name=\"white\"|<color name=\"black\"" app/src/main/res
      • lsp_diagnostics on modified XML/Kotlin files
      • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.TextTabSelectionStateTest"
      • ./gradlew :app:assembleDebug
    • 결과:
      • Kotlin/XML LSP 서버가 현재 환경에 설정되어 있지 않아 lsp_diagnostics는 실행 불가했다.
      • TextTabSelectionStateTest는 성공했다.
      • :app:assembleDebugBUILD SUCCESSFUL로 완료되어 리소스 병합과 ViewBinding 생성 가능성을 확인했다.
      • Gradle deprecation 안내는 기존 빌드 환경 메시지로 남아 있다.
  • 2026-05-19
    • 무엇/왜/어떻게: 구현 결과를 재검토한 뒤, Text Tab Bar가 실제 View 선택 상태를 컴포넌트 내부에서 보장하도록 view_text_tab_bar.xml 루트를 TextTabBarView로 보완했다. 기존 화면 적용은 하지 않았다.
    • 실행 명령/도구:
      • read(app/src/main/res/layout/view_title_bar_home.xml)
      • read(app/src/main/res/layout/view_title_bar_default.xml)
      • read(app/src/main/res/layout/view_text_tab_bar.xml)
      • read(app/src/main/res/color/color_text_tab_bar.xml)
      • read(app/src/main/java/kr/co/vividnext/sodalive/v2/widget/TextTabSelectionState.kt)
      • read(app/src/main/java/kr/co/vividnext/sodalive/v2/widget/TextTabBarView.kt)
      • read(app/src/test/java/kr/co/vividnext/sodalive/v2/widget/TextTabSelectionStateTest.kt)
      • lsp_diagnostics on modified XML/Kotlin files
      • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.TextTabSelectionStateTest"
      • ./gradlew :app:assembleDebug
      • ./gradlew :app:ktlintCheck
      • rg --files app/build/generated/data_binding_base_class_source_out/debug/out | rg "View(TextTabBar|TitleBar)"
    • 결과:
      • TDD RED 단계에서 TextTabSelectionState 미정의로 TextTabSelectionStateTest가 실패함을 먼저 확인했다.
      • Kotlin/XML LSP 서버가 현재 환경에 설정되어 있지 않아 lsp_diagnostics는 실행 불가했다.
      • TextTabSelectionStateTestBUILD SUCCESSFUL로 완료됐다.
      • :app:assembleDebugBUILD SUCCESSFUL로 완료됐다.
      • :app:ktlintCheckBUILD SUCCESSFUL로 완료됐다.
  • 2026-05-19
    • 무엇/왜/어떻게: 탭바 컴포넌트 네이밍을 CapsuleTabBarView 방식에 맞춰 TextTabBarView 계열로 통일했다.
    • 실행 명령/도구:
      • rg -n "TabText|tab_text|Tab Text|Tab-Text|view_tab_text_bar|color_tab_text_bar" app/src docs
      • apply_patch(app/src/main/java/kr/co/vividnext/sodalive/v2/widget/TabTextBarView.kt -> TextTabBarView.kt)
      • apply_patch(app/src/main/java/kr/co/vividnext/sodalive/v2/widget/TabTextSelectionState.kt -> TextTabSelectionState.kt)
      • apply_patch(app/src/test/java/kr/co/vividnext/sodalive/v2/widget/TabTextSelectionStateTest.kt -> TextTabSelectionStateTest.kt)
      • apply_patch(app/src/main/res/layout/view_tab_text_bar.xml -> view_text_tab_bar.xml)
      • apply_patch(app/src/main/res/color/color_tab_text_bar.xml -> color_text_tab_bar.xml)
    • 결과:
      • TabTextBarViewTextTabBarView로 변경했다.
      • TabTextSelectionStateTextTabSelectionState로 변경했다.
      • TabTextSelectionStateTestTextTabSelectionStateTest로 변경했다.
      • view_tab_text_bar.xmlview_text_tab_bar.xml로 변경했다.
      • color_tab_text_bar.xmlcolor_text_tab_bar.xml로 변경했다.
      • Text Tab 내부 view id도 tv_text_tab_first, tv_text_tab_second, tv_text_tab_third로 정리했다.
    • 추가 검증:
      • Kotlin/XML LSP 서버가 현재 환경에 설정되어 있지 않아 lsp_diagnostics는 실행 불가했다.
      • 병렬 Gradle 실행 중 Kotlin compile 캐시 접근 오류가 한 번 발생해 순차 실행으로 재검증했다.
      • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.TextTabSelectionStateTest"BUILD SUCCESSFUL로 완료됐다.
      • ./gradlew :app:assembleDebugBUILD SUCCESSFUL로 완료됐다.
      • ./gradlew :app:ktlintCheckBUILD SUCCESSFUL로 완료됐다.
      • ViewTextTabBarBinding, ViewTitleBarDefaultBinding, ViewTitleBarHomeBinding 생성 파일을 확인해 신규 컴포넌트 ViewBinding 생성 가능성을 확인했다.
      • git status --short 기준 기존 화면 Activity/Fragment/Layout 파일은 변경하지 않았다.
  • 2026-05-19
    • 무엇/왜/어떻게: 코드 리뷰 피드백을 검토해 숨김 탭의 selected 상태가 남을 가능성을 제거하고, 이미 선택된 탭 재선택 시 중복 콜백을 발생시키지 않도록 보완했다.
    • 실행 명령/도구:
      • oracle read-only code review
      • apply_patch(app/src/main/java/kr/co/vividnext/sodalive/v2/widget/TextTabBarView.kt)
    • 결과:
      • TextTabBarView는 메뉴가 줄어들어 숨겨지는 탭에도 isSelected = false를 적용한다.
      • TextTabBarView.selectTab()은 이미 선택된 index를 다시 선택하면 상태와 콜백을 변경하지 않는다.
      • view_title_bar_home.xml@drawable/img_text_logo_v2를 참조하며, 현재 해당 이미지 파일은 git 기준 untracked 상태이므로 커밋 시 함께 포함해야 한다.
    • 추가 검증:
      • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.TextTabSelectionStateTest"BUILD SUCCESSFUL로 완료됐다.
      • ./gradlew :app:assembleDebugBUILD SUCCESSFUL로 완료됐다.
      • ./gradlew :app:ktlintCheckBUILD SUCCESSFUL로 완료됐다.
  • 2026-05-19
    • 무엇/왜/어떻게: 추가 요청에 따라 TitleBar 좌우 padding을 20dp로 지정하고, Tab-Text Bar 텍스트를 왼쪽 정렬 및 텍스트 사이 20dp 간격 구조로 변경했다.
    • 실행 명령/도구:
      • read(app/src/main/res/layout/view_title_bar_home.xml)
      • read(app/src/main/res/layout/view_title_bar_default.xml)
      • read(app/src/main/res/layout/view_text_tab_bar.xml)
      • apply_patch(app/src/main/res/layout/view_title_bar_home.xml)
      • apply_patch(app/src/main/res/layout/view_title_bar_default.xml)
      • apply_patch(app/src/main/res/layout/view_text_tab_bar.xml)
      • lsp_diagnostics on modified XML files
      • rg -n "paddingHorizontal=\"20dp\"|gravity=\"start\|center_vertical\"|layout_marginEnd=\"20dp\"|layout_width=\"wrap_content\"" app/src/main/res/layout/view_title_bar_home.xml app/src/main/res/layout/view_title_bar_default.xml app/src/main/res/layout/view_text_tab_bar.xml
      • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.TextTabSelectionStateTest"
      • ./gradlew :app:assembleDebug
      • ./gradlew :app:ktlintCheck
    • 결과:
      • view_title_bar_home.xml, view_title_bar_default.xml 루트에 android:paddingHorizontal="20dp"를 적용했다.
      • view_text_tab_bar.xml 루트 gravity를 start|center_vertical로 변경했다.
      • Tab-Text Bar의 각 TextView는 wrap_content 폭을 사용하고, 첫 번째/두 번째 텍스트에 android:layout_marginEnd="20dp"를 적용해 텍스트 사이 간격을 20dp로 맞췄다.
      • XML LSP 서버가 현재 환경에 설정되어 있지 않아 lsp_diagnostics는 실행 불가했다.
      • TextTabSelectionStateTestBUILD SUCCESSFUL로 완료됐다.
      • :app:assembleDebugBUILD SUCCESSFUL로 완료됐다.
      • :app:ktlintCheckBUILD SUCCESSFUL로 완료됐다.
  • 2026-05-19
    • 무엇/왜/어떻게: 추가 요청에 따라 이번에 추가한 컴포넌트 XML의 하드코딩 color/spacing/radius 값을 점검하고, 기존 colors.xml, dimens.xml에 정의된 값으로 변경 가능한 항목을 치환했다.
    • 실행 명령/도구:
      • read(app/src/main/res/layout/view_title_bar_home.xml)
      • read(app/src/main/res/layout/view_title_bar_default.xml)
      • read(app/src/main/res/layout/view_text_tab_bar.xml)
      • read(app/src/main/res/color/color_text_tab_bar.xml)
      • read(app/src/main/res/values/colors.xml)
      • read(app/src/main/res/values/dimens.xml)
      • apply_patch(app/src/main/res/layout/view_title_bar_home.xml)
      • apply_patch(app/src/main/res/layout/view_title_bar_default.xml)
      • apply_patch(app/src/main/res/layout/view_text_tab_bar.xml)
      • rg -n "\b[0-9]+dp\b|#[0-9A-Fa-f]{6,8}" app/src/main/res/layout/view_title_bar_home.xml app/src/main/res/layout/view_title_bar_default.xml app/src/main/res/layout/view_text_tab_bar.xml app/src/main/res/color/color_text_tab_bar.xml
      • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.TextTabSelectionStateTest"
      • ./gradlew :app:assembleDebug
      • ./gradlew :app:ktlintCheck
    • 결과:
      • 20dp로 하드코딩되어 있던 TitleBar horizontal padding, Tab-Text Bar horizontal padding, Tab-Text 간격을 @dimen/spacing_20으로 변경했다.
      • Color 값은 이미 @color/black, @color/white, @color/gray_600, @color/color_text_tab_bar를 사용하고 있어 추가 치환할 하드코딩 hex 값은 없었다.
      • Radius 값은 이번 컴포넌트에 없었다.
      • 남아 있는 60dp, 52dp는 컴포넌트 고정 높이 요구사항 값이며 현재 dimens.xml에 대응 토큰이 없어 변경하지 않았다.
      • spacer용 0dplayout_weight 사용을 위한 Android 레이아웃 관례 값이므로 리소스로 치환하지 않았다.
      • TextTabSelectionStateTestBUILD SUCCESSFUL로 완료됐다.
      • :app:assembleDebugBUILD SUCCESSFUL로 완료됐다.
      • :app:ktlintCheckBUILD SUCCESSFUL로 완료됐다.