소개
누구도 재택근무가 새로운 일상으로 자리 잡을 것이라고 예상하지 못했습니다. 이 팬데믹은 모두에게 힘든 시기였지만, 국가 전체가 봉쇄 상태에 처해도 사람들이 집에서 편안하게 일할 수 있음을 증명했습니다. 사람들이 동료, 친구, 가족과 온라인으로 연결되어 있는 환경에서 말이죠.
비디오 통화는 현재 가장 필요한 도구이며, 사람들은 이를 위해 다양한 애플리케이션을 사용하고 있습니다. 그러나 이러한 애플리케이션은 일반적으로 사용자의 필요에 맞게 맞춤화되지 않습니다. 왜냐하면 이들은 일반적인 사용 사례를 해결하기 위해 공통된 UI로 설계되었기 때문입니다. 또한 이러한 애플리케이션은 데이터의 프라이버시를 침해할 수 있습니다.
https://giphy.com/gifs/election2020-dnc-2020-democratic-national-convention-KenZCPB6lReAsffAIE
해결책은 매우 간단합니다: 사용 사례에 따라 애플리케이션을 맞춤화할 수 있는 웹RTC 서비스입니다. 이 글에서는 Agora Flutter SDK를 사용하여 자체 비디오 통화 애플리케이션을 구현하는 방법을 예시로 살펴보겠습니다.
사전 준비
Flutter를 처음 사용하시는 경우, 여기에서 Flutter SDK를 설치하세요.
- Agora Flutter SDK v3.2.1
- VS Code 또는 선호하는 다른 IDE
- Agora 개발자 계정 (자세한 내용은 How to Get Started with Agora를 참조하세요)
프로젝트 설정
- Flutter 프로젝트를 생성합니다. 터미널을 열고 개발 폴더로 이동한 후 다음 명령어를 입력합니다:
flutter create agora_group_calling
2. pubspec.yaml
파일로 이동합니다. 해당 파일에서 다음 종속성을 추가합니다:
pubspec.yaml
패키지를 추가할 때 들여쓰기에 주의하세요. 들여쓰기가 잘못되면 오류가 발생할 수 있습니다.
3. 프로젝트 폴더에서 다음 명령어를 실행하여 모든 의존성을 설치하세요:
flutter pub get
4. 모든 종속성 파일을 준비한 후 파일 구조를 생성할 수 있습니다. lib 폴더로 이동한 후 다음과 같은 파일 구조를 생성합니다:

