블루투스 폰 브릿지(BPB) 개발기
야크쉐이빙을 하다, 예상치 못한 가능성을 발견하게 되었습니다.. 이 글은 '블루투스 폰 브릿지(Bluetooth Phone Bridge, 이하 BPB)'라는 시스템을 만들게 된 실제 계기와 그 과정에서 얻게 된 인사이트를 담은 기록입니다.
개발의 시작점: 왜 전화여야만 했나?
작년에 AI 타로 상담 서비스 를 출시했습니다.
타로 상담의 핵심은 내담자가 자신의 사연을 털어놓는 것입니다. 초기 버전의 앱에서는 이 사연을 텍스트로 직접 입력받도록 설계했습니다. 하지만 사용자들은 이 과정을 매우 번거롭고 어색하게 느꼈습니다. 결국 대부분의 사용자가 사연을 제대로 적지 않은 채 서비스를 이용하려 했고, 이는 상담의 질을 떨어뜨리는 근본적인 문제였습니다.
다음으로, 텍스트 입력을 제거하고 몇 가지 정형화된 사연을 제공하는 방식으로 수정했습니다. 이는 임시방편이었지만, 사용자 각자의 고유한 이야기를 담아내지 못한다는 아쉬움이 컸습니다.
그렇다면 음성은 어떨까? 앱에 STT(음성-문자 변환) 기능을 넣어 사연을 직접 말하게 하는 기능을 구상했습니다. 하지만 이 역시 또 다른 문제를 낳았습니다. 사용자는 멀쩡한 스마트폰 앱을 쓰다가, 갑자기 전화기에 대고 자신의 내밀한 사연을 이야기해야 하는 '감정적 컨텍스트 스위치' 문제에 직면합니다. 이는 생각보다 큰 심리적 장벽이었습니다.
여기서 발상의 전환이 이루어졌습니다. "처음부터 상호작용의 매개체가 전화통화였다면 어떨까?"
사용자는 '앱'이 아닌 '전화'라는, 본래부터 음성으로 대화하기 위한 매체를 통해 서비스를 이용합니다. 이 편안하고 익숙한 환경이라면 사용자는 훨씬 자연스럽게 자신의 이야기를 할 수 있을 것이라 생각했습니다.
이 아이디어를 구현하기 위해, 스마트폰으로 걸려온 전화를 PC에서 받아 AI와 연결하는 시스템, 즉 BPB 프로젝트가 시작되었습니다.
만들고 보니 알게 된 것들: BPB의 의외의 장점
BPB 시스템을 중고 알뜰폰 한 대와 미니 PC로 구현하고 보니, 초기 목표였던 '자연스러운 사용자 경험' 외에도 여러 예상치 못한 장점들을 발견하게 되었습니다.
-
앱 스토어 탈출: 사용자는 별도의 앱을 다운로드할 필요가 없습니다. 이는 개발자 입장에서 앱 스토어의 복잡한 심사 과정을 거칠 필요가 없다는 큰 이점을 의미합니다. 정책 변경이나 심사 거절의 리스크에서 자유로워집니다.
-
획기적인 인프라 비용 절감: 시스템은 클라우드 서버 없이, 로컬의 미니 PC와 중고폰으로만 운영됩니다. 서비스는 걸려오는 전화(inbound call)만 처리하므로, 비싼 통화 발신 요금을 낼 필요가 없습니다. 결과적으로 월 수천 원 수준의 알뜰폰 요금제가 인프라 비용의 전부가 됩니다.
-
뛰어난 접근성:
- 사용자 비용 부담 감소: 최근 통신사 요금제는 데이터 사용량은 제한해도 음성 통화는 무제한이거나 넉넉하게 제공하는 경우가 많습니다. 고객은 추가 비용 부담 없이 서비스를 이용할 수 있습니다.
- 정보 소외 계층 포용: 앱 사용에 익숙하지 않은 노인층이나 특정 장애인층에게 '전화'라는 레거시 미디어는 가장 익숙하고 접근하기 쉬운 인터페이스입니다. 기술이 오히려 특정 계층을 소외시키는 문제를 해결할 실마리를 찾은 셈입니다.
시스템 기술 스택
본 프로젝트는 리눅스 환경에서 블루투스 통신, 통화 제어, 오디오 처리, AI 연동을 위해 다음과 같은 오픈소스 기술 스택을 활용했습니다.
-
블루투스 오디오 (Bluetooth Audio)
- BlueZ: 리눅스 공식 블루투스 스택. HFP(Hands-Free Profile) 기능 사용을 위해 5.72+ 버전의 실험적(experimental) 모드 활성화가 필요합니다.
- HFP (Hands-Free Profile): 스마트폰과 PC 간 핸즈프리 통화를 위한 블루투스 프로필.
- SCO (Synchronous Connection-Oriented): HFP 통화의 음성 데이터 전송에 사용되는 블루투스 오디오 채널.
-
통화 관리 (Call Management)
- oFono: 오픈소스 텔레포니(Telephony) 스택. 모뎀 및 통화 상태(수신, 활성, 종료)를 관리합니다.
- D-Bus: BlueZ, oFono, 커스텀 애플리케이션 간의 프로세스 간 통신(IPC)을 위한 메시지 버스 시스템.
-
오디오 처리 (Audio Processing)
- PipeWire: 리눅스의 오디오/비디오 서버. SCO 채널 오디오 데이터의 라우팅 및 처리를 담당합니다.
- WirePlumber: PipeWire의 세션 매니저. 오디오 장치 간 정책을 관리합니다.
-
인공지능 통합 (AI Integration)
- Gemini API: 통화 내용 분석 및 응답 생성을 위한 Google의 생성형 AI 모델.
- gTTS (Google Text-to-Speech): 생성된 텍스트 응답을 음성으로 합성합니다.
- pydub / ffmpeg: 오디오 포맷 변환 및 처리를 위한 라이브러리.
시스템 아키텍처
BPB는 D-Bus를 통해 각 컴포넌트가 상호작용하는 구조입니다. 스마트폰의 통화 이벤트는 BlueZ와 oFono를 거쳐 직접 구현한 HandsfreeAudioAgent
로 전달되고, 여기서 SCO 오디오 스트림을 Gemini API와 연동합니다.
컴포넌트 연결 구조
D-Bus 인터페이스 구조
oFono가 BlueZ로부터 통화 이벤트를 수신하면, D-Bus를 통해 신호(Signal)를 발생시킵니다. HandsfreeAudioAgent
는 이 신호를 감지하여 오디오 스트림을 처리할 준비를 합니다.
System D-Bus
├── org.bluez (BlueZ)
│ └── ... (블루투스 장치 및 프로필)
│
├── org.ofono (oFono)
│ ├── /hfp/org/bluez/hci0/dev_XX... (모뎀)
│ │ ├── VoiceCallManager (통화 관리자)
│ │ └── CallVolume (볼륨 조절)
│ └── HandsfreeAudioManager
│ └── HandsfreeAudioAgent (구현된 에이전트)
│
└── org.pipewire (PipeWire)
└── Audio Routing (오디오 경로 설정)
주요 기술적 문제 및 해결 방안
1. 통화 시 블루투스 연결 해제
- 문제: 통화 연결 시 블루투스 연결이 끊어지는 현상.
- 원인: PipeWire의 세션 매니저인 WirePlumber와 oFono가 BlueZ의 HFP 프로필 제어권을 두고 경합하면서 충돌 발생.
- 해결: oFono가 통화 제어를 전담하도록 지정하고, WirePlumber의 블루투스 프로필 관리 기능을 비활성화하여 충돌을 회피.
2. "Ringing" 상태에서의 File Descriptor 처리 오류
- 문제: 전화 수신 중인 "ringing" 상태에서 오디오 채널(FD)를 처리할 때 "Transport endpoint is not connected" 오류 발생.
- 원인: "ringing" 상태는 통화가 완전히 수락되어 오디오 채널이 개방된 상태가 아니므로, FD에 대한 선제적 접근이 오류를 유발.
- 해결: 통화 상태를 추적하여 "ringing"일 때는 FD를
pending_connections
리스트에 저장. 통화가 실제 연결되는 "active" 상태가 되었을 때 저장된 FD를 가져와 처리하도록 로직을 수정.
3. SCO 오디오 라우팅 문제
- 문제: 오디오가 PC 대신 스마트폰 스피커로 출력되는 현상.
- 원인: PC와 스마트폰 모두 AG(Audio Gateway) 역할로 설정되거나, 일부 스마트폰의 보안 정책상 원격 오디오 라우팅이 제한되는 경우 발생.
- 해결: 현재로서는 완벽한 자동화 해결책은 부재. 통화 연결 후 스마트폰의 블루투스 설정에서 오디오 출력 기기를 수동으로 지정해야 합니다. 이는 향후 개선이 필요한 명확한 한계점입니다.
향후 과제
현재 자동응답 기능까지 구현된 BPB 시스템은 AI 타로 상담 외에도 다양한 가능성을 보여주고 있습니다. 향후 과제는 다음과 같습니다.
-
실시간 상호작용을 위한 Gemini Live API 도입: 현재 시스템은 별도의 STT와 TTS 프로세스를 순차적으로 거칩니다. 이 구조는 사용자의 발화를 인식하고 AI의 음성 응답을 생성하기까지 지연(latency)을 발생시켜 부자연스러운 대화 간격을 만듭니다. 향후에는 Gemini Live API를 도입하여, 말을 듣는 동시에 대답을 생성하는 스트리밍 기반 처리를 구현할 예정입니다. 이를 통해 응답 간격을 획기적으로 줄여 보다 자연스러운 실시간 상호작용을 구현하는 것을 목표로 합니다.
-
저비용 인프라 기반의 서비스 모델 구체화: 월 수천 원의 운영 비용만으로 서비스 제공이 가능하다는 점을 활용하여, 소상공인을 위한 AI 예약 시스템, 1인 기업을 위한 고객 응대 AI 비서 등 구체적인 서비스 모델을 구상하고 발전시킬 계획입니다.
-
접근성 확대를 통한 사회적 가치 창출: 노인층을 위한 AI 말벗 서비스, 장애인을 위한 음성 안내 시스템 등 BPB의 높은 접근성을 활용하여 정보 소외 계층을 도울 수 있는 공익적 서비스로의 확장 가능성을 탐색합니다.
-
기술적 안정성 고도화: SCO 오디오 자동 라우팅, 고음질 코덱(mSBC) 지원, 네트워크 예외 처리 강화 등 기술적 완성도를 높여 상용 서비스 수준의 안정성을 확보해야 합니다.
부록: 개발 환경 설정
-
필수 시스템 패키지:
sudo apt-get update sudo apt-get install bluez bluez-tools ofono pipewire wireplumber sudo apt-get install python3-dbus python3-gi python3-gi-cairo sudo apt-get install ffmpeg
-
필수 Python 패키지:
pip install google-generativeai gtts pydub numpy sounddevice
-
BlueZ 설정 (
/etc/bluetooth/main.conf
):[General] Experimental = true
-
사용자 권한 설정:
sudo usermod -a -G bluetooth,audio $USER
-
참고 자료: