264 lines
19 KiB
Markdown
264 lines
19 KiB
Markdown
# PRD: 채팅 탭 페이지
|
|
|
|
## 1. Overview
|
|
`ChatMainFragment`에 Figma `chat_001` 기준 채팅 탭 페이지를 구성하고, `GET /api/v2/chat/rooms` 응답을 채팅방 목록 UI로 표시한다.
|
|
|
|
---
|
|
|
|
## 2. Problem
|
|
- `ChatMainFragment`는 현재 `FragmentV2MainChatBinding`만 연결된 빈 Fragment다.
|
|
- Figma `177:3466`에는 대화 탭의 상단 타이틀, 필터 탭, 채팅방 목록, 플로팅 액션 버튼이 정의되어 있지만 Android 화면에 아직 반영되어 있지 않다.
|
|
- 기존 `v2` 위젯 중 일부는 재사용할 수 있으나, Figma의 채팅방 list item과 Direct badge는 전용 위젯이 없다.
|
|
- API 응답 모델은 `HomeChatModels.kt`에 있으나 채팅 탭 스펙에는 `ChatRoomList*` 계열 이름이 더 명확하다.
|
|
|
|
---
|
|
|
|
## 3. Goals
|
|
- `ChatMainFragment`에서 Figma `chat_001`과 동일한 채팅 탭 페이지 골격을 제공한다.
|
|
- 기존 위젯과 리소스를 우선 재사용하고, 없는 UI만 최소 신규 위젯으로 만든다.
|
|
- `GET /api/v2/chat/rooms`를 `filter`, `cursor` 파라미터와 함께 호출해 채팅방 목록을 표시한다.
|
|
- 필터 탭 `전체`, `AI 채팅`, `DM`을 제공하고 선택 상태에 맞게 첫 페이지 API를 다시 호출한다.
|
|
- 채팅방 item 터치 시 `chatType=AI`는 기존 채팅방 화면으로 이동한다.
|
|
|
|
---
|
|
|
|
## 4. Non-Goals
|
|
- 채팅방 상세 화면, 메시지 송수신, 채팅방 생성 flow 자체는 구현하지 않는다.
|
|
- `chatType=DM` item 클릭 시 이동할 DM 페이지 생성 및 연결은 다음 범위에서 진행한다.
|
|
- 하단 메인 내비게이션은 `MainV2Activity`의 기존 `BottomNavigationView`를 사용하며 `ChatMainFragment` 내부에 새로 만들지 않는다.
|
|
- Figma의 iOS StatusBar는 Android 앱에서 별도 View로 만들지 않는다.
|
|
- API 스키마를 임의 변경하지 않는다.
|
|
- unread dot 표시는 이번 PRD 범위에 포함하지 않는다.
|
|
- 실시간 unread 갱신, WebSocket/SSE, pull-to-refresh, skeleton/shimmer는 이번 PRD 범위에 포함하지 않는다.
|
|
- Analytics/logging은 별도 요구가 없으므로 추가하지 않는다.
|
|
|
|
---
|
|
|
|
## 5. Target Users
|
|
- 메인 하단 `대화` 탭에서 AI 채팅방과 DM 채팅방을 확인하려는 앱 사용자.
|
|
- `kr.co.vividnext.sodalive.v2.main.chat` 영역을 구현/유지보수하는 Android 개발자.
|
|
|
|
---
|
|
|
|
## 6. User Stories
|
|
- 사용자는 `대화` 탭을 눌렀을 때 최근 대화 목록을 바로 보고 싶다.
|
|
- 사용자는 `전체`, `AI 채팅`, `DM` 필터로 채팅방 유형을 빠르게 구분하고 싶다.
|
|
- 사용자는 각 채팅방에서 상대 프로필, 이름, 마지막 메시지, 마지막 메시지 시간을 확인하고 싶다.
|
|
- 사용자는 새 대화를 시작하는 플로팅 버튼을 발견할 수 있어야 한다.
|
|
|
|
---
|
|
|
|
## 7. Core Features
|
|
|
|
### Chat Tab Layout
|
|
`ChatMainFragment`의 루트 화면을 Figma `177:3466` 기준으로 구성한다.
|
|
|
|
#### Requirements
|
|
- 전체 배경은 `@color/black`을 사용한다.
|
|
- 상단에는 높이 `60dp` 수준의 타이틀바를 배치하고 제목은 `대화`로 표시한다.
|
|
- 타이틀바 우측에는 기존 `ic_bar_cash`, `ic_bar_search` 리소스를 재사용한다. 우측 상단의 action menu 이미지 사이 간격을 14dp로 지정하며, dimens에 정의되어 있는 값(`@dimen/spacing_14`)이 있으면 하드코딩 하지 않고 dimens의 값을 가져다가 설정한다. 타 화면 공통 컴포넌트 마크업 훼손을 피하기 위해 코드단(`ChatMainFragment.kt` 내)에서 동적으로 LayoutParams 마진을 부여한다.
|
|
- Figma의 하단 nav는 `MainV2Activity`의 기존 `BottomNavigationView`가 담당하므로 Fragment layout에는 포함하지 않는다.
|
|
- content 영역은 타이틀바 아래 필터 탭과 채팅방 목록으로 구성한다.
|
|
- 플로팅 액션 버튼은 우측 하단, 하단 내비게이션과 겹치지 않는 위치에 표시한다.
|
|
|
|
#### Edge Cases
|
|
- Android system bar inset은 `MainV2Activity.overrideRootWindowInsets()`의 기존 처리를 따른다.
|
|
- 미니 플레이어가 표시될 수 있으므로 플로팅 버튼과 목록 하단 padding은 `activity_main_v2.xml` 구조를 고려해 구현 계획에서 확정한다.
|
|
|
|
### Chat Room Filter Tabs
|
|
채팅방 유형 필터를 제공한다.
|
|
|
|
#### Requirements
|
|
- 탭 메뉴는 `전체`, `AI 채팅`, `DM` 순서로 표시한다.
|
|
- Figma의 pill 형태와 가장 가까운 기존 `CapsuleTabBarView`를 우선 재사용한다.
|
|
- selected 상태는 흰색 배경/검정 텍스트, normal 상태는 검정 배경/회색 stroke/흰색 텍스트로 표시되어야 한다.
|
|
- 기존 `CapsuleTabBarView`가 selected 텍스트 색상을 검정으로 처리하지 못하면 최소 범위로 위젯을 확장한다.
|
|
- 현재 선택되어 있지 않은 탭을 터치하면 해당 `filter` 값으로 첫 페이지 API를 호출한다.
|
|
- 탭은 서버 목록 조회의 filter 역할을 담당한다.
|
|
- 첫 페이지 로딩 시 화면에 표시 중인 모든 기존 목록 데이터를 지우고 새 응답 데이터로 다시 세팅한다.
|
|
|
|
#### Edge Cases
|
|
- API 요청 `filter`는 `ALL`, `AI`, `DM` 중 하나만 사용한다.
|
|
- `chatType`은 `AI`, `DM` 문자열만 사용한다.
|
|
- 탭 전환 후 표시할 item이 없으면 목록을 비운다. 별도 empty placeholder는 추가하지 않는다.
|
|
- 탭 전환 시점 또는 첫 번째 페이지 로드 시(isAppending = false인 상태) 목록의 스크롤을 최상단으로 이동시킨다.
|
|
|
|
### Chat Room List
|
|
API 응답의 채팅방 목록을 Figma의 `ChatList` 형태로 표시한다.
|
|
|
|
#### Requirements
|
|
- `RecyclerView` 기반 세로 목록으로 구성한다.
|
|
- 각 item은 프로필 이미지, 크리에이터/상대 이름, Direct badge, 마지막 메시지, 마지막 메시지 시간을 표시한다.
|
|
- 프로필 이미지는 원형 `58dp` 수준으로 표시하고 기존 이미지 로딩 정책을 따른다.
|
|
- 이름은 `18sp` bold, 마지막 메시지는 `16sp` medium, 시간은 `14sp` medium 기준으로 맞춘다.
|
|
- 마지막 메시지는 한 줄 말줄임 처리한다.
|
|
- `chatType`이 DM이면 Figma의 `Direct` badge를 표시하고, AI 채팅이면 표시하지 않는다.
|
|
- unread dot은 표시하지 않는다.
|
|
- 채팅방 item 터치 시 extras는 `roomId`만 전달한다.
|
|
- `chatType`이 `AI`이면 기존 `ChatRoomActivity`로 이동한다.
|
|
- `chatType`이 `DM`이면 이번 범위에서는 이동을 구현하지 않는다. DM 페이지 생성 및 연결은 다음 범위에서 진행한다.
|
|
|
|
#### Edge Cases
|
|
- `targetImageUrl`이 비어 있거나 로딩 실패하면 기존 placeholder 정책을 따른다.
|
|
- `targetName`이 비어 있으면 빈 문자열 그대로 표시하고 별도 대체 문구는 추가하지 않는다.
|
|
- `lastMessage`가 비어 있으면 빈 줄이 아닌 한 줄 영역을 유지한다.
|
|
- `lastMessageAt`은 ISO-8601 문자열이다.
|
|
- `lastMessageAt`은 디바이스에 설정된 Timezone으로 변환한 뒤 화면 표시 문자열로 변환한다.
|
|
- 일주일까지는 상대 시간으로 표시한다. 이 문구는 다국어 처리가 필요하다.
|
|
- 상대 시간으로 표시하지 않는 날짜는 언어별 날짜 포맷으로 표시한다.
|
|
- 일주일보다 오래되었고 올해 받은 메시지라면 아래 언어별 날짜 포맷을 적용한다.
|
|
- 한국어는 `M월 d일` 형태로 표시한다.
|
|
- 영어는 `MMM d` 형태로 표시한다.
|
|
- 일본어는 `M月d日` 형태로 표시한다.
|
|
- 올해가 2026년이고 2025년에 받은 메시지처럼 다른 연도 메시지라면 `yyyy.MM.dd` 형태로 표시한다.
|
|
|
|
### Floating Action Button
|
|
새 채팅 또는 대화 시작 진입점을 표시한다.
|
|
|
|
#### Requirements
|
|
- Figma 기준 `soda/400` 계열 원형 버튼과 plus icon을 사용한다.
|
|
- 기존 `btn_plus_round`, `ic_plus_no_bg`, `btn_add` 등 사용 가능한 리소스를 먼저 확인해 재사용한다.
|
|
- 적합한 기존 리소스가 없으면 `v2` 전용 drawable을 최소 신규 추가한다.
|
|
- 버튼 클릭 동작은 추후 결정한다.
|
|
|
|
#### Edge Cases
|
|
- 클릭 대상 화면이 확정되지 않았으므로 버튼은 표시하되 클릭 동작은 추가하지 않고 명시적 주석/계획 항목으로 남긴다.
|
|
|
|
### API Integration
|
|
채팅방 목록 API를 호출하고 UI state로 변환한다.
|
|
|
|
#### API Endpoint
|
|
- `GET /api/v2/chat/rooms`
|
|
|
|
#### Request Parameters
|
|
- `filter`: `ALL`, `AI`, `DM` 중 하나를 전달한다.
|
|
- `cursor`: 다음 페이지 요청 시 전달한다. 첫 페이지 요청 시에는 전달하지 않거나 서버 계약에 맞는 empty/null 값을 사용한다.
|
|
|
|
#### Response Model
|
|
현재 파일은 `app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/data/HomeChatModels.kt`이며, 기존 data class는 아래 이름을 사용한다.
|
|
|
|
```kotlin
|
|
data class ChatRoomListPageResponse(
|
|
val rooms: List<ChatRoomListItemResponse>,
|
|
val hasMore: Boolean,
|
|
val nextCursor: String?
|
|
)
|
|
|
|
data class ChatRoomListItemResponse(
|
|
val roomId: Long,
|
|
val chatType: String,
|
|
val targetName: String,
|
|
val targetImageUrl: String,
|
|
val lastMessage: String,
|
|
val lastMessageAt: String
|
|
)
|
|
```
|
|
|
|
#### Naming Requirements
|
|
- `HomeChatModels.kt`는 채팅 탭 스펙과 맞지 않으므로 `ChatRoomModels.kt` 또는 동등하게 명확한 이름으로 변경하는 것을 권장한다.
|
|
- DTO class 이름은 현재 `ChatRoomListPageResponse`, `ChatRoomListItemResponse`가 스펙에 적합하므로 유지 가능하다.
|
|
- API 응답 최상위가 실제로 `rooms`, `hasMore`, `nextCursor` 구조인지 구현 전 백엔드 계약 또는 샘플 응답으로 최종 확인한다.
|
|
|
|
#### Data Flow
|
|
- `ChatRoomApi` -> `ChatRoomRepository` -> `ChatMainViewModel` -> `ChatMainFragment` 흐름을 사용한다.
|
|
- Retrofit 응답은 기존 패턴처럼 `Single<ApiResponse<ChatRoomListPageResponse>>`를 우선 사용한다.
|
|
- token 전달은 기존 v2 홈 API 패턴과 `SharedPreferenceManager.token` 사용 방식을 따른다.
|
|
- UI model은 DTO와 분리해 `ChatRoomListUiItem` 같은 이름으로 둔다.
|
|
- 첫 페이지 요청은 기존 목록을 clear한 뒤 응답 목록을 새로 세팅한다.
|
|
- `hasMore=true`와 `nextCursor`가 제공되면 스크롤 pagination으로 다음 페이지 API를 호출한다.
|
|
- 다음 페이지 응답은 기존 목록 뒤에 append한다.
|
|
|
|
#### Edge Cases
|
|
- API success가 false이거나 data가 null이면 목록을 비우고 기존 Toast/error 정책을 따른다.
|
|
- 네트워크 오류가 발생해도 crash 없이 처리한다.
|
|
- `hasMore=false`이거나 `nextCursor`가 제공되지 않으면 추가 페이지를 호출하지 않는다.
|
|
|
|
---
|
|
|
|
## 8. UX / UI Expectations
|
|
- Figma 기준 화면 폭 `402`의 좌우 여백 감각을 Android `match_parent` 환경에서 유지한다.
|
|
- 필터 탭은 높이 `52dp`, 좌측 여백은 기존 `CapsuleTabBarView`의 `20dp` 또는 Figma `14dp` 중 구현 계획에서 기존 위젯 일관성을 우선해 결정한다.
|
|
- 채팅방 item은 세로 높이 약 `86dp`, 내부 padding `14dp`, 프로필과 텍스트 사이 간격 `14dp`를 기준으로 한다.
|
|
- Direct badge는 `soda/400` 배경, radius `4dp`, Pattaya font 기반 텍스트를 우선 적용한다.
|
|
- unread dot은 표시하지 않는다.
|
|
- 긴 이름과 긴 마지막 메시지는 겹치지 않고 말줄임 처리되어야 한다.
|
|
- 하단 내비게이션, 미니 플레이어, 플로팅 버튼이 서로 겹치지 않아야 한다.
|
|
|
|
---
|
|
|
|
## 9. Technical Constraints
|
|
- Android XML Views, ViewBinding, RecyclerView, RxJava3, Retrofit, Gson, Koin 구조를 따른다.
|
|
- 신규 `ViewModel`, API, Repository, adapter, custom view는 `kr.co.vividnext.sodalive.v2.main.chat` 하위에 작성한다.
|
|
- 재사용 가능한 전용 위젯이 필요하면 `kr.co.vividnext.sodalive.v2.widget` 하위에 작성하되, 단일 화면 전용이면 `v2.main.chat.ui` 하위에 둔다.
|
|
- 기존 `BaseFragment<FragmentV2MainChatBinding>` 구조를 유지한다.
|
|
- 구현 전 `docs/20260609_채팅_탭_페이지/plan-task.md`를 작성하고, 그 문서에 따라 최소 구현한다.
|
|
- 테스트는 mapper/formatter 단위 테스트와 Fragment layout/adapter 검증을 우선한다.
|
|
|
|
---
|
|
|
|
## 10. Widget Classification
|
|
|
|
| Figma 요소 | 기존 재사용 후보 | 판정 | 비고 |
|
|
| --- | --- | --- | --- |
|
|
| 하단 메인 nav | `MainV2Activity` + `BottomNavigationView` + `menu_main_v2_bottom_navigation` | 재사용 | Fragment 내부 구현 제외 |
|
|
| 타이틀바 우측 cash/search icon | `ic_bar_cash`, `ic_bar_search` | 재사용 | `view_title_bar_default`의 우측 액션 영역을 가변 아이콘 구조로 확장해 재사용한다 |
|
|
| 타이틀바 제목 `대화` | `view_title_bar_default` | 재사용 + 최소 확장 | 신규 chat 전용 title layout은 만들지 않는다 |
|
|
| 필터 탭 `전체/AI 채팅/DM` | `CapsuleTabBarView` | 재사용 + 최소 확장 가능 | selected 텍스트 색상 검정 처리 필요 가능성 |
|
|
| 채팅방 리스트 item | 없음 | 신규 필요 | `ChatRoomListItemView` 또는 `RecyclerView` item XML/Adapter |
|
|
| Direct badge | 없음 | 신규 필요 | 작은 badge View/drawable, Pattaya font 재사용 |
|
|
| 프로필 원형 이미지 | 기존 Coil 로딩 패턴, drawable placeholder | 재사용 | 전용 위젯은 신규 item 내부에서 처리 |
|
|
| unread dot | 해당 없음 | 구현 제외 | 이번 PRD에서는 unread dot을 표시하지 않는다 |
|
|
| 플로팅 plus 버튼 | `btn_plus_round`, `ic_plus_no_bg`, `btn_add` 후보 | 부분 재사용/확인 필요 | 크기/색상이 맞지 않으면 신규 drawable 최소 추가 |
|
|
|
|
---
|
|
|
|
## 11. Metrics
|
|
- `ChatMainFragment` 진입 시 `GET /api/v2/chat/rooms`가 한 번 호출된다.
|
|
- API 응답의 `rooms`가 `RecyclerView` item으로 표시된다.
|
|
- `전체`, `AI 채팅`, `DM` 탭 선택 시 각각 `ALL`, `AI`, `DM` filter로 첫 페이지 API가 호출된다.
|
|
- 첫 페이지 로딩 시 기존 목록이 clear되고 새 응답 목록이 표시된다.
|
|
- `hasMore=true`와 `nextCursor`가 제공되면 스크롤 pagination이 동작한다.
|
|
- DM item에만 `Direct` badge가 표시된다.
|
|
- 마지막 메시지는 한 줄 말줄임 처리된다.
|
|
- unread dot은 표시되지 않는다.
|
|
- `lastMessageAt`은 ISO-8601 문자열을 디바이스 Timezone 기준으로 변환한 뒤 일주일까지는 상대 시간으로 표시한다.
|
|
- 상대 시간으로 표시하지 않는 올해 메시지의 날짜 포맷은 한국어 `M월 d일`, 영어 `MMM d`, 일본어 `M月d日`로 표시된다.
|
|
- 상대 시간으로 표시하지 않는 다른 연도 메시지는 `yyyy.MM.dd`로 표시된다.
|
|
- 채팅방 item 터치 시 `chatType=AI`는 `ChatRoomActivity`로 이동하며 extras는 `roomId`만 전달한다.
|
|
- `chatType=DM` item 클릭 시 이동할 DM 페이지 생성 및 연결은 다음 범위에서 진행한다.
|
|
- 플로팅 버튼 클릭 동작은 추후 결정 사항으로 남긴다.
|
|
- 빈 응답, API 실패, 이미지 실패가 crash 없이 처리된다.
|
|
- 하단 메인 내비게이션은 기존 `MainV2Activity` 구조를 그대로 사용한다.
|
|
- mapper/formatter 단위 테스트와 관련 layout/adapter 테스트가 통과한다.
|
|
|
|
---
|
|
|
|
## 12. Open Questions
|
|
- 플로팅 plus 버튼 클릭 시 이동해야 하는 기존 화면 또는 신규 flow는 추후 결정한다.
|
|
- `chatType=DM` item 클릭 시 이동할 신규 DM 페이지의 Activity/Fragment 이름과 생성 범위는 다음 범위에서 확정한다.
|
|
- 상대 시간 표시의 다국어 string resource 문구는 구현 계획에서 확정한다.
|
|
|
|
---
|
|
|
|
## 13. References
|
|
- 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=177-3466&m=dev
|
|
- 대상 Fragment: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/ChatMainFragment.kt`
|
|
- 현재 layout: `app/src/main/res/layout/fragment_v2_main_chat.xml`
|
|
- 현재 DTO: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/data/HomeChatModels.kt`
|
|
- 기존 필터 탭 위젯: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/CapsuleTabBarView.kt`
|
|
- 하단 내비게이션: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt`
|
|
|
|
---
|
|
|
|
## 14. Verification Log
|
|
- 2026-06-09: `docs/prd/sample-prd.md`, `docs/agent-guides/work-plan-docs.md`, `docs/agent-guides/code-style.md`를 확인해 PRD 구조와 저장소 문서 규칙을 확인했다.
|
|
- 2026-06-09: Figma `177:3466`의 design context와 screenshot을 확인해 `title-bar`, `tab-bar`, `ChatList`, `button-floating`, `nav` 구조를 분석했다.
|
|
- 2026-06-09: `ChatMainFragment.kt`, `fragment_v2_main_chat.xml`, `HomeChatModels.kt`, `CapsuleTabBarView.kt`, `view_capsule_tab_bar.xml`, `activity_main_v2.xml`, `MainV2Activity.kt`, `view_title_bar_home.xml`을 확인해 재사용/신규 위젯 후보를 분류했다.
|
|
- 2026-06-09: 이번 단계는 PRD 작성만 수행했으며 구현/빌드/테스트는 실행하지 않았다.
|
|
- 2026-06-09: 사용자 추가 요구사항을 반영해 unread dot 미표시, `chatType`/API filter/cursor, `lastMessageAt` 표시 규칙, 탭 필터 재호출, pagination, 클릭 액션 범위를 보강했다.
|
|
- 2026-06-09: `chatType=DM` item 클릭 이동은 다음 범위로 분리하고, 상대 시간으로 표시하지 않는 올해 메시지의 언어별 날짜 포맷을 한국어 `M월 d일`, 영어 `MMM d`, 일본어 `M月d日`로 보강했다.
|
|
- 2026-06-09: 사용자 피드백에 따라 채팅 전용 `view_title_bar_chat.xml` 신규 생성을 제외하고, 기존 `view_title_bar_default.xml`의 우측 아이콘 영역을 가변 개수로 확장해 재사용하는 방향으로 위젯 분류를 갱신했다.
|
|
- 2026-06-10: Phase 6에서 채팅 탭 layout 골격과 CapsuleTab selected 색상 보정을 구현했다. 기존 `view_title_bar_default.xml`에 `ll_title_bar_actions`를 추가해 우측 action icon을 가변으로 담을 수 있게 했고, `fragment_v2_main_chat.xml`은 black `ConstraintLayout` root 아래 title bar, `view_capsule_tab_bar`, `rv_chat_rooms`, `btn_chat_floating`만 포함하도록 구성했다. floating button은 `bg_chat_floating_button.xml`의 `soda_400` 원형 배경과 기존 `ic_plus_no_bg`를 사용하며 클릭 동작은 추가하지 않았다. `CapsuleTabBarView`는 PRD 요구대로 selected tab black text, normal tab white text를 사용하도록 수정했다. 검증으로 `ChatMainFragmentLayoutTest`, `CapsuleTabBarViewTest`를 추가했고 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.chat.ChatMainFragmentLayoutTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.*CapsuleTab*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`가 모두 `BUILD SUCCESSFUL`로 통과했다. Phase 7 범위인 `ChatMainFragment` 동작 연결, 실제 title/icon 세팅, filter listener, navigation/click action은 구현하지 않았다.
|
|
- 2026-06-10: CapsuleTabBarView의 탭 전환 시 또는 첫 번째 페이지 로드 시(isAppending = false) RecyclerView의 스크롤을 최상단(position 0)으로 이동시키는 추가 요구사항을 수렴하고, 기존 PRD와 Plan-Task 문서를 갱신한 뒤 구현 및 검증을 완료했다.
|
|
- 2026-06-10: 채팅 타이틀바 우측 액션 메뉴 이미지 간의 간격을 14dp로 조정하는 요구사항을 반영했습니다. `dimens.xml`에 정의된 `@dimen/spacing_14` 리소스를 사용하여 하드코딩 없이 안전하게 `ChatMainFragment.kt`의 코드단에서 동적으로 LayoutParams를 통해 `marginStart` 값을 적용했습니다. 검증을 위한 `ChatMainFragmentLayoutTest.kt`도 업데이트하고 테스트 통과를 완료했습니다.
|