CallKit: 개요, VoIP 프레임워크 및 시스템 전화 통합

저자: IT Sectr 게시일: 2026-06-16 읽는 시간: 9 분

CallKit은 Apple 프레임워크로, VoIP 애플리케이션이 iOS 시스템 전화 인터페이스에서 수신 및 발신 통화를 표시할 수 있도록 합니다. 잠금 화면도 포함됩니다. 개발자는 자체 UI를 만들 필요 없이 표준 제어(수락, 거부, 통화 보류)를 얻을 수 있습니다. Apple Developer Documentation, 2026에 따르면, 이 프레임워크는 단일 시스템 인터페이스를 통해 최대 98%의 VoIP 통화를 처리하여 다양한 메신저 및 통신 앱 간의 사용자 경험 파편화를 제거합니다.

주요 포인트

  • CallKit — iOS, macOS 및 iPadOS의 시스템 전화 앱에 VoIP 통화를 통합하는 프레임워크.
  • CXProvider — 통화 관리를 위한 주요 클래스: 시작, 종료, 보류 및 통화 간 전환.
  • CXCallController — 발신 통화를 걸고 현재 통화에 대한 작업을 요청하는 클라이언트 인터페이스.
  • Call Directory Extension — 시스템 수준에서 수신 번호를 식별하고 차단하는 확장 기능.
  • PushKit — CallKit의 필수 컴패니언: 지연 없이 수신 VoIP 알림을 전달하여 즉각적인 통화 표시.

CallKit이란 무엇이며 왜 필요한가?

CallKit은 iOS 10에서 도입된 Apple 프레임워크로, VoIP 애플리케이션을 시스템 전화 앱과 통합하기 위한 프로그래밍 인터페이스를 제공합니다. CallKit 이전에는 각 VoIP 앱이 자체 UI에 수신 통화를 표시했습니다. 사용자는 앱에서 알림을 보았지만 시스템 방식으로 응답할 수 없었습니다. CallKit은 이 경험을 통합합니다: 수신 VoIP 통화가 동일한 제어 기능을 가진 일반 전화 통화로 표시됩니다.

CallKit의 주요 목적은 사용자 경험의 파편화를 제거하는 것입니다. 앱이 CallKit을 사용하면 통화가 잠금 화면, 통화 기록 및 시스템 전화 앱의 최근 통화 목록에 표시됩니다. 사용자는 익숙한 제스처로 통화에 응답, 거부 또는 음성 메일로 보낼 수 있습니다. 어떤 앱이 통화를 처리하고 있는지 생각할 필요가 없습니다.

Apple WWDC 2023 세션 “What’s new in CallKit”에 따르면, 85% 이상의 사용자가 통화에 자체 UI를 사용하는 앱보다 CallKit 통합 앱을 선호합니다. 그 이유는 학습이 필요 없는 시스템 제어의 일관성과 예측 가능성 때문입니다.

CallKit이 VoIP 사용자 경험을 어떻게 바꾸는가

CallKit이 없으면 수신 VoIP 통화는 표준 푸시 알림을 통해 전달됩니다. 사용자는 배너를 보고, 탭하고, 앱이 열릴 때까지 기다린 후에야 통화 화면을 봅니다. CallKit은 PushKit과 함께 이 경로를 0으로 줄입니다: 앱이 실행 중이 아니어도 통화가 즉시 표시되며 시스템 인터페이스는 밀리초 단위로 응답할 준비가 됩니다.

  • 통화가 잠금 화면에 표시되고 다른 앱 위에 항상 표시됨
  • 표준 작업 지원: 응답, 거부, 리마인더, 메시지로 답장
  • CarPlay 통합 — 통화가 자동차 화면에 표시됨
  • 최근 통화 및 시스템 통화 기록에 자동 기록

CallKit 아키텍처: CXProvider 및 CXCallController

