@@ -103,7 +103,6 @@ export default function useVoiceCall({ rooms = [], activeRoom = null, connected,
103103 useEffect ( ( ) => { myIdRef . current = myId } , [ myId ] )
104104 useEffect ( ( ) => { activeRoomIdRef . current = activeRoom ?. id } , [ activeRoom ] )
105105 useEffect ( ( ) => { activeOtherRef . current = activeOtherParticipant } , [ activeOtherParticipant ] )
106- useEffect ( ( ) => { enabledRef . current = enabled } , [ enabled ] )
107106
108107 const setCall = useCallback ( ( updater ) => {
109108 setCallState ( ( prev ) => {
@@ -353,7 +352,10 @@ export default function useVoiceCall({ rooms = [], activeRoom = null, connected,
353352 startedAt : null ,
354353 } )
355354
356- // 띠리링~ 벨소리 재생 시작
355+ // 띠리링~ 벨소리 재생 시작 (기존 벨소리가 있다면 중지하여 중복 재생 및 누수 방지)
356+ if ( ringtoneRef . current ) {
357+ ringtoneRef . current ( )
358+ }
357359 ringtoneRef . current = startRingtone ( )
358360
359361 // 시스템 알림(OS 연동) 띄우기 (전화기 아이콘 및 진동 효과 등)
@@ -424,6 +426,7 @@ export default function useVoiceCall({ rooms = [], activeRoom = null, connected,
424426 if ( signal . reason === 'BUSY' ) msg = '상대가 다른 통화 중입니다.'
425427 else if ( signal . reason === 'MIC_PERMISSION_DENIED' ) msg = '상대방의 마이크 접근 권한/기기 문제로 연결이 취소되었습니다.'
426428 else if ( signal . reason === 'DISABLED' ) msg = '상대방이 보이스톡 수신 알림을 꺼두어 연결할 수 없습니다.'
429+ else if ( signal . reason === 'TIMEOUT' ) msg = '응답 시간이 초과되어 통화가 취소되었습니다.'
427430
428431 cleanupLocal ( { ...INITIAL_CALL , error : msg } )
429432 return
@@ -455,6 +458,31 @@ export default function useVoiceCall({ rooms = [], activeRoom = null, connected,
455458
456459 useEffect ( ( ) => ( ) => cleanupLocal ( ) , [ cleanupLocal ] )
457460
461+ // 1분(60초) 무응답 자동 종료 타이머
462+ useEffect ( ( ) => {
463+ let timeoutId
464+ if ( call . status === 'incoming' || call . status === 'outgoing' || call . status === 'connecting' ) {
465+ timeoutId = setTimeout ( ( ) => {
466+ const current = callRef . current
467+ if ( current . status !== 'idle' ) {
468+ publishSignal ( 'REJECT' , { reason : 'TIMEOUT' } )
469+ }
470+ cleanupLocal ( { ...INITIAL_CALL , error : '응답 시간이 초과되어 보이스톡이 자동 종료되었습니다.' } )
471+ } , 60 * 1000 )
472+ }
473+ return ( ) => {
474+ if ( timeoutId ) clearTimeout ( timeoutId )
475+ }
476+ } , [ call . status , cleanupLocal , publishSignal ] )
477+
478+ // 보이스톡 설정 비활성화 시 통화 강제 종료
479+ useEffect ( ( ) => {
480+ enabledRef . current = enabled
481+ if ( ! enabled && callRef . current . status !== 'idle' ) {
482+ cleanupLocal ( )
483+ }
484+ } , [ enabled , cleanupLocal ] )
485+
458486 return {
459487 ...call ,
460488 remoteAudioRef,
0 commit comments