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개의 핵심 기능을 노출합니다: joinChannel
및 leaveChannel
는 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
media-controls
local-stream
로 전달하는 버튼을 포함합니다. - 원격 사용자는 구조가 유사하며, 모든 원격 사용자 div 요소를 포함하는 컨테이너로 사용되는 주요 구성 요소 (
remote-stream
)와 개별 원격 사용자를 위한 별도의 구성 요소remote-user
로컬 사용자 구성 요소
로컬 사용자 구성 요소를 생성하려면 다음 명령어를 사용합니다
ng generate component local-stream
ng generate component media-controls
다음 디렉토리가 생성됩니다. 다음 디렉토리와 해당 파일(.html,
.css,
.ts)
:

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를 통해 로컬 mic
및 camera
트랙을 초기화하고, 채널에 가입할 때 이를 게시하기 위한 리스너를 설정합니다. 사용자가 채널을 떠날 때 앱은 뷰를 제거하며, 이는 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-active
및 muted
클래스를 사용하여 업데이트됩니다.
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
)을 확인하세요:

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 client
from AgoraService
and 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-modal
및 localStream
의 가시성을 관리합니다. 이 컴포넌트는 채널과의 사용자 연결을 관리하고, 두 요소의 가시성을 업데이트하는 이벤트를 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와 함께 여정을 계속하기 위해 몇 가지 추가 단계입니다:
- Agora의 다른 SDK 탐색: 실시간 메시징을 위한 Agora Signaling 또는 강력한 인앱 채팅 기능을 제공하는 Agora Chat 등을 확인해 보세요.
- 라이브 비디오 통신 보안 강화: 인증 메커니즘, 엔드투엔드 암호화 및 기타 보안 조치를 구현하세요.
- Agora의 확장성 기능 탐색: 애플리케이션이 대규모 사용자 수를 예상하는 경우, 성능과 신뢰성을 유지하면서 사용자 기반이 성장함에 따라 애플리케이션을 확장하는 방법을 이해하세요.
기타 리소스
- Agora SDK의 기능과 기능을 더 잘 이해하려면 Agora Documentation을 참고하세요. API 참조, 샘플 코드, 베스트 프랙티스를 탐색하세요.
- Agora 개발자 커뮤니티에 참여하세요: X(Twitter) 또는 LinkedIn에서 경험을 공유하고 최신 동향을 확인하세요. 지원이 필요하신가요? 구현 관련 조언을 위해 StackOverflow를 통해 문의하세요.
- Agora의 Medium 게시물에서 더 많은 가이드를 통해 개발자 여정을 계속하세요.