Kakao i Connect Live::Kakao i Connect Live 2.0::개발자 가이드::iOS

페이지 이동경로

iOS SDK 개발자 가이드

iOS 버전의 카카오 i 커넥트 라이브 SDK 2.0은 iOS 앱에서 카카오 i 커넥트 라이브 서비스의 개발을 지원하는 프레임워크입니다. SDK에서 제공하는 메서드와 이벤트를 활용하여 화상과 음성을 교환하는 실시간 스트리밍 서비스(Live Streaming Service)를 쉽게 만들 수 있습니다.
SDK 개발 순서는 크게 다음으로 구분할 수 있으며, 이 과정에서 사용되는 주요 메서드는 signIn(인증), createLocalMedia(로컬 미디어 생성), createRoom(Room 생성), connect(Room 접속), publish(로컬 미디어 공유), subscribe(리모트 미디어 구독)입니다. 이때 사용되는 이벤트는 onConnected, onRemoteVideoPublished입니다.
SDK에서 제공하는 메서드에 대한 자세한 설명은 [카카오 i 기술문서] API 레퍼런스 > iOS 문서를 참고하시기 바랍니다.

iOS SDK 개발자 가이드 그림iOS SDK

개발 환경

SDK(기준 버전 2.2.0)를 사용하기 위해서는 다음의 개발 환경이 필요합니다.
iOS 개발 환경 특성상 Swift 버전과 Xcode 버전은 SDK 업데이트 시 Apple에서 배포된 최신 버전으로 변경될 수 있습니다.

개발환경
구분 설명
iOS 13 이상
Swift 5.6.1 이상(SDK 버전에 따라 변경될 수 있음)
Xcode 13.4 이상(SDK 버전에 따라 변경될 수 있음)
안내
iOS 버전의 카카오 i 커넥트 라이브 SDK 2.0은 SPM(Swift Package Manager) 배포 방식만 지원합니다.
현재 SDK 프레임워크는 Bitcode를 지원하지 않습니다.

주요 클래스

iOS 버전의 카카오 i 커넥트 라이브 SDK 2.0에서 제공하는 주요 클래스는 다음과 같습니다. 각 클래스별 상세 사양은 [카카오 i 기술문서] API 레퍼런스 > iOS 문서 참고하시기 바랍니다.

iOS SDK 주요 클래스
클래스 명 설명
ConnectLive Kakao i Connect Live SDK 2.0의 최상위 클래스이며 인증, Room 생성, 미디어 생성을 담당
Config 미디어와 Room 생성을 위한 설정 구조체
Room 스트리밍 서비스의 연결 단위 그룹을 나누는 개념의 클래스이며, 스트리밍 연결 및 기타 정보를 관리
RoomDelegate Room에서 발생하는 이벤트가 정의된 프로토콜로, Room 생성 시에 인자로 전달하면 Room 이벤트를 수신
LocalParticipant 로컬 사용자에 대한 정보를 제공하는 클래스
RemoteParticipant 리모트 사용자에 대한 정보를 제공하는 클래스
LocalMedia 로컬 미디어를 관리하기 위한 클래스
LocalVideo 로컬 미디어에 포함된 로컬 비디오 정보를 제공하는 클래스
LocalAudio 로컬 미디어에 포함된 로컬 오디오 정보를 제공하는 클래스
RemoteVideo 리모트 비디오 정보를 제공하는 클래스
RemoteAudio 리모트 오디오 정보를 제공하는 클래스
ScreenShare 익스텐션에서 화면 공유를 처리하기 위한 클래스

사전 작업

카카오 i 커넥트 라이브 SDK 2.0을 사용하여 실시간 스트리밍 서비스를 개발하기 전, 다음의 사전 작업을 수행해야 합니다.

서비스 인증 정보 발급받기

카카오 i 커넥트 라이브 SDK 2.0을 사용하기 위해서는 고객사에서 선정한 인증 방식에 따라 카카오 i 커넥트 라이브 콘솔에서 서비스 인증 정보를 발급받아야 합니다.
SDK 기본 인증 방식인 커넥트 라이브 내부 인증 방식의 경우, 콘솔에서 서비스 ID(serviceId)서비스 시크릿(serviceSecret) 발급받아야 합니다. 이 정보는 프로젝트 생성 시 자동 생성되며, 서비스 (앱)별로 구분되어 사용량 측정 및 과금의 기준이 됩니다. 따라서 하나 이상의 애플리케이션을 만들 경우에는 앱의 개수만큼 서비스 ID와 서비스 시크릿을 각각 발급받아야 합니다. 서비스 인증 정보를 발급받는 방법은 다음과 같습니다. 고급 인증 방식인 서비스 자체 인증 방식의 경우, 콘솔에서 서비스 ID(serviceId)를 발급받아야 합니다. 각 인증 방식 별 자세한 인증 프로세스는 [카카오 i 기술문서] 개발자 가이드 > 인증 및 키 처리 문서를 참고하시기 바랍니다.

인증 방식 구분
인증 방식 구분 설명
커넥트 라이브 내부 인증 카카오 i 커넥트 라이브 SDK 2.0의 기본 인증 방식
- 콘솔에서 서비스 ID(serviceId)서비스 시크릿(serviceSecret) 발급 필요
- SDK 기본 인증 방식으로 별도의 서버 및 개발 작업이 필요 없음
- 자세한 설명은 서비스 인증 정보 발급하기 참고
서비스 자체 인증 최종 사용자에게 신원 확인용 토큰을 부여하고, 권한을 부여하는 서비스 자체 인증 방식
- 콘솔에서 서비스 ID(serviceId) 발급 필요
- 수준 높은 보안 레벨을 충족하며, 별도의 서버 및 개발 작업 필요
- 자세한 설명은 서비스 인증 정보 발급하기 참고
주의
serviceId와 serviceSecret 정보가 외부로 유출될 경우에는 해킹 등 악용의 소지가 발생할 수 있으므로 코드 저장소 등에 노출되지 않도록 주의하시기 바랍니다.
안내
카카오 i 커넥트 라이브 콘솔에서는 사용자 인증 정보 관리, 워크스페이스 관리, 멤버 초대 등의 기능을 제공합니다. 콘솔 사용과 관련한 자세한 설명은 부록. 카카오 i 커넥트 라이브 콘솔 문서를 참고하시기 바랍니다.

