MTU(Maximum Transmission Unit)는 단일 GATT 트랜잭션에서 장치 간에 전송될 수 있는 하나의 BLE 패킷 내 유용한 데이터의 최대 크기입니다. BLE Classic(4.x)에서 MTU는 23바이트로 고정되어 있으며, 이는 소형 센서 판독값에는 충분하지만 파일 전송이나 OTA 업데이트에는 충분하지 않습니다. Bluetooth Core Specification 4.2(2014)는 MTU Size Request 절차를 도입하여 더 큰 MTU(최대 247바이트, BLE 5.0에서는 최대 251바이트)를 협상할 수 있게 되었습니다. 올바른 MTU 구성은 대량의 데이터를 전송하는 BLE 애플리케이션의 성능 핵심 요소 중 하나입니다.
핵심 포인트
Maximum Transmission Unit(MTU)는 BLE 맥락에서 장치가 단일 GATT 요청에서 수락할 수 있는 APDU(Application Protocol Data Unit)의 최대 크기입니다. MTU는 ATT(Attribute Protocol) 수준에서 정의되며 ATT 헤더(1바이트) + 유용한 데이터를 포함합니다. 기본적으로 모든 BLE 장치는 23바이트 MTU를 지원합니다(23 = 1바이트 ATT 헤더 + 22바이트 데이터).
MTU는 무선 채널의 물리적 한계가 아니라 GATT 수준에서의 장치 간 합의입니다. 링크 계층의 물리적 BLE 패킷 크기는 더 클 수 있지만(BLE 4.0에서 최대 27바이트, Data Length Extension 사용 시 BLE 5.0에서 최대 257바이트), GATT 계층은 트랜잭션당 전송되는 데이터 양을 제한합니다. DLE(Data Length Extension)는 물리적 패킷을 251바이트로 증가시키는 별도의 링크 계층 메커니즘으로, 별도로 협상해야 합니다.
MTU와 DLE의 차이점: ATT MTU — GATT 요청당 전송되는 데이터 양, DLE — 하나의 링크 계층 패킷에 들어가는 데이터 양. 최대 속도를 위해서는 두 매개변수를 모두 협상해야 합니다. DLE가 없으면 MTU가 247바이트인 경우에도 데이터는 여러 27바이트 링크 계층 패킷으로 조각화되어 처리량이 감소합니다.
MTU Size Request — GATT 연결 설정 후 Central이 시작하는 절차입니다. Central은 자신의 MTU 용량(수락 가능한 최대 크기)을 지정하여 MTU Request를 보냅니다. Peripheral은 자신의 값으로 MTU Response로 응답합니다. 결과 MTU는 두 값 중 최소값입니다. Central이 MTU 512를 제안하고 Peripheral이 128만 지원하는 경우 연결은 MTU 128을 사용합니다.
import CoreBluetooth
// iOS에서 최대 MTU 요청
func requestMTU(central: CBCentralManager,
peripheral: CBPeripheral) {
peripheral.maximumWriteValueLength(
for: .withoutResponse
)
// iOS는 연결 시 자동으로 MTU 협상
// BLE 5.0 장치의 MTU = 512
let mtu = peripheral.maximumWriteValueLength(
for: .withResponse
)
print("협상된 MTU: " +
String(mtu))
}
협상 타이밍: MTU Request는 서비스 검색(discoverServices) 후, 활성 데이터 전송이 시작되기 전에 전송되어야 합니다. iOS Core Bluetooth에서는 연결 시 MTU가 자동으로 협상됩니다. 개발자가 수동으로 MTU Request를 보낼 필요가 없습니다. Android에서는 requestMTU를 명시적으로 호출해야 합니다. 일단 협상되면 MTU는 해당 연결에 대해 고정됩니다. 연결을 끊고 다시 연결하지 않으면 재협상이 불가능합니다.
ATT(Attribute Protocol) — GATT가 구축되는 프로토콜입니다. ATT 패킷의 최대 크기는 257바이트(ATT_MTU-1)입니다. 이 중 1바이트는 Opcode(작업 유형), 1바이트는 Handle, 최대 255바이트는 Value입니다. 따라서 ATT 사양에서 허용하는 최대 MTU는 257바이트이지만(일부 오버헤드 필드가 여전히 필요하므로 실제로는 최대 251바이트가 사용됩니다).
MTU보다 큰 데이터를 전송하려면 애플리케이션 수준에서 조각화(fragmentation)를 사용합니다. 개발자가 수동으로 데이터를 ≤ MTU 크기의 청크로 분할하고 순차적으로 전송합니다. 각 청크는 별도의 GATT Write Request로 전송됩니다. 수신 측은 청크를 단일 버퍼로 조립합니다. GATT에는 조각화에 대한 기본 지원이 없습니다. 이는 개발자의 책임입니다.
| BLE 버전 | 최대 MTU | 최대 DLE | ATT MTU 제한 |
|---|---|---|---|
| BLE 4.0 / 4.1 | 23바이트 | 27바이트 | ATT 고정 |
| BLE 4.2 | 247바이트 | 251바이트 | 257바이트 |
| BLE 5.0 | 251바이트 | 251바이트 | 257바이트 |
| Android + iOS | 512 / 517 | 251바이트 | ATT 초과 |
흥미로운 사실: iOS와 Android는 각각 MTU 512 및 517바이트를 요청하지만 이 값은 ATT 제한을 초과합니다. 실제로 BLE 스택은 이러한 데이터를 자동으로 조각화하여 각각 최대 251바이트의 여러 순차적 GATT 요청으로 전송합니다. 개발자에게는 그 차이가 투명합니다. iOS에서 writeValue는 최대 512바이트까지 모든 크기에서 작동합니다.
MTU 크기는 BLE 연결 처리량에 직접적인 영향을 미칩니다. MTU 23바이트의 경우 이상적인 조건에서 최대 유용한 전송 속도는 약 7~10KB/s입니다. MTU를 247바이트로 늘리면 속도가 60~90KB/s(DLE 및 최적 연결 간격 사용 시)로 향상됩니다. 이는 이미지, 오디오 조각 또는 로그를 전송하는 애플리케이션에 특히 중요합니다.
BLE 전송 성능은 세 가지 요소에 따라 달라집니다: MTU(ATT 요청당 데이터 양), 연결 간격(교환 이벤트 발생 빈도), DLE(링크 계층 패킷당 데이터 양). 최대 속도를 위한 최적 구성: MTU = 247, DLE = 251, 연결 간격 = 7.5ms(최소값).
Bluetooth SIG 백서(2023)에 따르면 연결 간격 30ms에서 MTU를 23에서 247바이트로 늘리면 처리량이 8KB/s에서 42KB/s로 5배 증가합니다. 연결 간격 7.5ms에서는 처리량이 88KB/s에 도달합니다. 높은 속도가 필요하지 않은 애플리케이션(온도 센서, BLE 비콘)의 경우 표준 23바이트 MTU로 충분합니다.
iOS Core Bluetooth는 Peripheral에 연결할 때 MTU를 자동으로 협상합니다. 개발자는 maximumWriteValueLength를 통해 현재 MTU를 조회할 수 있지만 수동으로 설정할 수는 없습니다. iOS는 BLE 5.0 장치의 경우 최대 512바이트, BLE 4.2의 경우 최대 247바이트의 MTU를 사용합니다. 대량의 데이터를 쓰려면 보장된 전달을 위해 writeType: .withResponse를 사용하세요.
// Android(Kotlin)에서 MTU 요청
val bluetoothGatt: BluetoothGatt = ...
// MTU 517바이트 요청
bluetoothGatt.requestMtu(517)
// 콜백에서 결과 처리
override fun onMtuChanged(
gatt: BluetoothGatt,
mtu: Int,
status: Int
) {
if (status == BluetoothGatt.GATT_SUCCESS) {
println("MTU negotiated: $mtu")
}
}
Android는 BluetoothGatt.requestMtu(int)를 제공하여 최대 517바이트까지 모든 MTU를 요청할 수 있습니다. 실제 MTU는 주변 장치에 의해 결정됩니다. 장치가 23바이트만 지원하는 경우 Android는 MTU 23을 반환합니다. 현재 MTU를 확인하려면 gatt.requestMtu(0)을 사용하세요. 변경을 시도하지 않고 현재 값을 반환합니다. Android 12+는 TRANSPORT_LE를 통한 연결 시 자동 MTU 협상을 지원합니다.
크로스 플랫폼 프레임워크(Flutter, React Native)는 일반적으로 requestMTU를 위한 API를 제공합니다. FlutterBlue Plus 라이브러리에서는 MTU가 연결 매개변수로 설정됩니다. RxAndroidBle에서는 requestMtu 메서드를 통해 설정합니다. 애플리케이션 수준의 조각화를 방지하기 위해 데이터 전송을 시작하기 전, 서비스 검색 직후 항상 최대 MTU를 협상하는 것이 좋습니다.
OTA(Over-The-Air) 펌웨어 업데이트는 BLE에서 MTU를 가장 많이 요구하는 시나리오입니다. IoT 장치의 일반적인 펌웨어 크기는 100~500KB입니다. MTU 23바이트, 연결 간격 30ms의 경우 100KB 전송에 약 2~3분이 소요됩니다. MTU 247바이트, DLE 251바이트의 경우 20~40초입니다. MTU 512바이트(iOS)의 경우 10~15초입니다.
OTA 업데이트 프로세스에는 일반적으로 펌웨어를 ≤ MTU 크기의 패킷으로 조각화, Notify/Write를 통한 순차적 전송, 각 패킷의 체크섬 확인, 수신 확인이 포함됩니다. 패킷이 손실되면 장치가 재전송을 요청합니다. OTA 안정성은 MTU와 연결 간격의 올바른 선택에 크게 의존합니다.
OTA 권장 사항: 최대 MTU(247~512바이트) 협상, 연결 간격을 7.5~15ms(장치가 지원하는 경우)로 설정, DLE(Data Length Extension)를 사용하여 물리적 패킷을 251바이트로 증가시킵니다. 버퍼 메모리가 제한된 장치(예: nRF52 기반 BLE 모듈)의 경우 칩 사양에서 최대 MTU를 확인하세요.
자주 묻는 질문
연결은 기본 MTU인 23바이트를 사용합니다. 대부분의 IoT 시나리오(센서 판독값 전송)에서는 충분합니다. 대량의 데이터를 전송하는 경우 속도가 협상된 247바이트 MTU보다 5~10배 낮아집니다.
아니요, MTU는 연결 후 한 번 협상되며 연결을 끊고 다시 연결하지 않으면 변경할 수 없습니다. 따라서 활성 데이터 전송을 시작하기 전, 서비스 검색 직후 MTU를 협상하는 것이 좋습니다.
이는 각 스택에 대해 역사적으로 확립된 경험적 최대값입니다. 실제 ATT MTU는 여전히 사양에 따라 257바이트로 제한됩니다. 스택은 251바이트를 초과하는 데이터를 자동으로 여러 패킷으로 조각화하므로 512와 517의 차이는 중요하지 않습니다.
MTU — ATT 수준의 GATT 요청 크기. DLE — 링크 계층의 물리적 패킷 크기. DLE가 없으면 각 GATT 요청(최대 247바이트)이 27바이트 패킷으로 조각화됩니다. DLE가 있으면 단일 패킷으로 전송됩니다. 최대 속도를 위해서는 두 매개변수를 모두 협상해야 합니다.
심박수와 걸음 수를 전송하는 피트니스 트래커의 경우 표준 23바이트 MTU로 충분합니다. 운동 기록(10~50KB 크기)을 전송해야 하는 경우 스마트폰에 연결할 때 데이터 동기화 속도를 높이기 위해 MTU 247바이트를 협상하세요.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.