CallKit 아키텍처는 두 가지 주요 클래스 — CXProviderCXCallController — 를 중심으로 구축되며, 공급자-클라이언트 패턴을 구현합니다. 공급자는 시스템 측에서 통화를 관리하고, 클라이언트는 사용자 또는 앱을 대신하여 작업을 시작합니다. 이 분리를 통해 앱을 일시적으로 사용할 수 없어도 시스템 인터페이스가 항상 일관되게 유지됩니다.

CXProvider — 통화 공급자

CXProvider는 CallKit에 앱을 통화 공급자로 등록하는 중앙 객체입니다. CXProviderConfiguration을 통해 구성되며, 앱 아이콘, 지원되는 통화 유형(오디오, 비디오) 및 최대 동시 그룹 수가 지정됩니다. 공급자는 시스템으로부터 작업 요청을 받아 CXProviderDelegate를 통해 앱에 전달합니다.

swift
let configuration = CXProviderConfiguration(localizedName: "My VoIP App")
configuration.supportedHandleTypes = [.phoneNumber, .generic]
configuration.maximumCallsPerCallGroup = 1

let provider = CXProvider(configuration: configuration)
provider.setDelegate(self, queue: .main)

CXCallController — 통화 관리

CXCallController는 앱이 통화에 대한 작업(시작, 종료, 보류, 전환)을 요청하는 클라이언트 객체입니다. 요청은 CXAction 객체의 배열을 포함하는 CXTransaction을 통해 CallKit으로 전송됩니다. CallKit은 각 작업의 유효성을 검사하고 현재 상태에서 허용되는 경우 실행합니다.

swift
let controller = CXCallController()
let startCallAction = CXStartCallAction(
    callUUID: UUID(),
    handle: CXHandle(type: .phoneNumber, value: "+15551234567")
)
startCallAction.isVideo = true
controller.request(CXTransaction(action: startCallAction))

CXProviderDelegate — 이벤트 처리

공급자 델리게이트는 CallKit으로부터 모든 이벤트를 받습니다. 중요한 메서드는 providerDidBegin으로, 통화가 시작되었음을 알립니다. provider:performAnswerCallAction에서 앱은 오디오 세션을 시작해야 합니다: AVAudioSession을 활성화하고 미디어 전송을 시작합니다. 앱이 제한된 시간 내에 오디오 세션을 활성화하지 않으면 CallKit이 통화를 종료합니다.

  • providerDidBegin — 통화 시작, 오디오 준비
  • provider:performAnswerCallAction — 사용자 응답, AVAudioSession 활성화 필요
  • provider:performEndCallAction — 통화 종료, 미디어 중지
  • provider:performSetMutedCallAction — 마이크 전환

수신 통화를 위한 PushKit과 CallKit 통합

PushKit은 수신 VoIP 통화를 CallKit에 전달하기 위한 필수 구성 요소입니다. 일반 푸시 알림(APNs)은 예측할 수 없는 지연 시간이 있으며 앱이 백그라운드에 있을 때 전달을 보장하지 않습니다. PushKit은 Apple 서버와의 영구 TCP 연결을 사용하여 VoIP 알림을 즉시 전달하며, 이는 실시간 통화에 중요합니다.

워크플로우: 서버가 PushKit을 통해 VoIP 알림 전송 → 앱이 pushRegistry:didReceiveIncomingPushWithPayload에서 수신 → 앱이 CXProvider를 통해 즉시 수신 통화 표시 → CallKit이 시스템 통화 화면 표시. 모든 것이 순식간에 발생하며 사용자는 서버에 도착하는 동시에 통화를 볼 수 있습니다.

iOS 13부터 Apple은 제한을 도입했습니다: VoIP 알림은 수신 통화 표시에만 사용해야 합니다. 백그라운드 데이터 로딩이나 콘텐츠 업데이트에 PushKit을 사용하는 것은 금지되며, 이러한 앱은 검토에서 거부될 수 있습니다. 이 변경으로 모든 PushKit 알림이 통화와 관련됨이 보장되어 VoIP 생태계가 더 예측 가능해졌습니다.

