Back to Blog

아고라 웹 SDK를 Angular 17과 통합하는 방법: 단계별 가이드

Integrating Agora Web SDK with Angular 17: A Step-by-Step Guide

In today’s fast-paced digital world, real-time communication is a must-have feature in most web applications. Agora’s Video SDK simplifies building scalable real-time video applications.

This guide will walk through the process of implementing the Agora Web SDK with Angular 17, enabling seamless integration of live audio and video streaming into web apps.

초기 설정이 완료되면 Agora RTC Web SDK를 Angular 서비스로 구현하는 세부 사항에 대해 살펴보겠습니다. 특히 로컬 및 원격 비디오 스트림을 처리하기 위한 Angular 컴포넌트의 생성 및 관리에 중점을 둘 것입니다. 각 단계는 실용적이고 실습 중심의 이해를 제공하도록 설계되어 모든 컴포넌트를 효과적으로 연결할 수 있도록 도와드립니다. 이 가이드를 완료하면 웹 애플리케이션을 완전한 실시간 통신 플랫폼으로 변환할 준비가 될 것입니다.

시작해 보겠습니다.

필수 조건

  • Node.JS
  • Angular CLI (npm install -g @angular/cli )
  • Agora.io 계정
  • HTML/CSS/JS 기본 이해
  • 코드 편집기 (저는 VSCode를 선호합니다)

프로젝트 설정

새 프로젝트 생성 (agora-angular-demo를 프로젝트 이름으로 교체)

ng new agora-angular-demo

프로젝트 디렉토리로 이동하려면

cd agora-angular-demo

Agora Video SDK for Web을 설치하세요.

npm i agora-rtc-sdk-ng

아고라 Video SDK 구현

Agora SDK는 세 가지 핵심 요소로 구성되어 있습니다: Agora RTC 엔진, 로컬 사용자, 및 원격 사용자. 이 요소들을 Angular와 통합하기 위해 Agora RTC 엔진을 서비스로 설정하고, 로컬 사용자 및 원격 사용자를 컴포넌트로 구성합니다.

아고라 서비스

Agora 서비스를 생성하려면 다음 명령어를 사용합니다.

ng generate service agora

아고라 서비스 구성 agora.service.ts 파일을 열고 Agora Video Web SDK를 가져옵니다. 먼저 Agora 서비스의 속성을 설정합니다:

  • The client는 채널에 참여하고 나가는 데 사용되는 Agora RTC 클라이언트를 나타냅니다.
  • Agora AppId는 환경 변수에서 로드됩니다(Agora Console에서 확인하세요)
  • 다른 구성 요소가 가입 및 탈퇴 이벤트에 구독할 수 있는 관찰 가능한 channelJoined$를 설정합니다.

아고라 서비스는 3개의 핵심 기능을 노출합니다: joinChannelleaveChannel는 Agora 채널에 가입 및 탈퇴하는 기능이며, setupLocalTracks는 마이크 및 카메라 스트림을 초기화하는 기능입니다.

/* agora.service.ts */
import { Injectable } from '@angular/core';
import AgoraRTC, { ILocalTrack, IAgoraRTCClient } from 'agora-rtc-sdk-ng';
import { environment } from '../environments/environments';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AgoraService {
  private client: IAgoraRTCClient;
  private appId = environment.AgoraAppId

  private channelJoinedSource = new BehaviorSubject<boolean>(false);
  channelJoined$ = this.channelJoinedSource.asObservable();

  constructor() { 
    if(this.appId == '')
      console.error('APPID REQUIRED -- Open AgoraService.ts and update appId ')
    this.client = AgoraRTC.createClient({ mode: 'rtc', codec: 'vp9'})
  }

  async joinChannel(channelName: string, token: string | null, uid: string | null) {
    await this.client.join(this.appId, channelName, token, uid)
    this.channelJoinedSource.next(true)
  }

  async leaveChannel() {
    await this.client.leave()
    this.channelJoinedSource.next(false)
  }

  setupLocalTracks(): Promise<ILocalTrack[]> {
    return AgoraRTC.createMicrophoneAndCameraTracks();
  }

  getClient() {
    return this.client
  }
}

