먹고 기도하고 코딩하라

Carplay 지원 이야기 본문

앱/Swift

Carplay 지원 이야기

사과먹는사람 2023. 1. 8. 23:32
728x90
728x90

 

회사 들어와서 작은 업무 하나 맡고 운영 이슈 처리하다가 카플레이를 첫 프로젝트로 받았다.

애플 카플레이는 폰과 차를 연동해서 차에서 폰을 쉽게 사용하기 위해 만들어졌다. (운전 중 폰하면 위험하니까) 카플레이라서 음악 앱만 될 것 같지만 꼭 그렇지만도 않다. 우리 회사 앱 주력 서비스가 플레이어다 보니까, 카플레이를 지원해달라는 요구가 몇 년 전부터 있긴 했나보다. 오랜 숙원사업처럼 남아 있었는데 인력이 모자라서 진행하질 못하다가 이번에 내가 들어와서 맡게 됐다.

카플레이는 해본 적이 없지만... 뭐 일이라는 게 항상 해본 것만 할 수는 없는 거니까. 그나마 J님과 M 대리님이 미리 위키에 써두신 게 있어서 그걸 참고하면서 뚝딱뚝딱 만들었고 이제 QA를 목전에 두고 있다. 

카플레이를 개발하면서 웃겼던(이라고 쓰고 빡셌다고 읽는) 점을 몇 가지 적어보려 한다.

 

 

iOS 11~13, 14+를 지원할 수 있는 프레임워크가 다르다

우리 앱 최소 지원 버전은 iOS 11이다. 그래서 원칙적으로 따지면 카플레이도 iOS 11부터 사용할 수 있어야 하나... 사정이 있어 14 이상부터 카플레이를 사용할 수 있게 했다.

원래 14 미만 버전에서는 MediaPlayer import하고 identifier에 playable-content 추가한 다음 프로비저닝 새로 업데이트하면 MPPlayableContentManager를 사용해서 카플레이 구현이 가능하다.

근데 문제는 이게 iOS 7.1-14.0 되고 14 이후부터는 deprecated됐다는 거다. 물론 사용할 수는 있고 구현에 문제는 없다. 아주 찝찝한 상태로 개발해야 한다는 게 문제지. 

14부터는 아예 대놓고 Carplay 프레임워크라는 걸 도입해서 이걸로 카플레이를 구현하도록 유도한 듯하다. 확실히 MediaPlayer에서 지원하는 걸로 짰을 때와 Carplay에서 지원하는 걸로 짰을 때 UI도 살짝 다르고, Carplay가 리스트 다루기도 훨씬 쉬웠다. (MP로 하면 리스트 다루는 걸 Indexpath로 해야 하는데 이게 조금 빡세다)

 

entitlements에 2개를 추가하고 MediaPlayer 코드를 태우면 앱이 죽는다

그리고 가장 중대한 문제가 있었는데, MediaPlayer와 Carplay를 동시에 사용하려고 identifier에 playable-content와 carplay를 같이 enable시키고 프로비저닝 업데이트하고, MP 코드를 태우는 순간 앱이 죽는다.

내가 어떻게 해야했을까?

웃으면 된다고 생각하나?

이 이슈가 나한테만 국한된 것이고 뭔가 고쳐서 해결할 수 있는 건지 좀 월드와이드로 유명한 문제인지 알아보기 위해 검색을 해봤는데, 스택오버플로우에도 똑같은 고통을 호소하는 사람이 있었다. 거기에 답변으로 달린 건 "그냥 하나 빼버려"였다. 이거 반쪽짜리 답변 아닌가 싶었는데, 나중에 J님께 물어보니 맞다고 그거 안 된다고 해서 살짝 안심했다. 아 안 되는구나. 웃긴 건 애플에서 제공하는 카플레이 공식 문서에는 "예전 버전 지원하고 싶으면~ playable-content도 추가해~ entitlements에도 추가하고~~" 라고 써 있다는 거다.

이거.. 해본 걸까 애플은? ㅋㅋㅋㅋ ㅠㅠ

결국 이사님과의 논의 끝에 카플레이는 14 이상부터 지원하기로 했다. 정말이지 십년감수했다. 원래 MediaPlayer로 개발되어 있다면 딱히 상관없었겠지만 새로 개발하는데 deprecated된 건 쓰고 싶지가 않았기 때문이다.

 

왜 다 재생한 콘텐츠는 메타 데이터가 넘어오지 않을까?