Xcode에서 SDK 설치하기

SPM(Swift Package Manager) 지원 기능을 사용하여 Xcode에서 카카오 i 커넥트 라이브 SDK 2.0을 설치합니다.

코드예제SDK 설치 Syntax

Name: ConnectLiveSDK
Version Rules: 2.2 - Next Minor
Location: https://github.com/kakaoi-clive/ios-sdk.git

  1. Xcode에서 Project를 선택한 후, Package Dependencies에서 [+] 아이콘을 클릭하여 SDK 라이브러리를 추가해 설치합니다.

  2. 표시되는 메시지에서 GitHub 저장소를 선택 후, [Add package] 버튼을 클릭합니다.

  3. 사용할 SDK 버전을 선택합니다. 새 프로젝트인 경우 최신 버전을 사용하는 것을 권장합니다.

  4. 작업을 완료하면 Xcode에서 패키지 종속 항목 확인하며, 백그라운드에서 다운로드가 시작됩니다.

프로젝트 설정하기

앱 개발 시 설정이 필요한 항목은 다음과 같습니다. 화면 공유(Extension) 관련 설정은 화면 공유 기능을 구현 시에만 수행하시기 바랍니다.

화면 공유 관련 설정
구분 필수 여부 설명
카메라, 마이크 권한 설정 필수 Xcode > Info > Privacy - Camera Usage Description 추가
필수 Xcode > Info > Privacy - Microphone Usage Description 추가
백그라운드 동작 설정 필수 Xcode > Capabilities > Background Modes > Voice over IP 선택
bitcode 해제 설정 필수 Xcode > Build Settings > Build Options > Enable Bitcode > No 선택
화면 공유(Extension) 관련 설정 선택 화면 공유 기능 구현 시에만, Apple Developer에서 다음 작업을 수행해야 함
- 앱 ID 등록
- 앱 그룹 등록
- Provisioning Profile 설정

자세한 설명은 부가 기능 문서의 화면 공유 구현 참고

서비스 개발 순서

다음의 서비스 개발 순서는 카카오 i 커넥트 라이브 SDK 2.0을 사용하여 실시간 화상회의 서비스를 구현하는 방법을 기준으로 설명합니다. 서비스 개발은 사전 작업을 모두 완료 후 진행하시기 바랍니다.

안내
서비스 구현과 관련한 개발 샘플 및 구현 예제는 부록. 개발 샘플 문서를 참고하시기 바랍니다.

샘플 프로젝트

본 가이드에서 다루는 서비스 개발 순서는 샘플 프로젝트인 hello 프로젝트 기준입니다. 부록. 개발 샘플 문서를 참고하시면 샘플 프로젝트를 다운로드할 수 있습니다. 샘플 프로젝트를 다운로드한 후, hello 디렉터리의 hello.xcodeproj 파일을 실행하여 프로젝트를 살펴보거나 실행할 수 있습니다.

샘플 프로젝트 시나리오

샘플 프로젝트에서는 다음 시나리오를 기반으로, iOS 버전의 카카오 i 커넥트 라이브 SDK 2.0의 기본 기능을 확인할 수 있습니다.

샘플 프로젝트 시나리오 그림샘플 프로젝트 시나리오

  1. hello 프로젝트의 Main.storyboard에서 로컬뷰와 리모트뷰의 위치와 사이즈를 설정합니다.
  2. HelloViewController 클래스에서 roomId 값에 Room의 ID를 설정합니다.
  3. 프로젝트를 빌드한 후 앱을 시작합니다. 앱이 실행되면 왼쪽 하단에 로컬 화면이 표시됩니다.
  4. [JOIN] 버튼을 선택하면 설정한 roomId에 해당하는 Room으로 접속합니다.
  5. 리모트 참여자(상대방)가 동일 roomId로 동일 Room에 접속하면 서로 비디오와 오디오를 시청할 수 있습니다.
  6. [CLOSE] 버튼을 선택하여 연결을 끊고 앱을 종료합니다.

샘플 프로젝트 제약사항

  • 샘플에서는 단순 기능만 확인할 수 있으며, 예외 처리는 제외하였습니다.
  • 샘플의 경우 1:1 접속만 확인하는 용도입니다.
  • UI의 경우, 나 자신을 나타내는 LocalView와 상대방을 나타내는 RemoteView으로만 구성되어 있습니다.
  • 실행에 필요한 정보는 코드에서 직접 수정해야 합니다.
  • 접속 종료 시 앱을 강제 종료합니다.

시뮬레이터 사용 방법

카카오 i 커넥트 라이브 SDK 2.0에서 iOS 시뮬레이터는 동영상(MOV) 파일을 사용해 카메라를 에뮬레이팅(Emulating)합니다.
iOS 시뮬레이터에서 hello 샘플을 사용하기 위해서는 다음 설정이 필요합니다.

  1. 카메라 대신 표시하고 전송할 동영상(MOV) 파일을 프로젝트에 등록합니다.

  2. HelloViewController 클래스의 createLocalMedia() 메서드 설정값에 해당 동영상(MOV) 파일명을 넣습니다. 코드예제동영상(MOV) 파일명 설정

    config.mediaOptions.fileName = “파일명.mov
    

  3. 시뮬레이터를 동작시킨 후, LocalView에서 동영상 파일이 정상 재생되는지 확인합니다.

Step 1. SDK 추가하기

iOS 버전의 카카오 i 커넥트라이브 SDK 2.0을 사용하기 위해 SDK를 불러옵니다.

코드예제SDK import Syntax

import ConnectLiveSDK

Step 2. 서비스 인증하기

Room에 연결하기 위해서는 인증이 필요합니다. 콘솔에서 발급받은 서비스 ID와 서비스 시크릿 정보를 사용해 인증을 시도합니다. SDK 인증 방식은 커넥트 라이브 내부 인증과 서비스 자체 인증으로 구분됩니다. 각 인증 방식에 대한 자세한 설명은 signIn(카카오 i 커넥트 라이브 내부 인증)signIn(서비스 자체 인증)을 참고하시기 바랍니다.

코드예제signIn_커넥트 라이브 내부 인증 Syntax

// 커넥트 라이브 내부 인증
ConnectLive.signIn( serviceId: "ICLEXMPLPUBL", 
				    serviceSecret: "ICLEXMPLPUBL0KEY:YOUR0SRVC0SECRET") { code, message in
    if code == 0 {
        // 인증 성공
    } else {
        // 인증 실패
    }
}

코드예제signIn_서비스 자체 인증 Syntax

// 서비스 자체 인증
ConnectLive.signIn( serviceId: "ICLEXMPLPUBL", 
					token: "YOUR_SERVICE_TOKEN") { code, message in
	if code == 0 {
		// 인증 성공
	} else {
		// 인증 실패
	}
}

안내
인증 방식 별 인증 프로세스와 서비스 인증 정보에 대한 자세한 설명은 인증 및 키 처리 문서를 참고하시기 바랍니다.

Step 3. 오디오 세션 설정하기

iOS의 경우 카카오 i 커넥트 라이브 서비스를 사용하기 전 오디오 세션을 별도 설정해야 합니다.

  1. 서비스 객체나 서비스의 ViewController 초기화 시점에 오디오 세션을 설정합니다.

    • 각 파라미터의 타입은 Apple Developer 사이트의 AVAudioSession를 참고하시기 바랍니다. 일반적인 경우 mixWithOthers, defaultToSpeaker, allowBlueTooth 옵션이 사용됩니다.

    코드예제오디오 세션 설정 Syntax

    // 서비스 자체 인증
    ConnectLive.setAudioSessionConfiguration(category: .playAndRecord,
                                             mode: .videoChat,
                                             options: [.mixWithOthers, .defaultsToSpeaker, .allowBlueTooth])
    

  2. 오디오 세션 설정 시 선택 작업인 파라미터 AudioSessionDelegate를 지정하면 오디오 세션 이벤트를 수신할 수 있습니다. 각 이벤트의 세부 내용은 AudioSessionDelegate 문서와 Apple Developer 사이트의 AVAudioSession > Responding to Audio Session Notifications 문서를 참고하시기 바랍니다.

    코드예제오디오 세션 이벤트 수신 Syntax

    // 서비스 자체 인증
    // 오디오 세션 관련 이벤트 수신이 필요한 경우 별도 정의
    class CustomAudioDelegate: AudioSessionDelegate {
    	.
    	.
    	.
    }
    
    
    var delegate = CustomAudioDelegate() 
    
    // 오디오 세션 설정
    ConnectLive.setAudioSessionConfiguration(category: .playAndRecord,
                                             mode: .videoChat,
                                             options: [.mixWithOthers, .defaultsToSpeaker, .allowBlueTooth],
                                             delegate: delegate)
    

Step 4. 뷰(View) 구성하기

뷰(View)는 Ulkit, Storyboard, SwiftUI로 구성할 수 있습니다.

UIKit

일반적으로 영상을 표시하기 위한 뷰(View)는 UIKit 기반의 UIRenderView를 코드로 등록하여 사용할 수 있습니다.

코드예제UIKit 뷰 구성 Syntax

let localView = UIRenderView()

서비스에서 렌더뷰(RenderView)를 커스터마이징하거나 스토리보드에서 사용하는 경우, UIRenderView를 상속받은 별도의 클래스를 생성합니다. 샘플에서 RenderView라는 클래스를 생성하고, 내부 기능은 필요한 경우에 추가합니다.

코드예제UIRenderView 상속 클래스 생성 Syntax

class RenderView: UIRenderView {
	.
	.
	.
}

Storyboard

스토리보드의 경우 UIView로 등록 후, Identity Inspector에서 위에서 별도 정의한 커스텀 뷰로 변경하는 형태로 사용합니다.

  1. 스토리보드 편집 화면에서 UIView 두 개를 원하는 위치에 배치하고 이름을 LocalViewRemoteView로 변경합니다. 각 뷰의 Constraints를 조정하여 원하는 사이즈로 구성합니다.

    Storyboard 뷰 구성 그림Storyboard 뷰 구성

  2. 등록한 LocalView와 RemoteView를 각각 선택 후, Identity Inspector > Custom Class의 Class에 UIRenderView를 상속받아 생성한 클래스 이름을 입력합니다.

    Storyboard 커스텀 뷰 그림Storyboard 커스텀 뷰

SwiftUI

