뭘 하려고 했냐면

Subsonic 기반 음악 스트리밍 앱을 만들고 있었는데요. AVPlayer로 백그라운드 재생까지 잘 되고 있었거든요. 그런데 테스트하다가 이상한 걸 발견했어요.

  1. 노래 재생하고 앱을 백그라운드로 보냄
  2. YouTube 열어서 영상 재생 (노래 멈춤 — 여기까진 정상)
  3. YouTube 영상 닫음
  4. 앱에 돌아오면 일시정지 버튼이 보이는데 소리가 안 남

버튼을 누르면 재생이 시작되긴 하는데, 한 번 더 눌러야 하는 거예요. UI는 “재생 중"인데 실제론 멈춰있는 상태.

원인

AVPlayer 기반 앱은 다른 앱이 오디오 세션을 가져가면 시스템이 자동으로 AVPlayer를 pause시켜요. 문제는 이 사실을 앱에 알려주지 않는다는 거예요. 정확히는, 알려주긴 하는데 직접 들어야 해요.

AVAudioSession.interruptionNotification이라는 노티피케이션을 쏴주거든요. 이걸 안 듣고 있으면 AVPlayer는 멈췄는데 ViewModel의 isPlayingtrue인 상태가 돼요.

해결

AudioPlayerManager에 인터럽션 옵저버를 추가했어요:

private func setupInterruptionObserver() {
    interruptionObserver = NotificationCenter.default.addObserver(
        forName: AVAudioSession.interruptionNotification,
        object: AVAudioSession.sharedInstance(),
        queue: .main
    ) { [weak self] notification in
        guard let self,
              let userInfo = notification.userInfo,
              let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
              let type = AVAudioSession.InterruptionType(rawValue: typeValue) 
        else { return }

        switch type {
        case .began:
            Task { @MainActor in self.onInterruptionBegan?() }
        case .ended:
            let options = (userInfo[AVAudioSessionInterruptionOptionKey] as? UInt)
                .flatMap { AVAudioSession.InterruptionOptions(rawValue: $0) } ?? []
            let shouldResume = options.contains(.shouldResume)
            Task { @MainActor in self.onInterruptionEnded?(shouldResume) }
        @unknown default:
            break
        }
    }
}

핵심은 .ended 이벤트의 shouldResume 플래그예요. 시스템이 “너 다시 재생해도 돼"라고 알려주는 건데, 이게 true면 자동으로 재생을 재개할 수 있어요.

ViewModel 쪽은 간단해요:

func handleInterruptionBegan() {
    guard isPlaying else { return }
    isPlaying = false
}

func handleInterruptionEnded(shouldResume: Bool) {
    if shouldResume {
        isPlaying = true
        audioManager?.togglePlayPause()
    }
}

이렇게 하면:

  • YouTube가 오디오를 가져가면 → 버튼이 자동으로 일시정지 상태로 바뀜
  • YouTube 영상을 닫으면 → 자동으로 노래가 다시 재생됨

Spotify나 Apple Music이랑 똑같은 동작이에요.

주의할 점

shouldResume이 항상 true인 건 아니에요. 전화가 와서 인터럽션이 걸린 경우엔 통화 종료 후 shouldResume = true가 오지만, 어떤 앱은 오디오 세션을 놓으면서 이 플래그를 안 보내기도 해요. 그래서 shouldResume = false일 때는 일시정지 상태를 유지하는 게 안전해요.

배운 점

AVPlayer를 쓰는 음악/팟캐스트 앱이라면 interruptionNotification은 선택이 아니라 필수예요. 안 넣으면 UI가 거짓말하는 앱이 됩니다.