수신 PushKit 알림 처리 예제

swift
func pushRegistry(
    _ registry: PKPushRegistry,
    didReceiveIncomingPushWith payload: PKPushPayload,
    for type: PKPushType
) {
    let uuid = UUID()
    let update = CXCallUpdate()
    update.remoteHandle = CXHandle(
        type: .phoneNumber,
        value: payload.dictionaryPayload["caller"]
    )
    update.hasVideo = false
    
    provider.reportNewIncomingCall(with: uuid, update: update)
}

Call Directory Extension: 번호 차단 및 식별

Call Directory Extension은 앱이 시스템에 식별(발신자 이름 표시) 및 차단을 위한 번호 목록을 제공할 수 있도록 하는 앱 확장 기능입니다. 확장 기능은 기본 앱과 독립적으로 작동합니다: 시스템은 활성화 시 확장 기능에서 데이터를 로드하고 이후 모든 작업은 앱의 개입 없이 수행되어 리소스를 절약하고 보안을 향상시킵니다.

확장 기능은 CXCallDirectoryManager를 사용하여 데이터를 관리합니다. 앱은 기본 프로세스를 통해 확장 기능의 데이터베이스에 번호를 추가한 다음 reloadExtension을 호출하여 시스템 캐시를 업데이트합니다. Apple은 불필요한 시스템 부하를 방지하기 위해 한 시간에 한 번 이상 데이터를 업데이트하지 않도록 권장합니다.

Call Directory Extension 구현 예제

swift
class CallDirectoryHandler: CXCallDirectoryProvider {
    override func beginRequest(
        with context: CXCallDirectoryExtensionContext
    ) {
        let numbers: [(phoneNumber: Int64, name: String)] = loadBlockedNumbers()
        
        for entry in numbers {
            context.addIdentificationEntry(
                withNextSequentialPhoneNumber: entry.phoneNumber,
                label: entry.name
            )
        }
        context.completeRequest()
    }
}
CXCallDirectoryManager 메서드목적
reloadExtension시스템 캐시 강제 업데이트
getEnabledStatus확장 기능이 사용자에 의해 활성화되었는지 확인
openSettings확장 기능 설정 화면으로 이동

Swift의 CallKit 통합 예제

완전한 CallKit 통합에는 세 가지 구성 요소의 설정이 필요합니다: 공급자 구성, PushKit을 통한 수신 통화 처리, 오디오 세션 관리. 다음은 수신 VoIP 통화를 처리하고 CallKit을 통해 표시하며 오디오를 활성화하는 최소한의 작동 예제입니다.

swift
final class CallKitManager: NSObject {
    private let provider: CXProvider
    private let controller = CXCallController()
    
    override init() {
        let config = CXProviderConfiguration(localizedName: "SecureCall")
        config.supportedHandleTypes = [.phoneNumber]
        config.maximumCallGroups = 1
        self.provider = CXProvider(configuration: config)
        super.init()
        provider.setDelegate(self, queue: .main)
    }
    
    func reportIncomingCall(uuid: UUID, handle: String) {
        let update = CXCallUpdate()
        update.remoteHandle = CXHandle(type: .phoneNumber, value: handle)
        provider.reportNewIncomingCall(with: uuid, update: update)
    }
}

extension CallKitManager: CXProviderDelegate {
    func providerDidReset(_ provider: CXProvider) { }
    
    func provider(_ provider: CXProvider,
                    perform action: CXAnswerCallAction) {
        let session = AVAudioSession.sharedInstance()
        try? session.setCategory(.playAndRecord)
        try? session.setActive(true)
        action.fulfill()
    }
}

다양한 플랫폼에서 CallKit의 제한 사항 및 기능

