webRTC
webRTC
webRTC๋
WebRTC (Web Real-Time Communication)๋ ์น ๋ธ๋ผ์ฐ์ ๊ฐ์ ํ๋ฌ๊ทธ์ธ์ ๋์ ์์ด ์๋ก ํต์ ํ ์ ์๋๋ก ์ค๊ณ๋ API์ด๋ค.
W3C์์ ์ ์๋ ์ด์์ด๋ฉฐ, ์์ฑ ํตํ, ์์ ํตํ, P2P ํ์ผ ๊ณต์ ๋ฑ์ผ๋ก ํ์ฉ๋ ์ ์๋ค.
https://ko.wikipedia.org/wiki/WebRTC
์์ ๊ฐ์ด webRTC ๊ธฐ์ ์ ์์ฑ , ์์ p2p ํ์ผ๊ณต์ ๋ฅผ ์ค์๊ฐ์ผ๋ก ์ ์กํ๋ web ๊ธฐ์ ์ด๋ค.
์์ง๋ ๊ณ์ ๋ฐ์ ๋๊ฐ๊ณ ์์ผ๋ฉฐ webRTC๋ ํ์,์์ฑ ๊ณผ ๊ฐ์ ๊ธฐ๋ฅ ๊ตฌํ์ ์ ๊ณตํ๋ฉฐ
p2p์ ๊ฐ์ ํด๋ผ์ด์ธํธ์ ์ฐ๊ฒฐ์ ๊ฐ๋ฐ์๊ฐ ์ง์ ๊ตฌํํด์ผ ํ๋ค.
p2p ์ฐ๊ฒฐ์ ์ฌ๋ฌ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์กด์ฌํ๋ฉฐ ์์ง ๊ธฐ๋ณธ์ ์ผ๋ก ํ๋ฆฝ๋ ๋ฐฉ๋ฒ์ ์์ผ๋ฉฐ Stun , turn server๊ฐ ์กด์ฌํ๋ฉฐ ์ด ๋ถ๋ถ์ node.js์ peerJs๋ฅผ ์ด์ฉํ์ฌ ๊ตฌํํ๋ ค๊ณ ํ๋ค.
- webRTC๋ฅผ ์ด์ฉํ ๊ธฐ๋ฅ ๊ตฌํ
- https ์ธ์ฆ (ssl ์ธ์ฆํค ๋ฐ๊ธ)
- p2p ์ฐ๊ฒฐ (peerJs)
- ์ฑ ๋ฐฐํฌ (heroku)
๊ตฌํํ๋ ์์๋ก ์์ ๊ฐ์ด ์์ผ๋ฉฐ webRTC๋ ์๋ก๊ฐ ์๋ก๋ฅผ ์๋ณํ๋ ๊ณ ์ ์ id๊ฐ ์กด์ฌํด์ผํ๋ฉฐ ์๋ฒ๊ฐ p2p ๊ณต์ ์ ์ธ์ฆ๋ https ํ๋กํ ์ฝ์ ์ด์ฉํ์ฌ์ผ๋ง ์๋ก ๊ณต์ ๊ฐ ๊ฐ๋ฅํ๋ค. domain๊ณผ ssl์ธ์ฆ์ ๋ฐ์์ผ๋ง ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค.
์ฌ๊ธฐ์๋ ๋ฌด๋ฃ๋ก ํธ์คํ heroku๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ๋จํ๊ฒ webRTC๋ฅผ ๊ตฌํํด๋ณด๋ ค๊ณ ํ๋ค.
webRTC
package.json
์ค์นํ npm ๋ชจ๋
{"engines":{
"node":"^16.3.0",
"npm":"^7.15.1"},
"scripts":{
"start":"node index.js"},
"dependencies":{
"ejs":"^3.1.6",
"express":"^4.17.1",
"mongoose":"^5.12.15",
"peer":"^0.6.1",
"socket.io":"^4.1.2",
"uuid":"^3.4.0"}}
index.js
node Server๋ฅผ ๋๋ฆฌ๋ ๋ฉ์ธ ์คํฌ๋ฆฝํธ์ด๋ค.
const express = require("express");
const app = express();
const PORT = process.env.PORT||3030;
const server = require("http").Server(app);
const io = require("socket.io")(server);
const { v4: uuidV4 } = require("uuid");
const { ExpressPeerServer } = require('peer');
const peerServer = ExpressPeerServer(server, {
debug : true
});
app.set("view engine", "ejs");
app.use(express.static("public"));
app.use("/peerjs", peerServer);
app.get("/", (req, res) => {
res.redirect(`/${uuidV4()}`);
});
app.get("/:room", (req, res) => {
res.render("room", { roomId: req.params.room });
});
io.on("connection", (socket) => {
socket.on("join-room", (roomId, userId) => {
socket.join(roomId);
socket.to(roomId).emit('uesr-connected', userId);
socket.on('disconnect', ()=>{
socket.to(roomId).emit('user-disconnected',userId);
})
});
});
server.listen(PORT, () => {
console.log("listening on * : 3000");
});
script.js
ํด๋ผ์ด์ธํธ peer , socket์ ์ฐ๊ฒฐํ๊ณ webRtc ๊ตฌํ ์คํฌ๋ฆฝํธ์ด๋ค.
const socket = io("/");
const videoGrid = document.getElementById("video-grid");
const myVideo = document.createElement("video");
const peer = new Peer(undefined, {
path: "/peerjs",
host: "/",
port: "443",
});
myVideo.muted = true;
const peers = {};
// 1. ์ฌ์ฉ์ ๋น๋์ค ์ค๋์ค ๊ถํ ์์ฒญ
let myVideoStream;
navigator.mediaDevices
.getUserMedia({
video: true,
audio: false,
})
.then((stream) => {
// ๋ด๊ฐ ์ ์์ ๋น๋์ค ํ์
myVideoStream = stream;
addVideoStream(myVideo, stream);
// ์ ์์ ์ ์ํ ์ ์ ๋ค ๋น๋์ค ํ์
peer.on("call", (call) => {
console.log("์ ์ ์ ์ ๋ค" + call);
call.answer(stream);
const video = document.createElement("video");
call.on("stream", (userVideoStream) => {
addVideoStream(video, userVideoStream);
});
});
// ๋ค๋ฅธ ์ ์ ๋ค ์ ์์ ๋น๋์ค ํ์
socket.on("uesr-connected", (userId) => {
console.log("๋ค๋ฅธ์ ์ ์ ์์" + userId);
connectToNewUser(userId, stream);
});
});
socket.on("user-disconnected", (userId) => {
if (peers[userId]) peers[userId].close();
});
peer.on("open", (id) => {
socket.emit("join-room", ROOM_ID, id);
});
// ์ ์ ์ ์์ ์กฐ์ธ์ ์คํ
const connectToNewUser = (userId, stream) => {
const call = peer.call(userId, stream);
const video = document.createElement("video");
call.on("stream", (userVideoStream) => {
addVideoStream(video, userVideoStream);
});
call.on("close", () => {
video.remove();
});
peers[userId] = call;
}
// ๋น๋์ค ์์ฑ
const addVideoStream = (video, stream) => {
video.srcObject = stream; // ๋น๋์ค ์ค์ ์
ํ
??
video.addEventListener("loadedmetadata", () => {
video.play();
});
videoGrid.appendChild(video);
}
room.ejs
html ์์ง์คํ๋์ธ ejs๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ฉด์ ๊ตฌ์ฑํ๋ค.
<head>
<title>Document</title>
<script>
const ROOM_ID = "<%= roomId%>";
</script>
<link rel="stylesheet" href="style.css">
<script defer src="https://unpkg.com/peerjs@1.3.1/dist/peerjs.min.js"></script>
<script src="/socket.io/socket.io.js" defer></script>
<script src="script.js" defer></script>
<style>
#video-grid {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
video {
width: 400px;
height: 300px;
object-fit: cover;
padding: 8px;
}
</style>
</head>
<body>
<div class="main">
<div class="main__left">
<div class="main__videos">
<div id="video-grid">
</div>
</div>
<div class="main__controls">
<div class="main__controls__block">
<div onclick="muteUnmute()" class="main__controls__button main__mute_button">
<i class="fas fa-microphone"></i>
<span>Mute</span>
</div>
<div onclick="playStop()" class="main__controls__button main__video_button">
<i class="fas fa-video"></i>
<span>Stop Video</span>
</div>
</div>
<div class="main__controls__block">
<div class="main__controls__button">
<i class="fas fa-shield-alt"></i>
<span>Security</span>
</div>
<div class="main__controls__button">
<i class="fas fa-user-friends"></i>
<span>Participants</span>
</div>
<div class="main__controls__button">
<i class="fas fa-comment-alt"></i>
<span>Chat</span>
</div>
</div>
<div class="main__controls__block">
<div class="main__controls__button">
<span class="leave_meeting">Leave Meeting</span>
</div>
</div>
</div>
</div>
<div class="main__right">
<div class="main__header">
<h6>Chat</h6>
</div>
<div class="main__chat_window">
<ul class="messages">
</ul>
</div>
<div class="main__message_container">
<input id="chat_message" type="text" placeholder="Type message here...">
</div>
</div>
</div>
</body>
</html>
Leave a comment