16 KiB
16 KiB
20260324 라이브룸 캡쳐/녹화 보안 및 오디오 차단 통합 계획
작업 체크리스트
LiveRoomViewV2의 캡쳐/녹화 감지 및 기존 음소거 제어 포인트 확인- 캡쳐/녹화 시작 시 화면 검정 오버레이 적용
- 캡쳐/녹화 시작 시 음소거 강제 적용
- 종료 시 화면/음소거 상태 복원 로직 점검
- 진단/빌드/테스트 검증 수행 및 기록
수용 기준 (Acceptance Criteria)
UIScreen.main.isCaptured == true상태에서 라이브룸 주요 콘텐츠 위에 검정 화면이 표시된다.- 캡쳐/녹화 상태 진입 시 스피커 출력이 음소거된다.
- 내가 스피커 역할일 경우, 캡쳐/녹화 상태 진입 시 마이크도 음소거된다.
- 캡쳐/녹화 상태 해제 시 사용자의 기존 음소거 상태를 유지/복원한다.
후속 작업 체크리스트 (화면 캡쳐 미차단)
- 화면 녹화와 화면 캡쳐의 동작 차이를 iOS 시스템 제약 관점에서 확인
- 코드베이스 내 캡쳐 차단/보호 패턴 유무 조사
- 가능한 최소 수정안 적용 (캡쳐 시 검정 처리 보강)
- LSP/빌드/테스트 및 수동 QA 결과 기록
후속 수용 기준 (Pass/Fail)
- 원인: 화면 캡쳐 시 기존 로직이 검정 화면을 만들지 못한 이유를 코드와 플랫폼 제약으로 설명 가능
- 조치: 캡쳐 시점에도 검정 처리(또는 동등한 보호)가 적용되는 코드가 반영됨
- 안정성: 수정 파일
lsp_diagnostics무오류 - 회귀:
SodaLive,SodaLive-devDebug build 성공
검증 기록
1차 검증 (2026-03-24)
- 무엇/왜/어떻게:
- 무엇:
LiveRoomViewV2에 캡쳐/녹화 감지, 검정 오버레이, 음소거 강제/복원 로직을 추가하고 회귀를 확인. - 왜: 요청사항(검정 캡쳐 + 음소거) 충족과 기존 동작 안정성 보장.
- 어떻게: LSP 진단, 스킴 빌드, 테스트 액션 실행 결과를 기록하고 수동 확인 가능 범위를 점검.
- 무엇:
- 실행 명령:
lsp_diagnostics(filePath: SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
lsp_diagnostics:No diagnostics foundSodaLive빌드:** BUILD SUCCEEDED **(병렬 빌드 시 1회build.db locked발생 후 단독 재실행으로 성공)SodaLive-dev빌드:** BUILD SUCCEEDED **SodaLive테스트:Scheme SodaLive is not currently configured for the test action.SodaLive-dev테스트:Scheme SodaLive-dev is not currently configured for the test action.- 수동 QA: 현재 CLI/헤드리스 환경에서는 실제 기기/시뮬레이터에서 화면 캡쳐·녹화 시작 이벤트를 직접 조작하는 E2E 검증이 제한되어, 코드 경로(
UIScreen.capturedDidChangeNotification수신 시 검정 오버레이 + 음소거 적용, 해제 시 복원)까지 확인.
2차 검증 (2026-03-24) — 화면 캡쳐 미차단 후속 대응
- 무엇/왜/어떻게:
- 무엇: 화면 캡쳐가 그대로 저장되는 원인을 확인하고,
LiveRoomViewV2전체를 보안 컨테이너(isSecureTextEntry기반)로 감싸 캡쳐 보호를 보강. - 왜:
UIScreen.main.isCaptured/capturedDidChangeNotification은 녹화·미러링 상태 변화에는 반응하지만, 스크린샷은 사후 알림(userDidTakeScreenshotNotification)만 가능해 기존 오버레이 방식으로는 캡쳐본을 검정으로 바꾸지 못하기 때문. - 어떻게: 내부 패턴 탐색(explore) + iOS 공식 동작 조사(librarian)로 원인을 확정한 뒤,
ScreenCaptureSecureContainer를LiveRoomViewV2body 루트에 적용.
- 무엇: 화면 캡쳐가 그대로 저장되는 원인을 확인하고,
- 실행 명령:
lsp_diagnostics(filePath: SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build && xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test; xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
lsp_diagnostics:No diagnostics found- 빌드:
SodaLive,SodaLive-dev모두** BUILD SUCCEEDED ** - 테스트:
Scheme SodaLive is not currently configured for the test action.,Scheme SodaLive-dev is not currently configured for the test action. - 수동 QA: 현재 CLI 환경에서는 라이브룸 실화면에서 직접 스크린샷/녹화를 트리거하는 E2E 자동화가 불가하여, 기기/시뮬레이터에서 최종 캡쳐 결과 확인이 필요함.
- 참고:
isSecureTextEntry기반 전체 뷰 보호는 실무적으로 사용되는 우회 방식이며, iOS 버전별 동작 차이가 있을 수 있어 실제 단말 검증을 필수로 유지.
후속 작업 체크리스트 (확정 이슈/보강 통합)
LiveRoomViewV2캡쳐 보호 상태에서.overlay이펙트 렌더링 차단- 캡쳐 보호 오버레이가 터치를 통과시키지 않도록 입력 차단 유지
- 캡쳐 해제 시 마이크 복원 로직의 role 의존 조건 제거/보완
ScreenCaptureSecureView.setup()secure 컨테이너 탐색 흐름 점검- secure 컨테이너 탐색 실패 시 fail-open(
UITextField직접 사용) 제거 및 fail-closed 처리 적용 - 관련 진단/빌드/테스트 및 수동 QA 결과 누적 기록
후속 수용 기준 (확정 이슈/보강 Pass/Fail)
- Pass:
isScreenCaptureProtected == true일 때 검정 보호 레이어 최상단 노출 상태에서 하트/파티클 오버레이가 렌더링되지 않는다.- QA:
.overlay(alignment: .center)내부를if !isScreenCaptureProtected로 가드해 보호 상태에서 오버레이 뷰 트리를 생성하지 않음을 코드 레벨로 확인.
- QA:
- Pass:
isScreenCaptureProtected == false일 때 기존 하트/파티클 오버레이 동작이 유지된다.- QA: 보호 해제 상태에서 기존
WaterHeartView,bigHeartParticles렌더 경로가 동일하게 남아 있음을 확인.
- QA: 보호 해제 상태에서 기존
- Pass:
isScreenCaptureProtected == true일 때 하위 UI 상호작용이 차단된다.- QA: 오버레이에
.allowsHitTesting(true)가 적용되어 입력을 오버레이가 수신하는지 확인.
- QA: 오버레이에
- Pass: 캡쳐 해제 시 role 상태와 무관하게 강제 마이크 mute 복원이 누락되지 않는다.
- QA:
releaseForcedCaptureMute()에서shouldRestoreMicMuteAfterCapture경로가 role 조건 없이 동작하는지 확인.
- QA:
- Pass: secure 컨테이너 탐색 실패 시 일반 계층으로 폴백하지 않고 fail-closed(검정 오버레이 유지)로 동작한다.
- QA:
secureTextField.subviews.first ?? secureTextField폴백 제거 및 secure canvas 탐색 실패 시 콘텐츠 미탑재/오버레이 활성화를 코드 레벨로 확인.
- QA:
3차 검증 (2026-03-24) — 캡쳐 보호 오버레이 렌더 차단
- 무엇/왜/어떻게:
- 무엇: 캡쳐 보호 상태에서도 오버레이 하트/파티클이 노출될 수 있는 경로를 차단.
- 왜: 보호 상태의 화면 노출 가능성을 제거해 보안/정합성 리스크를 낮추기 위해.
- 어떻게:
LiveRoomViewV2.swift의.overlay렌더링을isScreenCaptureProtected와 연동해 보호 시 비활성화하고, 정적 진단/빌드/테스트 액션을 검증.
- 실행 명령:
lsp_diagnostics(filePath: /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift, severity: all)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build && xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test; xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
lsp_diagnostics:No diagnostics found- 빌드:
SodaLive,SodaLive-dev모두** BUILD SUCCEEDED ** - 테스트: 두 스킴 모두
is not currently configured for the test action로 테스트 액션 미구성 확인
4차 검증 (2026-03-24) — 캡쳐 보호 확정 이슈 패치
- 무엇/왜/어떻게:
- 무엇: 캡쳐 보호 중 입력 차단과 마이크 복원 누락 이슈를 패치.
- 왜: 확정된 중간 심각도 안정성 이슈를 제거하기 위해.
- 어떻게: 코드 수정 후 LSP 진단, 스킴 빌드, 테스트 액션, 수동 QA 가능 범위를 기록.
- 실행 명령:
lsp_diagnostics(filePath: /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift, severity: all)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build && xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test; xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" testRead(SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift:850~874, 1248~1271)
- 결과:
lsp_diagnostics:No diagnostics found- 빌드:
SodaLive,SodaLive-dev모두** BUILD SUCCEEDED ** - 테스트:
Scheme SodaLive is not currently configured for the test action.,Scheme SodaLive-dev is not currently configured for the test action. - 수동 QA(가능 범위):
- 오버레이 경로 확인:
if isScreenCaptureProtected { Color.black ... .allowsHitTesting(true) } - 복원 경로 확인:
if shouldRestoreMicMuteAfterCapture { if viewModel.isMute { viewModel.toggleMute() } ... } - 현재 CLI/헤드리스 환경에서는 실제 라이브룸 진입 후 캡쳐·녹화 이벤트를 조작하는 디바이스 E2E 검증이 제한됨.
- 오버레이 경로 확인:
5차 검증 (2026-03-24) — secure 컨테이너 폴백 fail-closed 보강
- 무엇/왜/어떻게:
- 무엇:
ScreenCaptureSecureView에서secureTextField.subviews.first ?? secureTextField폴백을 제거하고, secure canvas 식별 실패 시 검정 fail-closed 오버레이를 유지하도록 변경. - 왜: secure 렌더링 컨테이너 탐색 실패 시 일반 계층으로 콘텐츠가 붙어 캡쳐 보호가 무력화될 수 있는 fail-open 동작을 차단하기 위해.
- 어떻게:
CanvasView클래스명 기반 secure 컨테이너 탐색 + 실패 시ERROR_LOG1회 기록 및 콘텐츠 미탑재 처리.
- 무엇:
- 실행 명령:
lsp_diagnostics(filePath: SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build && xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test; xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
lsp_diagnostics:No diagnostics found- 빌드:
SodaLive,SodaLive-dev모두** BUILD SUCCEEDED ** - 테스트:
Scheme SodaLive is not currently configured for the test action.,Scheme SodaLive-dev is not currently configured for the test action.
통합 이력 (중복 문서 병합)
- 본 문서는 아래 연속 수정 문서를 하나로 통합한 기준 문서다.
docs/20260324_캡쳐보호초기진입음소거보완.mddocs/20260324_라이브녹화오디오차단수정.md
통합 작업 체크리스트 (추가)
LiveRoomViewModel에 idempotent 음소거 setter(setMute,setSpeakerMute) 추가- 캡쳐 보호 로직에서 toggle 호출을 setter 호출로 전환
- 라이브룸 진입 시 Agora 엔진 초기화와 캡쳐 보호 적용 순서 보강
- 라이브 캡쳐/녹화 시 오디오 녹음 경로 원인 분석 (코드+외부 문서)
- 기존 캡쳐 보호 음소거 로직의 연결 타이밍/상태 전이 검증
- 최소 수정으로 오디오 차단 누락 경로 패치
통합 수용 기준 (추가)
- 캡쳐가 이미 활성화된 상태로 라이브룸 진입해도 원격 오디오 음소거가 누락되지 않는다.
- 캡쳐 보호 진입/해제 시 마이크·스피커 음소거 상태가 토글 누적 없이 일관되게 유지/복원된다.
- 영상 보호(검정/보안 컨테이너) 상태에서 라이브 오디오가 녹화에 남지 않는다.
- 초기 진입/연결 완료/역할 전환 시점 모두에서 캡쳐 보호 음소거가 일관 적용된다.
- 변경 파일
lsp_diagnostics무오류 및SodaLive/SodaLive-devDebug build 성공.
통합 검증 기록 (추가)
6차 검증 (2026-03-24) — 초기 진입 캡쳐 보호 음소거 보완
- 무엇/왜/어떻게:
- 무엇:
LiveRoomViewModel에 상태 기반 음소거 setter를 추가하고,LiveRoomViewV2캡쳐 보호 경로를 toggle 호출에서 setter 호출로 전환. - 왜: 캡쳐가 이미 켜진 상태로 화면 진입 시 Agora 엔진 초기화 타이밍 때문에 스피커 강제 음소거가 누락될 수 있는 경로를 제거하기 위해.
- 어떻게:
onAppear에서initAgoraEngine()호출을 선행시키고, 캡쳐 보호 진입/복원 및 role 변경 경로를 idempotent setter 기반으로 정리.
- 무엇:
- 실행 명령:
lsp_diagnostics(filePath: /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/Live/Room/LiveRoomViewModel.swift, severity: all)lsp_diagnostics(filePath: /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift, severity: all)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build && xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test; xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
lsp_diagnostics: 두 파일 모두No diagnostics found- 빌드:
SodaLive,SodaLive-dev모두** BUILD SUCCEEDED ** - 테스트:
Scheme SodaLive is not currently configured for the test action.,Scheme SodaLive-dev is not currently configured for the test action. - 수동 QA: 현재 CLI/헤드리스 환경에서는 실제 단말에서 화면 녹화/미러링 상태로 진입하는 E2E 조작이 제한되어 코드 경로 기준으로 검증함.
7차 검증 (2026-03-24) — 녹화 오디오 차단 보강
- 무엇/왜/어떻게:
- 무엇:
agoraConnectSuccess시점에 현재 음소거 상태(isSpeakerMute,isMute)를 Agora 엔진에 재적용하고,applyScreenCaptureProtection(isCaptured: true)에서 role 조건을 제거해 캡쳐 상태면 마이크를 선제 음소거하도록 보강. - 왜: 캡쳐 상태에서 선행 음소거 이후 채널 join/rejoin 기본 구독 복귀, listener→speaker 전환 시점의 짧은 오디오 누출 창을 동시에 차단하기 위해.
- 어떻게: 코드베이스 탐색(explore 2건) + 외부 문서 조사(librarian 1건)로 원인을 확정하고 최소 패치 적용.
- 무엇:
- 실행 명령:
lsp_diagnostics(filePath: /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/Live/Room/LiveRoomViewModel.swift, severity: all)lsp_diagnostics(filePath: /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swift, severity: all)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build && xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test; xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" testRead(SodaLive/Sources/Live/Room/LiveRoomViewModel.swift:519~533)grep("func agoraConnectSuccess|if isSpeakerMute|if isMute", LiveRoomViewModel.swift)grep("func applyScreenCaptureProtection|if !viewModel.isMute|shouldRestoreMicMuteAfterCapture = true", LiveRoomViewV2.swift)
- 결과:
lsp_diagnostics: 두 파일 모두No diagnostics found- 빌드:
SodaLive,SodaLive-dev모두** BUILD SUCCEEDED ** - 테스트:
Scheme SodaLive is not currently configured for the test action.,Scheme SodaLive-dev is not currently configured for the test action. - 수동 QA(가능 범위):
- 캡쳐 진입 시 role과 무관한 마이크 선제 음소거 경로(
if !viewModel.isMute) 확인. - 연결 완료 시
isSpeakerMute/isMute상태 재적용 경로 확인. - CLI 한계로 실기기 녹화 E2E는 별도 디바이스 수동 검증 필요.
- 캡쳐 진입 시 role과 무관한 마이크 선제 음소거 경로(