그룹 비디오 통화 인터페이스 구축
먼저 main.dart
로 이동합니다.템플릿 코드를 다음 코드로 교체합니다:
이 코드는 Flutter 애플리케이션을 초기화하고 MyHomePage()
를 호출합니다. 이 메서드는 HomePage.dart
에서 정의되었습니다.
홈 페이지 구축
홈 페이지 작업을 계속하여 사용자에게 채널 이름을 입력하도록 요청할 것입니다. 채널 이름은 동일한 채널 이름을 가진 사용자를 단일 그룹 통화에 배치하는 고유한 문자열입니다:
HomePage.dart
이 코드는 왼쪽 이미지와 유사한 UI를 생성합니다. 이 UI에는 채널 이름을 입력하는 입력 필드와 가입 버튼이 있습니다. 가입 버튼은 onJoin
함수를 호출합니다. 이 함수는 먼저 사용자가 통화 중 카메라와 마이크에 접근할 수 있는 권한을 요청합니다. 사용자가 이 권한을 허용하면 다음 페이지인 CallPage.dart로 이동합니다.
사용자에게 카메라와 마이크에 대한 액세스 권한을 요청하려면 permission_handler라는 패키지를 사용합니다. 여기서는
_handleCameraAndMic()
라는 함수를 선언합니다. 이 함수는 onJoin()
함수에서 참조할 것입니다:
HomePage.dart — _handleCameraAndMic()
Now, in our onJoin()
function we create reference for the above function and then pass the channel name that the user submitted to the next page, CallPage.dart
콜 페이지 구축
CallPage.dart 파일을 시작하기 전에 Agora 개발자 계정에서 받은 App ID를 사용해야 합니다. (App ID 생성 방법은 여기를 참고하세요.) utils 폴더 내 AppID.dart 파일로 이동하여 appID라는 변수를 생성합니다.
var appID = '<--- Enter your app id here --->'
After this we move to our CallPage.dart and we begin by importing all the files.
importing the Agora SDK
Here I create a stateful widget called CallPage such that it’s constructor can read the channel name that the user submitted.
그런 다음 CallPageState에서 이 페이지를 만들 때 사용할 변수들을 선언합니다:
_users
는 채널에 있는 모든 사용자의uid
를 포함하는 목록입니다._infoStrings
는 통화 중 발생하는 모든 이벤트의 로그를 포함합니다.muted
는 사용자를 음소거하거나 음소거 해제하는 Boolean 상태 변수입니다._engine
는 RtcEngine 클래스를 위한 객체입니다.- dispose 메서드에서는 _users 목록을 초기화하고 RtcEngine을 파괴합니다.
- initState() 메서드에서는 다음 단계에서 선언할 initialize() 함수를 호출합니다.
Agora CallPageState
우리는 모든 주요 함수에서 공통으로 호출되는 initialize() 함수를 생성할 것입니다. initialize() 함수의 주요 용도는 Agora SDK를 초기화하는 것입니다. initialize 함수 내에서 _initAgoraRtcEngine()
및 _addAgoraEventHandlers()
함수의 참조를 생성합니다:
_initAgoraRtcEngine()
는 Agora SDK의 인스턴스로 사용됩니다. Agora 대시보드에서 받은 App ID를 사용하여 초기화합니다. 또한 enableVideo()
함수를 사용하여 비디오 모듈을 활성화합니다. 이 함수는 채널에 가입하기 전이나 통화 중에도 호출할 수 있습니다. 채널에 가입하기 전에 호출하면 통화가 기본적으로 비디오 모드로 시작됩니다. 반면, 채널에 가입하기 전에 호출하면 앱은 오디오 모드로 실행되며, 필요 시 나중에 비디오 모드로 전환할 수 있습니다.
_initAgoraRtcEngine()
_addAgoraEventHandlers()
는 주요 콜백 함수를 모두 처리하는 함수입니다. 따라서setEventHandler()
부터 시작합니다. 이 함수는 엔진 이벤트를 감시하고 해당 RtcEngine의 통계 정보를 수신합니다.
중요한 콜백 함수에는 다음과 같은 것이 있습니다:
joinChannelSuccess()
는 로컬 사용자가 지정된 채널에 가입할 때마다 트리거됩니다. 이 함수는 채널 이름, 사용자의 uid, 로컬 사용자가 채널에 가입하는 데 소요된 시간(ms 단위)을 반환합니다.leaveChannel()
는 그 반대입니다. 사용자가 채널을 떠날 때 트리거되며, 사용자가 채널을 떠날 때마다 호출되는 통계 정보를 반환합니다. 이 통계에는 latency, CPU 사용률, 지속 시간 등이 포함됩니다.userJoined()
는 원격 사용자가 특정 채널에 가입할 때 트리거되는 메서드입니다. 성공적인 콜백은 원격 사용자의 UID와 가입에 소요된 시간을 반환합니다.userOffline()
는 그 반대입니다. 사용자가 채널을 떠날 때 발생합니다. 성공적인 콜백은 UID와 오프라인 이유(드롭, 종료 등)를 반환합니다.firstRemoteVideoFrame()
는 원격 비디오의 첫 번째 비디오 프레임이 렌더링될 때 호출되는 메서드입니다. 이 메서드는 uid, 너비, 높이, 경과 시간을 반환하는 데 사용됩니다.
_addAgoraEventHandlers()
To end our initialize() function, we will be adding the joinChannel()
function. A channel acts as a common room for people to be in the same video call. This joinChannel() method can be called with something like:
joinChannel()
이 메서드를 성공적으로 실행하려면 네 가지 매개변수가 필요합니다:
- Token: 테스트 시 null로 설정할 수 있는 선택적 필드입니다. 하지만 프로덕션 환경으로 이동 시 토큰 서버에서 생성되어야 합니다.
- 채널 이름: 사용자를 공통 비디오 통화에 참여시키기 위해 문자열 입력값을 받습니다.
- 선택적 정보: 채널에 대한 추가 정보를 전달할 수 있는 선택적 필드입니다.
- uid: 채널에 가입하는 각 사용자의 고유 ID입니다. 이 필드에 0 또는 null 값을 전달하면 Agora가 자동으로 uid를 할당합니다.
이것은 이 비디오 통화 애플리케이션을 만들기 위해 필요한 모든 기능과 메서드를 요약한 것입니다. 이제 애플리케이션의 전체 UI를 담당할 위젯을 만들 수 있습니다.
여기서 두 개의 위젯(_viewRows()
및 _toolbar()
)을 선언합니다. 이 위젯은 최대 4명의 사용자를 표시하고 하단에 연결 끊기, 음소거, 카메라 전환 버튼을 추가합니다.
build
먼저 _viewRows()
부터 시작하겠습니다. 이를 위해 사용자와 그들의 uid를 알아야 합니다. 이는 그들의 동영상을 표시하기 위함입니다. 로컬 및 원격 사용자의 uid를 포함한 공통 목록이 필요합니다. 이를 구현하기 위해 _getRendererViews()
라는 위젯을 생성합니다. 이 위젯에서는 RtcLocalView와 RtcRemoteView를 사용합니다.
그런 다음 _videoView()
라는 위젯을 사용하여 확장된 뷰를 사용하고, 이를 _expandedVideoRow()
위젯을 사용하여 행에 배치합니다.
뷰를 적절히 구조화한 후, 스위치 케이스(switch case)를 사용하여 디자인을 하드코딩할 것입니다. 이 방법은 뷰를 쌓아 열을 생성합니다.
이로써 완전한 영상 통화 앱이 구현되었습니다. 이제 통화 끊기, 음소거, 카메라 전환 등의 기능을 추가하기 위해 _toolbar()라는 기본 위젯을 생성하고 세 개의 버튼을 추가하겠습니다:

여기서 세 개의 함수를 선언했습니다:
_onToggleMute()
스트림을 음소거하거나 음소거 해제할 수 있습니다. 여기서는muteLocalAudioStream()
메서드를 사용하며, 이 메서드는 스트림을 음소거하거나 음소거 해제하기 위해 Boolean 입력 값을 받습니다.
_onCallEnd()
통화를 종료하고 사용자를 홈 화면으로 돌아가게 합니다.
_onSwitchCamera()
이 메서드는 전면 카메라와 후면 카메라 사이를 전환할 수 있도록 합니다. 여기서는 원하는 기능을 구현하기 위해 switchCamera() 메서드를 사용합니다.
테스트
그룹 영상 통화 애플리케이션을 개발을 완료한 후에는 해당 애플리케이션을 기기에서 테스트할 수 있습니다. 이를 위해 터미널에서 프로젝트 디렉토리로 이동한 후 다음 명령어를 실행하세요:
flutter run
결론
축하합니다! Agora Flutter SDK를 사용하여 기본 기능(로컬 스트림 음소거, 카메라 전환, 통화 종료 등)을 갖춘 자체 그룹 통화 애플리케이션을 구현하셨습니다.
이 애플리케이션의 전체 코드는 여기에서 확인할 수 있습니다.
기타 자료
Agora Flutter SDK 및 기타 사용 사례에 대해 자세히 알아보려면 개발자 가이드 를 참고하세요.
위에서 논의된 기능과 더 많은 기능에 대한 완전한 문서는 여기에서 확인할 수 있습니다.
또한 Agora Developer Slack Community에 참여해 주시기를 초대합니다.