iOS CoreBluetooth 완벽 가이드: 권한 설정부터 데이터 송수신 구현까지
iOS 앱에서 블루투스 기기와 연동하기 위해 사용하는 Apple의 공식 CoreBluetooth 프레임워크 핵심 개념과 실전 구현 방법을 정리합니다. iOS 기기가 Central(중앙 제어 기기) 역할을 수행하여 주변 Peripheral(외부 기기)을 검색하고 연결하는 표준 개발 흐름을 다룹니다.
1. CoreBluetooth 핵심 구조 및 개념
CoreBluetooth는 BLE(Bluetooth Low Energy) 통신을 지원하는 iOS 전용 프레임워크입니다.
- CBCentralManager: iOS 기기를 Central 역할로 동작시키며 주변 기기 스캔, 연결, 상태 관리를 전담합니다.
- CBPeripheral: 연결 대상이 되는 외부 기기 객체로, 서비스 및 특성(Characteristic)을 포함합니다.
- CBService: Peripheral이 제공하는 기능 데이터의 단위입니다. (UUID로 식별)
- CBCharacteristic: Service 내부의 실질적인 데이터 필드로, Read, Write, Notify 등의 접근 권한을 포함합니다.
2. Info.plist 권한 설정
iOS 13 이상부터 블루투스 기능을 사용하려면 반드시 Info.plist에 접근 권한 설명 문구를 추가해야 합니다.
| Key | Type | 설명 |
| NSBluetoothAlwaysUsageDescription | String | iOS 13 이상 필수 권한 안내 문구 |
| NSBluetoothPeripheralUsageDescription | String | iOS 12 이하 하위 호환 권한 안내 문구 |
<key>NSBluetoothAlwaysUsageDescription</key>
<string>주변 블루투스 기기 검색 및 데이터 송수신을 위해 권한이 필요합니다.</string>
3. 구현 단계별 흐름
1단계: CBCentralManager 초기화 및 상태 확인
CBCentralManager를 생성하고 CBCentralManagerDelegate를 채택하여 블루투스 전원 상태를 모니터링합니다.
import CoreBluetooth
class BluetoothManager: NSObject, CBCentralManagerDelegate {
var centralManager: CBCentralManager!
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
// 블루투스 활성화 상태 - 기기 스캔 시작 가능
startScan()
case .poweredOff:
// 블루투스 비활성화 상태
break
case .unauthorized:
// 권한 거부 상태
break
default:
break
}
}
}
2단계: 주변 기기 스캔 및 연결 요청
scanForPeripherals를 통해 지정된 Service UUID의 기기를 탐색하거나 전체 탐색을 수행합니다.
extension BluetoothManager {
func startScan() {
// 특정 Service UUID 탐색 시 withServices 매개변수에 CBUUID 배열 전달
centralManager.scanForPeripherals(withServices: nil, options: nil)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("발견된 기기: \(peripheral.name ?? "이름 없음") (RSSI: \(RSSI))")
// 탐색된 기기 저장 및 스캔 중단 후 연결
centralManager.stopScan()
centralManager.connect(peripheral, options: nil)
}
}
3단계: Peripheral 연결 완료 및 Service/Characteristic 검색
연결 성공 후 CBPeripheralDelegate를 지정하고, 내부 Service와 Characteristic을 순차적으로 탐색합니다.
extension BluetoothManager: CBPeripheralDelegate {
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
// 서비스 탐색 시작
peripheral.discoverServices(nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
for service in services {
// 해당 서비스의 특성 탐색
peripheral.discoverCharacteristics(nil, for: service)
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else { return }
for characteristic in characteristics {
// 데이터 수신(Read) 또는 알림(Notify) 설정
if characteristic.properties.contains(.read) {
peripheral.readValue(for: characteristic)
}
if characteristic.properties.contains(.notify) {
peripheral.setNotifyValue(true, for: characteristic)
}
}
}
}
4단계: 데이터 읽기, 쓰기 및 실시간 수신
Characteristic의 데이터 업데이트 및 쓰기 완료 시 콜백 메서드를 통해 응답을 처리합니다.
extension BluetoothManager {
// 데이터 수신 처리 (Read / Notify 업데이트 공통)
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
guard let data = characteristic.value else { return }
// 수신된 Data 파싱 처리
print("수신 데이터: \(data)")
}
// 데이터 전송 (Write)
func sendData(_ data: Data, to peripheral: CBPeripheral, characteristic: CBCharacteristic) {
peripheral.writeValue(data, for: characteristic, type: .withResponse)
}
}
4. 백그라운드 블루투스 동작 설정
앱이 백그라운드 상태에서도 블루투스 통신을 유지해야 하는 경우, Xcode Signing & Capabilities -> Background Modes에서 다음 항목을 체크해야 합니다.
- Uses Bluetooth LE accessories: Central 모드로 백그라운드 데이터 수신 시 설정
- Acts as a Bluetooth LE accessory: Peripheral 모드로 백그라운드 신호 송출 시 설정
5. 핵심 한 줄 요약
CoreBluetooth 개발은 CBCentralManager 상태 확인 -> 기기 스캔 및 연결 -> CBPeripheral Service/Characteristic 탐색 -> 데이터 읽기/쓰기/알림 설정 순서로 진행됩니다.
'개발 > iOS' 카테고리의 다른 글
| MDM 프로파일 생성 및 서명·수명주기 관리 가이드 (0) | 2026.09.04 |
|---|---|
| Xcode 시뮬레이터 먹통으로 마우스 및 키보드 입력 불가 (0) | 2026.08.10 |
| iOS AppIntents 아키텍쳐: 온디바이스 AI 및 Siri에 앱 로직 연동하기 (0) | 2026.08.06 |
| 아이폰(iPhone) 개발자 모드(Developer Mode) 활성화 방법 정리 (0) | 2025.12.24 |
| iOS 개발자가 많이 하는 실수 - DispatchQueue main/global 큐 혼동과 sync/async 잘못 사용으로 인한 데드락·성능 저하 (0) | 2025.12.04 |
| iOS 개발자가 많이 하는 실수 - KVO(Key-Value Observing) 사용 시 removeObserver 누락 및 Strong Reference Cycle 실수 (0) | 2025.12.04 |
| iOS 개발자가 많이 하는 실수 - @escaping / non-escaping 클로저 차이를 잘못 이해해 크래시·경고가 발생하는 실수 (0) | 2025.12.04 |
| iOS 개발자가 많이 하는 실수 - capture list를 잘못 사용해 클로저가 의도와 다르게 작동하는 실수 (0) | 2025.12.04 |

