feat(widget): 공통 탭바와 타이틀바 컴포넌트를 추가한다

This commit is contained in:
2026-05-19 20:29:42 +09:00
parent 3121d9dca9
commit 4457941193
21 changed files with 1360 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
# PRD: 캡슐 탭바 컴포넌트
## 1. Overview
Figma `20:3590` 디자인을 기준으로 메뉴 개수가 많을 때 가로 스크롤되는 재사용 가능한 Capsule Tab Bar Component를 개발한다.
---
## 2. Problem
- 기존 `TextTabBarView`는 텍스트 탭 전용이며 최대 3개 메뉴를 전제로 한다.
- Figma `20:3590`은 capsule 형태 탭이 여러 개 나열되고, 메뉴가 화면 너비를 넘으면 가로 스크롤되어야 한다.
- 기존 Text Tab 요구사항과 capsule 탭의 높이, 패딩, radius, border, 스크롤 동작이 달라 같은 컴포넌트에 합치면 변경 범위와 회귀 위험이 커진다.
---
## 3. Goals
- 검정 배경, 높이 52dp의 재사용 가능한 Capsule Tab Bar를 제공한다.
- 탭 항목은 capsule 형태로 표시한다.
- 선택된 탭은 `soda_400` 배경과 white 텍스트로 표시한다.
- 선택되지 않은 탭은 black 배경, `gray_700` border, white 텍스트로 표시한다.
- 탭 내부 텍스트는 14sp medium 계열 Typography를 사용한다.
- 탭이 많아 화면 너비를 넘으면 가로로 스크롤되어야 한다.
- 메뉴 개수는 1개 이상을 허용하고, 항상 하나의 메뉴만 selected 상태여야 한다.
---
## 4. Non-Goals
- 기존 `TextTabBarView` 동작을 변경하지 않는다.
- 기존 화면에 신규 컴포넌트를 일괄 적용하지 않는다.
- Compose, 신규 Activity, Fragment, ViewModel을 추가하지 않는다.
- line 타입 또는 기존 text 타입 탭을 이번 컴포넌트에 통합하지 않는다.
- Figma에 없는 배지, 아이콘, 닫기 버튼, 스크롤 인디케이터를 추가하지 않는다.
---
## 5. Target Users
- XML 레이아웃을 작성하거나 유지보수하는 Android 개발자.
- 메뉴 개수가 동적으로 늘어날 수 있는 화면에서 capsule 탭 필터를 재사용하려는 개발자.
---
## 6. User Stories
- 개발자는 여러 개의 카테고리 메뉴를 capsule 형태로 표시하고 싶다.
- 개발자는 메뉴가 많을 때 화면 밖 메뉴를 가로 스크롤로 접근하게 하고 싶다.
- 개발자는 선택된 메뉴를 하나만 유지하고, 선택 변경 콜백으로 화면별 동작을 연결하고 싶다.
---
## 7. Core Features
### Capsule Tab Bar Component
Figma `20:3590` 기준 capsule 타입 탭바를 XML + Kotlin custom view로 제공한다.
#### Requirements
- Figma: https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=20-3590&m=dev
- Container width: `match_parent`
- Container height: `52dp`
- Container background: black
- Container horizontal start padding: `spacing_20`
- Tab list layout: horizontal, center vertical
- Tab gap: `spacing_8`
- Tab height: `34dp`
- Tab horizontal padding: `spacing_12`
- Tab vertical padding: `spacing_8`
- Tab corner radius: capsule 형태. 현재 `radius_100` 토큰이 없으므로 구현 시 신규 dimen 토큰 추가 또는 drawable radius 직접 지정 중 하나를 계획에서 명시한다.
- Selected tab: background `soda_400`, text white, no border
- Normal tab: background black, border `gray_700`, text white
- 메뉴가 화면 너비를 넘으면 가로 스크롤되어야 한다.
- 메뉴 개수는 1개 이상을 허용한다.
- selected index가 범위를 벗어나면 첫 번째 메뉴를 selected로 보정한다.
#### Edge Cases
- 메뉴가 0개이면 호출부 오류로 간주한다.
- 이미 선택된 탭을 다시 선택하면 중복 콜백을 호출하지 않는다.
- 메뉴 목록이 바뀌어 기존 selected index가 범위를 벗어나면 첫 번째 메뉴를 selected로 보정한다.
---
## 8. UX / UI Expectations
- 첫 번째 탭은 왼쪽 20dp 지점에서 시작한다.
- 각 탭은 8dp 간격으로 배치된다.
- 탭은 텍스트 길이에 따라 width가 늘어나며, 고정 균등 분배하지 않는다.
- 메뉴가 많으면 자연스럽게 좌우 스크롤된다.
- 스크롤 영역은 탭바 높이 52dp 안에서 세로 중앙 정렬되어야 한다.
---
## 9. Technical Constraints
- 현재 프로젝트는 XML Views + ViewBinding 기반이므로 XML 레이아웃과 Kotlin custom view 패턴을 사용한다.
- 신규 Kotlin 코드는 `kr.co.vividnext.sodalive.v2.widget` 패키지 하위에 둔다.
- 기존 `TextTabBarView``TextTabSelectionState`의 동작을 깨지 않는다.
- 기존 디자인 토큰 `soda_400`, `gray_700`, `white`, `black`, `spacing_8`, `spacing_12`, `spacing_20`을 우선 사용한다.
- 필요한 경우 capsule radius 전용 dimen 또는 drawable 리소스를 최소 범위로 추가한다.
---
## 10. Metrics
- 메뉴 1개 이상을 설정할 수 있고 항상 하나만 selected 상태다.
- 탭 항목이 화면 너비를 초과해도 가로 스크롤로 접근할 수 있다.
- Android 리소스 병합 및 디버그 빌드가 성공한다.
- 관련 선택 상태 테스트가 통과한다.
---
## 11. Open Questions
- 사용자 응답이 없으므로 기존 `TextTabBarView` 확장이 아닌 별도 `CapsuleTabBarView` 추가를 기본 설계로 확정한다.

