Files
sodalive-android/docs/20260319_라이브룸채팅삭제기능구현계획.md

15 KiB

20260319_라이브룸채팅삭제기능구현계획.md

개요

  • 라이브 룸에서 방장(크리에이터) 전용 채팅 삭제 기능을 추가하기 위한 구현 계획 문서다.
  • 기능 범위는 단건 삭제(길게 누름 + 확인 다이얼로그)와 강퇴 연계 일괄 삭제(다이얼로그 없이 즉시 삭제)다.
  • 본 문서는 계획을 먼저 확정하고, 구현/검증 결과는 하단 검증 기록에 누적한다.

요구사항 해석(확정)

  • 채팅 삭제 권한은 방장(크리에이터)만 가진다.
  • 삭제 대상 채팅을 길게 누르면 삭제 확인 다이얼로그를 띄운다.
  • 다이얼로그 본문은 [닉네임]: [채팅 내용] 형식으로 노출한다.
  • 다이얼로그 버튼은 취소/삭제 두 가지다.
  • 삭제 확정 시 모든 사용자 화면에서 동일 채팅이 제거되어야 한다.
  • 강퇴 시에는 다이얼로그 없이 해당 유저의 채팅을 즉시 일괄 삭제하고, 이 결과가 모든 사용자에게 동기화되어야 한다.

현재 구조 조사 요약

  • 일반 채팅은 LiveRoomActivity.inputChat()에서 agora.inputChat(message)로 RTM STRING 발송하고, 수신 측은 onMessageEvent STRING 분기에서 LiveRoomNormalChat을 리스트에 append 한다.
  • 실시간 제어 이벤트(방정보 수정, 채팅 얼림, 룰렛 등)는 LiveRoomChatRawMessageType + agora.sendRawMessageToGroup() + onMessageEvent BINARY 분기 패턴을 이미 사용 중이다.
  • 강퇴는 LiveRoomActivity.kickOut() -> LiveRoomViewModel.kickOut()(API) + LiveRoomRequestType.KICK_OUT peer 메시지 전송으로 처리되며, 강퇴 대상 단말은 수신 후 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.onMessageEvent BINARY 분기에 NORMAL_CHAT, DELETE_CHAT, DELETE_CHAT_BY_USER 처리 로직을 추가한다.
  • STRING 수신 분기는 fallback 경로로 유지하고, 삭제 식별자가 없는 legacy 메시지 처리 원칙을 명시한다.

4) 방장 전용 long-press 삭제 UX

  • LiveRoomChatAdapterLiveRoomNormalChat long-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
    • 기존 sendRawMessageToGroup API로 처리 가능해 직접 수정 가능성은 낮다.

리스크 및 확인사항

  • 일반 채팅 송신 형식을 raw 중심으로 전환할 경우, 구버전/타플랫폼과의 프로토콜 호환성 리스크가 있다.
  • fallback STRING 메시지는 삭제 식별자 정합성이 약할 수 있으므로, 삭제 대상 식별 우선순위를 명확히 정의해야 한다.
  • kickOut API 호출은 현재 성공/실패 콜백을 사용하지 않으므로, “API 성공 후 삭제 이벤트 전파” 순서 보장이 필요하면 ViewModel 시그니처 확장을 검토해야 한다.
  • 동일 사용자의 동일 본문 반복 메시지 삭제 시 단건 선택 정합성(정확히 1건 삭제) 검증이 필요하다.

외부 레퍼런스(요약)

검증 기록

  • 기록 템플릿(후속 누적):

    • YYYY-MM-DD
      • 무엇/왜/어떻게:
      • 실행 명령/도구:
        • 명령 또는 사용 도구
      • 결과:
  • 2026-03-19

    • 무엇/왜/어떻게: 라이브룸 채팅 삭제 구현 전에 기존 채팅 송수신/강퇴/어댑터 구조를 병렬 탐색하고, 요구사항을 충족하는 상세 구현 계획(예상 수정 파일/추가 항목/검증 항목)을 문서화했다.
    • 실행 명령/도구:
      • task(subagent_type="explore") x3
      • task(subagent_type="librarian") x2
      • grep("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 미설정 환경으로 실행 불가 응답을 반환했다.