Daily
Daily (Realtime Video)
Daily는 실시간 영상/음성 통화를 앱에 넣을 수 있게 해주는 WebRTC 기반의 실시간 화상 플랫폼이에요. 영상 통화, 라이브 스트리밍, 녹화, 자막(transcription)까지 모두 한 플랫폼에서 처리하고, WebRTC 인프라(시그널링, 미디어 협상, 네트워크 탐색, 디바이스 처리 등)의 복잡함을 대신 관리해 줘서 우리는 제품 개발에만 집중하면 돼요. SDK는 JavaScript(daily-js), React(daily-react), React Native, Android, iOS, Flutter, Python, REST API를 지원해요.
출처: 문서
본문
Daily는 커스터마이즈 가능한 화상 통화 컴포넌트, 핵심 SDK(daily-js), REST API로 크게 나뉘어요. Daily가 처리하는 WebRTC 인프라 덕분에 최대 1,000명의 실시간 통화나 RTMP 기반 대규모 라이브 스트리밍까지 스케일링할 수 있어요.
핵심 구성 요소
- 실시간 영상/음성: WebRTC 기반의 실시간 화상·음성 통화. 참가자 관리, 디바이스 관리, 권한, 대기실(waiting room) 등을 지원해요.
- Daily Prebuilt: 완성된 화상 통화 UI 컴포넌트. 몇 줄의 코드만으로 웹사이트나 앱에 통화 기능을 심을 수 있어요. 참가자 타일, 컨트롤, 채팅, 화면 공유, 이모지 반응, 손 들기, 참가자 목록이 기본으로 들어 있어요.
- daily-js: 프런트엔드 JavaScript SDK. iframe 모드(Prebuilt UI)와 call object 모드(headless, 커스텀 UI)를 지원해요.
- Rooms: 특정 영상 통화의 위치이자 설정을 나타내는 단위.
/roomsREST API로 방을 생성, 목록 조회, 설정 변경, 삭제할 수 있어요.
Rooms
방(room)은 하나의 영상 통화 공간과 그 설정을 의미해요. 방에는 url 속성이 있는데, 참가자들은 이 방 URL로 통화에 참여해요. 방 URL 형태는 다음과 같아요:
https://<your-domain>.daily.co/<room-name>/
방을 만들 때 설정(config)을 지정할 수 있어요. 기본 설정 중 일부를 살펴볼게요:
{
"id": "5e3cf703-5547-47d6-a371-37b1f0b4427f",
"name": "hello",
"api_created": false,
"privacy": "public",
"url": "https://api-demo.daily.co/hello",
"created_at": "2019-01-25T23:49:42.000Z",
"config": {
"max_participants": 4,
"nbf": 1548547221,
"exp": 1548633621
"start_video_off": true,
"enable_recording": 'cloud',
}
}
기본적으로 privacy는 "public"이라 방 이름을 아는 사람은 누구나 들어올 수 있어요. nbf(not before)와 exp(expires)를 설정하면 참가자가 특정 시간 전후에는 방에 접속하지 못하게 제한할 수 있어요. 방 URL을 코드에 하드코딩하는 건 테스트용으로만 쓰고, 운영 환경에서는 서버 쪽에서 방 URL을 생성하는 게 보안상 좋아요.
Daily Prebuilt 임베드하기
Daily Prebuilt은 방 URL만 있으면 단 몇 줄로 임베드할 수 있어요. CDN 스크립트로 로드하는 예시예요:
<html>
<script crossorigin src="https://unpkg.com/@daily-co/daily-js"></script>
<body>
<script>
call = window.Daily.createFrame();
call.join({ url: 'DAILY_ROOM_URL' });
</script>
</body>
</html>
NPM으로 설치할 수도 있어요:
npm install @daily-co/daily-js
// or with yarn
yarn add @daily-co/daily-js
createFrame()에 설정을 넘겨 Prebuilt UI를 커스터마이즈할 수 있어요. 예를 들어 전체 화면으로 띄우려면:
call = window.Daily.createFrame({
showLeaveButton: true,
iframeStyle: {
position: 'fixed',
top: '0',
left: '0',
width: '100%',
height: '100%',
},
});
daily-js로 커스텀 UI 만들기
daily-js(@daily-co/daily-js)는 WebRTC 기반 영상/음성 통화를 어떤 웹 앱에든 넣을 수 있게 해주는 JavaScript SDK예요. 두 가지 통합 모드가 있어요:
- Iframe mode (Prebuilt UI): Daily가
<iframe>안에 완성된 통화 UI(참가자 타일, 컨트롤, 채팅 등)를 렌더링해 줘요. 최소한의 코드로 빠르게 동작하는 통화 UI를 원할 때 좋아요. - Call object mode (Headless): Daily는 미디어 스트림만 관리하고 UI는 만들지 않아요. UI를 완전히 직접 설계해서 완전 커스텀 화상 UI를 만들고 싶을 때 좋아요.
설치부터 시작해요:
npm install @daily-co/daily-js
Iframe mode로 방에 접속해 볼게요. DailyIframe을 임포트해서 createFrame()을 호출하면 <iframe> 요소가 생성되고 기본적으로 document.body에 붙어요:
import Daily from '@daily-co/daily-js';
const callFrame = Daily.createFrame();
특정 컨테이너에 마운트하려면 첫 번째 인자로 요소를 넘기면 돼요:
const container = document.getElementById('call-container');
const callFrame = Daily.createFrame(container);
방에 접속하고 이벤트를 구독해 보면:
await callFrame.join({ url: 'https://your-domain.daily.co/room-name' });
callFrame
.on('joined-meeting', (event) => {
console.log('Joined!', event.participants);
})
.on('participant-joined', (event) => {
console.log('Participant joined:', event.participant.user_name);
})
.on('participant-left', (event) => {
console.log('Participant left:', event.participant.user_name);
})
.on('error', (event) => {
console.error('Call error:', event.errorMsg);
});
통화를 떠나려면 leave()를, iframe을 DOM에서 완전히 제거하고 리소스를 정리하려면 destroy()를 호출해요:
await callFrame.leave();
await callFrame.destroy();
Call object mode는 iframe 없이 headless 인스턴스로 동작해요. 모든 영상/음성 렌더링은 우리가 직접 책임져요:
import Daily from '@daily-co/daily-js';
const call = Daily.createCallObject({
startVideoOff: true,
startAudioOff: false,
});
이벤트를 구독해서 참가자마다 <div>를 만들고, 트랙이 시작되면 영상/음성 요소를 붙이는 패턴이에요:
function addParticipantDiv(sessionId) {
const div = document.createElement('div');
div.id = sessionId;
document.getElementById('participants').appendChild(div);
}
call
.on('joined-meeting', (event) => {
addParticipantDiv(event.participants.local.session_id);
})
.on('participant-joined', (event) => {
addParticipantDiv(event.participant.session_id);
})
.on('track-started', (event) => {
const { participant, track, type } = event;
const id = `${participant.session_id}-${type}`;
let el = document.getElementById(id);
if (!el) {
el = document.createElement(track.kind === 'video' ? 'video' : 'audio');
el.id = id;
el.autoplay = true;
el.playsInline = true;
document.getElementById(participant.session_id).appendChild(el);
}
el.srcObject = new MediaStream([track]);
})
.on('track-stopped', (event) => {
document.getElementById(`${event.participant.session_id}-${event.type}`)?.remove();
})
.on('participant-left', (event) => {
document.getElementById(event.participant.session_id)?.remove();
})
.on('error', (event) => {
console.error('Call error:', event.errorMsg);
});
참고로 @daily-co/daily-js는 가벼운 오케스트레이션 레이어예요. join()(또는 미리 load())을 호출하면 SDK가 Daily CDN에서 WebRTC 엔진과 미디어 파이프라인이 담긴 call bundle을 가져와요. 그래서 첫 접속 시 네트워크 요청이 발생하고, loading/loaded 이벤트가 그 시작과 끝을 알려줘요. 사용자에게 로딩 지연을 숨기려면 join() 전에 load()를 호출해 미리 받아둘 수 있어요.
다른 강점
- 녹화 & 라이브 스트리밍: 클라우드/로컬 녹화, RTMP 엔드포인트로의 라이브 스트리밍(커스텀 레이아웃·오버레이 지원).
- 실시간 자막(Transcription): Deepgram 기반 실시간 자막과 언어·모델 선택.
- 미디어 처리: Banuba 기반 배경 흐림/가상 배경, Krisp 기반 AI 노이즈 제거.
- 보안: meeting token, HIPAA 컴플라이언스, 종단간 암호화, 기업 네트워크 요구사항 대응.
- Webhook: 참가자 접속, 녹화 완료 등 통화 이벤트를 서버 쪽에서 수신.