로컬 및 원격 사용자 구성 요소

Agora 서비스 설정이 완료되었으므로 이제 로컬 및 원격 사용자를 위한 구성 요소를 생성해야 합니다. 요소를 분리하기 위해 몇 가지 구성 요소를 생성해야 합니다:

  • 로컬 사용자 컴포넌트는 두 개의 컴포넌트로 구성됩니다. local-stream 는 Agora 서비스와 상호작용하는 주요 컴포넌트이며, 내장된 컴포넌트 media-controls 는 사용자의 동작(마이크/비디오 음소거 또는 채널 이탈)을 local-stream로 전달하는 버튼을 포함합니다.
  • 원격 사용자는 구조가 유사하며, 모든 원격 사용자 div 요소를 포함하는 컨테이너로 사용되는 주요 구성 요소 (remote-stream)와 개별 원격 사용자를 위한 별도의 구성 요소 remote-user 로 구성됩니다. 이 구성 요소들은 원격 사용자가 오디오/비디오 스트림을 게시/게시 취소할 때마다 컨테이너에서 동적으로 추가/제거됩니다.

로컬 사용자 구성 요소

로컬 사용자 구성 요소를 생성하려면 다음 명령어를 사용합니다

ng generate component local-stream
ng generate component media-controls

다음 디렉토리가 생성됩니다. 다음 디렉토리와 해당 파일(.html, .css, .ts):

Integrating Agora Web SDK with Angular 17: A Step-by-Step Guide screenshot 2

HTML부터 시작하여 local-stream.component.html 파일을 열고, 점선으로 표시된 부분(placeholder 태그)을 다음과 같이 교체합니다:

<div id="local-video-container">
  <app-media-controls></app-media-controls>
  <div #localVideo id="local-video"></div>
</div>

다음으로 local-stream.component.ts 파일을 열습니다. 먼저 MediaControls를 import하여 MediaControls 컴포넌트를 local-stream 컴포넌트의 일부로 로드할 수 있도록 합니다. 그런 다음 #localVideo에 대한 참조를 추가하여 Agora SDK에 전달하여 해당 <div/>에서 동영상을 재생할 수 있도록 합니다. 사용자가 채널을 떠날 때마다 이벤트를 발생시키기 위해 EventEmitter를 추가해야 합니다. 다음으로 선언할 변수는 client로, 이는 AgoraService에서 전달됩니다. 이어서 로컬 트랙(마이크, 비디오, 화면)에 대한 참조를 추가하고 각 트랙의 음소거 상태를 표시하기 위해 localTracksActive를 추가합니다. 마지막으로 AgoraService가 채널에 가입했는지 추적하기 위해 구독과 해당 플래그를 설정합니다.

이 컴포넌트의 뷰가 초기화될 때 local client를 트리거하여 Agora SDK를 통해 로컬 miccamera 트랙을 초기화하고, 채널에 가입할 때 이를 게시하기 위한 리스너를 설정합니다. 사용자가 채널을 떠날 때 앱은 뷰를 제거하며, 이는 handleLeave()를 트리거하여 모든 트랙을 게시 취소하고 닫으며, 마이크 및 카메라 리소스를 해제합니다.

muteTrack()에서 우리는 setEnabled()setMuted() 대신 사용합니다. 원격 사용자에게는 효과가 매우 유사하지만, 로컬 사용자의 경험은 다릅니다. 왜냐하면 setEnabled()는 마이크/카메라 리소스를 해제하여 그들의 마이크나 카메라가 활성화되지 않았음을 표시하기 때문입니다.

import { AfterViewInit, Component, ElementRef, EventEmitter, Output, ViewChild } from '@angular/core'
import { ILocalTrack, IAgoraRTCClient } from 'agora-rtc-sdk-ng';
import { AgoraService } from '../agora.service';
import { MediaControlsComponent } from '../media-controls/media-controls.component';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-local-stream',
  standalone: true,
  imports: [MediaControlsComponent],
  templateUrl: './local-stream.component.html',
  styleUrl: './local-stream.component.css'
})
export class LocalStreamComponent implements AfterViewInit {
  @ViewChild('localVideo', { static: true }) localVideo!: ElementRef<HTMLDivElement>;
  @Output() leaveChannel = new EventEmitter<void>();

  private client: IAgoraRTCClient;

  private localMicTrack!: ILocalTrack;
  private localVideoTrack!: ILocalTrack;
  private localScreenTracks?: ILocalTrack[];

  private channelJoined: boolean = false;
  private subscription: Subscription = new Subscription();

  private localTracksActive = {
    audio: false,
    video: false,
    screen: false,
  }

  // Mapping to simplify getting/setting track-state
  private trackNameMapping: { [key:string]: 'audio' | 'video' | 'screen' } = {
    audio: 'audio',
    video: 'video',
    screen: 'screen',
  }

  constructor(private agoraService: AgoraService) {
    this.client = this.agoraService.getClient()
  }

  async ngAfterViewInit(): Promise<void> {
    [this.localMicTrack, this.localVideoTrack] = await this.agoraService.setupLocalTracks();
    this.localTracksActive.audio = this.localMicTrack ? true : false
    this.localTracksActive.video = this.localVideoTrack ? true : false
    // play video track in localStreamComponent div
    this.localVideoTrack.play(this.localVideo.nativeElement);
    this.subscription.add(this.agoraService.channelJoined$.subscribe(status => {
      this.channelJoined = status
      if(status) {
        this.publishTracks() // publish the tracks once we are in the channel
      }
    }))
  }

  async ngOnDestroy() {
    // leave the channel if the component unmounts
    this.handleLeaveChannel()
  }

  async publishTracks() {
    await this.client.publish([ this.localMicTrack, this.localVideoTrack ])
  }

  async unpublishTracks() {
    await this.client.publish([ this.localMicTrack, this.localVideoTrack ])
  }

  async handleLeaveChannel(): Promise<void> {
    if(this.channelJoined) {
      const tracks = [this.localMicTrack, this.localVideoTrack]
      tracks.forEach(track => {
        track.close()
      })
      await this.client.unpublish(tracks)
      await this.agoraService.leaveChannel()
    }
    this.leaveChannel.emit()
  }

  async muteTrack(trackName: string, enabled: boolean): Promise<boolean> {
    const track = trackName === 'mic' ? this.localMicTrack : this.localVideoTrack;
    await track.setEnabled(enabled);
    this.setTrackState(trackName, enabled)
    return enabled;
  }

  async startScreenShare(): Promise<boolean> {
    // TODO: add start screen share
    // Listen for screen share ended event (from browser ui button)
    // this.localScreenTracks[0]?.on("track-ended", () => {
    //   this.stopScreenShare()
    // })    
    return true;
  }

  async stopScreenShare(): Promise<boolean> {
    // TODO: add stop screenshare
    return false;
  }

  getTrackState(trackName: string): boolean | undefined {
    const key = this.trackNameMapping[trackName]
    if (key) {
      return this.localTracksActive[key]
    }
    console.log(`Get Track State Error: Unknown trackName: ${trackName}`)
    return
  }

  setTrackState(trackName: string, state: boolean): void {
    const key = this.trackNameMapping[trackName]
    if (key) {
      this.localTracksActive[key] = state
    }
    console.log(`Set Track State Error: Unknown trackName: ${trackName}`)
    return
  }

}

다음으로 media-controls 컴포넌트로 이동합니다. 다시 HTML부터 시작해 media-controls.component.html 파일을 열고, placeholder 태그를 다음과 같이 교체합니다:

<div id="local-media-controls">
  <button #micButton (click)="handleMicToggle($event)" class="media-active">Mic</button>
  <button #videoButton (click)="handleVideoToggle($event)" class="media-active">Video</button>
  <button #screenShareButton (click)="handleScreenShare($event)" class="media-active">Screen</button>
  <button #leaveButton (click)="handleLeaveChannel()" class="media-active">End</button>
</div>

구조는 간단합니다. 컨테이너와 버튼 세트입니다. 각 버튼에는 클릭 이벤트가 있으며, 이 이벤트는 media-controls.component.ts에서 정의합니다.

다음으로 media-controls.component.ts를 엽니다. 이 컴포넌트는 LocalStreamComponent를 사용하여 마이크와 카메라를 음소거/음소거 해제, 화면 공유 시작/중지, 채널 이탈 기능을 구현하기 때문에 비교적 가벼운 구조입니다. 버튼 상태는 media-activemuted 클래스를 사용하여 업데이트됩니다.

import { Component, ElementRef, ViewChild } from '@angular/core'
import { LocalStreamComponent } from '../local-stream/local-stream.component';

 @Component({
   selector: 'app-media-controls',
   standalone: true,
   imports: [],
   templateUrl: './media-controls.component.html',
   styleUrl: './media-controls.component.css'
 })
 export class MediaControlsComponent {
   // Buttons
   @ViewChild('micButton', { static: true }) micButton!: ElementRef<HTMLButtonElement>;
   @ViewChild('videoButton', { static: true }) videoButton!: ElementRef<HTMLButtonElement>;
   @ViewChild('screenShareButton', { static: true }) screenShareButton!: ElementRef<HTMLButtonElement>;
   @ViewChild('leaveButton', { static: true }) leaveButton!: ElementRef<HTMLButtonElement>;

   constructor (private localStream: LocalStreamComponent) {}

   handleMicToggle(e: Event): void {
     const isActive = this.localStream.getTrackState('mic') // get active state
     this.localStream.muteTrack('mic', !isActive)
     this.toggleButtonActiveState(e.target as HTMLDivElement)
   }

   handleVideoToggle(e: Event): void {
     const isActive = this.localStream.getTrackState('video') ?? false// get active state
     this.localStream.muteTrack('video', !isActive)
     this.toggleButtonActiveState(e.target as HTMLDivElement)
   }

   toggleButtonActiveState(button: HTMLDivElement): void {
     button.classList.toggle('media-active')    // Add/Remove active class
     button.classList.toggle('muted')           // Add/Remove muted class
   }

   handleScreenShare(e: Event): void {
     const isActive = this.localStream.getTrackState('screen') // get active state
     if (isActive) {
       this.localStream.startScreenShare()
     } else {
       this.localStream.stopScreenShare()
     }
     this.toggleButtonActiveState(e.target as HTMLDivElement)
   }

   handleLeaveChannel(): void {
     this.localStream.handleLeaveChannel()
   }
}

원격 사용자 구성 요소

원격 사용자 구성 요소를 생성하려면 다음 명령어를 사용합니다:

ng generate component remote-stream
ng generate component remote-user

다음 디렉토리가 생성됩니다. 다음 디렉토리와 해당 파일(.html, .css, .ts)을 확인하세요:

Integrating Agora Web SDK with Angular 17: A Step-by-Step Guide screenshot 3

HTML부터 시작하겠습니다. remote-stream.component.html 파일을 열고, placeholder 태그를 다음과 같이 교체합니다:

<div id="remote-video-container">
  <ng-container #remoteVideoContainer ></ng-container>
</div>

The structure is simple. The <div /> tag is wrapped in the <ng-container /> tag. The <ng-container /> tag uses Angular's built-in functionality to create a new component that can be added to or removed from the parent container without affecting the rest of the DOM.

Next, open the remote-stream.component.ts file. This component imports clientfrom AgoraServiceand detects when remote users publish or cancel streams, managing them within the UI.

