Files
sodalive-android/docs/20260609_채팅_탭_페이지/prd.md

18 KiB

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/roomsfilter, 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 리소스를 재사용한다.
  • 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 요청 filterALL, AI, DM 중 하나만 사용한다.
  • chatTypeAI, DM 문자열만 사용한다.
  • 탭 전환 후 표시할 item이 없으면 목록을 비운다. 별도 empty placeholder는 추가하지 않는다.

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만 전달한다.
  • chatTypeAI이면 기존 ChatRoomActivity로 이동한다.
  • chatTypeDM이면 이번 범위에서는 이동을 구현하지 않는다. 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는 아래 이름을 사용한다.

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=truenextCursor가 제공되면 스크롤 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, 좌측 여백은 기존 CapsuleTabBarView20dp 또는 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 응답의 roomsRecyclerView item으로 표시된다.
  • 전체, AI 채팅, DM 탭 선택 시 각각 ALL, AI, DM filter로 첫 페이지 API가 호출된다.
  • 첫 페이지 로딩 시 기존 목록이 clear되고 새 응답 목록이 표시된다.
  • hasMore=truenextCursor가 제공되면 스크롤 pagination이 동작한다.
  • DM item에만 Direct badge가 표시된다.
  • 마지막 메시지는 한 줄 말줄임 처리된다.
  • unread dot은 표시되지 않는다.
  • lastMessageAt은 ISO-8601 문자열을 디바이스 Timezone 기준으로 변환한 뒤 일주일까지는 상대 시간으로 표시한다.
  • 상대 시간으로 표시하지 않는 올해 메시지의 날짜 포맷은 한국어 M월 d일, 영어 MMM d, 일본어 M月d日로 표시된다.
  • 상대 시간으로 표시하지 않는 다른 연도 메시지는 yyyy.MM.dd로 표시된다.
  • 채팅방 item 터치 시 chatType=AIChatRoomActivity로 이동하며 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


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.xmlll_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.xmlsoda_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은 구현하지 않았다.