Develop Study/Architecture
(25.08.11) WebRTC기반의 P2P 통화 연결
최근 취업을 하게 되어,
회사의 솔루션 및 서비스를 분석하고자 한다.
항상 Backend 단에서의 API 및 WebSocket 기반의 내 메시지의 교환을 구성했지만, 이와 다르게
SDK 를 기준으로 시그널링만의 역할만 서버가 담당하고, 스트리밍을 UDP로 미디어를 스트리밍 = 영상통화를 할 수 있도록 하는 서비스를 샘플 코드를 기반으로 분석하고자 했다.
https://docs.knowledgetalk.co.kr/sample/p2p
P2P통화 연결 | Knowledgepoint
SAMPLEP2P통화 연결 설명 중앙 미디어 서버없이 종단 간 직접 연결하여, 연결을 하고 싶은 사용자에게 발신자의 영상을 보낼 수 있습니다. 단, NAT/방화벽 환경의 사용자가 외부망과의 통신을 위해
docs.knowledgetalk.co.kr
WebRTC, Web Real-Time Communication
- 웹 브라우저와 모바일 앱에서 플러그인 없이 실시간으로 오디오, 비디오, 데이터 통신을 할 수 있게 해주는 기술 표준
- 웹 애플리케이션과 웹사이트가 중간 서버 없이 브라우저 간에 실시간 통신
- 표준화된 API 를 통해 브라우저-브라우저 간 직접적인 미디어 스트림 및 데이터 전송
- 기본적으로 실시간 스트림을 위한 UDP 프로토콜을 주로 사용(TCP 사용가능)
- RTP/RTCP(Real-time Transport Protocol) 같은 프로토콜을 사용해 미디어 스트림 전송을 추가로 관리하면서 데이터 손실을 방어
- STUN/TURN 서버를 통해 NAT 환경을 피해 UDP 패킷을 주고 받음
특징
- 미디어 캡처 및 전송: 웹캠, 마이크 등 미디어를 상대방에게 전송
- 실시간 스트리밍: 지연시간이 매우 짧은 실시간 영상/음성 교환 가능
- → P2P, SFU 방식의 연결의 기반
- 데이터 채널: 영상, 음성뿐 아니라 텍스트, 파일 등 데이터 전송도 가능
P2P , Peer-to-Peer
- 중앙 서버를 거치지 않고, 사용자(피어)끼리 두 명 이상이 실시간으로 직접 연결해 데이터를 주고받는 방식
- 단, 무조건 중앙서버를 거치는 것이 아니라, 신호 교환(Signaling)시, 중간 서버가 이를 중계
- NAT 방화벽 환경에 의한 통신 제한 또는 공인 IP 공유가 어려울 경우
P2P의 장단점
- 장점
- 서버 비용 절감 (서버가 미디어 데이터 전달 안 함)
- 지연 시간 최소화
- 분산 구조로 특정 서버 장애 영향 적음
- 단점
- 많은 참여자일 경우 연결 관리와 네트워크 부하 증가
- NAT, 방화벽 문제로 연결 실패 가능성
- 클라이언트 성능에 의존 (연결 유지에 리소스 필요)
- → 따라서 1:1 영상통화 또는 파일 공유에 필요함
Knowledgetalk SDK의 P2P 통화 분석
Knowledgetalk 서버 API 통신 서버 중계를 통한 P2P 연결 준비
.... // Knowledgetalk SDK 객체에 식별코드, 인증키를 통한 연결 knowledgetalk.init(cpCode, authKey).then(result => { // 서버 연결 성공시 서버에서 200 코드 Response // 그렇지 않을 경우 alert 처리 if(result.code !== '200') { alert('init failed!'); return; } ... })
- Knowledgetalk 서버에서 P2P 통화를 사용하기 위해서 부여된 식별코드, 인증키로 인증을 API 를 통해서 진행
- init()
- 일반적이 REST API 가 아닌 SDK 제공의 API 이기 때문에 Java 매서드처럼 Params를 입력해서 요청하면서 API 의 비동기 처리ㅣ를 하게됨
- Javascript 기준 **Promise**를 반환하고, .then() 또는 **await**로 결과를 반환 → knowledgetalk.init(cpCode, authKey).then(...) 의 형태
- **result.code === '200'**에서 받는 **200**은 HTTP 상태 코드(HTTP status code)가 아니라, 서버가 API 응답 본문(JSON 데이터) 안에 포함시켜 보내는 응답 메시지 내 필드 값
- init()
- API 이기 때문에, 이는 WebRTC 영역이 아님 주의
추가 SDK API처리
// 방 생성 EventListener
createRoomBtn.addEventListener('click', async () => {
// 방 생성 요청
let roomData = await knowledgetalk.createRoom();
// 방생성 오류 처리
if(roomData.code !== '200'){
alert('createRoom failed!');
return;
}
...
})
// 방 입장 EventListener
joinRoomBtn.addEventListener('click', async () => {
let roomId = roomIdInput.value;
// 방 아이디를 통해 방 입장 요청 -> 성공시 roomData Response
let roomData = await knowledgetalk.joinRoom(roomId);
// 입장 요청 오류 처리
if(roomData.code !== '200'){
alert('joinRoom failed!');
return;
}
...
})
- SDK 를 통한 서버에서 방 연결을 위한 로직 처리가 진행
- createRoom() joinRoom() 을 통해서 각각 방생성/생성된 방 정보, 특정방상태/방멤버 를 Response 받아오는 호출
P2P 영상통화 연결
p2pBtn.addEventListener('click', async () => {
// 전송할 상대방 id 선언
let targetId = prompt('상대방 id 입력');
// localStream 객체를 생성
// navigator를 통해 허용된 사용자의 입력장치(비디오, 오디오) 를 함께 활용하기 위해 가져오기
let localStream = await navigator.mediaDevices.getUserMedia({video: true, audio: false});
// 요청한 사람 = 스트림을 시작한 사람의 VideoBox 생성 함수실행
createVideoBox(knowledgetalk.getUserId());
// 카메라 영상 스트림을 화면에 출력하기 위해 연결 (srcObject = localStream)
document.getElementById('multiVideo-' + knowledgetalk.getUserId()).srcObject = localStream;
// 다른 targetId 인 상대방 user 과 webRTC 연결설정과 publish
await knowledgetalk.publishP2P(targetId, 'cam', localStream);
})
- navigator.mediaDevices.getUserMedia({video: true, audio: false})
- 사용자의 카메라(비디오)와 마이크(오디오) 권한을 요청해서, 실제로 미디어 스트림을 받아오는 Web API
- WebRTC 통신에서 미디어 스트림(영상, 음성)을 주고받으려면 먼저 로컬 스트림을 확보해야하므로
- localStream 객체는 WebRTC가 상대방과 주고받을 영상 데이터의 원본 소스
- knowledgetalk.publishP2P(targetId, 'cam', localStream)
- WebRTC P2P 연결을 실제로 생성
- 내부적으로는 WebRTC의 PeerConnection 객체 생성(브라우저) → 신호 교환(signaling) 과정을 통한 연결 설정
- signaling은 Peer간 ‘누가 있다’, ‘어떤 미디어를 주고받을지’, ‘네트워크 정보(ICE candidate)’ 등의 정보를 주고 받는 WebSocket형태를 띄고 있음 ex) offer, answer 등등
- 이후에 미디어 스트리밍이 WebRTC, UDP 기반의 P2P 통신이 진행이 되는 것 → 브라우저간 직접
전체 Knowledgetalk SDK를 활용한 시그널링(Signaling) FLOW

