월패드 현관 통화 기능을 개발하면서 현관카메라, 월패드, 주방TV, 욕실폰이 하나의 통화 세션을 공유하는 과정에서 예상하지 못한 화면 전환 문제가 발생했습니다.
현관에서 호출이 들어오면 월패드에는 방문자 영상이 표시되고, 동시에 주방TV와 욕실폰에도 호출이 전달되도록 구성되어 있었습니다.
문제는 주방TV 또는 욕실폰에서 통화를 수락하는 순간 발생했습니다.
실제 현관카메라와 주방TV 또는 욕실폰 사이의 통화는 정상적으로 유지되고 있었지만, 월패드에 표시되던 CallFragment가 갑자기 종료되고 홈 화면으로 이동했습니다.
처음에는 카메라 프레임 처리나 메모리 부족, Surface 종료 문제를 의심했지만 로그와 통화 세션 상태를 추적한 결과 원인은 화면 전환 상태 처리 로직에 있었습니다.
1. 문제 증상
발생했던 현상은 다음과 같았습니다.
- 현관카메라 호출 수신 정상
- 월패드 방문자 영상 출력 정상
- CallFragment 진입 정상
- 주방TV/욕실폰 호출 수신 정상
- 주방TV 또는 욕실폰에서 통화 수락 정상
- 현관카메라와 주방TV/욕실폰 통화 정상
- 실제 DoorCallSession은 Talking 상태 유지
- 월패드 CallFragment만 종료
- 월패드가 홈 화면으로 이동
특히 화면이 종료되었음에도 실제 현관 통화 세션은 계속 유지되고 있다는 점이 중요한 단서였습니다.
2. 현관카메라와 주방TV/욕실폰 통화 구조
월패드 시스템에서는 현관카메라 호출이 발생하면 월패드뿐만 아니라 주방TV와 욕실폰에도 호출을 전달합니다.
코드 내부에서는 주방TV와 욕실폰을 SubPhone으로 관리하고 있습니다.
전체 흐름은 다음과 같습니다.
현관카메라
→ 월패드
→ 주방TV
→ 욕실폰
현관에서 call_request가 발생하면 월패드 CallFragment가 표시되고 동시에 BaseMCU를 통해 주방TV와 욕실폰으로 호출 패킷을 전달합니다.
주방TV 또는 욕실폰이 통화를 수락하면 DoorCallSession의 Actor가 SubPhone으로 변경됩니다.
3. 처음 의심했던 카메라와 메모리 문제
문제가 발생하던 시점에는 현관 카메라 1920x1080 영상을 처리하면서 TVOUT 출력도 동시에 수행하고 있었습니다.
로그에서는 GC 발생과 메모리 사용량 증가도 확인되었습니다.
Background young concurrent copying GC
84MB / 108MB
또한 TVOUT에서는 1920x1080 영상을 720x480으로 변환하여 주방TV/욕실폰 출력용 프레임을 계속 전송하고 있었습니다.
이 때문에 처음에는 카메라 프레임 ByteArray 생성이나 메모리 부족으로 Fragment 또는 Surface가 비정상적으로 종료되는 것으로 의심했습니다.
프레임 버퍼 재사용, NV21 크기 검증, Camera release 순서 등을 점검했지만 화면이 홈으로 이동하는 직접적인 원인은 아니었습니다.
4. 통화 상태 로그 확인
주방TV 또는 욕실폰에서 통화를 수락한 시점의 로그를 확인했습니다.
DoorCallSession -> phase=Talking actor=SubPhone(id=42)
이 상태는 통화 종료가 아니라 현재 통화 주체가 주방TV 또는 욕실폰으로 변경되었다는 의미였습니다.
정상적인 통화 흐름은 다음과 같습니다.
현관카메라 call_request
→ Ringing
→ 주방TV/욕실폰 통화 수락
→ Talking / Actor.SubPhone
→ 현관카메라와 주방TV/욕실폰 통화 진행
→ talk_off 또는 end_result
→ Idle
즉 Talking / Actor.SubPhone은 정상적인 통화 진행 상태였습니다.
5. CallFragment가 종료되는 과정
로그를 계속 확인하자 주방TV 또는 욕실폰이 통화를 수락한 직후 다음과 같은 흐름이 발생했습니다.
Talking actor=SubPhone
→ wallpad preview close requested
→ Call timer 해제
→ SIP/Surface 해제
→ 월패드 카메라 반납
→ CallFragment onDestroyView
→ CallViewModel onCleared
→ 홈 화면 이동
처음에는 onDestroyView가 문제의 원인이라고 생각했지만 실제로는 반대였습니다.
Navigation에서 CallFragment가 먼저 제거되었기 때문에 결과적으로 onDestroyView가 호출되고 있었습니다.
6. WallpadActivity 상태 처리 문제 확인
최종적으로 문제는 WallpadActivity의 observeDoorCallSession 로직에서 확인되었습니다.
기존 코드는 DoorCallSession이 Talking 또는 Monitoring 상태이고 Actor가 SubPhone이면 CallFragment를 즉시 종료하도록 되어 있었습니다.
기존 코드:
DoorCallSession.Phase.Talking,
DoorCallSession.Phase.Monitoring -> {
if (subPhoneOwns && doorCallShowing && onDoorScreen) {
navController.popBackStack()
doorCallShowing = false
}
}
이 로직 때문에 주방TV 또는 욕실폰이 통화를 수락하는 순간 월패드 CallFragment가 Navigation Stack에서 제거되고 홈 화면으로 이동했습니다.
7. Actor와 Phase의 역할 분리
DoorCallSession에는 Phase와 Actor가 존재합니다.
Phase
- Idle
- Ringing
- Monitoring
- Talking
Actor
- Wallpad
- SubPhone
여기서 Actor.SubPhone은 통화가 끝났다는 의미가 아닙니다.
현재 현관카메라 통화의 주체가 주방TV 또는 욕실폰이라는 의미입니다.
반면 Phase.Idle은 실제 현관 통화 세션이 종료된 상태입니다.
기존 구현에서는 Actor가 SubPhone으로 변경된 것을 화면 종료 조건처럼 사용한 것이 문제였습니다.
8. 수정 내용
Talking 또는 Monitoring 상태에서 SubPhone이 통화를 소유하더라도 CallFragment를 종료하지 않도록 변경했습니다.
수정 코드:
DoorCallSession.Phase.Talking,
DoorCallSession.Phase.Monitoring -> {
if (subPhoneOwns) {
WpLogger.d(
"[DOOR_CALL_UI] 서브폰이 통화 소유 중이지만 " +
"CallFragment 유지 phase=${session.phase} actor=${session.actor}"
)
}
if (onDoorScreen) {
doorCallShowing = true
}
}
기존의 navController.popBackStack() 호출을 제거하고, 실제 화면 종료는 DoorCallSession.Phase.Idle에서만 수행하도록 유지했습니다.
9. 수정 후 동작
수정 후 통화 흐름은 다음과 같이 변경되었습니다.
현관카메라 호출
→ 월패드 CallFragment 표시
→ 주방TV/욕실폰 호출 전달
→ 주방TV 또는 욕실폰 통화 수락
→ DoorCallSession Talking / Actor.SubPhone
→ 월패드 CallFragment 유지
→ 현관카메라와 주방TV/욕실폰 통화 진행
→ talk_off / end_result
→ DoorCallSession Idle
→ CallFragment 종료
→ 홈 화면 이동
이제 주방TV나 욕실폰에서 통화를 수락하더라도 월패드 화면이 임의로 종료되지 않습니다.
10. 이번 문제에서 확인한 점
이번 문제를 통해 여러 장치가 하나의 통화 세션에 참여하는 구조에서는 통화 주체와 통화 상태를 명확하게 구분해야 한다는 점을 확인했습니다.
월패드, 현관카메라, 주방TV, 욕실폰처럼 여러 장치가 동시에 연동되는 환경에서는 Actor가 변경되는 것을 통화 종료로 판단하면 안 됩니다.
Actor는 현재 통화를 누가 수행하고 있는지를 나타내고, Phase는 실제 통화 세션의 진행 상태를 나타내도록 역할을 분리해야 합니다.
또한 Fragment가 갑자기 종료되는 문제를 분석할 때 Fragment 내부 코드만 확인하기보다 Activity나 전역 상태 Observer에서 Navigation을 발생시키는 코드까지 함께 확인하는 것이 중요했습니다.
이번 문제 역시 처음에는 카메라와 메모리 문제를 의심했지만 실제 원인은 WallpadActivity의 DoorCallSession 상태 처리에서 실행되던 popBackStack이었습니다.
수정 이후 현관카메라, 월패드, 주방TV, 욕실폰 간 통화 상태가 정상적으로 유지되고 실제 통화 종료 시점에만 월패드 화면이 종료되도록 안정화할 수 있었습니다.