앱 개발

BLE 기기 연동 앱, 연결이 자꾸 끊기거나 안 될 때 확인할 것

BLE 기기 연동 앱, 연결이 자꾸 끊기거나 안 될 때 확인할 것

BLE 기기 연동 앱, 연결이 자꾸 끊기거나 안 될 때 확인할 것

블루투스 저전력(BLE) 기기 연동 앱에서 자주 생기는 연결 문제를 안드로이드 권한, 스캔 제한, 제조사 차이, GATT 오류와 재시도, MTU, 페어링, 백그라운드 재연결, 펌웨어 프로토콜, 다기종 테스트 순서로 정리했습니다.

블루투스 저전력(BLE) 기기 연동 앱에서 자주 생기는 연결 문제를 안드로이드 권한, 스캔 제한, 제조사 차이, GATT 오류와 재시도, MTU, 페어링, 백그라운드 재연결, 펌웨어 프로토콜, 다기종 테스트 순서로 정리했습니다.

9분 소요

9분 소요

BLE 기기 연동 앱에서 "연결이 안 된다", "가끔 끊긴다"는 문의는 원인이 한 군데인 경우가 드뭅니다. 안드로이드 버전별 권한, 스캔 방식, 제조사마다 다른 블루투스 동작, 연결 실패 후 재시도 방식, 펌웨어와 앱 사이의 약속이 겹쳐서 생깁니다. 저희가 헬스케어 기기와 IoT 기기 연동 앱을 만들면서 반복해서 확인하는 지점을 순서대로 정리했습니다.

BLE 기기 연동 앱, 연결이 자꾸 끊기거나 안 될 때 확인할 것 대표 이미지

먼저 권한과 설정부터 의심합니다

연결 문제 문의의 상당수는 실제로는 권한 문제입니다. 안드로이드는 버전에 따라 필요한 권한이 다릅니다.

안드로이드 버전별 BLE 권한
12 이상
  • 근처 기기 권한 요청
  • 스캔, 연결 권한 따로
  • 조건부로 위치 권한 생략
11 이하
  • 정밀 위치 권한 필요
  • 위치 서비스도 켜야 함
  • 백그라운드는 권한 추가
막힌 지점마다 다른 안내 문구를 띄웁니다

안드로이드 12(API 31) 이상을 대상으로 하는 앱은 주변 기기를 찾을 때 BLUETOOTH_SCAN, 이미 찾은 기기와 통신할 때 BLUETOOTH_CONNECT 권한을 요청해야 합니다. 둘 다 실행 중에 사용자 허락을 받는 권한이고, 사용자에게는 "근처 기기" 권한으로 보입니다. 스캔 결과로 위치를 추정하지 않는다면 BLUETOOTH_SCAN에 neverForLocation 플래그를 붙여 위치 권한 없이 쓸 수 있습니다. 다만 안드로이드 공식 문서에 이 플래그를 쓰면 일부 BLE 비콘이 스캔 결과에서 걸러진다고 적혀 있어서, 비콘을 다루는 앱이라면 쓰지 않아야 합니다.

안드로이드 11 이하에서는 BLE 스캔에 ACCESS_FINE_LOCATION 권한이 필요합니다. 여기서 자주 놓치는 게 휴대폰의 위치 서비스 자체가 꺼져 있는 경우입니다. 권한은 허용했는데 위치 서비스가 꺼져 있어 기기가 하나도 안 잡히는 상황이 실제로 많습니다. 안드로이드 10과 11에서 서비스로 백그라운드 스캔을 하려면 ACCESS_BACKGROUND_LOCATION까지 필요합니다.

그래서 저희는 스캔을 시작하기 전에 블루투스 켜짐, 권한, (해당 버전이라면) 위치 서비스 상태를 하나씩 확인하고, 막힌 지점마다 다른 안내 문구를 띄우도록 만듭니다. "기기를 찾을 수 없습니다" 한 문장으로 끝내면 사용자도, 고객센터도 원인을 알 수 없습니다.

Companion Device Manager도 검토해 볼 만합니다

안드로이드 8.0부터는 Companion Device Manager(CDM)라는 방식이 있습니다. 시스템이 앱 대신 주변 기기를 찾아 선택 화면을 띄워 주고, 사용자가 기기를 고르면 앱이 그 기기에 접근할 수 있게 됩니다. 안드로이드 문서에 따르면 이 방식은 위치 권한이 필요 없습니다. 다만 위치 서비스는 켜져 있어야 한다고 되어 있습니다.

연결된 뒤에는 백그라운드에서 앱을 깨우거나 포그라운드 서비스를 시작할 수 있는 별도 권한을 쓸 수 있어서, 스마트워치나 측정기처럼 한 사용자가 한 기기를 계속 쓰는 제품에 잘 맞습니다. 반대로 매장에서 여러 기기를 번갈아 연결하는 앱이라면 직접 스캔하는 쪽이 낫습니다.

스캔을 너무 자주 하면 결과가 안 옵니다

개발 중에 흔히 하는 실수가 기기를 못 찾으면 스캔을 멈췄다 다시 시작하는 코드를 짧은 간격으로 반복하는 것입니다. 저희가 겪어 본 바로는 짧은 시간 안에 스캔 시작을 여러 번 하면 이후 스캔에서 결과가 아예 오지 않는 안드로이드 기기가 적지 않습니다. 오류 콜백도 없이 조용히 아무것도 안 잡히기 때문에 원인을 찾기 어렵습니다.

안드로이드 문서도 원하는 기기를 찾으면 바로 스캔을 멈추고, 스캔을 반복문으로 돌리지 말고, 시간 제한을 두라고 안내합니다. 저희는 스캔 필터에 기기의 서비스 UUID(기기가 제공하는 기능을 구분하는 고유 번호)나 이름 규칙을 넣어 필요한 기기만 받고, 한 번 스캔할 시간을 정해 두고, 재시도 사이에 간격을 둡니다.

제조사와 기기마다 다르게 동작합니다

BLE는 같은 안드로이드 버전이라도 휴대폰 제조사와 칩셋에 따라 동작이 꽤 다릅니다. 어떤 기기는 연결 직후 바로 서비스 탐색을 하면 실패하고 잠깐 기다리면 성공합니다. 어떤 기기는 이전 연결을 제대로 정리하지 않고 새 연결을 시도하면 계속 실패합니다. 절전 기능이 공격적인 기기에서는 앱이 백그라운드로 가자마자 연결이 끊기기도 합니다.

BLE 연결 단계별 점검
  1. 스캔필터와 시간 제한을 두고 찾기
  2. 연결실패하면 close 후 다시 연결
  3. 서비스 탐색기기에 따라 잠깐 기다린 뒤 시도
  4. 알림 구독GATT 작업은 대기열로 하나씩
  5. 첫 데이터 수신단계별 시간과 결과를 로그로
로그에는 휴대폰 모델명을 함께 남깁니다

이런 차이는 문서에 정리되어 있지 않은 경우가 많아서, 실제 기기로 확인하는 수밖에 없습니다. 그래서 연결 과정의 각 단계(스캔, 연결, 서비스 탐색, 알림 구독, 첫 데이터 수신)에 걸린 시간과 결과를 기기 모델명과 함께 로그로 남기도록 만들어 둡니다. 출시 후 특정 모델에서만 문제가 생겼을 때 이 로그가 없으면 재현부터 막힙니다.

GATT 오류와 재시도 방식

안드로이드에서 BLE 연결이 실패하면 연결 상태 콜백에 상태 코드가 함께 옵니다. 실무에서 가장 자주 보는 건 133번입니다. 이름은 일반 오류에 가까워서 코드만 보고는 원인을 특정하기 어렵고, 기기가 범위를 벗어났을 때, 이전 연결이 남아 있을 때, 동시에 연결 요청이 겹쳤을 때 등 여러 상황에서 나옵니다.

저희가 쓰는 재시도 원칙은 이렇습니다.

  • 실패하면 기존 BluetoothGatt 객체를 close()로 확실히 정리한 뒤 새로 연결합니다. 정리하지 않고 다시 연결하면 연결 자원이 쌓여 다음 연결도 실패하기 쉽습니다.
  • 재시도 간격을 조금씩 늘리고 횟수 상한을 둡니다. 계속 실패하면 스캔부터 다시 시작합니다.
  • 읽기, 쓰기, 알림 구독 같은 GATT 작업은 하나가 끝난 뒤 다음 것을 보내도록 대기열로 관리합니다. 안드로이드 BLE는 이전 작업이 끝나기 전에 다음 요청을 보내면 무시되거나 실패하는 경우가 많습니다.
  • 모든 콜백에 시간 제한을 둡니다. 응답이 영영 오지 않는 경우가 있어서, 일정 시간이 지나면 실패로 보고 연결을 정리합니다.

MTU와 데이터 크기

MTU는 한 번에 주고받을 수 있는 데이터 크기입니다. 기본값이 작아서 측정 기록을 여러 건 받거나 펌웨어를 업데이트하는 앱은 연결 직후 더 큰 MTU를 요청합니다.

안드로이드 14부터는 동작이 하나 바뀌었습니다. 공식 문서에 따르면 첫 번째 GATT 클라이언트가 requestMtu()를 호출하면 앱이 요청한 값과 상관없이 517바이트로 요청하고, 같은 연결에서 이후 요청은 무시합니다. 기기 펌웨어가 MTU 협상을 제대로 처리하지 않고 감당할 수 없는 크기까지 받아들인다고 답하면, 안드로이드 14 휴대폰에서만 큰 데이터 전송이 깨질 수 있습니다. 문서는 펌웨어가 실제로 감당할 수 있는 값으로 응답하게 고치거나, 앱에서 기기가 지원하는 크기와 협상된 값 중 작은 쪽에 맞춰 나눠 보내라고 안내합니다.

본딩과 페어링

페어링은 두 기기가 암호화 키를 주고받는 과정이고, 본딩은 그 키를 저장해 다음 연결에도 쓰는 것입니다. 보안이 필요한 특성(characteristic, 기기 안의 개별 데이터 항목)에 접근하면 시스템 페어링 창이 뜨는데, 이 시점이 기기마다 달라서 사용자가 창을 놓치거나 취소하는 일이 생깁니다.

자주 생기는 문제는 한쪽만 본딩 정보를 지운 경우입니다. 사용자가 기기를 초기화했는데 휴대폰에는 이전 본딩 정보가 남아 있으면 연결이 계속 실패합니다. 앱에서 이런 상황을 감지하면 휴대폰 블루투스 설정에서 기기를 삭제하고 다시 등록하라는 안내를 보여 주는 게 현실적입니다. 본딩이 꼭 필요한지, 앱 수준에서 인증을 처리할지는 펌웨어 팀과 초기에 정해야 합니다.

백그라운드에서 다시 연결하기

iOS에서 앱이 백그라운드에서도 BLE 기기와 통신하려면 bluetooth-central 백그라운드 모드를 켜야 합니다. 애플 문서에 따르면 이 모드에서는 연결이나 끊김, 기기가 보낸 데이터 같은 이벤트가 생길 때 시스템이 앱을 깨우고, 앱은 이벤트마다 약 10초 안에 처리를 끝내야 합니다. 백그라운드 스캔은 같은 기기를 여러 번 발견해도 한 번으로 합쳐지고, 스캔 간격도 길어집니다.

메모리 부족으로 시스템이 앱을 종료하면 연결도 함께 끊깁니다. 이때 쓰는 게 상태 보존과 복원(state preservation and restoration)입니다. CBCentralManager를 만들 때 복원 식별자를 넣어 두면, 시스템이 연결 중이던 기기와 구독 중이던 특성을 기억해 두었다가 관련 이벤트가 생기면 앱을 다시 실행해 willRestoreState로 넘겨줍니다. 이 콜백에서 기기 객체를 다시 연결해 두지 않으면 앱이 깨어나도 아무 일도 일어나지 않습니다.

안드로이드는 백그라운드에서 연결을 유지하려면 보통 포그라운드 서비스를 씁니다. 상단에 알림을 띄워 둔 상태로 동작하는 서비스입니다. 안드로이드 14부터는 포그라운드 서비스 유형을 반드시 선언해야 하고, 기기 연결 용도라면 connectedDevice 유형과 해당 권한, 그리고 BLUETOOTH_CONNECT 같은 실행 권한이 필요합니다. 구글 플레이 콘솔에서도 포그라운드 서비스 사용 목적을 신고해야 합니다. 앞서 말한 CDM을 쓰면 기기가 근처에 있을 때 앱을 깨우는 기능을 활용할 수 있어 대안이 됩니다.

펌웨어와 앱 사이의 약속을 문서로 남깁니다

연결은 되는데 데이터가 이상하게 오는 문제는 대부분 프로토콜 합의가 모호해서 생깁니다. 바이트 순서, 값의 단위, 여러 패킷으로 나뉜 데이터의 시작과 끝 표시, 명령에 대한 응답이 오지 않을 때의 처리, 펌웨어 버전별 차이 같은 것들입니다.

저희는 개발 초반에 서비스와 특성 목록, 각 명령과 응답의 바이트 구성, 오류 코드, 타이밍 조건을 표로 정리해 펌웨어 쪽과 같이 확인합니다. 앱에서 펌웨어 버전을 읽어 기능을 나누는 코드도 처음부터 넣어 둡니다. 기기는 이미 시장에 풀려 있는데 펌웨어 업데이트 경로가 없으면, 결국 앱이 모든 버전을 계속 감당해야 하기 때문입니다. 알코파인드 음주측정기 연동 앱처럼 기기와 앱이 한 제품으로 움직이는 서비스는 이 문서가 사실상 설계도 역할을 합니다.

휴대폰을 여러 대 놓고 테스트합니다

BLE 앱 테스트는 에뮬레이터만으로는 부족합니다. 실제 휴대폰과 실제 기기가 있어야 제조사별 차이와 간헐적인 끊김이 보입니다. 최소 지원 안드로이드 버전 기기, 최신 안드로이드 기기, 제조사가 다른 기기 몇 대, 그리고 iOS 구형과 신형을 섞어 둡니다.

끊김 재현 시나리오
  • 기기 전원 갑자기 끄기
  • 멀어졌다가 다시 다가가기
  • 연결 중 블루투스 끄기
  • 앱을 백그라운드에 오래 두기
  • 데이터 전송 중 전화 받기
  • 기기 초기화 후 다시 연결하기
같은 시나리오를 수십 번 돌려야 보입니다

시나리오도 정상 연결만 보면 안 됩니다. 기기 전원을 갑자기 끄기, 거리를 벌렸다가 다시 다가가기, 연결 중 블루투스 끄기, 앱을 백그라운드로 보낸 뒤 오래 두기, 데이터 전송 중 전화 받기, 기기 초기화 후 다시 연결하기를 반복합니다. 같은 시나리오를 수십 번 돌려야 간헐적인 실패가 보입니다.

자주 묻는 질문

아이폰에서는 되는데 안드로이드에서만 연결이 안 됩니다.

가장 먼저 안드로이드 버전별 권한과 위치 서비스 상태를 확인합니다. 그다음은 GATT 작업이 겹치지 않는지, 실패 후 연결 객체를 정리하는지를 봅니다. 특정 제조사에서만 생긴다면 연결 직후 대기 시간이나 재시도 간격을 조정해 보는 경우가 많습니다.

앱을 끄면 기기 데이터를 받을 수 없나요?

iOS는 백그라운드 모드와 상태 복원으로, 안드로이드는 포그라운드 서비스나 CDM으로 상당 부분 처리할 수 있습니다. 다만 OS가 허용하는 범위 안에서만 동작하므로, 측정 기록을 기기에 저장해 두었다가 다음 연결 때 몰아서 받는 구조를 함께 두는 게 안전합니다.

펌웨어가 아직 개발 중인데 앱 개발을 시작해도 되나요?

프로토콜 문서가 먼저 합의되어 있다면 가능합니다. 개발용 보드나 휴대폰 두 대로 기기를 흉내 내어 앱 쪽을 먼저 만들 수 있습니다. 다만 실제 기기가 나오면 타이밍과 MTU 관련 문제가 새로 보이기 때문에 통합 테스트 기간은 따로 잡아야 합니다.

이미 출시한 앱의 연결 문제도 봐 줄 수 있나요?

가능합니다. 문제가 생기는 휴대폰 모델과 OS 버전, 재현 순서, 가능하면 펌웨어 프로토콜 문서를 함께 주시면 원인을 좁히는 속도가 훨씬 빨라집니다.

BLE 기기 연동 앱의 연결 안정성이 고민이시면 상담으로 문의해 주세요. 저희가 해 온 기기 연동 작업은 BLE·IoT 앱 개발 페이지에서 보실 수 있습니다.

앱 개발이 고민되시나요?
앱 개발이 고민되시나요?

기획부터 출시까지, 모바일파트너스가 함께합니다