스토리보드 없이 SwiftUI만 사용하는 경우, SDK에서 별도의 렌더링을 위한 뷰는 제공하지 않습니다. 이 경우는 UIViewRepresentable을 상속받아 UIRenderView를 SwiftUI의 뷰 타입으로 변환하는 형식을 사용합니다. 다음은 단순한 예시이므로 세부 사항은 Apple SwiftUI의 UIViewRepresentable 문서를 참고하시기 바랍니다.

  1. UIViewRepresentable을 상속받아 SwiftUI에서 사용할 뷰를 생성합니다.

    코드예제SwiftUI 뷰 구성 Syntax

    struct RenderView: UIViewRepresentable, Hashable {
    	var uiView = UIRenderView()
    	var id: String { uiView.uuid }
    
    	init(contentMode: UIView.ContentMode) {
    		uiView.videoContentMode = contentMode
    	}
    
    	public func makeUIView(content: Content) -> UIRenderView {
    		return uiView
    	}
    
    	public func updateUIView(_ uiView: UIRenderView, content: Context) {
    	}
    }
    

  2. 실제 화면에 표시할 뷰들을 원하는 형태로 배치합니다. 실제 화면 구성 시 화면 내의 UI 데이터가 갱신될 때마다 RenderView의 생성 또는 삭제가 반복되지 않도록 객체를 관리해야 합니다. 각 서비스에 맞게 ObservedObject, Stat, Binding 등 SwiftUI 요소들을 사용해 서비스에 맞는 UI 개발이 이루어져야 합니다.

    코드예제SwiftUI 뷰 배치 Syntax

    struct RoomView: View {
    	var body: some View {
    		ZStack {
    			RenderView()
    			VStack {
    				Divider()
    				HStack {
    					Divider()
    					RenderView()
    				}
    			}
    			.padding(8)
    		}
    	}
    }
    

Step 5. 로컬 미디어 생성하기

LocalMedia 클래스는 Local Participant(로컬 참여자)가 생성한 오디오와 비디오를 제어합니다. LocalMedia 클래스 생성을 위한 LocalMediaOptions은 데이터 수신을 위한 리시버(Receiver) 설정을 위한 구조체입니다. LocalMediaOptions는 Config 객체 내에 포함되어 있으며, 별도 생성은 불가능합니다. 로컬 미디어에 대한 기본 설정이 포함되어 있으며, 필요시 해당 설정을 수정해 로컬 미디어를 생성할 때 전달합니다.
로컬 미디어 설정과 관련한 자세한 설명은 Config 문서를 참고하시기 바랍니다.

코드예제LocalMedia 클래스 생성 Syntax

struct Config {
	/// 로컬 미디어 옵션
  var mediaOptions: LocalMediaOptions = LocalMediaOptions()
    .
		.
		.
}

// 로컬 미디어 옵션 변경
var config = Config()
config.mediaOptions.audio = true
config.mediaOptions.video = true

로컬 미디어 생성 및 옵션 설정

로컬 미디어의 생성 방법은 다음과 같습니다.

  1. LocalMedia Options에서 비디오 소스를 설정합니다. 비디오 소스는 camera, file, by-pass 캡처러로 구분되며, camera는 실제 단말에서만 지원합니다. 시뮬레이터의 경우 file로 고정되며, 프로젝트 내에 포함된 mov 파일을 fileName에 설정해야 합니다.

    코드예제비디오 소스 설정 Syntax

    enum CapturerType {
        /// 카메라
        case camera
    
        /// 바이패스
        case bypass
    
        /// 파일
        case file
    }
    

  2. LocalMedia Options에서 오디오 타입(audioType)은 마이크 입력 시 에코 캔슬 적용 여부를 설정합니다. voice 인 경우 에코 캔슬레이션(echo cancellation) 기능을 활성화하며, music의 경우는 에코 캔슬레이션(echo cancellation)을 활성화하지 않습니다.

    코드예제에코 캔슬 설정 Syntax

    enum AudioProcessingType: String {
       case voice
       case music
    }
    

  3. Config의 LocalMediaOptions 설정을 기반으로, createLocalMedia() 메서드를 사용하여 LocalMedia 클래스를 생성합니다.

    코드예제로컬 미디어 생성 Syntax

    // 로컬 미디어 생성
    let media = ConnectLive.createLocalMedia(config: config)
    

  4. (선택 작업) 로컬 미디어의 비디오에 뷰를 연결하기 위해 비디오 스트림에 UIRenderView를 붙입니다. 구독이 시작되면 스트림이 수신되고, 설정된 렌더러로 화면이 보이게 됩니다.

    • 이 작업은 화상 채팅 등 화면이 필요한 서비스에 한해 필요한 선택 작업입니다.

    코드예제로컬 비디오에 뷰 연결 Syntax

    // 렌더뷰 붙이기
    media.attach(localView)
    

  5. (선택 작업) 로컬 미디어의 비디오에 뷰를 연결했다면, 뷰 해제 작업이 필요합니다. 비디오 스트림에 붙어있는 UIRenderView를 해제합니다.

    코드예제뷰 해제 Syntax

    // 렌더뷰 해제
    media.detach()
    

  6. 로컬 미디어의 캡처러(Capturer) 동작을 시작하여 로컬 미디어를 시작합니다. 로컬 비디오에 UIRenderView가 연결되어 있으면 로컬 비디오 화면이 UIRenderView를 통해 재생됩니다. 이 메서드는 명시적으로 호출해 주지 않아도 미디어 배포가 시작되면 자동으로 호출됩니다.

    코드예제로컬 미디어 시작 Syntax

    Task {
    	try? await media.start()
    }
    

Step 6. Room 생성하기

