해당 글은 MoMyeon 프로젝트 진행 중 WebRTC에 대한 기본적인 개념을 공유하기 위해 작성한 글입니다.
WebRTC (Web Real-Time Communication): 웹을 위한 실시간 커뮤니케이션 - WebRTC
위키에서는 WebRTC를 ‘웹 브라우저끼리 실시간으로 통신할 수 있도록 설계된 API’ 라고 설명한다.
여기서 중요한 부분은 웹 브라우저끼리
통신한다는 점이다.
💡 RTP(Real Time Transport Protocol): UDP를 기반으로 하는 프로토콜. 인코딩/디코딩 정보, 패킷 손실 감지 등의 기능을 하며 UDP에 비해 성능이 높고 효율적이다.
Mesh: 브라우저가 모든 부담을 가진다.
MCU (Multipoint Control Unit): 중간에 있는 서버가 통신하는 브라우저의 데이터를 하나로 합쳐 전송한다.
SFU (Selective Forwarding Unit): 중간에 있는 서버가 각 브라우저의 요청/응답 스트림을 라우팅한다.
네트워크 방화벽 때문에 바로 직접적인 연결을 하기 어렵다.
public IP를 알기 위해 STUN, TURN 서버를 추가적으로 사용해야 한다!
*STUN (Session Traversal Utilities for NAT)
💡 NAT : 외부 IP 요청이 라우터를 경유하도록 하는 기술, 방화벽 기능
💡 Symmetric NAT : 외부에서 요청하는 서버마다 다른 포트를 매핑하는 NAT
ICE(Interactive Connectivity Establishment)
const rtcConfig = {
// ICE 서버의 설정. 내용으로 TURN, STUN 서버의 url이 들어간다.
iceServers: [
{
urls: ['turn:turn-test.ml:3478?transport=tcp'],
username: 'hello',
credential: 'world',
},
],
};
RTC PeerConnection
const peerConnection = new RTCPeerConnection(rtcConfig);
const rtcConfig = {
// ICE 서버의 설정. 내용으로 TURN, STUN 서버의 url이 들어간다.
iceServers: [
{
urls: ['turn:turn-test.ml:3478?transport=tcp'],
username: 'hello',
credential: 'world',
},
],
};
const peerConnection = new RTCPeerConnection(rtcConfig);
// pcRef는 RTC peer connection을 가리키는 useRef 객체이다.
const createNewOffer = async () => {
// 생략..
const newOffer = await pcRef.current.createOffer();
await pcRef.current.setLocalDescription(newOffer);
socketRef.current.emit('new-offer', {
offer: newOffer,
roomId: roomIdRef.current,
});
};
const handleRemoteOffer = async (offer) => {
// 생략..
const remoteOffer = new RTCSessionDescription(offer);
await pcRef.current.setRemoteDescription(remoteOffer);
const newAnswer = await pcRef.current.createAnswer(remoteOffer);
await pcRef.current.setLocalDescription(newAnswer);
socketRef.current.emit('new-answer', {
answer: newAnswer,
roomId: roomIdRef.current,
});
};
const handleRemoteAnswer = async (answer) => {
const remoteAnswer = new RTCSessionDescription(answer);
await pcRef.current.setRemoteDescription(remoteAnswer);
};
pcRef.current.addEventListener('icegatheringstatechange', () => {
if (
pcRef.current.signalingState === 'stable' &&
pcRef.current.iceGatheringState === 'complete'
) {
socketRef.current.emit('new-ice', {
iceCandidates: iceCandidateRef.current,
roomId: roomIdRef.current,
});
}
});
//
socket.on('remote-ice', ({ iceCandidates }) => {
iceCandidates.forEach((candidate) => {
pcRef.current.addIceCandidate(candidate);
});
});
pcRef.current.addEventListener('track', (event) => {
const [remoteStream] = event.streams;
remoteVideoRef.current.srcObject = remoteStream;
});
How to set up and configure your own TURN server using Coturn