15 KiB
15 KiB
20260319_라이브룸채팅삭제기능구현계획.md
개요
- 라이브 룸에서 방장(크리에이터) 전용 채팅 삭제 기능을 추가하기 위한 구현 계획 문서다.
- 기능 범위는 단건 삭제(길게 누름 + 확인 다이얼로그)와 강퇴 연계 일괄 삭제(다이얼로그 없이 즉시 삭제)다.
- 본 문서는 계획을 먼저 확정하고, 구현/검증 결과는 하단
검증 기록에 누적한다.
요구사항 해석(확정)
- 채팅 삭제 권한은 방장(크리에이터)만 가진다.
- 삭제 대상 채팅을 길게 누르면 삭제 확인 다이얼로그를 띄운다.
- 다이얼로그 본문은
[닉네임]: [채팅 내용]형식으로 노출한다. - 다이얼로그 버튼은
취소/삭제두 가지다. - 삭제 확정 시 모든 사용자 화면에서 동일 채팅이 제거되어야 한다.
- 강퇴 시에는 다이얼로그 없이 해당 유저의 채팅을 즉시 일괄 삭제하고, 이 결과가 모든 사용자에게 동기화되어야 한다.
현재 구조 조사 요약
- 일반 채팅은
LiveRoomActivity.inputChat()에서agora.inputChat(message)로 RTM STRING 발송하고, 수신 측은onMessageEventSTRING 분기에서LiveRoomNormalChat을 리스트에 append 한다. - 실시간 제어 이벤트(방정보 수정, 채팅 얼림, 룰렛 등)는
LiveRoomChatRawMessageType+agora.sendRawMessageToGroup()+onMessageEventBINARY 분기 패턴을 이미 사용 중이다. - 강퇴는
LiveRoomActivity.kickOut()->LiveRoomViewModel.kickOut()(API) +LiveRoomRequestType.KICK_OUTpeer 메시지 전송으로 처리되며, 강퇴 대상 단말은 수신 후finish()로 종료한다. - 현재 라이브룸 채팅 모델/어댑터에는 단건 삭제를 위한 명시적 식별자/long-press 콜백/삭제 브로드캐스트 타입이 없다.
설계 결정
- 삭제 동기화는 기존 제어 이벤트와 동일하게 RTM BINARY raw message로 처리한다.
- 단건 삭제 정합성을 위해 라이브룸 일반 채팅에 식별자(
chatId)를 추가한다. - 강퇴 연계 일괄 삭제는
targetUserId기반 raw message 이벤트로 처리한다. - 롱프레스 진입은
LiveRoomChatAdapter에서LiveRoomNormalChat항목에만 연결하고, 실제 권한 검증은LiveRoomActivity에서isHost로 최종 보장한다. - 기존 STRING 채팅 수신 분기는 호환성 fallback으로 유지하고, 삭제 정합성이 필요한 경로는 raw payload 기반으로 처리한다.
완료 기준 (Acceptance Criteria)
- AC1: 방장(크리에이터)만 채팅 길게 누름 시 삭제 확인 다이얼로그를 볼 수 있다.
- AC2: 삭제 다이얼로그에
[닉네임]: [채팅 내용]형식과취소/삭제버튼이 노출된다. - AC3: 단건 삭제 확정 시 모든 사용자 화면에서 동일 채팅이 제거된다.
- AC4: 유저 강퇴 시 다이얼로그 없이 해당 유저의 채팅이 일괄 삭제된다.
- AC5: 강퇴 기반 일괄 삭제도 모든 사용자 화면에 동일하게 반영된다.
- AC6: 기존 채팅/후원/강퇴 흐름(삭제 기능 외)은 회귀 없이 유지된다.
구현 체크리스트
1) 채팅 모델/식별자 확장
LiveRoomNormalChat에 단건 삭제용chatId필드를 추가한다.- 강퇴 기반 일괄 삭제 범위를 위해 작성자 식별이 가능한 채팅 타입(
LiveRoomNormalChat,LiveRoomDonationChat, 필요 시LiveRoomRouletteDonationChat)의 사용자 식별 정보를 정리한다.
2) Raw message 스키마 확장
LiveRoomChatRawMessageType에 삭제 관련 타입(NORMAL_CHAT,DELETE_CHAT,DELETE_CHAT_BY_USER)을 추가한다.LiveRoomChatRawMessage에 삭제/일반채팅 동기화에 필요한 필드(chatId,targetUserId)를 nullable로 추가한다.
3) LiveRoomActivity 송신/수신 경로 반영
inputChat()에서 일반 채팅 raw payload 송신 + 로컬 리스트 반영 로직을 정리한다.rtmEventListener.onMessageEventBINARY 분기에NORMAL_CHAT,DELETE_CHAT,DELETE_CHAT_BY_USER처리 로직을 추가한다.- STRING 수신 분기는 fallback 경로로 유지하고, 삭제 식별자가 없는 legacy 메시지 처리 원칙을 명시한다.
4) 방장 전용 long-press 삭제 UX
LiveRoomChatAdapter에LiveRoomNormalChatlong-press 콜백을 추가한다.LiveRoomActivity에서 방장 권한(isHost) 검증 후 삭제 확인 다이얼로그를 노출한다.- 다이얼로그 본문은 문자열 포맷으로
[닉네임]: [채팅 내용]을 구성하고취소/삭제액션을 연결한다.
5) 강퇴 연계 일괄 삭제
kickOut(userId)경로에서 강퇴 대상 사용자의 채팅 일괄 삭제 raw 이벤트를 다이얼로그 없이 즉시 브로드캐스트한다.- 수신 측에서
targetUserId에 해당하는 채팅을 일괄 제거하고 리스트를 갱신한다.
6) 문자열/국제화
values/strings.xml에 라이브룸 채팅 삭제 다이얼로그 제목/본문 포맷 문자열을 추가한다.values-en/strings.xml,values-ja/strings.xml에도 동일 키를 추가한다.
7) 검증
lsp_diagnostics로 수정 파일의 신규 오류 유무를 확인한다../gradlew :app:testDebugUnitTest를 실행한다../gradlew :app:assembleDebug를 실행한다.- 수동 QA: 방장/일반유저 2계정으로 단건 삭제 및 강퇴 일괄 삭제의 전 사용자 반영을 확인한다.
영향 파일(예상)
필수
app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt- 방장 전용 long-press 삭제 진입, 삭제 확인 다이얼로그, 단건/일괄 삭제 raw 이벤트 송신, 수신 분기 삭제 처리, 강퇴 연계 삭제 처리 추가.
app/src/main/java/kr/co/vividnext/sodalive/live/room/chat/LiveRoomChatRawMessage.kt- 삭제/일반채팅 동기화용 raw message type 및 payload 필드 추가.
app/src/main/java/kr/co/vividnext/sodalive/live/room/chat/LiveRoomChat.kt- 단건 삭제용
chatId및 작성자 기반 일괄 삭제 대응 필드(필요 타입) 추가.
- 단건 삭제용
app/src/main/java/kr/co/vividnext/sodalive/live/room/chat/LiveRoomChatAdapter.kt- 일반 채팅 아이템 long-press 콜백 전달 경로 추가.
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/java/kr/co/vividnext/sodalive/agora/Agora.kt- 기존
sendRawMessageToGroupAPI로 처리 가능해 직접 수정 가능성은 낮다.
- 기존
리스크 및 확인사항
- 일반 채팅 송신 형식을 raw 중심으로 전환할 경우, 구버전/타플랫폼과의 프로토콜 호환성 리스크가 있다.
- fallback STRING 메시지는 삭제 식별자 정합성이 약할 수 있으므로, 삭제 대상 식별 우선순위를 명확히 정의해야 한다.
kickOutAPI 호출은 현재 성공/실패 콜백을 사용하지 않으므로, “API 성공 후 삭제 이벤트 전파” 순서 보장이 필요하면 ViewModel 시그니처 확장을 검토해야 한다.- 동일 사용자의 동일 본문 반복 메시지 삭제 시 단건 선택 정합성(정확히 1건 삭제) 검증이 필요하다.
외부 레퍼런스(요약)
- Agora Signaling 메시지 페이로드 구조화 권장(문자열/바이너리 + 앱 스키마):
- Stream Chat Android 삭제 권한/삭제 확인 다이얼로그/삭제 이벤트 처리 패턴:
검증 기록
-
기록 템플릿(후속 누적):
- YYYY-MM-DD
- 무엇/왜/어떻게:
- 실행 명령/도구:
명령 또는 사용 도구
- 결과:
- YYYY-MM-DD
-
2026-03-19
- 무엇/왜/어떻게: 라이브룸 채팅 삭제 구현 전에 기존 채팅 송수신/강퇴/어댑터 구조를 병렬 탐색하고, 요구사항을 충족하는 상세 구현 계획(예상 수정 파일/추가 항목/검증 항목)을 문서화했다.
- 실행 명령/도구:
task(subagent_type="explore")x3task(subagent_type="librarian")x2grep("LiveRoomChatRawMessageType|kickOut\(|onMessageEvent|setOnLongClickListener|confirm_delete_title")ast_grep_search("agora.sendRawMessageToGroup($$$)")read(LiveRoomActivity.kt, LiveRoomChat.kt, LiveRoomChatAdapter.kt, LiveRoomChatRawMessage.kt, LiveRoomViewModel.kt, Agora.kt, LiveApi.kt, strings*.xml, dialog_live.xml)bash("rg -n ...")시도apply_patch(본 문서 생성/상세화)
- 결과:
LiveRoomActivity중심의 채팅 입력(STRING)/제어이벤트(BINARY)/강퇴(peer) 경로를 확인했다.- 단건 삭제와 강퇴 일괄 삭제를 위해 필요한 확장 지점(모델, raw 타입, 어댑터 콜백, Activity 분기, 문자열 리소스)을 파일 단위로 확정했다.
- 현재 실행 환경에서
rg명령은 미설치(command not found)로 확인되어 동일 탐색은grep/ast_grep_search/read로 보완했다.
-
2026-03-19
- 무엇/왜/어떻게: 계획 문서 기준으로 라이브룸 채팅 삭제 기능(방장 long-press 단건 삭제, 강퇴 연계 일괄 삭제, 전 사용자 동기화)을 실제 코드에 반영하고 빌드/테스트/수동 실행 검증을 수행했다.
- 실행 명령/도구:
- 코드 반영:
apply_patch(LiveRoomActivity.kt,LiveRoomChat.kt,LiveRoomChatAdapter.kt,LiveRoomChatRawMessage.kt,strings.xml,values-en/strings.xml,values-ja/strings.xml) - 정적 진단:
lsp_diagnostics(LiveRoomActivity.kt, LiveRoomChat.kt, LiveRoomChatAdapter.kt, LiveRoomChatRawMessage.kt, strings*.xml) - 테스트/빌드:
./gradlew :app:testDebugUnitTest :app:assembleDebug - 수동 실행:
adb devices,./gradlew :app:installDebug,adb shell monkey -p kr.co.vividnext.sodalive.debug -c android.intent.category.LAUNCHER 1,adb shell run-as kr.co.vividnext.sodalive.debug am start --user 0 -n kr.co.vividnext.sodalive.debug/kr.co.vividnext.sodalive.live.room.LiveRoomActivity --el roomId 1
- 코드 반영:
- 결과:
- 방장 long-press 삭제 진입/확인 다이얼로그/단건 삭제 브로드캐스트, 강퇴 시 즉시 일괄 삭제 브로드캐스트가 코드 경로에 반영되었다.
:app:testDebugUnitTest,:app:assembleDebug,:app:installDebug가 성공했다.lsp_diagnostics는 Kotlin/XML LSP 미설정 환경으로 실행 불가 응답을 반환했다.- 단말 앱 실행 및
LiveRoomActivity인텐트 실행까지는 확인했으나, 이 환경에서는 방장/일반유저 2계정 동시 접속 시나리오를 재현할 계정/세션 준비가 없어 최종 E2E(두 사용자 화면 동시 확인)는 후속 수동 검증이 필요하다.
-
2026-03-19
- 무엇/왜/어떻게: Oracle 리뷰에서 삭제 이벤트 수신부의 권한 검증 누락(host-only 보장 약화)과 STRING fallback 채팅 개별 삭제 취약점이 확인되어, 삭제 수신 분기에 방장 검증을 추가하고 fallback 삭제 로직을 보완했다.
- 실행 명령/도구:
- 리뷰:
task(subagent_type="oracle") - 코드 반영:
apply_patch(LiveRoomActivity.kt) - 정적 진단:
lsp_diagnostics(LiveRoomActivity.kt) - 테스트/빌드:
./gradlew :app:testDebugUnitTest :app:assembleDebug - 수동 실행:
adb devices,./gradlew :app:installDebug,adb shell monkey -p kr.co.vividnext.sodalive.debug -c android.intent.category.LAUNCHER 1,adb shell run-as kr.co.vividnext.sodalive.debug am start --user 0 -n kr.co.vividnext.sodalive.debug/kr.co.vividnext.sodalive.live.room.LiveRoomActivity --el roomId 1
- 리뷰:
- 결과:
DELETE_CHAT,DELETE_CHAT_BY_USER수신 처리에서 발신자가 방장인지 검증하도록 반영했다.chatId가 비어있는 legacy STRING 채팅도targetUserId + message기준으로 단건 삭제를 시도하도록 보완했다.:app:testDebugUnitTest,:app:assembleDebug,:app:installDebug가 모두 성공했다.lsp_diagnostics는 Kotlin LSP 미설정 환경으로 실행 불가 응답을 반환했다.
-
2026-03-19
- 무엇/왜/어떻게: 사용자 요청에 따라 채팅 삭제 다이얼로그 본문 포맷에서 대괄호를 제거했다.
- 실행 명령/도구:
- 코드 반영:
apply_patch(app/src/main/res/values/strings.xml,app/src/main/res/values-en/strings.xml,app/src/main/res/values-ja/strings.xml) - 검증:
grep("screen_live_room_chat_delete_message_format"),lsp_diagnostics(strings*.xml),./gradlew :app:testDebugUnitTest :app:assembleDebug,adb devices,./gradlew :app:installDebug,adb shell monkey -p kr.co.vividnext.sodalive.debug -c android.intent.category.LAUNCHER 1,adb shell run-as kr.co.vividnext.sodalive.debug am start --user 0 -n kr.co.vividnext.sodalive.debug/kr.co.vividnext.sodalive.live.room.LiveRoomActivity --el roomId 1
- 코드 반영:
- 결과:
screen_live_room_chat_delete_message_format값이[%1$s]: [%2$s]에서%1$s: %2$s로 변경되어 대괄호 없이 노출된다.- 테스트/빌드/설치 및 앱/액티비티 실행이 성공했다.
lsp_diagnostics는 XML LSP 미설정 환경으로 실행 불가 응답을 반환했다.
-
2026-03-19
- 무엇/왜/어떻게: 삭제 이벤트 payload에서
targetChatId를 제거하고chatId단일 필드로 통일해, 삭제 송신/수신이 동일 키를 사용하도록 정리했다. - 실행 명령/도구:
- 코드 반영:
apply_patch(app/src/main/java/kr/co/vividnext/sodalive/live/room/chat/LiveRoomChatRawMessage.kt,app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt,docs/20260319_라이브룸채팅삭제기능구현계획.md) - 참조 확인:
grep("targetChatId"),grep("targetChatId", include="*.md") - 정적 진단:
lsp_diagnostics(LiveRoomActivity.kt, LiveRoomChatRawMessage.kt) - 테스트/빌드:
./gradlew :app:testDebugUnitTest :app:assembleDebug - 수동 실행:
adb devices,./gradlew :app:installDebug,adb shell monkey -p kr.co.vividnext.sodalive.debug -c android.intent.category.LAUNCHER 1,adb shell run-as kr.co.vividnext.sodalive.debug am start --user 0 -n kr.co.vividnext.sodalive.debug/kr.co.vividnext.sodalive.live.room.LiveRoomActivity --el roomId 1
- 코드 반영:
- 결과:
LiveRoomChatRawMessage에서targetChatId필드를 제거했고,DELETE_CHAT송신은chatId에 삭제 대상 채팅 ID를 담아 전송하도록 변경했다.DELETE_CHAT수신도message.chatId를 읽어 삭제하도록 변경했으며, 저장소 내targetChatId문자열 참조는 0건이다.:app:testDebugUnitTest,:app:assembleDebug,:app:installDebug가 성공했고 앱/액티비티 실행까지 확인했다.lsp_diagnostics는 Kotlin LSP 미설정 환경으로 실행 불가 응답을 반환했다.
- 무엇/왜/어떻게: 삭제 이벤트 payload에서