초보자도 간단하게 만들어 보는 영상통화 기능(webRTC로 영상통화 만들기)
저번 시간에 이어 webRTC에 대해서 배웠다면 간단하게 영상통화 기능을 만들어 보는 방법을 공유할려함.
혹시 webRTC를 모른다 하면 아래 링크 ㄱㄱ
https://cbn1218.tistory.com/68
초보자도 이해 가능한 WebRTC 에 대해서 알아보자
1. WebRTC 무엇인가?먼저 webRTC 의 진짜 본명은 WebRTC(Web Real-Time Communication) 이름에서 알수 있듯이 web기술의 하나로 웹 브라우저나 모바일 앱 간에 실시간 음성, 영상, 데이터 통신을 가능하게 해
cbn1218.tistory.com
간단하게 구현 환경을 설명하자면
운영OS : 윈도우11
백엔드 : node.js
프론트엔드 : html, css, javaScript
node.js 다운로드 받아 깔고 필요한 라이브러리를 npm으로 다운로드 받아 실행할수 있다는 가정하에 컨텐츠가 작성되었음.
(혹시 node.js에 대해서 전혀 모르겠다 하시는 분들은 댓글 남겨주시면 위에 설정 하는 부분을 컨텐츠를 만들어 보겠움)
1. 결과
구현한걸 동영상으로 촬영할려고 했으나 영상편집이 못함 이슈가 있어서 이미지와 console창으로 대체함.
참고로 테스트 환경은 같은 로컬환경에서 pc와 핸드폰 으로 각각 크롬으로 접속하여 테스트해봄.
2.설명
전체적인 구조는 pc가 시그날링서버 겸 클라이언트A 이고, 핸드폰은 클라이언트B 역할을 하고 있음.
클라이언트A가 서버에 먼저 접속하고 클라이언트B가 접속후 시그날링 서버를 통해 A와 B가 협상후 webRTC를 연결하여
영상통화가 가능함.
협상 과정에서 시그날링서버가 중요한대
관련 내용은 아래 링크 참조
https://cbn1218.tistory.com/69
시그날링 서버 (Signaling Server)란 무엇인가??
저번 webRTC 전체 흐름에 이어 오늘은 시그날링 서버(Signaling Server)에 대해서 간단히 설명하고자함. 시그날링서버가 뭘까?peer to peer가 연결되기전에 먼저 선행해야 되는게 시그날링 서버와의 연결
cbn1218.tistory.com
(1)시그날링서버 ( server.js )
시그날링 서버에 대해서 간단히 설명하자면 피어 간 직접 연결이 설정되기 전에 필요한 정보를 중계하는 중간자 역할 을 하는
서버임.
node.js로 구현한 시그날링 서버 코드로 npm 으로 express, http, socket.io 는 다운로드 받아 server.js 에 로드 시켜놔야함.
node.js를 통해 http기반으로 웹서버와 시그날링서버를 구현함.
const express = require('express');
const http = require('http');
const socketIO = require('socket.io');
// Express 앱 생성
const app = express();
// 정적 파일들을 서빙
app.use(express.static(__dirname + '/'));
// HTTP 서버와 Socket.io 초기화
const server = http.createServer(app);
const io = socketIO(server);
// Socket.io 이벤트 핸들링
io.on('connection', (socket) => {
console.log('새로운 클라이언트 연결:', socket.id);
// 클라이언트에서 전달되는 offer, answer, ICE candidate 중계
socket.on('offer', (data) => {
console.log('offer 받음');
socket.broadcast.emit('offer', data);
});
socket.on('answer', (data) => {
console.log('answer 받음');
socket.broadcast.emit('answer', data);
});
socket.on('ice-candidate', (data) => {
console.log('ICE candidate 받음');
socket.broadcast.emit('ice-candidate', data);
});
socket.on('disconnect', () => {
console.log('클라이언트 연결 해제:', socket.id);
});
});
// 서버 실행 (포트 3000)
server.listen(3000, () => {
console.log('서버가 포트 3000에서 실행 중...');
});
(2) WebRTC 클라이언트 부분
브라우저에서 실행되는 WebRTC 클라이언트로,
사용자의 카메라/마이크를 가져오고,
시그널링 서버(Sokcet.IO)를 통해 다른 피어와 연결한 후,
P2P 방식으로 영상/음성 데이터를 송수신하는 WebRTC 클라이언트 애플리케이션 부분 임.
**코드 흐름**
1. 서버 연결 | socket = io() 로 실시간 연결 시작 |
2. 내 카메라 켜기 | getUserMedia() 사용 |
3. 통화 시작 | 버튼 누르면 상대방에게 offer 보냄 => 시그날링서버 통해 offer보냄 |
4. 상대가 수락 | 상대는 answer로 응답 => 시그날링서버 통해 answer 보냄 |
5. ICE 정보 교환 | 서로 연결 잘 된걸 확인후 ICE 정보를 교환 |
6. 연결 성공 | 영상/소리 주고받으며 통화 시작 🎉 |
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>WebRTC Node.js 예제</title>
<style>
video {
width: 45%;
margin: 5px;
background: #000;
}
</style>
</head>
<body>
<h2>간단한 WebRTC 영상 통화</h2>
<video id="localVideo" autoplay muted></video>
<video id="remoteVideo" autoplay></video>
<br>
<button id="startCall">통화 시작</button>
<!-- Socket.io 클라이언트 라이브러리 -->
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io();
const localVideo = document.getElementById('localVideo');
const remoteVideo = document.getElementById('remoteVideo');
let localStream;
let peerConnection;
const peerConfig = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' } // 무료 STUN 서버
]
};
// 1. 로컬 미디어 스트림 가져오기
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream => {
localStream = stream;
localVideo.srcObject = stream;
})
.catch(error => console.error('미디어 스트림 가져오기 실패:', error));
// 2. 피어 연결 생성 함수
function createPeerConnection() {
peerConnection = new RTCPeerConnection(peerConfig);
// peer 연결 상태 확인하기
peerConnection.onconnectionstatechange = () => {
console.log('연결 상태:', peerConnection.connectionState);
};
// 로컬 스트림의 모든 트랙을 피어 연결에 추가
localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
// ICE candidate 발생 시 시그널링 서버로 전송
peerConnection.onicecandidate = event => {
if (event.candidate) {
socket.emit('ice-candidate', event.candidate);
}
};
// 원격 스트림이 추가되면 video 태그에 설정
peerConnection.ontrack = event => {
remoteVideo.srcObject = event.streams[0];
};
}
// 3. Socket.io 받은 메시지 처리
//상대방으로부터 offer 메세지를 받으면...
socket.on('offer', async offer => {
if (!peerConnection) {
createPeerConnection();
}
await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
socket.emit('answer', answer);
});
//상대방으로부터 answer 메세지를 받으면...
socket.on('answer', async answer => {
await peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
});
//상대방으로부터 ice-candidate 메세지를 받으면...
socket.on('ice-candidate', candidate => {
if (peerConnection) {
peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
}
});
// 4. 통화 시작 버튼 이벤트 핸들러 (offer 생성 및 전송)
document.getElementById('startCall').addEventListener('click', async () => {
createPeerConnection();
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
//소켓을 통해 offer 메세지 전달
socket.emit('offer', offer);
});
</script>
</body>
</html>
기본적으로 node.js에 대한 기본지식과 소켓통신, webRTC에대한 지식등 배경지식이 많이 필요하기 때문에
어렵긴 하지만 일단 구현해보면서 코드를 이해해 보는게 어떻까 싶음.
참고로 테스트를 할때 클라이언트A 와 B를 각각 브라우져를 따로 열어 localhost로 접속하여 테스트할때는
테스트가 가능하나 클라이언트A 는 컴퓨터, 클라이언트B 는 ip를 쳐서 모바일로 접속하여 테스트시
http로 접속하기 때문에 모바일의 미디어스트림을 가져올수 없는 에러가 날가능성이 있음.
그때 크롬에서 예외적으로 허용시켜서 테스트 하는방법이 있는데 그부분에 대해서도 한번 포스팅 하겠음.