Skip to content

WebRTC 컴포넌트 모듈화 이후 화상연결 장애 #75

@Tae4an

Description

@Tae4an

문제 배경

좀 더 원할한 디버깅을 위해서 기존의 모놀리식 WebRTCComponent를 여러 hook으로 분리하는 모듈화 작업을 진행했습니다. 모듈화 이전에는 정상 작동하던 화상연결이 모듈화 이후 여러 문제를 발생시켰습니다.

발생한 문제들 (시간순)

1차 문제: 화면 자체가 표시되지 않음

// 참가자 정보는 정상적으로 업데이트됨
[Participants] Updated list: (2) [{...}, {...}]
// 하지만 remoteStreams는 비어있음
VideoGrid render: {remoteStreamsArray: Array(0)}

원인 분석

  1. handleTrack 함수에서 참가자의 senderId를 못 받아오는 현상 발견
  2. 모듈화 과정에서 WebRTC peer connection의 메타데이터(senderId) 전달 누락

시도한 해결방법

  1. event에 직접 senderId 할당 시도
peerConnection.current.ontrack = (event) => {
    event.senderId = peerConnection.current.senderId;  // ❌ 실패: event는 읽기 전용
    onTrack(event);
};
  1. event 복제 후 데이터 추가 시도
peerConnection.current.ontrack = (event) => {
    const enhancedEvent = { ...event };  // ❌ 실패: 얕은 복사로 인한 문제
    enhancedEvent.senderId = peerConnection.current.senderId;
    onTrack(enhancedEvent);
};
  1. 새로운 객체로 감싸서 전달 (최종 해결)
peerConnection.current.ontrack = (event) => {
    const enhancedEvent = {
        ...event,
        senderId: peerConnection.current.senderId,
        streams: event.streams,
        track: event.track
    };
    onTrack(enhancedEvent);
};

2차 문제: 검은 화면으로 표시

1차 문제 해결 후, 화면은 표시되지만 검은색으로만 보이는 현상 발생

원인 분석

로그 확인 결과:

// 첫 번째 스트림 생성 시
[RemoteStream] New stream created: {
    id: '6ff418c8-...',
    active: true,
    tracks: Array(2)
}

// 두 번째 스트림 생성 시
[RemoteStream] New stream created: {
    id: '0c6c40f2-...',
    active: false,  // 문제 발견
    tracks: Array(2)
}
  • 동일 트랙으로 여러 스트림이 생성되며 활성 상태가 손실되는 현상 확인

시도한 해결방법

  1. enabled 상태만 변경
stream.getTracks().forEach(track => {
    track.enabled = true;
}); // ❌ 실패: 스트림 자체는 여전히 비활성
  1. 새 스트림에 트랙 직접 추가
const newStream = new MediaStream();
stream.getTracks().forEach(track => {
    newStream.addTrack(track);
}); // ❌ 실패: 참조 문제로 인해 동일 현상 발생
  1. 트랙 클론 후 새 스트림 생성 (최종 해결)
const updateRemoteStream = useCallback((senderId, stream, userName) => {
    if (!senderId || !stream) return;

    setRemoteStreams(prev => {
        const newStreams = new Map(prev);
        
        // 기존 스트림 정리
        const existingStream = newStreams.get(senderId);
        if (existingStream?.stream) {
            existingStream.stream.getTracks().forEach(track => track.stop());
        }

        // 새 스트림 생성
        const clonedTracks = stream.getTracks().map(track => track.clone());
        const newStream = new MediaStream(clonedTracks);
        
        // 모든 트랙 활성화
        clonedTracks.forEach(track => track.enabled = true);

        newStreams.set(senderId, {
            stream: newStream,
            userName
        });

        return newStreams;
    });
}, []);

교훈

  1. 모듈화 시 데이터 흐름 주의

    • 컴포넌트 분리 시 메타데이터 전달 계획 필수
    • 상태 관리 및 이벤트 전달 구조 명확히 설계 필요
  2. WebRTC 객체의 특성 이해

    • Event 객체의 불변성 주의
    • MediaStream과 MediaStreamTrack의 생명주기 이해 중요
    • 참조 vs 복사 차이 이해 필요
  3. 디버깅 용이성

    • 모듈화로 인해 문제 발생 지점 파악이 더 수월
    • 각 모듈별 상세 로깅의 중요성
  4. 코드 리팩토링 전략

    • 큰 변화는 단계적으로 진행
    • 각 단계별 테스트 필요성
    • 이전 상태로의 롤백 계획 필요

향후 과제

  • 3인 이상 다중 연결 시 발생하는 문제 해결 필요
  • WebSocket 연결 불안정 시 재연결 로직 개선 필요
  • ICE Candidate 처리 로직 최적화 필요

#WebRTC #Refactoring #BugFix #P2P

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions