CBCentralManager는 iOS의 Core Bluetooth 프레임워크의 중앙 클래스로, BLE 주변 기기 스캔, 연결 및 상호 작용을 관리합니다. Core Bluetooth(iOS 5+, 2011)는 GATT 수준에서 BLE 스택 위에 높은 수준의 추상화를 제공하여 Link Layer 및 HCI의 세부 정보를 개발자로부터 숨깁니다. CBCentralManager는 Central 역할을 구현합니다. scanForPeripherals를 통해 전파를 스캔하고, connect를 통해 연결을 시작하며, discoverServices를 통해 서비스를 발견하고 데이터 전송을 관리합니다. Apple Developer Documentation(2024)에 따르면, CBCentralManager는 BLE 5.0을 지원하는 기기에서 최대 7개의 동시 BLE 연결을 지원합니다.
핵심 사항
CBCentralManager는 iOS의 BLE 아키텍처에서 Central 역할을 구현하기 위한 주요 Core Bluetooth 클래스입니다. 광고 중인 기기 스캔부터 데이터 전송 및 연결 해제까지 BLE 연결의 전체 수명 주기를 관리합니다. CBCentralManager는 CBCentralManagerDelegate를 통해 비동기적으로 작동하며 Bluetooth 스택의 이벤트를 앱에 알립니다.
CBCentralManager 초기화는 state restoration 프로세스를 시작합니다. 관리자는 기기의 Bluetooth 상태를 확인하고 앱이 종료된 경우 이전 연결을 복원합니다. 초기화 프로세스는 Bluetooth 상태에 따라 50~500ms가 소요될 수 있습니다. BLE 작업을 시작하기 전에 앱은 centralManagerDidUpdateState 호출을 기다려야 합니다.
Core Bluetooth 아키텍처는 Delegation 패턴을 기반으로 합니다. CBCentralManager는 이벤트 처리(기기 발견, 연결, 오류)를 CBCentralManagerDelegate 프로토콜에 위임합니다. 특정 주변 기기 작업에는 CBPeripheralDelegate 프로토콜이 사용되며, 발견된 서비스, 특성 및 수신 데이터를 알립니다. 이 비동기 모델은 논블로킹 UI를 보장합니다.
CBCentralManager는 여러 상태를 거칩니다. 이는 BLE 스택을 사용할 수 있는지 여부를 결정합니다. 상태는 델리게이트를 통해 전달됩니다: centralManagerDidUpdateState(_:). 개발자는 모든 상태를 처리해야 합니다. poweredOn뿐만 아니라 Bluetooth가 꺼져 있거나 사용할 수 없는 경우도 포함됩니다.
| 상태 | 값 | 개발자 조치 |
|---|---|---|
| .poweredOn | Bluetooth 켜짐 및 준비 완료 | 스캐닝 시작 |
| .poweredOff | Bluetooth 꺼짐 | 사용자에게 알림 표시 |
| .unauthorized | 권한 없음 | 설정에서 권한 요청 |
| .unsupported | 기기가 BLE 미지원 | BLE 기능 숨기기 |
| .unknown | 상태가 정의되지 않음 | 다음 업데이트 대기 |
| .resetting | Bluetooth 재시작 중 | 복구 대기 |
권한 없음 상태는 iOS 13+ 이후 점점 더 일반화되고 있습니다. 이 버전부터 앱은 Info.plist에 NSBluetoothAlwaysUsageDescription 권한이 있어야 합니다. 이 권한이 없으면 중앙 관리자가 .unauthorized 상태로 전환되고 스캐닝이 불가능합니다. 사용자는 언제든지 설정 > 개인정보 보호 > Bluetooth에서 권한을 변경할 수 있습니다.
scanForPeripherals(withServices:options:)는 스캐닝을 시작하는 주요 메서드입니다. withServices 매개변수는 필터링을 위한 서비스 UUID 배열을 허용합니다. nil을 전달하면 모든 기기가 발견되어 전력 소비가 크게 증가합니다. 앱에 필요한 서비스 UUID로 항상 필터링하는 것이 좋습니다. 스캔 옵션에는 CBCentralManagerScanOptionAllowDuplicatesKey(동일한 기기에 대한 반복 알림)가 포함됩니다.
import CoreBluetooth
class BLEController: NSObject,
CBCentralManagerDelegate {
private var centralManager: CBCentralManager!
override init() {
super.init()
centralManager =
CBCentralManager(
delegate: self,
queue: nil
)
}
func startScanning() {
let serviceUUID =
CBUUID("180F") // 배터리 서비스
centralManager.scanForPeripherals(
withServices: [serviceUUID],
options: [
CBCentralManagerScanOptionAllowDuplicatesKey: false
]
)
}
}
기기가 발견되면 centralManager(_:didDiscover:advertisementData:rssi:)가 호출됩니다. advertisementData 매개변수에는 기기 이름(CBAdvertisementDataLocalNameKey), 서비스 UUID(CBAdvertisementDataServiceUUIDsKey) 및 제조업체 데이터(CBAdvertisementDataManufacturerDataKey)를 포함한 광고 패킷 데이터의 전체 사전이 포함됩니다. RSSI는 발견 시점의 dBm 단위 신호 레벨입니다.
connect(_:options:)는 발견된 주변 기기와 BLE 연결을 설정하는 메서드입니다. connect를 호출한 후 iOS는 기기에 연결을 시도합니다. 연결 성공은 centralManager(_:didConnect:)를 통해 확인되고, 오류는 centralManager(_:didFailToConnect:error:)를 통해 전달됩니다. 연결 옵션에는 백그라운드 알림을 위한 CBConnectPeripheralOptionNotifyOnConnectionKey, CBConnectPeripheralOptionNotifyOnDisconnectionKey 및 CBConnectPeripheralOptionNotifyOnNotificationKey가 포함됩니다.
// BLE 기기에 연결
func connectToPeripheral(
_ peripheral: CBPeripheral
) {
centralManager.connect(peripheral, options: nil)
// 주변 기기 델리게이트 설정
peripheral.delegate = self
}
// 델리게이트: 연결 성공
func centralManager(
_ central: CBCentralManager,
didConnect peripheral: CBPeripheral
) {
print("연결됨: " +
"\(peripheral.name ?? "unknown")")
// 서비스 발견 시작
peripheral.discoverServices(nil)
}
// 델리게이트: 연결 오류
func centralManager(
_ central: CBCentralManager,
didFailToConnect peripheral: CBPeripheral,
error: Error?
) {
print("Connection failed:
\(error?.localizedDescription ?? "")")
}
iOS의 연결 시간 초과는 30초입니다. 이 시간 내에 기기가 연결 요청에 응답하지 않으면 didFailToConnect가 호출됩니다. 시간 초과는 기기까지의 거리, 간섭 및 기기가 현재 광고 중인지 여부에 영향을 받습니다. 연결하기 전에 기기가 연결 가능한 광고 모드(ADV_IND, ADV_NONCONN_IND 아님)에 있는지 확인하세요.
연결 후 주변 기기의 서비스(discoverServices) 및 특성(discoverCharacteristics)을 발견해야 합니다. 데이터를 읽거나 쓰기 전에 필수 단계입니다. 프로세스는 비동기식입니다. discoverServices는 peripheral(_:didDiscoverServices:)를 통해 결과를 반환하고, discoverCharacteristics는 peripheral(_:didDiscoverCharacteristicsFor:error:)를 통해 결과를 반환합니다.
discoverServices에 nil 대신 관련 UUID 배열을 전달하는 것이 좋습니다. 필터링은 발견 속도를 높이고 전력을 절약합니다. 서비스를 찾을 수 없는 경우 iOS는 빈 배열을 보고합니다. 특성을 발견한 후에는 값 읽기(readValue), 알림 구독(setNotifyValue) 또는 데이터 쓰기(writeValue)를 수행할 수 있습니다.
중요한 세부 사항: MTU는 연결 후 자동으로 협상됩니다. 현재 MTU를 얻으려면 peripheral.maximumWriteValueLength(for: .withResponse) 또는 .withoutResponse를 사용하세요. iOS에서 BLE 5.0 기기의 최대 MTU는 512바이트입니다. MTU보다 큰 데이터를 전송해야 하는 경우 애플리케이션 수준에서 조각화를 구현하세요.
iOS에서 BLE 기기의 백그라운드 스캐닝에는 특별한 구성이 필요합니다. Core Bluetooth는 백그라운드 실행을 지원하지만 상당한 제한이 있습니다. 백그라운드에서 작업하려면 프로젝트 Capabilities의 Background Modes에서 bluetooth-central을 활성화하고, state restoration을 위해 CBCentralManagerOptionRestoreIdentifierKey 옵션으로 CBCentralManager를 초기화하며, 백그라운드 전환 시 중앙 관리자 이벤트를 처리해야 합니다.
iOS의 백그라운드 BLE 제한 사항: UUID 필터링 없이 scanForPeripherals는 백그라운드에서 작동하지 않습니다. 앱은 스캐닝을 위해 구체적인 서비스 UUID를 지정해야 합니다. iOS는 BLE 이벤트 전달을 무기한 지연시킬 수 있습니다. Core Bluetooth는 앱이 백그라운드에 있어도 일치하는 기기가 발견되면 자동으로 스캐닝을 재개합니다. 백그라운드 스캐닝 시간 초과: iOS는 전력 절약을 위해 10~30분 후에 스캐닝을 중지할 수 있습니다.
State Restoration은 앱 재시작 또는 iOS 재부팅 후 BLE 연결을 복원할 수 있는 Core Bluetooth 메커니즘입니다. 사용하려면 초기화 시 CBCentralManagerOptionRestoreIdentifierKey를 지정하고, 델리게이트에서 centralManager(_:willRestoreState:)를 구현하며, 전달된 사전에서 연결된 주변 기기 목록을 복원합니다. State Restoration은 피트니스 트래커나 의료 기기와 같이 백그라운드에서 작동하는 BLE 앱에 중요한 기능입니다.
CBCentralManager는 여러 시나리오에서 오류를 생성합니다: 연결 실패(didFailToConnect), 연결 끊김(didDisconnectPeripheral), 특성을 읽거나 쓸 수 없음(didWriteValue 오류). 모든 Core Bluetooth 오류는 CBErrorDomain 도메인의 Error 객체를 통해 반환됩니다. 가장 일반적인 코드: CBErrorConnectionTimeout(0x04), CBErrorPeripheralDisconnected(0x07), CBErrorOperationNotSupported(0x0A).
연결 복구 전략: didDisconnectPeripheral을 수신하면 오류 코드를 확인합니다. 오류가 CBErrorConnectionTimeout 또는 CBErrorPeripheralDisconnected인 경우 1~5초 후에 자동 재연결을 예약합니다. 오류가 CBErrorOperationNotSupported인 경우 기록하고 작업을 다시 시도하지 마십시오. 중요한 연결(의료 기기)의 경우 최대 간격 60초로 지수 백오프를 사용합니다.
// 자동 재연결로 연결 끊김 처리
func centralManager(
_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: Error?
) {
guard let error = error else {
return // 예상된 연결 끊김
}
print("Disconnected: \(error.localizedDescription)")
// 자동 재연결
if shouldAutoReconnect {
DispatchQueue.main.asyncAfter(
deadline: .now() + reconnectDelay
) {
central.connect(peripheral)
}
}
}
iOS에서 견고한 BLE 앱을 개발할 때 유의 사항: Core Bluetooth는 약한 신호에서 모든 패킷 전달을 보장하지 않습니다. 안정적인 전송을 위해 writeType .withResponse(확인된 쓰기)를 사용하고 주변 기기로부터 데이터를 수신하기 위해 알림(setNotifyValue)을 구독하세요. 프로덕션에서 연결 문제 진단을 위해 오류 로그를 유지하세요.
자주 묻는 질문
centralManagerDidUpdateState를 통해 관리자 상태를 확인하세요. Info.plist에 NSBluetoothAlwaysUsageDescription 권한이 있는지, 기기에서 Bluetooth가 활성화되어 있는지, 주변 기기가 올바른 유형(연결 가능한 광고, 연결 불가능이 아님)으로 광고하고 있는지 확인하세요.
BLE 5.0을 지원하는 기기(iPhone 8 이상)에서 최대 7개의 동시 연결이 가능합니다. 구형 기기에서는 최대 3~5개입니다. 스캔된 기기 수에는 제한이 없지만 활성 연결에는 Bluetooth 컨트롤러가 설정한 엄격한 제한이 있습니다.
UUID 필터링으로 스캔하고 기기를 찾으면 스캐닝을 중지하는 것이 좋습니다. 지속적인 스캐닝은 배터리를 소모합니다: 1시간 연속 스캐닝은 iPhone 배터리의 약 10~15%를 소비합니다. 타이머와 조건을 사용하여 스캐닝을 중지하세요.
CBCentralManager는 외부 BLE 기기(Central 역할)를 스캔하고 연결하는 데 사용됩니다. CBPeripheralManager는 iOS 기기 자체가 BLE 주변 기기로 작동(서비스 광고)하도록 합니다. 하나의 인스턴스는 하나의 역할만 수행할 수 있습니다.
centralManager(_:didDisconnectPeripheral:error:)를 구현합니다. 오류가 nil이 아닌 경우 지수 백오프(1초 → 2초 → 4초 → 8초 → 최대 60초)로 자동 재연결을 예약합니다. 오류가 nil인 경우 기기가 정상적으로 연결이 끊긴 것입니다(예: 사용자가 기기의 버튼을 누름).
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.