Core Bluetooth는 iOS, iPadOS 및 macOS에서 Bluetooth Low Energy와 상호작용하기 위한 Apple의 프레임워크입니다. 이 프레임워크는 두 BLE 역할 모두에서 작동하기 위한 완전한 API 세트를 제공합니다: 중앙 장치(CBCentralManager)는 주변 기기 스캔 및 연결용, 주변 장치(CBPeripheralManager)는 BLE 서버 에뮬레이션용입니다. Core Bluetooth는 물리적 라디오에서 애플리케이션 수준 GATT 프로필까지 BLE 프로토콜 스택을 추상화합니다. Apple Developer, 2026에 따르면 Core Bluetooth는 BLE 개발을 위한 유일한 공식 Apple API이며 BLE 4.0–5.4, extended advertising, 2M PHY 및 LE Audio를 지원합니다.
핵심 사항
Core Bluetooth는 BLE 스택을 Bluetooth SIG 사양에 정의된 두 가지 논리적 역할로 나눕니다. 중앙 장치 역할(Central)은 CBCentralManager 클래스로 표현됩니다 — 스캔을 시작하고, 연결을 설정하며, 연결된 CBPeripheral 목록을 관리합니다. 주변 장치 역할(Peripheral)은 CBPeripheralManager로 표현됩니다 — 서비스와 특성을 게시하고, 중앙 요청에 응답하며, 알림을 보냅니다. 하나의 iOS 세션은 다른 BLE 라디오에서 두 역할을 동시에 수행할 수 있지만, 일반적인 애플리케이션은 하나의 역할을 사용합니다.
Core Bluetooth 아키텍처에는 다섯 가지 주요 추상화가 포함됩니다. CBCentralManager는 장치의 Bluetooth 어댑터 상태를 관리합니다: poweredOn(작업 준비 완료), poweredOff(Bluetooth 비활성화), unauthorized(권한 없음), unsupported(BLE 사용 불가). CBPeripheral은 UUID, 이름, RSSI 및 GATT 계층 구조를 가진 원격 BLE 장치를 나타냅니다. CBService — 특성의 논리적 그룹. CBCharacteristic — 읽기/쓰기/알림을 위한 데이터 포인트. CBPeripheralManager는 주변 기기를 에뮬레이션하기 위한 로컬 GATT 서버를 생성합니다.
| 클래스 | 역할 | 주요 메서드 |
|---|---|---|
| CBCentralManager | 중앙 장치 | scanForPeripherals, connect, cancelPeripheralConnection, retrievePeripherals |
| CBPeripheral | 원격 주변 | discoverServices, discoverCharacteristics, readValue, writeValue, setNotifyValue |
| CBPeripheralManager | 로컬 주변 | addService, removeService, startAdvertising, respondToRequest, updateValue |
| CBCentral | 원격 중앙 | maximumUpdateValueLength, identifier, ancsAuthorized |
CBCentralManager 상태는 모든 BLE 작업을 제어합니다. 앱이 시작되면 centralManagerDidUpdateState가 현재 Bluetooth 상태와 함께 호출됩니다. 상태가 .poweredOn이 아니면 시스템이 BLE 호출을 무시합니다. 개발자는 각 스캔 및 연결 전에 상태를 확인해야 합니다. .poweredOff에서 .poweredOn으로의 전환은 iOS 설정에서 Bluetooth가 활성화될 때 발생합니다 — 델리게이트가 다시 호출되고 앱이 스캔을 재개할 수 있습니다.
CBCentralManager는 중앙 장치 측의 모든 BLE 작업을 위한 진입점입니다. 초기화는 델리게이트(CBCentralManagerDelegate)와 DispatchQueue를 받습니다 — Apple은 단순성을 위해 메인 큐, 성능을 위해 직렬 큐를 권장합니다. 초기화 후 프레임워크는 자동으로 Bluetooth 상태를 확인하고 centralManagerDidUpdateState:를 호출합니다 — 처리해야 할 첫 번째 필수 델리게이트입니다.
스캔은 scanForPeripheralsWithServices:options: 메서드로 시작됩니다. 첫 번째 매개변수는 필터링할 CBUUID 서비스의 배열입니다: 원하는 서비스의 UUID를 알고 있으면 전달하여 전력 소비와 검색 시간을 줄일 수 있습니다. nil이면 범위 내의 모든 BLE 장치가 검색됩니다. 옵션에는 .allowDuplicatesKey(동일한 장치의 중복 검색)와 .solicitedServiceUUIDsKey(중앙에 게시된 서비스용)가 포함됩니다.
import CoreBluetooth
class BLECentral: NSObject {
private var centralManager: CBCentralManager!
private var discoveredPeripherals: [CBPeripheral] = []
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: .main)
}
// BLE 스캔 시작
func startScan() {
guard centralManager.state == .poweredOn else {
print("Bluetooth를 사용할 수 없음")
return
}
// 모든 장치 스캔(nil = 필터 없음)
centralManager.scanForPeripherals(withServices: nil,
options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
}
// 스캔 중지
func stopScan() {
centralManager.stopScan()
}
// 선택한 장치에 연결
func connect(to peripheral: CBPeripheral) {
centralManager.connect(peripheral, options: nil)
}
}
// MARK: - CBCentralManagerDelegate
extension BLECentral: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
startScan()
}
}
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any],
rssi: NSNumber) {
if !discoveredPeripherals.contains(where: { $0.identifier == peripheral.identifier }) {
discoveredPeripherals.append(peripheral)
print("Found devices: \(peripheral.name ?? "Unknown"), RSSI: \(rssi)")
}
}
func centralManager(_ central: CBCentralManager,
didConnect peripheral: CBPeripheral) {
print("Connected: \(peripheral.identifier)")
peripheral.delegate = self
peripheral.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: Error?) {
print("Disconnected: \(peripheral.identifier)")
}
}
BLECentral 클래스는 BLE 장치 스캔 및 연결의 전체 주기를 보여줍니다. centralManagerDidUpdateState는 Bluetooth가 활성화될 때 스캔을 시작합니다. didDiscoverPeripheral은 identifier로 중복 제거하여 발견된 장치를 discoveredPeripherals 배열에 수집합니다. 연결(didConnect) 후 서비스 검색이 즉시 시작됩니다 — 이는 GATT 작업 전 필수 단계입니다.
CBPeripheralManager는 iOS에서 BLE 주변 장치를 에뮬레이션하기 위한 클래스입니다. 주변 역할의 앱은 서비스와 특성을 게시하고, 중앙 장치의 읽기/쓰기 요청을 수락하며, 알림을 보낼 수 있습니다. CBPeripheralManager는 iPhone에서 에뮬레이션되는 BLE 액세서리에 사용됩니다: 리모컨, 키보드, 트래커, IoT 게이트웨이.
CBPeripheralManager의 수명 주기는 초기화와 CBPeripheralManagerDelegate로 시작됩니다. peripheralManagerDidUpdateState:를 통해 poweredOn 확인을 받은 후 서비스가 게시되고(addService:), 광고가 시작됩니다(startAdvertising:). 광고 데이터 CBAdvertisementData에는 로컬 이름(CBAdvertisementDataLocalNameKey), 서비스 UUID(CBAdvertisementDataServiceUUIDsKey), 전송 전력 수준(CBAdvertisementDataTxPowerLevelKey)이 포함됩니다. 최대 광고 패킷 크기는 BLE 4.0의 경우 31바이트, extended advertising BLE 5.0+의 경우 251바이트입니다.
// CBPeripheralManager를 통한 iOS의 BLE 주변 기기
class BLEPeripheral: NSObject {
private var peripheralManager: CBPeripheralManager!
let serviceUUID = CBUUID(string: "1234")
let characteristicUUID = CBUUID(string: "5678")
override init() {
super.init()
peripheralManager = CBPeripheralManager(delegate: self, queue: .main)
}
// 특성으로 서비스 게시
func setupService() {
let characteristic = CBMutableCharacteristic(
type: characteristicUUID,
properties: [.read, .write, .notify],
value: nil,
permissions: [.readable, .writeable]
)
let service = CBMutableService(type: serviceUUID, primary: true)
service.characteristics = [characteristic]
peripheralManager.add(service)
}
// 광고 시작
func startAdvertising() {
let advertisementData: [String: Any] = [
CBAdvertisementDataLocalNameKey: "My BLE Device",
CBAdvertisementDataServiceUUIDsKey: [serviceUUID]
]
peripheralManager.startAdvertising(advertisementData)
}
}
// MARK: - CBPeripheralManagerDelegate
extension BLEPeripheral: CBPeripheralManagerDelegate {
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if peripheral.state == .poweredOn {
setupService()
}
}
func peripheralManager(_ peripheral: CBPeripheralManager,
didAdd service: CBService,
error: Error?) {
if error == nil {
startAdvertising()
}
}
// 읽기 요청 처리
func peripheralManager(_ peripheral: CBPeripheralManager,
didReceiveRead request: CBATTRequest) {
let data = "CurrentValue".data(using: .utf8)!
request.value = data
peripheralManager.respond(to: request, withResult: .success)
}
// 쓰기 요청 처리
func peripheralManager(_ peripheral: CBPeripheralManager,
didReceiveWrite requests: [CBATTRequest]) {
for request in requests {
if let value = request.value {
print("Write: \(value)")
}
}
peripheralManager.respond(to: requests.first!, withResult: .success)
}
}
BLEPeripheral 클래스는 읽기, 쓰기 및 알림을 지원하는 단일 특성으로 BLE 서버를 생성합니다. 초기화 후 peripheralManagerDidUpdateState는 addService:를 통해 서비스를 게시한 다음 startAdvertising:를 통해 광고를 시작합니다. didReceiveRead 및 didReceiveWrite 핸들러는 중앙 장치의 수신 GATT 요청에 응답합니다. 알림 전송에는 updateValue:forCharacteristic:onSubscribedCentrals: 메서드가 사용됩니다.
GATT 작업(Generic Attribute Profile)은 Core Bluetooth에서 데이터 교환의 기초입니다. 서비스 및 특성 검색 후 중앙 장치는 세 가지 유형의 작업을 수행할 수 있습니다: 특성 값 읽기, 값 쓰기, 알림/인디케이션 구독. 각 작업은 비동기적이며 해당 CBPeripheralDelegate 콜백을 통해 결과를 반환합니다.
읽기: readValueForCharacteristic:를 호출하여 수행합니다. 값은 peripheral:didUpdateValueForCharacteristic:error:로 도착합니다. 중요: 읽기는 캐시된 값이 아닌 장치의 현재 값을 반환합니다. 장치가 읽기를 지원하지 않는 경우(.read 속성), 호출은 오류를 반환합니다. 큰 값(MTU보다 큰)의 경우 BLE는 GATT 수준에서 자동으로 데이터를 분할하고 재조립합니다.
쓰기: writeValue:forCharacteristic:type:을 호출하여 수행합니다. BLE는 두 가지 쓰기 모델을 지원합니다: withResponse(신뢰할 수 있음, 확인 포함)와 withoutResponse(빠름, 확인 없음). CBCharacteristic.properties 속성은 사용 가능한 쓰기 유형을 정의합니다. 단일 쓰기 패킷의 최대 크기는 MTU에 의해 제한됩니다: BLE 4.0의 경우 23바이트(20바이트 페이로드 + 3바이트 헤더), 확장 MTU(MTU 251)가 있는 BLE 5.0의 경우 최대 247바이트.
알림: setNotifyValue:true forCharacteristic:을 호출하여 활성화합니다. 구독 후 주변 장치는 특성 값이 변경될 때마다 peripheral:didUpdateValueForCharacteristic:을 통해 자동으로 업데이트를 전송합니다. 알림을 비활성화하려면 setNotifyValue:false forCharacteristic:을 호출합니다. Core Bluetooth는 주변 장치의 CCCD 디스크립터를 자동으로 관리합니다.
| 작업 | 메서드 | 델리게이트 | 전송 유형 |
|---|---|---|---|
| 읽기 | readValueForCharacteristic: | didUpdateValueForCharacteristic | 폴링(요청-응답) |
| withResponse 쓰기 | writeValue:forCharacteristic:type:withResponse | didWriteValueForCharacteristic | 확인 포함 |
| withoutResponse 쓰기 | writeValue:forCharacteristic:type:withoutResponse | 델리게이트 없음 | 확인 없음 |
| 알림 | setNotifyValue:true forCharacteristic: | didUpdateNotificationStateForCharacteristic + didUpdateValueForCharacteristic | 주변에서 푸시 |
백그라운드 모드에서 Core Bluetooth는 BLE 앱이 백그라운드에 있는 동안 스캔을 계속하고, 연결을 유지하며, 알림을 받을 수 있도록 합니다. 활성화하려면 Xcode에서 “Uses Bluetooth LE accessories” 기능을 활성화하고(Info.plist → Required background modes → App communicates using Core Bluetooth) UIBackgroundModes에 “bluetooth-central” 키를 추가합니다. 주변 역할의 경우 — “bluetooth-peripheral”.
State Restoration은 iOS에 의한 앱 재시작 후 BLE 연결 상태를 복원하기 위한 Core Bluetooth의 메커니즘입니다. 백그라운드 모드가 활성화되어 있고 CBCentralManager 또는 CBPeripheralManager 초기화에서 restoreIdentifier가 지정된 경우, iOS는 앱 종료 시 BLE 스택 상태를 저장하고 다음 실행 시 복원합니다. 델리게이트 centralManager:willRestoreState:는 저장된 CBPeripheral 및 보류 중인 연결이 포함된 딕셔너리를 받습니다.
// State Restoration을 사용한 Core Bluetooth 구성
class BLECentralWithRestoration: NSObject {
let restoreIdentifier = "com.app.blecentral"
private var centralManager: CBCentralManager!
override init() {
super.init()
let options: [String: Any] = [
CBCentralManagerOptionRestoreIdentifierKey: restoreIdentifier,
CBCentralManagerOptionShowPowerAlertKey: true
]
centralManager = CBCentralManager(delegate: self,
queue: nil,
options: options)
}
}
extension BLECentralWithRestoration: CBCentralManagerDelegate {
// 재시작 후 상태 복원
func centralManager(_ central: CBCentralManager,
willRestoreState dict: [String : Any]) {
if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey]
as? [CBPeripheral] {
for peripheral in peripherals {
peripheral.delegate = self
// GATT 검색 복원
peripheral.discoverServices(nil)
}
}
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
print("복원 후 Bluetooth 준비 완료")
}
}
}
BLECentralWithRestoration 구성에서 CBCentralManagerOptionRestoreIdentifierKey 키는 상태 보존을 활성화합니다. iOS에 의해 앱이 종료된 경우(예: 메모리 부족), 다음 실행 시 centralManager:willRestoreState:는 이전에 연결된 CBPeripheral 목록을 받습니다. 앱은 델리게이트를 복원하고 서비스 재검색을 수행합니다 — 사용자는 연결 중단을 인지하지 못합니다. State Restoration이 없으면 앱 종료 시 모든 BLE 세션이 손실됩니다.
완전한 예제로 Swift의 BLE 앱이 하나의 프로젝트에서 중앙 및 주변 장치를 결합합니다. 앱은 두 가지 모드로 작동할 수 있습니다: BLE 장치 검색 및 연결(중앙) 또는 BLE 액세서리 에뮬레이션(주변). 아래는 시작 시 역할을 선택하는 공통 BLE 관리자를 사용한 아키텍처입니다.
// 중앙 및 주변을 위한 범용 BLE 관리자
class BLEManager {
enum Role {
case central
case peripheral
}
private let role: Role
private var centralManager: CBCentralManager?
private var peripheralManager: CBPeripheralManager?
let advertisedServiceUUID = CBUUID(string: "A001")
init(role: Role) {
self.role = role
switch role {
case .central:
centralManager = CBCentralManager(delegate: nil, queue: .main)
case .peripheral:
peripheralManager = CBPeripheralManager(delegate: nil, queue: .main)
}
}
// 중앙 장치: 스캔
func scanForDevices() {
centralManager?.scanForPeripherals(withServices: nil, options: nil)
}
// 주변 장치: 광고
func advertiseService() {
let data: [String: Any] = [
CBAdvertisementDataServiceUUIDsKey: [advertisedServiceUUID]
]
peripheralManager?.startAdvertising(data)
}
}
// 시작 시 사용
let isCentral = UserDefaults.standard.bool(forKey: "isCentral")
let manager = BLEManager(role: isCentral ? .central : .peripheral)
if isCentral {
manager.scanForDevices()
} else {
manager.advertiseService()
}
BLEManager는 초기화 시 역할을 선택하고 해당 Manager(CBCentralManager 또는 CBPeripheralManager)를 생성합니다. 역할 플래그는 UserDefaults에 저장하거나 구성 서버를 통해 전달할 수 있습니다. 이 접근 방식을 통해 BLE 앱은 사용 사례에 적응할 수 있습니다: 판매 시점에서 iPhone은 중앙으로서 결제 단말기를 스캔하고, IoT 게이트웨이에서는 주변으로서 센서 데이터를 수집합니다.
자주 묻는 질문
Core Bluetooth는 iOS, iPadOS 및 macOS에서 BLE 개발을 위한 Apple의 프레임워크입니다. 중앙(CBCentralManager) 및 주변(CBPeripheralManager) 장치 모두에 API를 제공합니다. BLE 4.0–5.4, extended advertising, 2M PHY 및 LE Audio를 지원합니다. Core Bluetooth는 BLE 통신을 위한 유일한 공식 Apple API이며 Bluetooth Low Energy로 작업하는 모든 iOS 앱에 필요합니다.
CBCentralManager는 중앙 장치 역할을 위한 클래스입니다: BLE 주변 기기를 스캔하고, 연결을 설정하며, 특성을 읽고 씁니다. CBPeripheralManager는 주변 역할을 위한 클래스입니다: 서비스를 게시하고, 읽기/쓰기 요청에 응답하며, 알림을 보냅니다. 하나의 iPhone은 다른 관리자 인스턴스를 통해 두 역할을 동시에 수행할 수 있습니다.
백그라운드 BLE 작동을 위해 Xcode에서 “Uses Bluetooth LE accessories” 기능을 활성화하고 UIBackgroundModes에 “bluetooth-central” 키를 추가합니다. 주변 역할의 경우 — “bluetooth-peripheral”. State Restoration을 위해 관리자 초기화 시 restoreIdentifier를 지정합니다. 이러한 설정 없이 백그라운드 앱은 BLE 이벤트를 수신하지 못하고 연결이 끊어집니다.
일반적인 원인: CBCentralManager.state != .poweredOn(Bluetooth 비활성화 또는 권한 없음), 델리게이트가 설정되지 않음, 장치가 범위를 벗어났거나 광고 패킷을 보내지 않음. Info.plist의 NSBluetoothAlwaysUsageDescription 권한, centralManagerDidUpdateState의 Bluetooth 상태를 확인하고 scanForPeripherals가 .poweredOn일 때만 호출되는지 확인하세요.
네, Core Bluetooth는 여러 BLE 장치에 동시 연결을 지원합니다. 각 CBPeripheral은 자체 델리게이트를 통해 독립적으로 관리됩니다. iOS는 시스템 수준에서 동시 BLE 연결 수를 제한합니다(일반적으로 iPhone의 경우 5–7개). 1:N 시나리오(예: 10개의 트래커가 있는 피트니스 센터)에서는 주변 기기를 대기열에 넣고 순환적으로 서비스해야 합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.