2023년도에 Express와 Socket.IO를 활용한 실시간 랜덤매칭 화상채팅 앱 프로젝트를 진행한적이 있습니다.
이 과정에서 WebRTC를 이용하여 클라이언트 간 실시간 P2P 화상채팅을 구현했고 이를 위해 필수적인 개념인 WebRTC, Signaling(Offer, Answer, ICE) 에 대해 학습하고 적용했습니다.
이번 포스팅에서는 당시의 경험을 되새겨볼겸 WebRTC와 Signaling 개념을 정리해보겠습니다.
Socket.IO
클라이언트와 서버 간의 실시간 양방향 통신을 지원하는 라이브러리입니다.
웹소켓(WebSocket) 프로토콜을 직접 사용할 수도 있지만 Socket.IO는 이를 래핑해서 이벤트 기반통신을 쉽게 구현할 수 있게 해줍니다.
이 라이브러리를 통해 실시간으로 데이터를 쉽게 주고받을 수 있고 채팅, 실시간 알림, 게임 등 다양한 분야에서 사용됩니다.
WebRTC (Web Real-Time Communication)
브라우저 간 P2P(피어 투 피어) 연결을 통해 오디오, 비디오 및 데이터 스트림을 직접 주고받을 수 있는 기술입니다.
하지만 브라우저끼리 직접 연결을 설정하는 과정에서
같은 다양한 네트워크 이슈가 발생할 수 있기 때문에 Signaling (시그널링) 과정이 필요합니다.
즉, Signaling 없이는 브라우저 끼리 연결이 불가능하기 때문에 WebRTC가 전송할 데이터 스트림을 브라우저 끼리 직접 전달하기 위해선 Signaling을 통해 두 클라이언트가 연결 정보를 주고 받아야합니다.
Signaling은 두 브라우저(로컬 - 원격) 간의 연결 정보를 교환하는 과정입니다.
WebRTC는 브라우저 간 직접(P2P) 미디어 스트림을 주고받는 기술이지만,
서로의 네트워크 환경을 모르기 때문에 직접 연결할 수 없고, 그렇기 때문에 Signaling 서버를 통해 연결 정보를 교환해야 합니다.
이때 3단계의 Signaling 과정을 수행해서
- 연결 설정(Offer/Answer),
- 네트워크 경로 탐색(ICE Candidate 교환),
- 연결 확정(Handshake)
두 브라우저가 직접 연결될 수 있습니다.
Caller(통화를 거는 측)가 자신의 미디어 정보(SDP)를 포함한 Offer를 생성해 상대방(Answerer)에게 전송합니다.
Answerer는 Offer를 수신한 후, 자신의 미디어 정보가 포함된 SDP Answer를 반환합니다.
SDP (Session Description Protocol)란?
SDP는 WebRTC에서 연결 설정을 위한 프로토콜입니다.
브라우저가 사용할 오디오, 비디오 코덱, 네트워크 정보 등을 포함하는데
쉽게 집 주소와 어떤 데이터를 주고받을지 정하는 계약서라고 생각하면 됩니다.
Caller는 SDP Offer를 생성하고, Answerer는 이에 대한 응답으로 SDP Answer를 생성합니다.
예시)
Caller: "제 연결 정보는 이렇습니다" (SDP Offer 생성 및 전송)
Answerer: "넹 그렇게 알아둘게요ㅋㅋ 제 연결 정보는 이거에요" (받은 SDP Offer 저장 및 SDP Answer 생성 및 전송)
Caller: "아 그렇군요 저도 저장해둘게요" (받은 SDP Answer 저장)
(상호간의 Offer, Answer 교환은 Socket.IO 서버가 중개하게 됨)
이제 이전 단계에서 서로가 누군지 알게된 두 브라우저 간 P2P 연결을 설정하려면 서로의 네트워크 정보를 알아야 합니다.
이때 ICE (Interactive Connectivity Establishment) Candidate를 이용해
가능한 네트워크 경로(IP, 포트 정보 등)를 주고받으며 최적의 연결 경로를 탐색합니다.
예시)
Caller: "저 이 IP와 포트에서 연결 가능합니다" (ICE Candidate 전송)
Answerer: "저도 연결할만한 IP를 보낼게용 ㅋㅋ" (ICE Candidate 교환)
Caller & Answerer: "음.. 보니까 이 경로로 연결하면 되겠네요 ㅎㅎ"
(상호간의 ICE 교환은 Socket.IO 서버가 중개하게 됨)
Handshake(악수)는 서로 간의 연결이 정상적으로 이루어졌음을 확인하는 과정입니다.
WebRTC에서는 SDP Offer/Answer 교환과 ICE Candidate 탐색이 완료되면
양쪽 브라우저가 "이제 연결할 준비가 됐어요 ^^!"라고 신호를 주고받고 최종적으로 연결됩니다.
Handshake 란?
네트워크 통신에서 연결이 확립되었음을 확인하는 과정입니다.
WebRTC에서도 최종적으로 연결이 확정되면 Handshake가 완료되었다고 표현합니다.
예시)
이제 모든 준비가 끝났으니까 실제로 연결해봅시다! (두근두근)
이 모든 과정이 백엔드 Signaling 서버를 통해 이루어지게 되고 Signaling이 완료되면 브라우저 간 P2P 연결이 확립됩니다.
브라우저 간 P2P 연결을 시도할 때, NAT나 방화벽 문제로 인해 직접 연결이 어려운 경우가 많습니다.
그 문제를 해결하기 위해 STUN과 TURN 서버를 사용합니다.
NAT는 공인 IP(인터넷에서 직접 접근 가능한 IP)와 사설 IP(로컬 네트워크에서만 사용되는 IP)를 변환하는 기술입니다.
인터넷 환경에서 NAT가 필요한 이유
IPv4 주소는 제한되어 있기 때문에 하나의 공인 IP 주소를 여러 기기가 공유해야 합니다.
그래서 라우터가 내부 네트워크(사설 IP)를 사용해서 다수의 장치를 인터넷에 연결해주는데 NAT 뒤에 있는 클라이언트는 외부에서 직접 접근할 수 없다는 문제가 발생합니다. (아파트까지 왔는데 호수를 모르는 상황 ;;)
결론적으로 WebRTC는 P2P 연결을 원하지만 NAT 뒤에 있는 클라이언트들은 서로의 실제 IP를 몰라서 직접적인 연결이 어려우니까 STUN 또는 TURN 서버를 통해 해결한다! 라고 이해하면 됩니다.
클라이언트가 자신의 공인 IP 주소와 포트 정보를 확인할 수 있도록 도와주는 서버입니다.
이 서버를 활용함으로써 WebRTC 연결 과정에서 클라이언트가 ICE Candidate로 사용될 수 있는 네트워크 경로를 탐색할 수 있게됩니다.
만약 STUN 서버를 통해 경로를 탐색했다면 STUN 서버만으로 P2P 연결을 성립할 수 있습니다.
예시)
클라이언트: "STUN 서버님 저 통신해야하는데.. 제 공인 IP 주소를 알려주세요"
STUN 서버: "네 xxx.x.xxx.x:xxxx 네요"
클라이언트: "제 IP 여기래요!" (ICE Candidate 교환/전송)
실제로 WebRTC에서 가장 많이 사용되는 무료 STUN 서버로는
Google에서 제공하는 stun:stun.l.google.com:19302 등이 있습니다.
TURN 서버는 STUN 서버만으로는 해결할 수 없는 네트워크 문제를 보완하는 역할을 합니다.
대부분의 기업용 방화벽 또는 대칭형 NAT 환경에서는 직접적인 P2P 연결이 불가능할 수 있는데 이때 TURN 서버가 중계 역할을 하게 됩니다. (근데 비싸서 최후의 수단으로 쓴대요)
예시)
클라이언트: "직접 연결이 불가능하네요... 도와주세요..."
TURN 서버: "네 ^^ 데이터 스트림을 저희 서버 통해서 경유할 수 있게 도와드릴게요 ㅎㅎㅎ"
클라이언트: "아.. 너무 비싼데... 어쩔 수 없다....." (데이터 스트림 중계 시작)
WebRTC에서 RTCPeerConnection은 브라우저 간 P2P 연결을 설정하고 유지하는 핵심 인터페이스입니다.
실제로 Signaling과 오디오, 비디오, 데이터 스트림 교환의 구현을 위해선 규격이기 때문에 무조건 사용해야합니다.
1) SDP Offer & SDP Answer (연결 정보 교환)
createOffer() → Caller가 SDP Offer를 생성
createAnswer() → Answerer가 SDP Offer에 응답할 SDP Answer 생성
2) Local & Remote Description (연결 정보 설정)
setLocalDescription() → 생성한 Offer 또는 Answer를 로컬 연결 정보로 설정
setRemoteDescription() → 상대방으로부터 받은 SDP를 원격 연결 정보로 설정
3) ICE Candidate (네트워크 경로 설정)
addIceCandidate() → 네트워크 후보(ICE Candidate)를 추가하여 최적의 연결 경로 설정
4) 미디어 트랙 추가 (오디오/비디오 연결)
addTrack() → 미디어 스트림의 트랙(오디오/비디오)를 P2P 연결에 추가

위 다이어그램에서 Nginx는 WebRTC Signaling 요청을 Express 서버로 전달하고 Signaling이 완료된 후 클라이언트끼리 P2P 연결이 설정되는 구조임을 확인할 수 있습니다.
이상으로 WebRTC Signaling 개념을 정리해보았습니다.
핵심은 서버는 WebRTC 연결을 돕는 역할을 할 뿐
실제 미디어 데이터는 서버를 거치지 않고 브라우저 간 직접 교환된다는 점입니다.
이러한 구조 덕분에 서버의 트래픽 부담을 최소화하면서도 저지연 P2P 화상통화를 구현할 수 있습니다.