같은 발신자 표시, 다른 구현: Android 오버레이와 iOS Call Directory
전화가 왔을 때 번호만으로는 누가 걸었는지 알기 어렵다. 앱에 번호와 이름이 있어도 개인 주소록에 저장하지 않았다면 전화 화면에서 그 정보를 볼 수 있는 것은 아니다. 나는 개인 주소록을 건드리지 않고 앱이 관리하는 목록으로 발신자 이름을 표시하는 기능을 구현했다. 대상은 앱 안에서 이루어지는 인터넷 전화가 아니라 일반 전화 수신이었다.
사용자에게 필요한 결과는 두 플랫폼에서 같았다. 하지만 iOS에서 선택한 구조를 Android에 그대로 적용할 수는 없었다. 목록을 어디에 등록하는지, 이름을 누가 화면에 그리는지부터 나누어 검토해야 했다.
iOS: 목록 등록과 확장 서명을 함께 준비했다
iOS에서는 CallKit의 Call Directory 확장을 사용했다. 이 방식은 전화번호와 표시할 이름을 시스템에 등록하고, 전화가 오면 시스템이 등록된 정보를 이용해 발신자를 식별한다. Apple 문서에 따르면 개인 연락처에 일치하는 번호가 있는지 먼저 확인하고, 일치하는 연락처가 없으면 Call Directory 항목을 확인한다. 앱의 목록을 개인 주소록에 복사하지 않아도 되는 경로였다. Apple의 발신자 식별 문서
Call Directory는 확장이 실행될 때 번호와 이름을 일괄 등록한다. 수신 때마다 웹 서비스에 조회하는 방식과 달리 등록된 목록을 이용하므로, 이 방식을 선택한다면 앱의 원본 목록과 시스템에 등록된 목록을 구분하고 변경을 반영하는 절차도 설계해야 한다. Apple의 Call Directory 등록 방식
실제 구현에서 시간이 든 부분은 확장용 서명 설정이었다. 코드는 준비되어 있었지만 설정이 갖춰져 있지 않았다. 확장용 프로비저닝 프로파일을 새로 발급하고 Xcode의 확장 관련 설정도 추가하는 작업이 필요했다.
Call Directory 확장은 별도 타깃으로 구성되는 실행 단위다. Xcode에서 확장을 빌드하면 .appex 번들이 만들어지고, 이를 포함하는 앱과 확장 모두 서명되어야 한다. 앱 본체의 서명 설정만 확인해서는 확장까지 실행 가능한 상태인지 판단하기 어렵다. Apple의 앱 확장 생성 안내
발신자 표시를 구현할 때는 목록을 등록하는 코드와 함께 확장 타깃, 서명, 앱에 확장을 포함하는 설정을 확인해야 한다. 내가 겪은 지연도 표시 로직을 작성한 뒤 남아 있던 확장 설정에서 발생했다.
Android: 이름을 그리는 화면을 앱이 맡았다
Android에서도 iOS처럼 앱의 목록을 등록해 기존 전화 화면에 이름이 표시되는 구조를 검토했다. 그러나 검토한 환경에서는 그 방식으로 요구사항을 충족하는 경로를 적용하지 못했다. 그래서 전화 수신 화면 위에 앱이 별도 정보를 표시하는 오버레이 방식을 선택했다.
Android에는 CallScreeningService라는 발신자 식별용 API도 있다. 이 API의 문서는 앱이 선택한 자체 화면으로 식별 정보를 표시할 수 있다고 설명한다. 따라서 발신자 식별 API가 있는지와 기존 전화 앱의 이름 표시 영역에 앱의 목록을 연결할 수 있는지는 구분해 검토해야 한다. Android의 CallScreeningService 문서
Android의 앱 샌드박스는 앱 사이의 접근을 제한한다. 다른 앱의 화면에 정보를 표시하려면 플랫폼이 허용하는 경로를 확인해야 한다. 이 보안 원칙만으로 검토한 방식이 적용되지 않은 구체 원인까지 확정할 수는 없지만, 화면을 어느 앱이 책임지는지 따져야 하는 이유는 된다. 나는 오버레이를 이용해 앱이 직접 이름을 표시하도록 구현했다. Android의 앱 샌드박스 설명
오버레이를 선택하면 화면을 그리는 코드 외에 표시 권한을 준비해야 한다. 공개 API에서 이 절차를 볼 수 있는 예가 SYSTEM_ALERT_WINDOW다. 다른 앱 위에 창을 표시하는 데 쓰는 권한이며, 일반적인 앱에서는 사용자가 설정 화면에서 허용하는 절차가 필요하다. Android의 오버레이 권한 문서
실제 작업에서는 오버레이의 표시와 종료를 다루면서 권한 요청 기능을 앱에 연결하는 데 시간이 들었다.
배포 조건도 선택에 영향을 주었다. 이 앱은 스토어를 거치지 않고 APK 파일을 직접 설치하고 업데이트하는 방식이었다. 따라서 Google Play 심사를 거치는 앱과는 선택 조건이 달랐다. 직접 배포하더라도 OS가 요구하는 표시 권한을 허용받는 작업은 남았고, 이 부분을 앱 기능으로 준비해야 했다.
확인한 결과와 남겨야 할 검증 기록
두 플랫폼에서 시험한 기기로 전화를 걸어 앱의 목록에 있는 이름이 표시되는 것을 확인했다. 개인 주소록에는 번호를 추가하지 않았다. 확인한 상태는 앱 실행 중, 백그라운드, 잠금 화면이었다.
| 항목 | iOS | Android |
|---|---|---|
| 표시 경로 | Call Directory 확장에 등록한 정보를 시스템이 표시 | 앱이 오버레이로 표시 |
| 구현에서 시간이 든 부분 | 확장용 프로비저닝 프로파일과 Xcode 설정 | 오버레이 권한 요청 기능 연결 |
| 확인한 앱 상태 | 실행 중, 백그라운드, 잠금 화면 | 실행 중, 백그라운드, 잠금 화면 |
이 결과의 범위는 시험한 기기에서의 표시 확인이다. 기기 모델과 OS 버전은 이 글의 검증 정보에 포함하지 못했으므로 지원 버전 범위를 판단하는 자료로 사용하기는 어렵다. 강제 종료 상태의 결과도 이 글의 확인 범위에 포함하지 않았다.
같은 기능을 검증한다면 기기 모델, OS 버전, 앱 상태, 권한 상태를 함께 기록하는 편이 좋다. 목록을 수정한 뒤 표시가 바뀌는지, 권한을 해제했을 때 앱이 어떻게 안내하는지, 통화가 끝난 뒤 오버레이가 남지 않는지도 별도 확인 항목으로 잡을 수 있다. 이 항목들은 여기서 모두 검증했다고 주장하는 결과가 아니라 추가 시험을 위한 기준이다.
다음 구현에서는 설정 작업도 처음부터 범위에 넣는다
사용자에게 보이는 요구사항은 “전화가 오면 이름을 표시한다”였지만, iOS에서는 시스템에 정보를 전달하는 확장과 그 서명을 준비해야 했다. Android에서는 앱이 화면을 표시하고 권한 요청을 연결해야 했다. 번호와 이름을 다루는 로직만으로는 어느 쪽도 끝나지 않았다.
다음에 OS와 연결되는 기능을 설계할 때는 표시 주체와 데이터 전달 경로를 먼저 확인하고, 필요한 권한과 서명 설정까지 작업 범위에 넣으려 한다. 이 경험에서 실제로 시간이 든 작업은 iOS 확장용 프로파일 발급과 Android 권한 요청 기능 연결이었다.