Firebase + WebRTC Codelab

1. 소개

이 Codelab에서는 브라우저의 WebRTC API와 신호를 위한 Cloud Firestore를 사용하여 간단한 영상 채팅 애플리케이션을 빌드하는 방법을 알아봅니다. 이 애플리케이션은 FirebaseRTC라고 하며 WebRTC가 사용 설정된 애플리케이션을 빌드하는 방법의 기본 사항을 설명하는 간단한 예시입니다.

학습할 내용

  • WebRTC를 사용하여 웹 애플리케이션에서 영상 통화 시작
  • Cloud Firestore를 사용하여 원격 참여자에게 알림

필요한 항목

이 Codelab을 시작하기 전에 다음을 설치했는지 확인합니다.

  • 일반적으로 Node.js와 함께 제공되는 npm - 노드 LTS 권장

2. Firebase 프로젝트 만들기 및 설정

Firebase 프로젝트 만들기

  1. Firebase Console에서 프로젝트 추가를 클릭한 후 Firebase 프로젝트의 이름을 FirebaseRTC로 지정합니다.

Firebase 프로젝트의 프로젝트 ID를 기억합니다.

  1. '프로젝트 만들기'를 클릭합니다.

빌드하려는 애플리케이션은 웹에서 사용 가능한 두 가지 Firebase 서비스를 사용합니다.

  • Cloud Firestore를 사용하면 구조화된 데이터를 클라우드에 저장하고 데이터가 업데이트될 때 즉시 알림을 받을 수 있습니다.
  • Firebase 호스팅에서 정적 애셋 호스팅 및 제공

이 Codelab의 경우 클론할 프로젝트에 Firebase 호스팅을 이미 구성했습니다. 하지만 Cloud Firestore의 경우 Firebase Console을 사용하여 서비스를 구성하고 사용 설정하는 방법을 안내해 드립니다.

Cloud Firestore 사용 설정

앱에서 Cloud Firestore를 사용하여 채팅 메시지를 저장하고 새 채팅 메시지를 수신합니다.

Cloud Firestore를 사용 설정해야 합니다.

  1. Firebase Console 메뉴의 개발 섹션에서 데이터베이스를 클릭합니다.
  2. Cloud Firestore 창에서 데이터베이스 만들기를 클릭합니다.
  3. 테스트 모드로 시작 옵션을 선택한 다음 보안 규칙에 대한 면책조항을 읽은 후 사용 설정을 클릭합니다.

테스트 모드를 사용하면 개발 중에 데이터베이스에 자유롭게 쓸 수 있습니다. 이 Codelab의 뒷부분에서 데이터베이스 보안을 강화하겠습니다.

3. 샘플 코드 가져오기

다음과 같이 명령줄에서 GitHub 저장소를 클론합니다.

git clone https://github.com/webrtc/FirebaseRTC

샘플 코드를 FirebaseRTC 디렉터리에 클론했어야 합니다. 이제부터 이 명령줄에서 명령줄이 실행되는지 확인합니다.

cd FirebaseRTC

시작 앱 가져오기

편집기에서 FirebaseRTC의 파일을 열고 아래 안내에 따라 파일을 변경합니다. 이 디렉터리에는 아직 작동하지 않는 WebRTC 앱으로 구성된 Codelab의 시작 코드가 포함되어 있습니다. 이 Codelab에서는 계속해서 작동하도록 합니다.

4. Firebase 명령줄 인터페이스 설치하기

Firebase 명령줄 인터페이스 (CLI)를 사용하면 로컬에서 웹 앱을 제공하고 웹 앱을 Firebase 호스팅에 배포할 수 있습니다.

  1. 다음 npm 명령어를 실행하여 CLI를 설치합니다. sh npm -g install firebase-tools
  1. sh firebase --version 명령어를 실행하여 CLI가 올바르게 설치되었는지 확인합니다.

Firebase CLI 버전이 v6.7.1 이상인지 확인합니다.

  1. sh firebase login 명령어를 실행하여 Firebase CLI를 승인합니다.

앱의 로컬 디렉터리 및 파일에서 Firebase 호스팅의 앱 구성을 가져오도록 웹 앱 템플릿을 설정했습니다. 그러나 그러려면 앱을 Firebase 프로젝트와 연결해야 합니다.

  1. 다음 명령어를 실행하여 앱을 Firebase 프로젝트와 연결합니다. sh firebase use --add

  2. 메시지가 표시되면 프로젝트 ID를 선택한 다음 Firebase 프로젝트에 별칭을 지정합니다.

별칭은 여러 환경 (프로덕션, 스테이징 등)에서 사용하는 경우 유용합니다. 그러나 이 Codelab에서는 default 별칭을 사용하면 됩니다.

  1. 명령줄의 나머지 안내를 따릅니다.

5. 로컬 서버 실행

실제로 앱에서 작업을 시작할 준비가 되었습니다. 로컬에서 앱을 실행해 보겠습니다.

  1. 다음 Firebase CLI 명령어를 실행합니다. sh firebase serve --only hosting

  2. 명령줄에 다음과 같은 응답이 표시됩니다.hosting: Local server: http://localhost:5000

Firebase 호스팅 에뮬레이터를 사용하여 로컬에서 앱을 제공하고 있습니다. 이제 http://localhost:5000에서 웹 앱을 사용할 수 있습니다.

  1. http://localhost:5000에서 앱을 엽니다.