import { Component, OnInit, OnDestroy, ViewChild, ViewContainerRef, ComponentRef } from '@angular/core'
import { AgoraService } from '../agora.service';
import { IAgoraRTCClient, IAgoraRTCRemoteUser, UID } from 'agora-rtc-sdk-ng';
import { RemoteUserComponent } from '../remote-user/remote-user.component';

@Component({
  selector: 'app-remote-stream',
  standalone: true,
  imports: [],
  templateUrl: './remote-stream.component.html',
  styleUrl: './remote-stream.component.css'
})
export class RemoteStreamComponent implements OnInit, OnDestroy {
  client: IAgoraRTCClient;
  remoteUserComponentRefs: Map<string, ComponentRef<RemoteUserComponent>>;

  @ViewChild('remoteVideoContainer', { read: ViewContainerRef }) remoteVideoContainer!: ViewContainerRef;

  constructor(private agoraService: AgoraService) {
    this.client = this.agoraService.getClient()
    this.remoteUserComponentRefs = new Map()
  }

  ngOnInit(): void {
    // add listeners when component mounts
    this.client.on('user-published', this.handleRemoteUserPublished)
    this.client.on('user-unpublished', this.handleRemoteUserUnpublished)
  }

  ngOnDestroy(): void {
    // remove listeners when component is removed
    this.client.off('user-published', this.handleRemoteUserPublished)
    this.client.off('user-unpublished', this.handleRemoteUserUnpublished)
  }

  private handleRemoteUserPublished = async (user: IAgoraRTCRemoteUser, mediaType: "audio" | "video" | "datachannel") => {
    await this.client.subscribe(user, mediaType)
    if (mediaType === 'audio') {
      user.audioTrack?.play()
    } else if (mediaType === 'video') {
      const uid = user.uid
      // create a remote user component for each new remote user and add to DOM
      const remoteUserComponentRef: ComponentRef<RemoteUserComponent> = this.remoteVideoContainer.createComponent(RemoteUserComponent)
      remoteUserComponentRef.instance.uid = uid
      remoteUserComponentRef.instance.onReady = (remoteUserDiv) => {
        user.videoTrack?.play(remoteUserDiv)
      }
      this.remoteUserComponentRefs.set(uid.toString(), remoteUserComponentRef)
    }
  }

  private handleRemoteUserUnpublished = async (user: IAgoraRTCRemoteUser, mediaType: "audio" | "video" | "datachannel") => {
    if(mediaType === 'video') {
      const remoteUserUid = user.uid.toString()
      // retrieve the div from remoteUserComponentRefs and remove it from DOM
      const componentRef = this.remoteUserComponentRefs.get(remoteUserUid)
      if(componentRef) {
        const viewIndex = this.remoteVideoContainer.indexOf(componentRef?.hostView)
        this.remoteVideoContainer.remove(viewIndex)
        // remove entry from remoteUserComponentRefs
        this.remoteUserComponentRefs.delete(remoteUserUid)
      } else {
        console.log(`Unable to find remoteUser with UID: ${user.uid}`)
      }
    }
  }

  clearRemoteUsers():void {
    this.remoteVideoContainer.clear();
    this.remoteUserComponentRefs.clear();
  }
}

remote-user 컴포넌트로 이동한 후 HTML 파일(remote-user.component.html)을 열고, placeholder 태그를 다음과 같이 교체합니다:

<div id="remote-user-{{uid}}-container" class="remote-video-container">
  <div #remoteVideo id="remote-user-{{uid}}-video" class="remote-video"></div>
</div>

remote-user.component.ts. 이 컴포넌트는 간단합니다. 각 컴포넌트를 고유하게 구분하기 위한 UID, 내장된 div 요소에 대한 참조, 그리고 뷰가 초기화된 후 실행되는 콜백 함수 onReady를 포함합니다. 이 콜백 함수는 div 요소가 존재해야 Agora SDK에 전달되어 <video /> 요소를 추가할 수 있기 때문에 중요합니다.

import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
import { UID } from 'agora-rtc-sdk-ng';