User Flow
HOST
- "CreateRoom" 버튼 누르기: 먼저, 방을 만들기 위해 CreateRoom 버튼을 누릅니다.
- roomId 확인: CreateRoom 버튼을 누르면, "room id" 입력란에 방 ID가 자동으로 생성
- "JoinRoom" 버튼 누르기: 생성된 방에 입장하기 위해 JoinRoom 버튼을 클릭
- 멤버에게 방 ID 공유: 방 ID를 친구에게 알려주기
- 4-1. 멤버가 해당 roomId로 JoinRoom 클릭해 방에 들어오기
- "p2p" 버튼 누르기: 친구가 방에 들어오면, p2p 버튼 클릭
- 멤버의 ID 입력: "상대방 id 입력" 프롬프트 창이 나타나면, 멤버의 ID를 입력하고 확인
MEMBER
- "room id" 입력: 방을 만든 친구에게서 받은 방 ID를 "room id" 입력란에 직접 입력
- "JoinRoom" 버튼 누르기: 입력한 방 ID로 방에 입장하기 위해 JoinRoom 버튼 클릭
- "p2p" 버튼 누르기: 친구의 영상이 보이면, p2p 버튼 클릭
- 호스트의 ID 입력: "상대방 id 입력" 프롬프트 창이 나타나면, 호스트의 ID를 입력하고 p2p 클릭
STUN/TURN을 고려하기
- STUN 성공 시 → 브라우저끼리 직접 연결 (UDP)
- STUN 실패 시 → TURN 서버 중계로 연결
STUN (Session Traversal Utilities for NAT)
- 클라이언트가 자신의 공인 IP:포트 = 주소만 확인할 수 있도록 하는 역할
- NAT 환경에서 P2P 직접 연결을 시도할 때 필수
- NAT 뒤에 클라이언트의 주소를 확인할 수도 있음
- 방화벽이 있을 경우 실패할 수 있음
- 위의 시그널링에 사용하는 중계 서버 외에 P2P로 미디어를 흘려야할 때 또 서로의 IP 주소를 알아야하기 때문에 STUN 을 사용할 수 밖에 없음
TURN (Traversal Using Relays around NAT)
- 직접 P2P 연결이 불가능할 때 미디어(음성/영상)를 중계(relay)하는 역할
- 클라이언트는 TURN 서버에 미디어 스트림을 보내고, TURN 서버가 그 미디어를 상대 피어에게 전달(중계)하는 방식
- UDP 외 TCP/TLS도 지원하기 때문에 UDP 없는 환경에서도 가능
- 외부 서버이기 때문에 비용이 발생
STUN/TURN 이 반드시 필요하진 않지만, 브라우저 (클라이언트 단) 에서 확인할 수 없기 때문에, 후에
실제 SSL 인증서를 설치하고, PeerConnection 에서 STUN 또는 TURN 을 경유하는지 보고, 다이어그램으로 한번더 정리해보고자 한다.

'Develop Study > Architecture' 카테고리의 다른 글
| (25.08.18) SFU 기반의 그룹통화/방송 연결 (3) | 2025.08.18 |
|---|---|
| (25.05.23) OAuth 2.0 카카오 간편 로그인 구현하기 - Spring Boot (0) | 2025.05.23 |
| (25.05.21) OAuth 2.0 간편 소셜 로그인 (0) | 2025.05.21 |
| (25.02.24) Event Sourcing (0) | 2025.02.24 |