Firebase 프로젝트에 연결된 FirebaseRTC의 사본이 표시됩니다.

앱이 Firebase 프로젝트에 자동으로 연결되었습니다.

6. 새 방 만들기

이 애플리케이션에서 각 영상 채팅 세션을 채팅방이라고 합니다. 사용자는 애플리케이션에서 버튼을 클릭하여 새 채팅방을 만들 수 있습니다. 그러면 원격 당사자가 같은 방에 참여할 때 사용할 수 있는 ID가 생성됩니다. ID는 Cloud Firestore에서 각 회의실의 키로 사용됩니다.

각 채팅방에는 쿠폰과 답변 모두에 대한 RTCSessionDescriptions와 각 당사자의 ICE 후보가 포함된 별도의 컬렉션 두 개가 포함됩니다.

첫 번째 작업은 호출자의 초기 쿠폰을 사용하여 새 채팅방을 만들기 위한 누락된 코드를 구현하는 것입니다. public/app.js를 열고 // Add code for creating a room here 주석을 찾아 다음 코드를 추가합니다.

const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);

const roomWithOffer = {
    offer: {
        type: offer.type,
        sdp: offer.sdp
    }
}
const roomRef = await db.collection('rooms').add(roomWithOffer);
const roomId = roomRef.id;
document.querySelector('#currentRoom').innerText = `Current room is ${roomId} - You are the caller!`

첫 번째 줄은 호출자의 오퍼를 나타내는 RTCSessionDescription를 만듭니다. 그러면 로컬 설명으로 설정되고, 마지막으로 Cloud Firestore의 새 채팅방 객체에 기록됩니다.

다음으로, 데이터베이스 변경사항을 수신 대기하고 피호출자의 답변이 추가된 시점을 감지합니다.

roomRef.onSnapshot(async snapshot -> {
    console.log('Got updated room:', snapshot.data());
    const data = snapshot.data();
    if (!peerConnection.currentRemoteDescription && data.answer) {
        console.log('Set remote description: ', data.answer);
        const answer = new RTCSessionDescription(data.answer)
        await peerConnection.setRemoteDescription(answer);
    }
});

그러면 피호출자가 응답의 RTCSessionDescription를 작성할 때까지 기다렸다가 호출자 RTCPeerConnection에서 원격 설명으로 설정합니다.

7. 채팅방에 참여 중

다음 단계는 기존 채팅방에 참여하기 위한 로직을 구현하는 것입니다. 채팅방 참여 버튼을 클릭하고 참여하기 위한 채팅방 ID를 입력하면 됩니다. 여기에서 할 일은 답변에 대한 RTCSessionDescription 생성을 구현하고 그에 따라 데이터베이스의 채팅방을 업데이트하는 것입니다.

const offer = roomSnapshot.data().offer;
await peerConnection.setRemoteDescription(offer);
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);

const roomWithAnswer = {
    answer: {
        type: answer.type,
        sdp: answer.sdp
    }
}
await roomRef.update(roomWithAnswer);

위의 코드에서는 먼저 호출자에서 쿠폰을 추출하고 원격 설명으로 설정한 RTCSessionDescription를 만듭니다. 다음으로 답변을 만들고 로컬 설명으로 설정한 후 데이터베이스를 업데이트합니다. 데이터베이스를 업데이트하면 호출자 측에서 onSnapshot 콜백이 트리거되며, 그러면 피호출자의 답변에 따라 원격 설명이 설정됩니다. 그러면 호출자와 피호출자 간의 RTCSessionDescription 객체 교환이 완료됩니다.

8. ICE 후보 수집

또한 호출자와 피호출자가 서로 연결하기 전에 WebRTC에 원격 피어에 연결하는 방법을 알려주는 ICE 후보도 교환해야 합니다. 다음 작업은 ICE 후보를 수신 대기하고 데이터베이스의 컬렉션에 추가하는 코드를 구현하는 것입니다. collectIceCandidates 함수를 찾아 다음 코드를 추가합니다.

async function collectIceCandidates(roomRef, peerConnection,
                                    localName, remoteName) {
    const candidatesCollection = roomRef.collection(localName);

    peerConnection.addEventListener('icecandidate', event -> {
        if (event.candidate) {
            const json = event.candidate.toJSON();
            candidatesCollection.add(json);
        }
    });

    roomRef.collection(remoteName).onSnapshot(snapshot -> {
        snapshot.docChanges().forEach(change -> {
            if (change.type === "added") {
                const candidate = new RTCIceCandidate(change.doc.data());
                peerConnection.addIceCandidate(candidate);
            }
        });
    })
}

이 함수는 두 가지 작업을 합니다. WebRTC API에서 ICE 후보를 수집하고 데이터베이스에 추가하고 원격 피어에서 추가된 ICE 후보를 수신 대기하여 RTCPeerConnection 인스턴스에 추가합니다. 데이터베이스 변경사항을 수신 대기할 때 새로운 추가를 필터링하지 않는 것이 중요합니다. 그러지 않으면 동일한 ICE 후보가 반복적으로 추가되기 때문입니다.

9. 결론

이 Codelab에서는 Cloud Firestore를 사용하여 WebRTC 신호를 구현하는 방법과 이를 사용하여 간단한 영상 채팅 애플리케이션을 만드는 방법을 배웠습니다.

자세한 내용은 다음 자료를 참고하세요.

  1. FirebaseRTC 소스 코드
  2. WebRTC 샘플
  3. Cloud Firestore