WebRTC
WebRTC๋ Web Real-Time Communication์ ์ค๋ง๋ก, ๋ธ๋ผ์ฐ์ ๋ค์ด ์๋ฒ๋ฅผ ๊ฑฐ์น์ง ์๊ณ P2P๋ก ํต์ ์ ๊ฝ๊ฒ ํ ์ ์๋ ์นํ์ค ํ๋กํ ์ฝ ์ค ํ๋๋ค.
์๋ฒ๋ฅผ ๊ฑฐ์น์น ์์ผ๋ ๋ ๋น ๋ฅด๊ณ ์ฆ๊ฐ์ ์ธ ํต์ ์๋น์ค๊ฐ ๊ฐ๋ฅํ๋ค.
์ด๋ฅผํ ๋ฉด ์ค(Zoom) ๊ฐ์ ์น ๊ธฐ๋ฐ์ ํ์ ์ฑํ ์๋น์ค๋ค์ด ์ด ๊ธฐ์ ์ ํตํด ๋ง๋ค์ด์ง๋ค.
๊ตฌ๊ธ์ด 2011๋ ์ ์ฒ์ ์คํ์์ค๋ก ๋ฐํํด์ ํผ์ง๊ธฐ ์์ํ๊ณ , ํ์ฌ๋ ์๋น์์ ๋ธ๋ผ์ฐ์ ์์ ์ง์๋๋ค.
๋๋ถ๋ถ์ ์์์ด๋ ์ค๋์ค ๋ฑ์ ์ฃผ๊ณ ๋ฐ๋๋ฐ ์ฌ์ฉํ์ง๋ง, ํ ์คํธ ๋ฑ์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ๋๋ ์ฌ์ฉํ ์ ์๋ค.
๊ตฌ์กฐ
๋ธ๋ผ์ฐ์ ๋ผ๋ฆฌ ๊ณง์ฅ ํต์ ์ ํ ์ ์๊ธด ํ์ง๋ง, ๊ทธ๋๋ ์๋ฌด๊ฒ๋ ์์ด ํด๋ผ์ด์ธํธ๋ผ๋ฆฌ ์๋ก์ ์ฃผ์๋ฅผ ์๊ธฐ๋... ๋ถ๊ฐ๋ฅํ๋ค.
์ฌ๊ธฐ์๋ ์๋ฒ๋ ์ค์ํ๋ค. ํด๋ผ์ด์ธํธ์๊ฒ ํต์ ์ ์ฐ๊ฒฐํ ๋ค๋ฅธ ํด๋ผ์ด์ธํธ์ ์ ๋ณด๋ฅผ ์ค์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋์ ์ค๊ฐ์ Stun Server๋ผ๋ ์ค๊ณ์๋ฅผ ๋๊ณ , ์๋ก๊ฐ์ IP๋ฅผ ์์๋ผ ์ ์๋๋ก ํ๋ค.
์ฃผ์ํ ํจ์๋ ๋ค์๊ณผ ๊ฐ๋ค.
RTCPeerConnection: P2P ์ฐ๊ฒฐ์ ๋ง๋ค๊ณ ํ์ํ๋ ๊ฐ์ฒด
RTCSessionDescription: P2P ์ฐ๊ฒฐ์ ๊ฐ ์๋ํฌ์ธํธ์ ์ฐ๊ฒฐ ์ ๋ณด
Stun ์๋ฒ์ RTCPeerConnection
Stun ์๋ฒ๋ ํด๋ผ์ด์ธํธ์ public IP์ ํฌํธ ๋ฑ์ ์ถ์ถํด์ฃผ๋ ์ค๊ฐ ์๋ฒ๋ค. ์๋ ๋ก์ปฌ ๋ด์์๋ ๋ก์ปฌ IP๋ ๋ฐ๋ก ์ ์ ์์ง๋ง, public IP๋ ์๊ธฐ๊ฐ ์ฝ์ง ์์ ์ ์๋ค.
๊ทธ๋ฅ ๋ก์ปฌํ ์คํธ๋ง ํ ๋๋ ์์ด๋ ๋์ง๋ง, ์ค์ฌ์ฉํ ๋๋ ๊ฑฐ์ ํ์๋ก ์ฌ์ฉํ๋ค.
๊ฐ๋จํ๊ฒ ๋์ฐ๋ ๋ฐฉ๋ฒ์ coturn ๋ฑ์ ์์ฑ๋ ํ๋ก๊ทธ๋จ์ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
์ฐ๋ถํฌ์์๋ ๊ทธ๋ฅ ํจํค์ง๋งค๋์ ๋ก ๋ฐ๋ก ๊น๊ณ ์คํํ ์ ์๋ค.
sudo apt-get update
sudo apt-get install coturn -y
sudo systemctl kill coturn
sudo turnserver --log-file stdout

