WebRTC (Web Real-Time Communication)란 별도의 플러그인이나 소프트웨어 없이 실시간으로 데이터(음성, 영상, 텍스트, 파일)를 서버(중계자) 없이 브라우저 간에 교환할 수 있도록 하는 기술이다.
WebRTC 기술은 다양한 용도로 사용되는데, 주로 음성 채팅, 화상 채팅, 데이터 공유 등을 수행할 수 있다.
WebRTC 기술을 사용하면 브라우저끼리 p2p 통신을 사용하여 별도의 서버가 필요 없어 보이지만, 사실 다음과 같은 서버들을 필요로 한다.
1) 시그널링(Signaling)이라 불리는, 클라이언트들의 통신을 조정하기 위한 메타데이터의 교환 서버 (Signaling Server)
2) NAT 환경 및 방화벽 대응을 위한 서버 (STUN / TURN Server)
이해를 돕기위해 WebRTC 전체 아키텍처를 나타내보았다.
그럼 왜 이러한 서버들이 필요한지 간략히 알아보자.
서로 다른 네트워크 환경에 있는 두 개의 peer간에 통신을 위해서는 서로 상대방의 정보(위치 및 포맷)를 먼저 파악할 필요가 있다. 이를 위해 Signaling Server가 필요로 하며, Signaling Server를 통해 데이터를 주고 받는 프로세스를 Signaling이라 부른다.
다시 말해, Signaling은 서로 다른 두 peer간 미디어 통신을 하기 위한 사전작업으로 이해할 수 있다!
Signaling 과정에서는 크게 SDP(Session Description Protocol)와 ICE(Interactive Connectivity Establishment) Candidate들을 주고받는다.
- SDP 형식에는 미디어를 전송하기 위한 설정 값(해상도, 포맷, 코덱 등)이 포함되어 있다.
- ICE는 peer 간을 연결할 수 있게 하도록 최적 경로를 찾아주는 프레임워크이며, STUN/TURN 서버를 통해 찾아낸 주소를 Candidate라 한다.
Signaling에 대한 흐름을 더 깊이있게 이해하고 싶다면, 해당 문서(https://web.dev/webrtc-infrastructure/)를 참고하면 될 듯 싶다. SDP와 Candidate 흐름과 함께 시그널링 서버를 구축하는 코드 예제가 같이 설명되어 있다.
통상적인 네트워크에서 통신하기 위해서는 Public IP를 필요로 하며, NAT 환경(사설망)에 놓인 클라이언트는 자신의 Public IP를 알지 못한다.
STUN 서버는 peer 자신의 Public IP를 알려주는 서버이다.
만약 두 peer가 같은 NAT 환경(사설망)에 있거나, 보호 정책이 엄격한 NAT/Router, 방화벽 등의 이유로 P2P 통신이 실패되는 때도 있는데 , 이러한 상황에서 사용되는 것이 TURN 서버이다.
TURN 서버는 Public 망에 존재하므로 각 peer들이 접속할 수 있으며, 각 peer들은 P2P 통신을 하는 것이 아닌 TURN 서버(릴레이 서버 역할)를 경유하여 통신하게 된다.
아무래도 직접 통신하지 않고 TURN 서버가 모든 트래픽을 중계하며 통신하기 때문에 오버헤드와 지연이 발생할 수 있어 어쩔 수 없는 경우에만 사용된다.
아래는 WebRTC에서 데이터 교환이 일어나는 전체적인 다이어그램을 나타낸다.
WebRTC에는 크게 3가지 구현방식이 존재하며, 각 방식에는 장단점이 존재하니 자신의 환경에 맞는 방식을 선택하여 구현할 필요가 있다.
전형적인 P2P WebRTC 방식으로 이때까지 설명했던 WebRTC 구현 방식과 동일하다. 즉, 각 peer는 다른 모든 사용자에게 스트림을 보내고, 또한 peer는 다른 모든 사용자로부터 스트림을 받는 방식이다.
해당 방식은 직접적으로 peer간 연결되므로 실시간성이 보장된다는 장점이 있지만, 동시 사용자 수가 늘어날수록 각 클라이언트의 과부하가 급격히 증가하므로 확장성 면에서 가장 비효율적인 방식이다.
따라서, MESH 방식은 동시 최대 사용자 수가 4~6명일 때 사용하면 가장 비용 효율적인 방식이다.
MCU 방식은 각 peer는 중앙 서버(Media Server)로 스트림을 보내고, 중앙 서버에서는 스트림을 가공(인코딩/디코딩)하여 모든 peer들에게 다시 스트림을 보내는 방식이다.
즉, P2P 연결이 아닌 Client와 Server 간의 peer를 연결하며, MESH 방식보다 더 많은 사용자를 처리할 수 있다.
각 Client의 부담은 적어져 N:M 구조에서 사용될 수 있지만, 실시간성이 저해된다는 점과 중앙 서버는 다중 스트림을 처리해야 되므로 서버 비용이 비싸다는 단점이 존재한다.
SFU는 3가지 방식 중 WebRTC 어플리케이션에서 가장 많이 사용되는 방식이라 한다. 해당 방식도 MCU과 마찬가지로 중앙 서버가 포함되며, 각 peer는 Server에게만 자신의 스트림을 보내면 된다. (UpLink 1개)
MCU 방식과의 차이점이라면 서버에서는 추가 처리 없이 해당 스트림을 다른 Client들에게 보내며, 각 Client들은 상대방 수만큼 DownLink를 유지해야 한다.
즉, 1:N 방식이나 소규모 N:M 형식의 스트리밍에 적합하다 할 수 있으며, 대규모 N:M 구조에서는 여전히 각 Client들의 부하가 증가한다는 단점이 있다.
OpenVidu는 웹 또는 모바일 환경에서 화상 회의 기능을 쉽게 추가할 수 있도록 해주는 오픈소스 멀티 플랫폼이다. OpenVidu의 핵심 기술은 WebRTC이며, 내부적으로 Kurento(WebRTC 미디어 서버 역할을 하는 클라이언트 API 세트) 기반으로 동작한다.
OpenVidu의 장점은 모든 하위 수준의 작업들을 추상화시켜, WebRTC, ICE Candidates, KMS 등의 까다로운 기술들을 몰라도 쉽게 사용할 수 있다는 점이다.
또한 많은 프론트엔드 기술들을 호환하고, 자체 호스트 기능을 사용하여 서버에 쉽게 배포할 수 있다.
OpenVidu 공식 문서에서는 친절하게 각 프론트엔드 기술마다 튜토리얼을 제공해준다. 여기서는 React를 사용하므로 해당 문서를 참조하여 추가 기능을 위해 커스터마이징 하였다. openvidu-server는 자체 호스팅하여, 나중 EC2에 docker 컨테이너로 띄울 예정이다.
로컬 환경에서 테스트하고자 한다면 튜토리얼에 있는 git project를 클론하여 실행한 뒤, 컴퓨터에 docker를 설치하여 터미널에 아래 명령어를 입력하여 컨테이너를 실행시켜주도록 한다.
docker run -p 4443:4443 --rm -e OPENVIDU_SECRET=MY_SECRET openvidu/openvidu-server-kms:latest
컨테이너가 실행되고, 인터넷 주소창에 localhost:4443
에 접속하면 로컬 openvidu-server가 정상적으로 작동되면서 로컬
환경에서 테스트할 수 있어진다.
아래 사진은 튜토리얼을 보고 커스터마이징을 마친 상태이다. 튜토리얼에 중요 코드 부분들은 설명이 잘돼 있어, 내부 흐름을 이해하는데 크게 어렵지 않았다.
디자인은 google meet를 참조하였고, styled-component 라이브러리와 Material-UI를 사용하였다.
기본 튜토리얼에서 추가한 기능은 다음과 같다.
- 캠, 마이크, 스피커 on/off 기능
- 구독자 발언 감지 기능 (테두리 색 변형)
- 그룹 채팅 기능 (오른쪽 하단 아이콘 클릭 시 슬라이드로 표시)
위 기능들을 위해 state 값을 다음과 같이 정의하였다.
this.state = {
mySessionId:'SessionA',
myUserName: 'Participant'+Math.floor(Math.random() * 100),
session: undefined,
mainStreamManager: undefined,
publisher: undefined, // 로컬 웹캠 스트림
subscribers: [], // 다른 사용자의 활성 스트림
isMic: true,
isCamera: true,
isSpeaker: true,
isChat: false,
};
각 기능들의 코드는 아래 github 주소를 참고하면 될 것 같다.
https://github.com/zzho-o/study-platform_front/blob/master/src/OnlineMeeting/OnlineMeeting.jsx
해당 문서에서 docker를 이용해 원하는 곳에 OpenVidu를 배포하는 가이드가 설명되어 있어 해당 내용을 참고하였다.
OpenVidu는 Docker-Compose로 배포되며, 아래 서비스들의 컨테이너들로 구성되어있다.
- Coturn 서버는 TURN 서버를 말한다.
- https 통신(WebRTC 사용하므로)을 제공하기 위해 리버스 프록시 기능으로 Nginx를 사용한다.
먼저 EC2에 접속해서 각 OS에 맞는 docker와 docker-compose(1.24 이상 버전)를 설치해주도록 한다.
HTTPS(WebRTC를 사용하므로)를 사용해 배포하기 위해 Public IP를 가리키는 도메인 이름을 구성할 필요가 있다. 본인은 가비아에서 .shop 도메인을 (현재 기준) 500원 주고 구매하였다.
다음으로 OpenVidu 설치 전 반드시 아래 포트들이 열려 있어야 정상적으로 서버가 작동한다. EC2 보안 그룹 - 인바운드 규칙에서 아래 포트들에 대해서는 모든 IP를 허용해준다.
그리고 또 하나 주의할 점은 아래 밑줄 그인 포트들에 대해서는 OpenVidu를 구성하는 컨테이너들이 할당받아야 한다. 즉, 다른 서비스들이 해당 포트 중 1개라도 할당받고 있다면 OpenVidu 서비스가 정상적으로 실행되지 않는다.
특히, 많이 사용되는 Nginx(80번), Redis(6379번) 등을 이미 설치하였다면 PID를 찾아 미리 kill -9 시켜주어야 한다.
# OpenVidu 배포하기 위해 루트 권한 필요
$ sudo su
# OpenVidu 설치 권장 폴더 /opt 이동
$ cd /opt
# OpenVidu 설치
$ curl https://s3-eu-west-1.amazonaws.com/aws.openvidu.io/install_openvidu_latest.sh | bash
# 설치 후 openvidu 폴더로 이동
$ cd openvidu
# 도메인 설정과 OpenVidu 통신을 위한 Secret 값이 담긴 설정 파일 작성
$ nano .env
.env 파일에서 다음 항목들을 설정해준다.
# 앞서 구성한 도메인 주소 작성
DOMAIN_OR_PUBLIC_IP=
# openvidu server와 통신을 위한 secret key (클라이언트에서 필요)
OPENVIDU_SECRET=MY_SECRET
# 인증서 타입으로 letsencrypt 사용
CERTIFICATE_TYPE=letsencrypt
# 인증서 타입이 letsencrypt일 경우 알림 발송을 위한 이메일 작성
LETSENCRYPT_EMAIL=user@example.com
인증서 타입의 기본값은 selfsigned 이므로 Cerbot으로 인증서로 발급받기 위해 let's encrypt 타입으로 설정해준다. 설정 파일을 저장하고 openvidu를 실행시켜본다.
# /opt/openvidu 폴더에서 명령어 실행
$ ./openvidu start
# 종료는 ./openvidu stop
Creating openvidu-docker-compose_coturn_1 ... done
Creating openvidu-docker-compose_app_1 ... done
Creating openvidu-docker-compose_kms_1 ... done
Creating openvidu-docker-compose_nginx_1 ... done
Creating openvidu-docker-compose_redis_1 ... done
Creating openvidu-docker-compose_openvidu-server_1 ... done
----------------------------------------------------
OpenVidu Platform is ready!
---------------------------
* OpenVidu Server: https://DOMAIN_OR_PUBLIC_IP/
* OpenVidu Dashboard: https://DOMAIN_OR_PUBLIC_IP/dashboard/
----------------------------------------------------
이제 ctrl + c를 누르면 OpenVidu가 백그라운드에서 실행된다. 그리고 https://DOMAIN_OR_PUBLIC_IP/ 를 주소창에 입력하여 아래 홈페이지에 접속된다면 배포가 성공적으로 된 것이다.
이제 클라이언트 코드에서 서버를 가리키는 변수와 Secret 값만 변경해서 실행시키면 다른 네트워크 환경에 있는 Client들과도 통신이 잘 작동된다.
https://andonekwon.tistory.com/59?category=447798
STUN/TURN 서버 이해하는데 참고하였습니다.
https://millo-l.github.io/WebRTC-구현-방식-Mesh-SFU-MCU/
WebRTC 구현 방식 이해하는데 참고하였습니다.
https://velog.io/@jsb100800/개발-WebRTC-SpringBoot-Vue.js를-활용한-Group-Video-Call
WebRTC 기술을 이해하는데 참고하였습니다.
https://developer.mozilla.org/enUS/docs/Web/API/WebRTC_API/Connectivity
WebRTC 다이어그램 그림 인용
https://docs.openvidu.io/en/stable/deployment/ce/on-premises/
온프레미스에 openvidu 배포하는데 참고하였습니다.
인터넷에 찾아봐도 다 어렵고 제대로 된 정보가 없었는데 이렇게 쉽게 설명해주시니 머리속에 쏙쏙 들어오네요... 앞으로 이 블로그 자주 이용해서 참고용으로 사용하겠습니다!! 감사합니다!!