웬만한 기능은 다 구현했고(사실 리스트 잘 보여주고, 최근 재생 목록 업데이트 잘 해주고, 재생 관련 기능들 확실하게 구현하는 게 작업의 90%를 차지해서 작업 자체가 어렵지는 않았다) 특이한 케이스들이 몇 가지 남아서 근 일주일동안은 이슈를 좀 살펴봤다. 주로 목록에서 다 들은 콘텐츠를 선택했을 때 이슈가 생겼다.

그 중 가장 골치아팠던 이슈는 다 들은 콘텐츠를 선택했을 때 카플레이에서 빈 화면이 나오고, 커버 사진, 저자, 들은 시간 등의 메타 데이터가 전혀 넘어오지 않는 이슈였다. 도저히 원인을 알 수가 없었다. 못 고치면 분명히 QA에서 문제가 될 치명적인 이슈였다.

이 이슈가 쉽게 해결될 줄 알았더니 전혀 그렇지 않았다. 브레이크 포인트를 촘촘하게 찍어서 확인하는데도 플레이어 코드는 워낙 복잡하고, 또 내가 도메인 지식이 부족하다 보니 API 요청 이후 데이터가 플레이어로 넘어가는 흐름을 읽기 쉽지 않았고 원인을 찾아낼 수가 없었다.

결국 차장님과 함께 2시간 넘게 코드를 보면서 차장님이 은혜와 자비를 베풀어주셔서 원인을 잡아내고 문제를 해결했다. (차장님이 핸들러 중복도 빼고 scene 찾는 부분도 클로저 넘겨서 빼는 걸로 중복 삭제하라고 권해주셔서 코드 정말 많이 깔끔해졌다. 무한감사..)

이게 웃긴 게, CPNowPlayingTemplate 문서에는 템플릿이 MPNowPlayingInfoCenter나 MPNowPlayingSession의 정보를 가져와서 표시한다고 되어 있다. 그럼 이해하기로, MPNowPlayingInfoCenter의 nowPlayingInfo 딕셔너리가 변하면 뭐 업데이트가 된다든지 하는 그런 로직이어야 할 것 같은데 그렇지가 않은 거다. 

실제로 카플레이의 NowPlaying 화면의 업데이트 시점은 AVPlayer의 play 시점에서 업데이트된다. 이 얼마나 무서운 일인가... 사실 play 부분에서 뭔가 있다고 느끼긴 했지만, 정확히 뭐가 범인인지를 찾아내질 못하고 있었는데 rate 변경하는 부분이 핵심이라는 걸 알고 할 말을 잃었다. 결국 AVPlayer에 대한 사전 지식이 부족했기 때문에 디버깅을 제대로 못한 거였다. 내가 거의 다 찾았는데 등잔 밑이 어둡다고 코앞에서 놓쳤구나, 그래서 2시간을 내리 들이붓고 몇날며칠을 썼구나 하는 생각을 하니 굉장히 허무했다. ㅜㅜ 함부로 속단하는 게 위험하다는 걸 배웠다. 

 

폰에 앱이 실행되지 않은 상태에서 카플레이만 켤 때 어떤 일이 일어날까?

폰에서 앱을 켜지 않았는데 카플레이 앱만 켰다면 다음을 실행하게 된다.

  • AppDelegate.application(didFinishLaunchingWithOptions:)
  • AppDelegate.messaging (Firebase)
  • CPSceneDelegate

당연한 말이지만 폰의 sceneDelegate 쪽은 타지 않는다. 폰의 scene이 올라온 상태가 아니기 때문이다. 그래서 만약 폰 앱이 살아있지 않고 카플레이 단독으로 앱이 살아있을 때에도 뭔가 되도록 보장해야 한다면, AppDelegate의 생명주기 메소드 안에 동작을 실행하도록 작성해야 한다.

 

카플레이는 인터넷에 자료도 많지 않고, 애플이 제공하는 57쪽짜리 프로그래밍 가이드 문서와 Carplay developer docs에 매달려야 한다. 스택오버플로우 재야의 고수가 남긴 답변, 이런 것 자체가 드물다. 문서와 예제를 얼마나 잘 파먹는지가 카플레이 프로젝트의 성패를 가르는 것 같다.

사실 카플레이 오디오 앱은 프로그래밍하기가 그렇게 까다로운 편은 아니라고 생각하지만, 특이 케이스에 대한 이슈가 생겼을 때는 인터넷을 뒤져도 뭐 나오는 게 없으니 참 난감했다.

이제 QA 시작이고, 아마 QA에서 내가 예상도 못한 뭔가 나올 수도 있겠지만 ㅠ 일단은 다시 플레이어 이슈로 돌아가야겠다.

 

 

 

728x90
반응형
Comments