상대방을 의미하는 리모트 참여자(Remote Participant)와 통신하기 위해서는 같은 Room에 참여해야 합니다. Room 객체를 생성해 해당 룸에 참여할 수 있으며, 참여 이후에는 Room의 부가 기능들을 사용할 수 있습니다.

  1. Room 생성 시 Config 정보를 전달하여 수신 설정을 변경할 수 있습니다.

    • 다른 참여자의 영상은 구독과 해제를 통해 미디어 스트림을 수신하게 됩니다. 스트림 수신은 통신 환경과 단말 성능, 서비스 품질과 관련이 많으므로 가급적 제한된 수의 스트림만을 사용해야 합니다.
    • 특정 개수의 리시버를 설정해 놓으면, 비디오 구독시 해당 리시버 구성 내에서만 구독이 이루어지게 됩니다. 구독 요청 시 리시버 수가 부족한 경우 오류가 발생하므로, 다른 영상의 구독을 해제한 뒤에 사용해야 합니다. 설정에 대한 자세한 설명은 Config 문서를 참고하시기 바랍니다.

    코드예제Config 정보 전달 Syntax

    struct Config {
    		.
    		.
    	/// 초기 생성할 영상 리시버의 수
    	var videoReceiverInitialCount: Int = 9
    	
    	/// 최대 영상 리시버의 수
    	var videoReceiverMaximumCount: Int = 20
    	
    	/// 영상 리시버가 부족한 경우 증가 단위(아직 지원하지 않음)
    	var videoReceiverGrowthRate: Int = 1
    }
    

  2. Room 연결, 참여자, 미디어 정보, 메시지 등 Room과 관련한 다양한 이벤트를 전달받기 위해 RoomDelegate 프로토콜을 상속받아 구현하고, Room 생성 시 인자로 전달합니다. 다음은 필수로 구현해야 하는 이벤트 메서드에 대한 예시입니다. 이 이외의 메서드는 필요한 경우에 한해 구현하면 됩니다.

    코드예제필수 구현 이벤트 메서드 Syntax

    class CustomRoomDelegateClass: RoomDelegate {
    	func onConnected(participantIds: [String]) {
    	}
    
    	func onDisconnected(reason: DisconnectReason) {
    	}
    
    	func onError(code: Int, message: String, isCritical: Bool) {
    	}
    
    	func onParticipantEntered(remoteParticipant: RemoteParticipant) {
    	}
    
    	func onParticipantLeft(remoteParticipant: RemoteParticipant) {
    	}
    }
    

  3. Room 설정을 위한 Config와 이벤트 RoomDelegate를 파라미터로 전달해 새로운 Room 객체를 생성합니다.

    코드예제새로운 Room 객체 생성 Syntax

    let config = Config()
    let delegate: CustomRoomDelegateClass = CustomRoomDelegateClass()
    
    // Room 객체 생성
    let room = ConnectLive.createRoom(config: config, delegate: delegate)
    

Step 7. Room 접속 및 미디어 게시하기

Room에 접속하고, 생성된 로컬 미디어를 게시하여 실제 미디어 스트림을 송출합니다.

  1. connect() 메서드를 사용하여 Room에 연결 시, 데이터 채널 생성 및 SDP(Session Description Protocol) 교환 등 실제 RTP(Real-time Transport Protocol) 스트림 송수신을 위한 모든 절차가 진행됩니다.

    • roomId는 영문 대•소문자, 숫자, - 만 사용할 수 있으며, 길이는 최소 1자, 최대 32자입니다.

    코드예제connect() Syntax

    room.connect(roomId: "{roomId}")
    

  2. 생성한 LocalMedia 객체를 Room에 게시합니다. 아직 Room에 연결된 상태가 아닌 경우 연결 후에 게시가 이루어집니다.

    코드예제LocalMedia 객체 게시 Syntax

    // 로컬 미디어 게시
    do {
    	try room.publish(media)
    } catch {
    	// 로컬 미디어 게시 오류
    }
    

Step 8. 참여자 정보 얻기

상대방을 의미하는 리모트 참여자(Remote Participant)의 비디오를 구독하기 위해서는 각 참여자의 정보를 얻어야 합니다. 참여자 목록 등의 정보는 Room 객체를 통해 얻어올 수 있습니다.

참여자 정보 획득하기

각 참여자 정보는 Room 클래스에서 제공하는 프로퍼티와 메서드를 통해 얻을 수 있습니다. 참여자는 오브젝트(Object) 타입이므로, 참조 후 별도 저장 시 레퍼런스 카운터를 관리해야 Room 해제 시 참여자들도 정상적으로 해제됩니다.

참여자에 대한 자세한 설명은 LocalParticipantRemoteParticipant를 참고하시기 바랍니다.

코드예제참여자 정보 획득 Syntax

/// 로컬 참여자
var localParticipant = room.localParticipant
 
/// 리모트 참여자 가져오기
var remoteParticipants = room.remoteParticipants
 
/// 구독 중인 비디오 사용자 목록
var videoOccupants = room.getVideoOccupants()
 
/// 서버에서 오디오에 할당된 사용자 목록
var audioOccupants = room.getAudioOccupants()

로컬 참여자의 비디오/오디오 정보 얻기

로컬 참여자(Local Participant)는 비디오와 오디오 배열을 가지고 있으며, 구독을 위해서는 해당 참여자의 비디오와 오디오를 얻어야 합니다. 로컬 비디오와 오디오 정보는 LocalMedia 또는 LocalParticipant 클래스에서 얻을 수 있습니다. 비디오 ID를 알고 있는 경우, 해당 ID를 통해 비디오를 가져올 수 있으며, 로컬 미디어 생성 시 MediaOptions에서 지정한 extraValue를 통해 특정 비디오를 가져올 수 있습니다. 참여자들의 비디오/오디오의 세부 정보는 LocalVideo, LocalAudio, RemoteVideo, RemoteAudio 문서를 참고하시기 바랍니다.

