WebRTC(Web Real-Time Communication)는 웹 브라우저 간에 플러그인의 도움 없이 서로 통신할 수 있도록 설계된 API이다. W3C에서 제시된 초안이며, 음성 통화, 영상 통화, P2P 파일 공유 등으로 활용될 수 있다.
WebRTC 등장 이전에는 고가의 라이선스 구매 비용을 들여서 통화 기능, 화상회의 기능 등을 개발해야 했다. 하지만 2010년, IT 기술 중에서도 유독 라이선스 사용에 많은 비용이 드는 기술이었던 VoIP(Voice over Internet Protocol) 시장에 기존에는 상상하기 어려웠던 뉴스가 등장했다.
구글이 WebRTC의 근간이 되는 여러 독점 기술 기업들(On2, GIPS)을 인수해서 WebRTC 기술을 오픈소스로 풀어버렸다. 이 기술들은 그전까지는 그야말로 세계 탑 수준의 미디어 엔진 및 코덱이었는데 이것을 오픈 한 뒤, 급기야 크롬 브라우저에 탑재시키고 표준화 단체까지 만든다. 그리고 이것을 2011년 말에 완료해버리는 놀라운 실행력을 보여준다. 기존 통신사와 거대 통신솔루션 기업들은 깜짝 놀라게 됐다. 그리고 오늘날 WebRTC의 시대가 열렸다.
사실 오늘날의 WebRTC가 있기까지 구글의 기여를 빼놓을 수 없다. 구글 덕분에 VoIP 서비스 이상의 다양한 혁신적인 서비스를 개발할 수 있는 세상이 열렸고 비대면이 익숙해지는 시대에 널리 쓰이는 기술 중 하나가 되었다. 구글이기에 가능했던 시도가 아니었을까 싶다.
Latency 레이턴시 / 지연 시간
레이턴시는 자극과 반응 사이의 시간이며, 더 일반적인 관점에서는 관찰되는 시스템에서의 어떠한 물리적 변화에 대한 원인과 결과 간의 지연 시간
WebRTC는 기본적으로 P2P 스트리밍 기술에서 출발하였지만, WebRTC 선구자들은 이 P2P의 특징을 확장해서 다양한 응용서비스를 개발해왔다.
WebRTC를 이용한 서비스는 크게 3가지 방식으로 사용한다.
첫 번째는 1:1
방식, 두 번째는 방송에 사용되는 1:N
방식, 세 번째는 회의형 방식인 N:N
방식이다.
1:1
방식 서비스WebRTC가 제공하는 P2P 기능을 기반으로 하는 서비스로, 기존의 VoIP 기술이나 전화 서비스 등을 대체할 수 있다. 국가 공인 전화번호만 부여되지 않을 뿐, 품질이나 기능성에서는 WebRTC가 전화보다 뒤떨어질 이유가 없다. WebRTC는 이미 우리가 접하는 수많은 통화 앱(카카오톡 보이스톡 등)에서 사용되어 왔다. 전화 영어나 심리 및 의료상담 등에서도 WebRTC를 활용하고 있다. 특히 하이퍼커넥트의 Azar는 WebRTC를 활용한 1:1 방식 서비스 중 가장 크게 성공한 서비스 중 하나.
1:N
방송 서비스기존 방송 기술의 지연시간은 보통 5~20초로, 3초 아래의 지연시간을 보장하기 어렵다. WebRTC는 낮은 지연시간을 필요로 하는 생방송 서비스에서 인기를 끌고 있다. 특히 라이브 커머스, 라이브 옥션, 라이브 퀴즈 방송 등 시청자와 활발한 소통이 필요한 분야에서 필수 기술로 자리 잡고 있다.
대량 접속 환경에서 WebRTC의 P2P 성격은 버려지지만, 여전히 생방송에서 요구되는 낮은 지연 시간은 보장된다. Hopin과 같은 웨비나 서비스는 WebRTC를 활용한 1:N 방송 서비스 중 가장 크게 성공한 서비스이다.
N:N
회의형 서비스비즈니스나 교육을 위해서만 WebRTC N:N 기술이 사용되는 건 아니다. 클럽하우스 서비스처럼 왁자지껄한 원격이 필요한 분야라면 WebRTC의 N:N 기술은 어디에나 존재할 수 있다. 우리는 이미 수많은 N:N 회의형 킬러 서비스들을 알고 있는데, 이들은 모두 WebRTC 기술을 활용하고 있다.
WebRTC 기술이 절실한 시기이지만 체감적으로 WebRTC 응용 서비스가 많은 편은 아니다. WebRTC 기술은 다른 IT 기술에 비해 전문가가 부족하고 기술 자체도 어려워, 관련 기술 문서도 매우 부족한 편.
WebRTC는 UDP 기반의 RTP 프로토콜을 사용하고 있다.
품질을 보정하는 기술이 내장되어 있지만, 네트워크 기술에 대한 이해와 충분한 개발 경험이 없다면 WebRTC 기반의 서비스 개발에 성공했다고 안정적인 품질을 보장할 수 없다는 것을 곧 깨닫게 될 것.
1:1 통화 서비스가 아닌 화상회의나 방송 서비스를 개발하려면 다양한 미디어 처리를 위한 서버들이 필요하다.
'송출'에서 'Origin'에 이르는 부분과 'Edge'에서 '수신'에 이르는 과정만이 WebRTC가 관여하는 부분이고 나머지는 모두 각자의 플랫폼과 각자의 방식으로 구현하거나 감안해야 하는 영역임.
WebRTC는 클라이언트에서 미디어에 변화를 많이 주고, 서버의 경우 미디어 전송이나 분배에 더 집중하지만, 서버도 녹화나 기본적인 수준의 미디어 처리는 필요할 때가 많다. 이 때문에 조직 내에 미디어를 자유롭게 다룰 수 있는 엔지니어도 필요하고 모바일 플랫폼의 입출력 미디어 장치에 대한 경험을 가진 엔지니어도 필요하다.
WebRTC를 적극적으로 활용하는 서비스들은 사용자와의 인터랙션을 적극적으로 유도하는 경우가 많다. 이를 위해서는 영상만 실시간으로 제공되기보단 필수적으로 실시간 채팅과 같은 메시징 인프라가 제공되어야 한다. 즉, 채팅이나 화면효과 그리고 사용자의 여러 가지 이벤트 등을 실시간으로 전파하고 공유할 수 있는 서버가 꼭 갖춰져야 한다.
WebRTC 미디어 서버는 WebRTC 기반의 미디어 스트림을 중개 및 분배하는 역할을 하는 서버이다. WebRTC 기반의 방송과 화상회의형 서비스는 이 미디어 서버가 꼭 필요하다. Janus나 Pion등과 같이 유명한 오픈소스가 존재하여 비교적 쉽게 데모 수준의 서비스를 구현해볼 수 있다.
적게는 2-4명 많게는 몇천 명이 대화하는 서비스가 주를 이룹니다. 따라서 개별 방에 대한 트랜잭션을 미리 예측하거나 대응하기보다는 확장성(Scalability)을 매우 유연하게 제어하고 자동화하는 방식을 고민해야한다.
미디어 서버를 클라우드 서비스에 적합하게 구성한다는 것은 매우 큰 도전과제 중 하나이다.
1. 데이터를 주고 받을 채널을 생성하고 채널을 생성한 Peer가 제안(Offer) 객체를 만들어 다른 Peer에게 전달
2. 피어는 제안과 함께 받은 채널을 확인하고 응답(Answer) 객체를 생성해 전달
3. 두 Peer에게 icecandidate라는 이벤트가 발생
4. Candidate 객체를 서로 주고 받아 설정, 두 Peer 간의 연결 완료
5. 앞서 언급한 채널을 통해 메세지 실시간으로 주고 받을 수 있게 됨.
6. Peer간의 연결을 위해 Offer, Answer, Candidate 정보를 교환하기 위해 별도의 서버가 필요하지만 WebRTC의 핵심은 서버없이 Peer 간의 데이터 전송이므로 일단 연결이 되면 더 이상 서버는 필요치 않게 된다.
const socket = io();
const roomName = "room1";
let peerConnection;
let dataChannel;
document.querySelector("form").addEventListener("submit", (event) => {
event.preventDefault();
if(dataChannel) {
const value = document.querySelector("form input").value
dataChannel.send(value);
}
});
function onReady() {
peerConnection = new RTCPeerConnection({
iceServers: [{ urls: [
"stun:stun.l.google.com:19302",
"stun:stun1.l.google.com:19302",
"stun:stun2.l.google.com:19302",
"stun:stun3.l.google.com:19302",
"stun:stun4.l.google.com:19302",
]
}]});
peerConnection.addEventListener("icecandidate", (event) => {
socket.emit("ice", event.candidate, roomName);
});
}
window.onload = () => {
socket.emit("join", roomName, onReady);
}
function onMessage(msg) {
const ul = document.querySelector("ul");
const li = document.createElement("li");
li.innerText = msg;
ul.append(li);
}
socket.on("welcome", async () => {
dataChannel = peerConnection.createDataChannel("chat");
dataChannel.addEventListener("message", (event) => {
onMessage(event.data);
});
const offer = await peerConnection.createOffer();
peerConnection.setLocalDescription(offer);
socket.emit("offer", offer, roomName);
});
socket.on("offer", async (offer) => {
peerConnection.addEventListener("datachannel", (event) => {
dataChannel = event.channel;
dataChannel.addEventListener("message", (event)=> {
onMessage(event.data);
});
})
peerConnection.setRemoteDescription(offer);
const answer = await peerConnection.createAnswer();
peerConnection.setLocalDescription(answer);
socket.emit("answer", answer, roomName);
});
socket.on("answer", (answer) => {
peerConnection.setRemoteDescription(answer);
});
socket.on("ice", (ice) => {
peerConnection.addIceCandidate(ice);
});
import http from "http"
import express from "express";
import SocketIO from "socket.io";
const app = express();
app.set("view engine", "pug");
app.set("views", __dirname + "/views");
app.use("/public", express.static(__dirname + "/public"));
app.get("/", (_, res) => res.render("home"));
const httpServer = http.createServer(app);
const wsServer = SocketIO(httpServer);
const port = 3000;
const handleListen = () => console.log(`Listening on http://localhost:${port}`)
httpServer.listen(port, handleListen);
wsServer.on("connection", socket => {
socket.on("join", (roomName, done) => {
socket.join(roomName);
done();
socket.to(roomName).emit("welcome");
});
socket.on("offer", (offer, roomName) => {
socket.to(roomName).emit("offer", offer);
});
socket.on("answer", (answer, roomName) => {
socket.to(roomName).emit("answer", answer);
});
socket.on("ice", (ice, roomName) => {
socket.to(roomName).emit("ice", ice);
})
});
Web RTC로 프로젝트를 하게 되어 제대로 알아보았다. 생각한 것보다 만만치 않은 챌린징한 주제인데 잘 해낼 수 있었음 한다.