[WebRTC] WebRTC로 Zoom Clone하기

김우경·2021년 4월 18일
1

이번 졸업 프로젝트를 이루는 여러 기능 중 가장 핵심 기능은 실시간 화상회의 기능이다. 오디오, 비디오 등을 real-time으로 공유하는 기능을 제공하는 여러 API중 WebRTC를 사용해서 기능을 구현하기로 했고, 오늘부터 공부를 해보려고 합니다.!

전반적인 이해는 Google Developers - WebRTC Tutorial을 참고했습니다.

WebRTC란?

MDN - WebRTC 문서에 따르면,

WebRTC(Web Real-Time Communication)은 웹 애플리케이션과 사이트가 중간자 없이 브라우저 간에 오디오나 영상 미디어를 포착하고 마음대로 스트림할 뿐 아니라, 임의의 데이터도 교환할 수 있도록 하는 기술입니다.

이다. Google Chrome이 오픈소스화 한 프로젝트에서 기원했고, 다음과 같은 과정을 통해 데이터를 주고받는다.

한마디로 별다른 플러그인이나 소프트웨어 설치없이 브라우저간 Real Time Communication을 제공하는 JavaScript API이다.

주요 기능

위키피디아에 따르면,

  • getUserMedia: 오디오와 비디오 미디어를 가져온다.
  • RTCPeerConnection: 피어 간 오디오, 비디오 통신을 활성화한다.
  • RTCDataChannel: 피어 간 양방향 임의 데이터 통신을 허용한다.

Zoom-Clone 실습

자세한 동작은 실습을 통해 익히려고 한다. How To Create A Video Chat App With WebRTC의 튜토리얼을 참고해서 실습을 해봤습니다.

환경설정

pakage.json 파일 생성

npm init -y

dependency 추가

  • express : express 서버 사용
    npm i express ejs socket.io
  • uuid : Universally Unique IDentifier의 약자로 사용자에게 고유 ID를 붙혀 구분하는데 등에 사용 -> 개설한 회의방에 고유한 아이디 붙히기용
    npm i uuid
  • nodemon : 파일이 수정되면 자동으로 노드 애플리케이션을 재시작
    npm i --save-dev nodemon
    -> 자동 재시작이 가능하도록 package.json의 scripts를 수정해준다.
"scripts": {
    "devStart": "nodemon server.js"
  },

PeerJS

Peer-to-Peer connection을 쉽게 구현할 수 있게 도와주는 API이다. peerjs.com에서 자세한 설명을 찾을 수 있다.
-> 여기서는 여러 user를 connect하고, user들에게 고유 id를 부여하기 위해 사용한다.

설치하기

npm i -g peer

3001번 포트 열기

peerjs --port 3001

코드

server.js

const express = require('express')
const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server)
const { v4: uuidV4 } = require('uuid')

app.set('view engine', 'ejs') // view engine 설정
app.use(express.static('public')) // html, css등 static file들 경로 설정

// creating a new room -> redirect to the room
app.get('/', (req, res) => {
    res.redirect(`/${uuidV4()}`) // dynamic url -> url 뒤에 uuid 붙음
})

// 해당 room으로 들어가게
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) // new user join시 알림
        socket.broadcast.to(roomId).emit('user-connected', userId);

        socket.on('disconnect', () => {
            socket.broadcast.to(roomId).emit('user-disconnected', userId);
        })
    })
})

server.listen(3000)

script.js

const socket = io('/') // socket이 app이 실행중인 root path와 연결되도록
const videoGrid = document.getElementById('video-grid')
const myPeer = new Peer(undefined, {
    host: '/', //root
    port: '3001'
}) // connection to peer server
const myVideo = document.createElement('video')
myVideo.muted = true
const peers = {}

// webcam 띄우기
navigator.mediaDevices.getUserMedia({
    video: true,
   // audio: true
}).then(stream => {
    addVideoStream(myVideo, stream) // video 띄우기
    
    // recieving calls
    myPeer.on('call', call => {
        call.answer(stream) //추가된 new user의 call 띄우기
        const video = document.createElement('video')
        call.on('stream', userVideoStream => {
            addVideoStream(video, userVideoStream);
        }) ///추가된 new user의 입장에서도 기존 user 띄우기
    })

    socket.on('user-connected', userId => {
        connectToNewUser(userId, stream)
    })
})

// 방 나간 유저 바로 지우기
socket.on('user-disconnected', userId => {
    if (peers[userId])  peers[userId].close()
})

// 비디오 띄우기
function addVideoStream(video, stream) {
    video.srcObject = stream
    video.addEventListener('loadedmetadata', () => {
        video.play()
    })
    videoGrid.append(video)
}

// making calls
function connectToNewUser(userId, stream) {
    const call = myPeer.call(userId, stream)
    const video = document.createElement('video')
    call.on('stream', userVideoStream => {
        addVideoStream(video, userVideoStream)
    })
    call.on('close', () => {
        video.remove()
    })

    peers[userId] = call;
}

room.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>
        const ROOM_ID = "<%= roomId %>";
    </script>
    <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>
    <title>Document</title>
    <style>
        #video-grid{
            display: grid;
            grid-template-columns: repeat(auto-fill, 300px);
            grid-auto-rows: 300px;
        }
        video {
            width: 100%;
            height: 100%;
            object-fit: cover;
        }
    </style>
</head>
<body>
    <div id="video-grid"></div>
</body>
</html>

nodejs를 잘 모르는 상태에서 구현하려고 하니 한번에 100% 이해는 어려운것같다^_ㅠ,, 일단 오늘은 여기까지

profile
Hongik CE

0개의 댓글