์ด๋ ๊ฒ ์คํ๋๋ฉด ๋๋ค.
์คํด์๋ฒ๋ UDP 3478 ํฌํธ๋ก ๊ฐ๋ฐฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ค์๊ณผ ๊ฐ์ด PeerConnection ์์ฑ์ ์ฌ์ฉํ ์ ์๋ค.
const config = {
iceServers: [{ urls: "stun:54.180.105.67" }],
};
const localConnection = new RTCPeerConnection();์๊ทธ๋๋ง(Signaling) ์๋ฒ
WebRTC์ ํ์ํ ๋ ํ๋์ ๋ถ๊ฐ์์๊ฐ ์๊ทธ๋๋ง ์๋ฒ๋ค.
์คํด์๋ฒ๊ฐ ๊ทธ๋ฅ IP๋ง ๋๋ ค์ฃผ๋ ๊ฐ๋จํ ์์
๋ง ํ๋ค๋ฉด, ์ด๊ฑด ์ง์ ๊ฐ๊ฐ์ Peer์ Peer๋ฅผ ์ง์ ์ฐ๊ฒฐํด์ฃผ๋ ์ญํ ์ ๋ด๋นํ๋ค.
์๋ก์ด Peer๊ฐ ๋ค์ด์ฌ๋๋ง๋ค ๊ธฐ์กด์ Peer์ Broadcast ๋ฑ์ ํด์ค์ผ ํ๊ธฐ ๋๋ฌธ์ ์น์์ผ์ผ๋ก ๊ตฌํ์ ๋ง์ด ํ๋ค.
์๋๋ ๊ทธ์ ๋ํ ๊ฐ๋จํ ๊ตฌํ๋ก๋ค.
const Express = require("express");
const fs = require("fs");
const WebSocket = require("ws");
const app = Express();
app.get("/", (req, res) => {
const html = fs.readFileSync("./index.html", "utf-8");
res.send(html);
});
const port = 8080;
const wss = new WebSocket.Server({ port });
wss.on("connection", (socket) => {
console.log("์๋ก์ด ์ฐ๊ฒฐ์ด ์์ฑ๋์์ต๋๋ค.");
// offer๋ฅผ ์์ ํ๊ณ ๋ค๋ฅธ ํด๋ผ์ด์ธํธ๋ก ์ ๋ฌ
socket.on("offer", (offer) => {
console.log("์๋ก์ด offer๋ฅผ ์์ ํ์์ต๋๋ค.");
socket.broadcast.emit("offer", offer);
});
// answer๋ฅผ ์์ ํ๊ณ ๋ค๋ฅธ ํด๋ผ์ด์ธํธ๋ก ์ ๋ฌ
socket.on("answer", (answer) => {
console.log("์๋ก์ด answer๋ฅผ ์์ ํ์์ต๋๋ค.");
socket.broadcast.emit("answer", answer);
});
// ICE candidate๋ฅผ ์์ ํ๊ณ ๋ค๋ฅธ ํด๋ผ์ด์ธํธ๋ก ์ ๋ฌ
socket.on("candidate", (candidate) => {
console.log("์๋ก์ด ICE candidate๋ฅผ ์์ ํ์์ต๋๋ค.");
socket.broadcast.emit("candidate", candidate);
});
// ํด๋ผ์ด์ธํธ์ ์ฐ๊ฒฐ์ด ๋์ด์ง ๋
socket.on("disconnect", () => {
console.log("์ฐ๊ฒฐ์ด ์ข
๋ฃ๋์์ต๋๋ค.");
});
});
app.listen(5500, () => {
console.log("Server started on port 5500");
});P2P ์ฐ๊ฒฐ
์ฐ์ ๋ค์๊ณผ ๊ฐ์ด Peer Connection ๊ตฌ์ฑ์ ์์ฑํด์ผ ํ๋ค.
localConnection = new RTCPeerConnection();
sendChannel = localConnection.createDataChannel("sendChannel");
remoteConnection = new RTCPeerConnection();
remoteConnection.ondatachannel = receiveChannelCallback;
๊ทธ ๋ค์์๋ RTCPeerConnection๋ฅผ ์ด์ฉํด์ Peer๋ผ๋ฆฌ Offer์ Answer๋ผ๋ ๋ฉ์์ง๋ฅผ ๊ตํํ๋ค.
์ด๋ฐ ์์ผ๋ก ๋ณด์ธ๋ค.

์ด๊ฒ๋ ๋ณดํต ์๊ทธ๋๋ง ์๋ฒ๋ฅผ ํตํด ์ฃผ๊ณ ๋ฐ๋๋ค.

๊ทธ ๋ค์์๋ ICE Candidate๋ผ๊ณ ํ๋ ๊ฐ Peer์ ๋คํธ์ํฌ ์ฃผ์ ์ ๋ณด๋ฅผ ๊ตํํด์ ์ฐ๊ฒฐ ์์
์ ๋ง๋ฌด๋ฆฌ์ง๋๋ค.
๊ทธ๋ฌ๋ฉด ๋ฐ์ดํฐ ์ฑ๋ ๋ฑ์ ์ฌ์ฉํด์ ์ง์์ ์ผ๋ก P2P ํต์ ์ ์ฃผ๊ณ ๋ฐ์ ์ ์๋ค.


๊ทธ๋ ๋ค.
์ฐธ์กฐ
https://developer.mozilla.org/ko/docs/Web/API/WebRTC_API
https://acstory.tistory.com/534
https://github.com/coturn/coturn
https://github.com/aljanabim/simple_webrtc_signaling_server
https://github.com/mdn/samples-server/blob/master/s/webrtc-simple-datachannel/main.js