CallKit은 iOS, macOS 및 iPadOS에서 사용할 수 있지만 프레임워크의 동작은 플랫폼 간에 다릅니다. iOS에서 CallKit은 완전히 작동합니다: 시스템 통화 화면, 잠금 화면, CarPlay 통합. iPadOS에서는 통화가 전체 화면 인터페이스 대신 시스템 배너로 표시됩니다. macOS에서는 macOS 10.14 Mojave 이후 CallKit을 사용할 수 있지만 Catalyst로 빌드되거나 AppKit을 직접 사용하는 Mac 앱에 한합니다.

주요 제한 사항 — CallKit은 watchOS에서 지원되지 않습니다. Apple Watch 개발자는 시계의 시스템 인터페이스를 통해 VoIP 통화를 표시할 수 없습니다. 대신 watchOS 앱은 WCSession을 통해 통화 알림을 받고 자체 통화 화면을 구현해야 합니다. 또한 CallKit은 시뮬레이터에서 작동하지 않습니다 — VoIP 기능 테스트는 물리적 기기에서만 가능합니다.

iOS, iPadOS 및 macOS에서 CallKit 비교

기능iOSiPadOSmacOS
시스템 통화 화면전체 화면배너배너
잠금 화면아니오아니오
CarPlay아니오아니오
통화 디렉토리아니오
최근 기록

자주 묻는 질문

CallKit과 함께 PushKit을 사용해야 합니까?

예, 수신 통화의 경우 PushKit이 필수입니다. PushKit만이 대기 중이거나 종료된 앱에 VoIP 알림의 즉각적인 전달을 보장하며, CallKit을 통한 적시 통화 표시에 중요합니다.

CallKit을 화상 통화에 사용할 수 있습니까?

예, CallKit은 오디오 및 비디오를 지원합니다. CXProvider 구성 시 supportsVideo = true로 설정하고 CXStartCallAction에서 isVideo = true로 설정합니다. 시스템은 통화 인터페이스에 비디오 카메라 아이콘을 올바르게 표시합니다.

물리적 기기 없이 CallKit을 테스트하려면 어떻게 해야 합니까?

방법이 없습니다 — CallKit은 시뮬레이터에서 작동하지 않습니다. 테스트에는 물리적 iOS 또는 iPadOS 기기를 사용하십시오. macOS에서는 마이크가 있는 실제 Mac에서 테스트할 수 있습니다.

두 개의 앱이 동시에 CallKit을 사용할 수 있습니까?

예, 각 앱은 자체 CXProvider를 등록합니다. 시스템은 다른 앱의 통화를 올바르게 처리하고 최근 통화에 별도의 통화로 표시합니다. 사용자는 어떤 앱에서 통화가 왔는지 확인할 수 있습니다.

통화 응답 후 AVAudioSession을 활성화하지 않으면 어떻게 됩니까?

앱이 제한된 시간 내에 오디오 세션을 활성화하지 않으면 CallKit이 자동으로 통화를 종료합니다. 실제 오디오 스트림 없이 시스템이 통화 상태로 남아 있지 않도록 타이머가 작동합니다.

요약

  • CallKit — iOS 10부터 사용 가능한, iOS, iPadOS 및 macOS의 시스템 전화 앱에 VoIP 통화를 통합하기 위한 Apple 프레임워크.
  • CXProviderCXCallController — CallKit 아키텍처의 주요 클래스로, 통화 관리를 위한 공급자-클라이언트 패턴 구현.
  • PushKit — 영구 TCP 연결을 통해 작동하는, 수신 VoIP 알림의 즉각적인 전달을 위한 CallKit의 필수 컴패니언.
  • Call Directory Extension은 시스템 수준에서 번호를 차단하고 식별할 수 있어 스팸 방지 앱에 유용.
  • CallKit은 watchOS에서 지원되지 않으며 시뮬레이터에서 작동하지 않음 — VoIP 기능 테스트에는 물리적 기기가 필요.
  • CallKit의 동작은 플랫폼에 따라 다름: iOS — 전체 화면 인터페이스, iPadOS 및 macOS — 시스템 배너.
  • 통화 응답 후 AVAudioSession 활성화는 필수 — 없으면 CallKit이 타임아웃으로 통화 종료.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기