코드예제로컬 참여자 비디오, 오디오 정보 획득 Syntax

// 로컬 미디어에서 비디오, 오디오 얻기
var video = media.video
var audio = media.audio

// 로컬 참여자
var participant = room.localParticipant

// 로컬 참여자에서 비디오, 오디오 얻기
var video = participant.videos.first?.value
var audio = participant.audios.first?.value

// 특정 extraValue의 비디오, 오디오 얻기
var video = pariticpant.videos.values.first { $0.extraValue == "hello" }
var audio = participant.audios.values.first { $0.extraValue == "test" }

리모트 참여자의 비디오/오디오 정보 얻기

리모트 참여자의 비디오 및 오디오 정보는 리모트 참여자(Remote Participant)에게서 얻을 수 있습니다.

코드예제리모트 참여자 비디오, 오디오 정보 획득 Syntax

// 리모트 참여자
var participant = room.remoteParticipants["참여자 ID"]

// 리모트 참여자에서 비디오, 오디오 얻기
var video = participant.videos?.first.value
var audio = aprticipant.audios?.first.value


// 특정 extraValue의 비디오, 오디오 얻기
var video = pariticpant.videos.values.first { $0.extraValue == "hello" }
var audio = participant.audios.values.first { $0.extraValue == "test" }

Step 9. 리모트 참여자의 비디오 구독하기

Room의 오디오는 각 참여자의 발화 여부에 따라 자동으로 수신이 이루어집니다. 비디오의 경우 많은 인원이 참가하는 서비스의 특성상 모든 비디오를 수신하지 않습니다. 각 서비스 상황에 맞게 특정 리모트 참여자의 비디오를 직접 구독하는 방식으로 비디오 스트림을 수신해야 합니다.

안내
Config에서 설정된 최대 리시버 수 이상 구독할 수 없으며, 구독 수만큼 데이터 트래픽이 발생하므로 필요로 하는 비디오만 구독해야 합니다.
  1. Room 클래스의 subscribe() 메서드를 통해 리모트 참여자(Remote Participant)의 비디오 ID를 인자로 사용하여 비디오 구독을 요청합니다.

    • 단일 비디오나 여러 개의 비디오에 대해 구독을 요청할 수 있으며, 개별 구독을 요청하는 경우 Callback을 통해 구독 결과가 바로 전달됩니다. 여러 개의 비디오 구독 요청 시에는 에러가 발생할 때에만 RoomDelegate의 onError를 통해 이벤트가 전달됩니다.
    • Room의 참여자 테이블은 각 참여자의 정보를 포함하며, 참여자 정보에는 각 스트림 정보가 포함되어 있습니다.

    코드예제비디오 구독 요청 Syntax

    guard let room = self.room else { return }
    guard let participant = room.remoteParticipants[participantId] else { return }
    guard let remoteVideo = participant.videos[videoId] else { return }
    
    // 비디오 구독 여부 확인
    if remoteVideo.isSubscribed {
    	print("[subscribe] \(participantId)의 remoteVideo 이미 구독 중")
    	return
    }
    
    
    // 비디오 구독
    Task {
    	let result = await room.subscribe(videoId: remoteVideo.id)
    	switch result {
    	case .success():
    		print("[subscribe] 구독 시작")
    
    	case .failure(let error):
    		if case let ConnectLiveError.error(code, message) = error {
    			print("[subscribe] 구독 오류, \(code), \(message)")
    		} else {
    			print("[subscribe] 구독 오류, \(error.localizedDescription)")
    		}
    	}
    }
    

  2. 리모트 참여자의 비디오에 뷰(View)를 연결하기 위해, 구독 중인 비디오 스트림에 렌더뷰(RenderView)를 첨부하여 비디오 스트림으로 전달되는 데이터를 해당 뷰에 표시합니다. 만약 렌더뷰가 이미 다른 스트림이 첨부되어 있는 경우 기존 연결은 분리됩니다.

    코드예제리모트 비디오에 뷰 연결 Syntax

    guard let room = self.room else { return }
    guard let participant = room.remoteParticipants[participantId] else { return }
    guard let remoteVideo = participant.videos[videoId] else { return }
    
    // 뷰 연결
    remoteVideo.attach(renderView.uiView)
    

Step 10. 리모트 비디오 구독 해제하기

구독 중인 리모트 참여자(Remote Participant)의 비디오의 구독을 해제합니다. 구독이 해제되면 실제 데이터는 전송되지 않습니다. 각 서비스에서는 시나리오에 따라 비디오 스트림의 구독 및 해제를 반복하게 됩니다.

  1. 구독 중인 리모트 참여자의 비디오 스트림은 비디오 데이터를 수신합니다. 화면에 보이지 않거나 렌더러(Renderer)가 할당되지 않은 사용자는 비디오 구독을 해제하여 데이터가 수신되지 않도록 합니다.

    코드예제비디오 구독 해제 Syntax

    guard let room = self.room else { return }
    guard let participant = room.remoteParticipants[participantId] else { return }
    guard let remoteVideo = participant.videos[videoId] else { return }
    
    // 비디오 구독 여부 확인
    if !remoteVideo.isSubscribed {
        print("[unsubscribe] \(participantId)의 remoteVideo 구독 중이 아닙니다")
        return
    }
    
    // 비디오 구독 해제
    Task {
        let result  = await room.unsubscribe(videoId: remoteVideo.id)
        switch result {
    
        case .success():
            print("[unsubscribe] 구독 해제")
    
        case .failure(let error):
            if case let ConnectLiveError.error(code, message) = error {
                print("[unsubscribe] 구독 오류, \(code), \(message)")
            } else {
                print("[unsubscribe] 구독 오류, \(error.localizedDescription)")
            }
        }
    }
    

  2. 리모트 참여자의 비디오에서 뷰(View)를 해제합니다. 구독 중인 비디오 스트림에 렌더뷰(Render View)를 첨부하면, 비디오 스트림으로 전달되는 데이터를 해당 뷰에 표시하게 됩니다. 만약 렌더뷰가 이미 다른 스트림이 첨부되어 있는 경우에는 기존 연결은 분리됩니다.

    코드예제리모트 비디오 뷰 해제 Syntax

    guard let room = self.room else { return }
    guard let participant = room.remoteParticipants[participantId] else { return }
    guard let remoteVideo = participant.videos[videoId] else { return }
    
    // 뷰 해제
    remoteVideo.detach()
    