View File

@@ -0,0 +1,134 @@
# PRD: 타이틀바 및 탭 텍스트바 컴포넌트
## 1. Overview
XML 레이아웃 기반 화면에서 재사용할 수 있는 Title Bar Component와 Tab-Text Bar Component를 개발한다.
---
## 2. Problem
- 화면마다 타이틀 영역과 탭 텍스트 영역을 개별 구현하면 높이, 배경색, 정렬, 텍스트 스타일이 달라질 수 있다.
- Home 화면과 일반 화면의 타이틀 바 구조는 유사하지만 좌측 영역의 콘텐츠가 다르므로 재사용 가능한 변형이 필요하다.
- 탭 텍스트 바는 최대 3개 메뉴와 단일 선택 상태를 일관되게 보장해야 한다.
---
## 3. Goals
- 전체 너비, 높이 60dp, 검정 배경, 세로 가운데 정렬을 갖는 재사용 가능한 Title Bar Component를 제공한다.
- Home Title Bar는 좌측에 `img_text_logo_v2`, 우측에 화면별로 교체 가능한 메뉴 아이콘을 배치할 수 있어야 한다.
- Default Title Bar는 좌측에 화면명 텍스트, 우측에 화면별로 교체 가능한 메뉴 아이콘을 배치할 수 있어야 한다.
- 전체 너비, 높이 52dp, 검정 배경, 세로 가운데 정렬을 갖는 재사용 가능한 Tab-Text Bar Component를 제공한다.
- Tab-Text Bar는 최대 3개 메뉴를 지원하고 반드시 하나만 selected 상태가 되도록 설계한다.
- Tab-Text Bar 텍스트는 `Typography.Heading3`을 사용하고 normal 색상은 `gray_600`, selected 색상은 white를 사용한다.
---
## 4. Non-Goals
- 이번 범위에서는 실제 화면에 컴포넌트를 일괄 적용하지 않는다.
- 신규 Activity, Fragment, ViewModel을 만들지 않는다.
- Compose 컴포넌트 또는 Compose Theme를 추가하지 않는다.
- 3개를 초과하는 탭 메뉴 지원, 스크롤 탭, 아이콘 탭, 배지 기능은 포함하지 않는다.
- 메뉴 아이콘 클릭 시 수행할 화면별 비즈니스 로직은 컴포넌트 내부에 고정하지 않는다.
- Figma에서 제공되지 않은 추가 애니메이션, 그림자, divider, pressed 효과는 추가하지 않는다.
---
## 5. Target Users
- XML 레이아웃을 작성하거나 유지보수하는 Android 개발자.
- 신규 v2 화면에서 공통 상단 바와 텍스트 탭 바를 재사용하려는 개발자.
---
## 6. User Stories
- 개발자는 Home 화면에서 로고와 메뉴 아이콘이 포함된 상단 바를 반복 구현 없이 재사용하고 싶다.
- 개발자는 일반 화면에서 화면명과 메뉴 아이콘이 포함된 상단 바를 같은 규격으로 사용하고 싶다.
- 개발자는 화면별 요구에 맞게 우측 메뉴 아이콘을 교체하고 클릭 동작을 주입하고 싶다.
- 개발자는 최대 3개 메뉴 중 하나만 선택되는 텍스트 탭 바를 재사용하고 싶다.
---
## 7. Core Features
### Title Bar Component
재사용 가능한 상단 타이틀 바를 XML 기반으로 제공한다.
#### Requirements
- Width: `match_parent`
- Height: `60dp`
- Alignment: 세로 가운데 정렬
- Background: black
- 공통 구조는 `좌측 콘텐츠 영역` + `빈 영역` + `우측 메뉴 아이콘 영역`으로 구성한다.
- 우측 메뉴 아이콘은 사용하는 화면마다 다른 drawable을 지정할 수 있어야 한다.
- 우측 메뉴 아이콘은 없는 화면에서도 사용할 수 있도록 숨김 처리가 가능해야 한다.
#### Home Title Bar
- Figma: https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=20-3575&m=dev
- 좌측 콘텐츠는 `img_text_logo_v2`를 표시한다.
- 중앙 빈 영역은 가변 폭으로 남은 공간을 채운다.
- 우측 메뉴 아이콘은 화면에서 교체 가능해야 한다.
#### Default Title Bar
- Figma: https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=20-3576&m=dev
- 좌측 콘텐츠는 화면명 텍스트를 표시한다.
- 화면명 텍스트는 사용하는 화면마다 변경 가능해야 한다.
- 중앙 빈 영역은 가변 폭으로 남은 공간을 채운다.
- 우측 메뉴 아이콘은 화면에서 교체 가능해야 한다.
#### Edge Cases
- 메뉴 아이콘이 없으면 우측 아이콘 영역을 숨기거나 클릭 불가능한 상태로 둔다.
- Default Title Bar의 화면명이 비어 있으면 빈 텍스트 그대로 표시하지 않고 호출부에서 유효한 화면명을 제공해야 한다.
- `img_text_logo_v2` 리소스가 없는 경우 구현 단계에서 기존 로고 리소스 존재 여부를 먼저 확인하고, 없으면 해당 리소스를 추가 대상으로 명시한다.
### Tab-Text Bar Component
최대 3개 텍스트 메뉴를 보여주는 재사용 가능한 탭 바를 XML 기반으로 제공한다.
#### Requirements
- Figma: https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=20-3585&m=dev
- Width: `match_parent`
- Height: `52dp`
- Alignment: 세로 가운데 정렬
- Background: black
- Typography: `Typography.Heading3`
- Normal Color: `gray_600`
- Selected Color: white
- 메뉴 개수는 1개 이상 3개 이하만 허용한다.
- 메뉴 선택 시 선택된 메뉴만 selected 상태가 되고 나머지는 normal 상태가 되어야 한다.
- 초기 상태에서도 반드시 하나의 메뉴가 selected 상태여야 한다.
#### Edge Cases
- 메뉴가 0개이면 호출부 오류로 간주하고 컴포넌트 사용을 허용하지 않는다.
- 메뉴가 4개 이상이면 호출부 오류로 간주하고 컴포넌트 사용을 허용하지 않는다.
- selected index가 메뉴 범위를 벗어나면 첫 번째 메뉴를 selected로 보정하는 단순한 동작을 기본값으로 한다.
---
## 8. UX / UI Expectations
- Title Bar와 Tab-Text Bar는 모두 검정 배경 위에서 세로 중앙 정렬되어야 한다.
- Home Title Bar는 `img_text_logo_v2`와 우측 메뉴 아이콘 사이에 빈 영역이 자연스럽게 확장되어야 한다.
- Default Title Bar는 화면명과 우측 메뉴 아이콘 사이에 빈 영역이 자연스럽게 확장되어야 한다.
- Tab-Text Bar는 selected 텍스트가 white, normal 텍스트가 `gray_600`으로 명확히 구분되어야 한다.
- 터치 가능한 메뉴 아이콘과 탭 텍스트는 화면별 클릭 핸들러를 연결할 수 있어야 한다.
---
## 9. Technical Constraints
- 현재 프로젝트는 XML Views + ViewBinding 기반이므로 XML 레이아웃과 Kotlin View/ViewBinding 사용 패턴을 우선한다.
- 재사용 레이아웃은 `app/src/main/res/layout` 아래에 둔다.
- 기존 유사 패턴은 `toolbar_audio_content_main.xml`, `activity_audio_content_main.xml`, `activity_can_status.xml`, `fragment_message.xml`을 참고한다.
- 색상은 기존 `colors.xml`의 black, white, `gray_600` 토큰을 사용한다.
- Typography는 기존 `app/src/main/res/values/typography.xml``Typography.Heading3`을 사용한다.
- 기존 화면의 동작이나 레이아웃을 요청 없이 변경하지 않는다.
---
## 10. Metrics
- Title Bar 높이 60dp와 Tab-Text Bar 높이 52dp가 문서와 구현에서 일치한다.
- Home Title Bar와 Default Title Bar가 각각 요구된 좌측 콘텐츠를 지원한다.
- 메뉴 아이콘과 화면명은 호출 화면에서 변경 가능하다.
- Tab-Text Bar는 1~3개 메뉴만 허용하고 항상 하나의 selected 상태를 유지한다.
- Android 리소스 병합 및 디버그 빌드가 성공한다.
---
## 11. Open Questions
- Figma 조회는 현재 도구 타임아웃으로 확인하지 못했으므로, 구현 단계에서는 제공된 텍스트 요구사항을 우선 기준으로 삼는다.
- `img_text_logo_v2`가 이미 drawable 리소스로 존재하는지 구현 전 확인이 필요하다.