@Component({
  selector: 'app-remote-user',
  standalone: true,
  imports: [],
  templateUrl: './remote-user.component.html',
  styleUrl: './remote-user.component.css'
})
export class RemoteUserComponent implements OnInit, AfterViewInit {
  @Input() uid!: UID;
  @Input() onReady?: (element: HTMLElement) => void;
  @ViewChild('remoteVideo') remoteVideo!: ElementRef<HTMLElement>;

  constructor(private elementRef: ElementRef) {}

  ngOnInit(): void {
    console.log(`Remote user component initialized for UID: ${this.uid}`)
  }

  ngAfterViewInit(): void {
    if (this.onReady){
      this.onReady(this.remoteVideo.nativeElement)
    }
  }

  get nativeElement(): HTMLElement {
    return this.elementRef.nativeElement;
  }

  get remoteVideoDivId(): string {
    return this.remoteVideo.nativeElement.id
  }
}

모달 양식 컴포넌트 추가

Agora의 유연성을 보여주기 위해, 사용자가 채널 이름을 입력할 수 있는 양식을 추가하여 단일 클라이언트가 여러 채널 간에 전환할 수 있는 방법을 보여드리겠습니다.

모달 양식 컴포넌트를 생성하려면 다음 명령어를 사용하세요:

ng generate component join-modal
ng generate component remote-user

먼저 HTML부터 시작하겠습니다. join-modal.component.html 파일을 열고, placeholder 태그를 다음과 같이 교체합니다:

<div #modalOverlay id="overlay" class="modal">
   <div id="modal-container">
     <div id="modal-header">
       <div id="title">
         <h1>Join Channel</h1>
       </div>
     </div>
     <form #joinChannelForm="ngForm" id="join-channel-form" (ngSubmit)="onSubmit(channelName.value, agoraToken.value)">
       <div id="modal-body">
         <div class="form-group">
           <label for="form-channel-name">Channel Name</label>
           <input ngModel name="channelName" #channelName="ngModel" type="text" id="form-channel-name" class="form-control">
           <label for="form-agora-token">Token</label>
           <input ngModel name="agoraToken" #agoraToken="ngModel" type="text" id="form-agora-token" class="form-control">
         </div>
         <div id="modal-footer">
           <button type="submit" id="join-channel-btn">Join Channel</button>
         </div>
       </div>
     </form>
   </div>
 </div>

다음으로 join-modal.component.ts 파일을 열습니다. 이 컴포넌트는 폼 데이터를 AgoraService에 전달하여 채널에 가입합니다. 폼은 joinChannel 이벤트를 App 컴포넌트에 발송하여 폼을 제거하고 local-stream

import { Component, ElementRef, EventEmitter, AfterViewInit, Output, ViewChild} from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AgoraService } from '../agora.service';

@Component({
  selector: 'app-join-modal',
  standalone: true,
  imports: [FormsModule],
  templateUrl: './join-modal.component.html',
  styleUrl: './join-modal.component.css'
})
export class JoinModalComponent implements AfterViewInit {

  @ViewChild('modalOverlay') modalOverlay!: ElementRef<HTMLDivElement>;
  @ViewChild('joinChannelForm') joinChannelForm!: ElementRef<HTMLFormElement>;
  @Output() joinChannel = new EventEmitter<void>();

  constructor(private agoraService: AgoraService) {}

  ngAfterViewInit () {
    // show the modal once the page has loaded
    this.modalOverlay.nativeElement.classList.add('show')
  }

  async onSubmit(channelName: string, agoraToken?: string, userID?: string) {
    await this.agoraService.joinChannel(channelName, agoraToken ?? null, userID ?? null)
    this.joinChannel.emit() // notify the app to hide the model and show the local video
  }
}    

앱 구성 요소

이제 통합의 대부분을 설정했으므로 모든 것을 App 구성 요소에서 통합할 수 있습니다. HTML부터 시작하여 app.component.html 파일을 열고 placeholder를 다음과 같이 교체합니다:

<main id="app-container" class="main">
  <app-remote-stream #remoteStreamsContainer></app-remote-stream>
  <app-local-stream *ngIf="isLocalStreamVisible" (leaveChannel)="handleLeaveChannel()"></app-local-stream>
  <app-join-modal *ngIf="isJoinModalVisible" (joinChannel)="handleJoinChannel()"></app-join-modal>
</main>
<router-outlet />

다음으로 app.component.ts 파일을 열습니다. 이 컴포넌트는 우리가 생성한 컴포넌트를 가져오고 join-modallocalStream의 가시성을 관리합니다. 이 컴포넌트는 채널과의 사용자 연결을 관리하고, 두 요소의 가시성을 업데이트하는 이벤트를 app에 전송합니다.

import { Component, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';

import { JoinModalComponent } from './join-modal/join-modal.component';
import { RemoteStreamComponent } from './remote-stream/remote-stream.component';
import { LocalStreamComponent } from './local-stream/local-stream.component';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ 
    CommonModule, 
    RouterOutlet, 
    JoinModalComponent, 
    RemoteStreamComponent, 
    LocalStreamComponent,
  ],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent {

  @ViewChild('remoteStreamsContainer') remoteStreamsComponent!: RemoteStreamComponent;
  title = 'agora-angular-demo';

  isJoinModalVisible = true;
  isLocalStreamVisible = false;

  handleJoinChannel() {
    this.isJoinModalVisible = false;
    this.isLocalStreamVisible = true;
  }

  handleLeaveChannel() {
    this.isLocalStreamVisible = false;
    this.isJoinModalVisible = true;
    this.remoteStreamsComponent.clearRemoteUsers()
  }
}

Agora Video SDK를 성공적으로 통합하셨습니다! 🎉

코드를 테스트하세요

모든 설정이 정상적으로 이루어졌는지 확인하려면 로컬 서버를 시작하세요:

ng serve

브라우저에서 URL http://localhost:4200/를 열습니다.

다중 사용자를 시뮬레이션하려면 여러 탭을 열고 동일한 채널 이름을 사용합니다.

다음 단계

그게 전부입니다! 어렵지 않죠? 이제 Agora의 실시간 음성 및 비디오 기능을 Angular 프로젝트에 통합할 준비가 되었습니다. Agora와 함께 여정을 계속하기 위해 몇 가지 추가 단계입니다:

  1. Agora의 다른 SDK 탐색: 실시간 메시징을 위한 Agora Signaling 또는 강력한 인앱 채팅 기능을 제공하는 Agora Chat 등을 확인해 보세요.
  2. 라이브 비디오 통신 보안 강화: 인증 메커니즘, 엔드투엔드 암호화 및 기타 보안 조치를 구현하세요.
  3. Agora의 확장성 기능 탐색: 애플리케이션이 대규모 사용자 수를 예상하는 경우, 성능과 신뢰성을 유지하면서 사용자 기반이 성장함에 따라 애플리케이션을 확장하는 방법을 이해하세요.

기타 리소스

  • Agora SDK의 기능과 기능을 더 잘 이해하려면 Agora Documentation을 참고하세요. API 참조, 샘플 코드, 베스트 프랙티스를 탐색하세요.
  • Agora 개발자 커뮤니티에 참여하세요: X(Twitter) 또는 LinkedIn에서 경험을 공유하고 최신 동향을 확인하세요. 지원이 필요하신가요? 구현 관련 조언을 위해 StackOverflow를 통해 문의하세요.
  • Agora의 Medium 게시물에서 더 많은 가이드를 통해 개발자 여정을 계속하세요.


RTE Telehealth 2023
Join us for RTE Telehealth - a virtual webinar where we’ll explore how AI and AR/VR technologies are shaping the future of healthcare delivery.

Learn more about Agora's video and voice solutions

Ready to chat through your real-time video and voice needs? We're here to help! Current Twilio customers get up to 2 months FREE.

Complete the form, and one of our experts will be in touch.

Try Agora for Free

Sign up and start building! You don’t pay until you scale.
Try for Free