Step 11. 기타 Room 기능 사용하기

Room 객체는 메시지 전송, 송수신 정보를 위한 다양한 부가 기능들을 제공합니다.

메시지 전송 하기

Room 클래스의 sendUserMessage() 메서드를 통해 participantIds 배열에 참여자 ID(participantIds)를 지정하여 해당 참여자들에게 메시지를 전달할 수 있습니다. 빈 배열을 지정하면 Room의 전체 참여자에게 메시지가 전달됩니다.

코드예제메시지 전송 Syntax

Task { [weak self] in
    guard let self = self else { return }
    guard let room = self.room else { return }

    let result = await room.sendUserMessage(participantIds: [], message: message)
    switch result {
    case .success():
        print("메시지 전송 성공")

    case .failure(let error):
        print("메시지 전송 실패, error:\(error.localizedDescription)")
    }
}

오디오 수신 차단 하기

setMuted() 메서드를 사용하여 서비스에서 모든 수신 오디오를 활성화 또는 비활성화할 수 있습니다.

코드예제오디오 수신 비활성화 Syntax

/// 모든 수신 오디오 차단
room.setMuted(true)

WebRTC 통계 정보 얻기

getLocalStatsReport() 메서드 또는 getRemoteStatsReport() 메서드를 사용하여 리모트 참여자 또는 리모트 참여자가 생성한 미디어의 송출 스트림에 대한 WebRTC 통계 정보를 확인할 수 있습니다.

코드예제WebRTC 통계 정보 획득 Syntax

/// 송신 세션(로컬 참여자)의 stat 정보 목록
room.getLocalStatsReport() { results in
	// results: [String: Statistics]
}
 
/// 수신 세션(리모트 참여자)의 stat 정보 목록
room.getRemoteStatsReport() { results in
	// results: [String: Statistics]
}

Step 12. 접속 해제하기

마지막 단계는 Room에서 접속을 해제하는 작업입니다.

  1. disconnect() 메서드를 호출하여 Room 연결을 해제합니다. 해제가 완료되면 RoomDelegate의 onDisconnected(reason:) 이벤트가 호출됩니다.

    코드예제disconnect() Syntax

    room.disconnect()
    

  2. 인증은 내부적으로 인증 정보 갱신을 위한 작업들이 수행되므로, 연결이 종료된 이후에는 signOut() 메서드를 호출하여 인증도 같이 해제해야 합니다.

    코드예제signOut() Syntax

    ConnectLive.signOut()
    

주요 이벤트

필수적으로 구현해야 하는 Room 이벤트 중 중요한 이벤트에 대해 설명합니다. RoomDelegate의 전체 이벤트 목록이나 각 이벤트 세부 내용은 RoomDelegate 문서를 참조하시기 바랍니다.
이벤트 처리에 포함된 코드는 예시로서 간단한 참여자 ID 목록을 업데이트하도록 구성되어 있습니다.

onConnected

onConnected는 참여자가 Room과의 연결이 완료되었을 때 발생하는 이벤트입니다. 해당 이벤트는 이미 Room에 참여하고 있는 Remote Participant(리모트 참여자) 목록을 인자로 전달합니다.

코드예제onConnected Syntax

func onConnected(participantIds: [String]) {
	// 별도 참여자 ID 목록 구성 예시
	self.participantIds = participantIds.sorted(by: <)
}

onDisconnected

onDisconnected는 Room 연결이 해제되면 호출됩니다. 연결 해제 이유가 함께 전달됩니다.

코드예제onDisconnected Syntax

func onDisconnected(reason: DisconnectReason) {
	// 해제 시 사용했던 객체를 해제하거나 초기화 등의 처리
	self.participantIds.removeAll() 

	// 종료 이유에 따른 처리 예시
	switch reason {
	case disconnected:
		// 사용자가 직접 종료하거나 에러에 의한 종료
		if self.latestErrorCode != 0 {
			// 에러로 인해 종료되었는지 확인해야 합니다.
		}
		break

	case destroyed:
		// 서버에서 Room이 종료됨
		break

	case kicked:
		// 관리자에 의해 연결이 해제됨
		break
}

onError

onError는 Room에서 발생한 에러가 전달되는 이벤트입니다. 중요 에러의 경우 onError의 isCritial이 true로 전달되고, 연결이 해제되어 onDisconnected 이벤트가 전달됩니다. onDisconnected에서 에러로 인한 종료를 구분하기 위해서는 별도로 에러코드와 메시지를 저장하여 사용합니다.
클라이언트 에러 코드와 메시지는 Error Code 문서를 참조하시기 바랍니다.

코드예제onError Syntax

func onError(code: Int, message: String, isCritial: Bool) {
	if isCritial {
		// 에러로 인해 연결이 종료될 예정
		self.latestErrorCode = code
		self.latestErrorMessage = message
	} else {
		// 종료되지 않는 에러로 간단한 토스트나 알림 메시지 표시
	}
}

onParticipantEntered

onParticipantEntered는 Room에 참여자가 접속하면 발생하는 이벤트입니다. 새로운 참여자가 입장하면 각 서비스는 특성에 맞게 목록을 재구성하거나 화면을 업데이트해야 합니다.

코드예제onParticipantEntered Syntax

func onParticipantEntered(remoteParticipant: RemoteParticipant) {
	// 참여자 입장시 저장된 참여자 id 목록에 해당 참여자 id를 넣는 예
	let pid = remoteParticipant.id
	var slice = participantIds[...]
	while !slice.isEmpty {
		let middle = slice.index(slice.startIndex, offsetBy: slice.count / 2)
		if id < slice[middle] {
			slice = slice[..<middle]
		} else {
			slice = slice[slice.index(after: middle)...]
		}
	}
	self.participantIds.insert(id, at: slice.startIndex)
}

onParticipantLeft

onParticipantLeft는 참여자가 Room에서 퇴장하면 발생하는 이벤트입니다.

코드예제onParticipantLeft Syntax

func onParticipantLeft(remoteParticipant: RemoteParticipant) {
	// 참여자 퇴장시 저장된 참여자 id 목록에서 퇴장한 참여자 제거 예
	if let index = participantIds.firstIndex(wherer: { $0 == remoteParticipant.id } ) {
		participantIds.remote(at: index)
	}
}

onRemoteVideoPublished

onRemoteVideoPublished는 Remote Participant(리모트 참여자)의 비디오 송출 시작 시 발생하는 이벤트입니다. 이 이벤트 이후에 구독으로 해당 비디오를 수신할 수 있습니다.

코드예제onRemoteVideoPublished Syntax

func onRemoteVideoPublished(remoteParticipant: RemoteParticipant, remoteVideo: RemoteVideo) {
	
}

onRemoteVideoUnpublished

onRemoteVideoUnpublished는 Remote Participant(리모트 참여자)의 비디오 송출 중단 이벤트입니다. 만약 구독 중인 비디오인 경우 SDK에서 구독 해제가 자동으로 수행됩니다. 구독 해제 이외 렌더러(Renderer)를 제거하거나 교체하는 등의 필요한 작업을 진행합니다.

코드예제onRemoteVideoUnpublished Syntax

func onRemoteVideoUnpublished(remoteParticipant: RemoteParticipant, remoteVideo: RemoteVideo) {
	
}

onRemoteAudioSubscribed

onRemoteAudioSubscribed는 Remote Participant(리모트 참여자)의 오디오 구독 이벤트입니다. 서비스에서는 이 이벤트를 통해 발화 관련 UI 업데이트 또는 사용자 화면 표시 등을 추가할 수 있습니다. 이 이벤트는 마이크 입력에 민감하게 반응하므로, 매우 빠른 간격으로 발생할 수 있습니다. 이 이벤트 수신 시 UI를 업데이트하면 디바이스의 부하가 심해질 수 있으므로, 이벤트 수의 카운팅이나 상태 처리 등 별도의 threshold 루틴을 통해 UI 업데이트를 진행해야 합니다.

코드예제onRemoteAudioSubscribed Syntax

func onRemoteAudioSubscribed(remoteParticipant: RemoteParticipant, remoteAudio: RemoteAudio) {
	
}

onRemoteAudioUnsubscribed

onRemoteAudioUnsubscribed는 리모트 참여자의 오디오 구독을 해제했을 때 발생하는 이벤트입니다. 이 이벤트는 마이크 입력에 민감하게 반응하므로, 매우 빠른 간격으로 발생할 수 있습니다. 해당 이벤트 수신 시 UI를 업데이트하면 디바이스 부하가 심해질 수 있으므로, 이벤트 수의 카운팅이나 상태 처리 등 별도의 threshold 루틴을 통해 UI 업데이트를 진행해야 합니다.

코드예제onRemoteAudioUnsubscribed Syntax

func onRemoteAudioUnsubscribed(remoteParticipant: RemoteParticipant, remoteAudio: RemoteAudio) {
	
}

에러 처리

iOS 버전의 카카오 i 커넥트 라이브 SDK 2.0에서 발생하는 에러는 onError 이벤트와 throws로 구분됩니다. onError 이벤트는 Room에서 발생하는 에러를 전달합니다. SDK에서 제공하는 메서드 호출 시 에러가 발생하면 Throws를 통해 에러를 처리해주는 곳으로 해당 에러를 전달합니다.

안내
클라이언트에서 발생하는 에러 코드는 Error Code 문서를 참고하시기 바랍니다.

onError

Room 이벤트인 RoomDelegate는 func onError(code: Int, message: String, isCritical: Bool) 이벤트를 포함하고 있습니다. Room에서 발생하는 오류는 onError를 통해 에러 코드가 전달됩니다. onError에 대한 자세한 설명은 onError를 참고하시기 바랍니다.

throws

SDK의 메서드를 호출 시 일부 메서드는 ConnectLiveError를 전달합니다. 예외의 에러가 ConnectLiveSDK의 에러인지 에러 코드와 메시지를 확인할 수 있습니다.

코드예제ConnectLiveError Syntax

do {
	try room.publish(media)
} catch {
	if case let ConnectLiveError.error(code, message) = error {
		// SDK ConnectLive error
	} else {
		// swift Error
	}
}

이 문서가 만족스러운 이유를 알려주세요.
이 문서에 아쉬운 점을 알려주세요.
평가해주셔서 감사합니다.

더 자세한 의견은 documentation@kakaoenterprise.com 으로 제보해주세요.