본문 바로가기
UIKit Project/UIKit Troubleshooting

[LilsMusic] MusicKit을 활용한 음악앱 만들기 - 뮤직비디오 로딩 최적화

by thekoon 2024. 3. 13.

새싹 메모리스의 iOS 개발과정을 열심히 진행중에 있습니다!

지금은 개인 출시 프로젝트가 한창인데 

저는 예전부터 관심이 있던 음악앱을 MusicKit을 활용해 구현해보기로 했습니다!

 

MusicKit은 AppleMusic에 가입 + 개발자 계정까지 있어야 사용 가능한 비싼 API인데요

마침 제가 둘 다 충족하고, 돈도 많이 내고 있는 김에 겁 없이 도전해보았습니다!

만들다 보니 MusicKit은 비교적 최근 프레임워크가 자료도 많이 없었고, GPT도 아직 잘 모르는 친구이더라구요.

게다가 SwiftUI용 API로 설계되었기 때문에 UIKIt에서의 자료는 더더욱 없어서 처음에 어려움을 많이 겪었습니다!

아직까지 많이 구현하지는 못했지만 오늘 너무 큰 난관에 봉착했고, 문제를 해결해나가는 과정을 기록해보려 합니다.

 

제가 생각했던 이번 앱의 핵심 기능은

MusicKit의 MusicVideo 컬렉션을 받아와 

인스타그램의 릴스처럼 하나하나 넘기면서

마음에 드는 노래가 있으면 내 플레이리스트에 바로 저장한다! 인데요

인스타그램의 릴스를 평소에도 많이 사용했으니

잘 구현할 수 있겠지 생각이 들어서 가벼운 마음으로 도전했지만 생각보다 구현에 힘든점이 많았습니다.

특히 릴스를 보면서 로딩뷰가 나온적이 거의 없는데 어떻게 그렇게 잘 구현했는지 만들면 만들수록 궁금해지더라구요

우선 지금까지의 진행상황을 정리해보면

 

- MusicVideo타입에서 MusicVideo를 제공 안한다?

왜 MusicKit의 Album, Song, Track 타입은 플레이어로 재생이 가능한데

MusicVideo타입은 재생이 안되는걸까요..

제가 못 찾은거일수도 있는데 레퍼런스와 자료가 부족하다보니 찾기 힘들었습니다.

 

테스트 기간에 음악 재생만 테스트하고,

MusicVideo타입에 URL이 제공되는것만 확인해 당연히 뮤직비디오의 URL을 제공해주는 줄 알고있었는데

해당 URL은 재생이 아니라 진짜 AppleMusic 사이트로 이동하는 URL이었습니다..ㅎㅎ

 

그래서 핵심 기능을 구현 못할수도 있겠다 -> 지금 기획이 반려당하면 나는 무슨 앱을 만들지.. -> 다시 시작하는건 절대 안돼..어떻게든 해결해야지라는 생각이 들어 다급히 MusicVideo의 모든 프로퍼티를 뒤지기 시작했습니다.

 

정말 정말 다행히 previewAssets에서 뮤직비디오 미리보기 URL을 제공해주더라구요!

게다가 하이라이트를 짧게 보여주는 릴스에는 오히려 더 적합하다는 생각이 들어 신나게 구현하기 시작했습니다!

URL을 두 가지 제공해주길래 둘 다 테스트해봤는데

확장자 .m4v는 720p로 릴스를 세로로 꽉 채우다 보니 720p로도 화질이 좋지 않았습니다.

확장자 .m3u8는 처음 보는 확장자고, 테스트할때 앱 내에서 다운로드 받아도 바로 재생할 수 없어서 조금 찾아보았는데

애플이 개발했다니 믿고 써보고 싶어졌습니다. ㅎㅎ

찾아보니 애플 뿐만아니라 릴스 같이 짧은 스트리밍을 하는 곳에서 많이 사용하는 확장자라고 합니다.

실제로 스트리밍 테스트를 했을때는 처음에는 720p보다 화질이 안 좋았지만, 몇 초 지나면 화질이 좋아지는 모습을 보여줘서 이걸 사용하기로 했습니다.

 

처음 프로토타입으로 만들었던 릴스뷰 입니다.

처음에는 국가나 장르별로 릴스를 나눠서 재생하려 했는데

cell이 많아지고, 동시에 영상을 모두 재생하다보니 부드럽지 못하고, cell을 그리지 못하는 현상도 발생했습니다.

멘토님께 여쭤보니 서버에서 내려주는 데이터가 애초에 이렇게 구현하는 목적이 아니고,

동시에 여러 동영상을 재생하기 힘들 수 있다는 말씀을 해주셔서 기획을 조금 수정했습니다.

 

 

UICollectionView의 CompositionalLayout을 활용해

cell을 화면 가득 채우고, paging을 구현해 한 화면에 하나의 영상만 재생되도록 구현했습니다.

 

 

하지만 cell이 나올때마다 url로 해당 주소에 접근하기 시작하고, 영상을 재생하기 때문에 중간중간 곡을 빠르게 넘기면 딜레이가 생기고 화질도 저하되었습니다.

 

 

제가 원하던 인스타그램의 릴스처럼 버벅임이 없는 로딩이 되지는 않았고, 처음 앱에 진입할때 로컬에 데이터를 캐싱했다가 바로 재생하면 괜찮지않을까? 생각이 들게 되어 처음으로 영상 캐싱을 구현해보게 되었습니다.

여러 우여곡절 끝에 캐싱을 해서 저장을 했습니다. 캐싱이 너무 빨리 다운로드되서 이상하다 생각했었는데

아까 gpt가 알려준것 처럼 .m3u8은 텍스트 기반 url로 구성된 파일이라 파일 크기가 많이 작고, AVplayer에서 바로 재생이 되지는 않는것 같았습니다! 

 

그래서 다시 m4v 파일로 캐싱을 시도하고, 이번엔 캐싱을 한 곡당 약 1, 2초정도 소요되며 하는 것 같았습니다.

m4v 파일의 용량이 약 7메가 정도여서 이제 제대로 다운로드 받아졌다는 생각이 들었고, 여러 우여곡절을 거치며 드디어 재생에 성공했습니다!

 

이제서야 인스타에보 보던 것 처럼 빠르게 넘겨도 딜레이 없이 재생이 되는 것을 보고 성공이다..!했는데

이것도 문제가 있었습니다ㅎㅎ

 

AppDelegate에서 앱을 시작할때 캐싱을 확인하고 다운로드를 한 뒤 repository에 저장하고, 

 

이 배열의 index를 기준으로 cell을 구성했는데요!

영상 캐싱 하나에 1,2초가 소요되므로, 앱을 켜자마자 릴스 탭에 들어와 모든 영상을 캐싱하기 전에 빠르게 릴스를 넘기면 index오류로 앱이 터지는 현상이 발생했습니다!

분기처리로 해결 할 수 있을 것 같지만, 화질도 720p를 확대하지 때문에 그렇게 좋지 않아서 아쉽지만 캐싱 방식은 보류하고, hlsURL을 조금 더 최적하해보기로 했습니다.

 

 

 

그래서 고민을 하다가 dequeueReusableCell의 특성을 활용해 현재 cell들을 configure할때 바로 영상들을 재생시켰습니다.

그렇게 하니 딜레이는 이전보다 확연히 줄어들었는데, 화면을 넘기기 전 화면 아래의 cell들의 영상의 소리까지 재생이 되서 소리가 겹치는 현상이 발생했습니다.

영상의 딜레이를 최대한 줄이고 싶었기 때문에 아래 cell까지 영상을 재생하고있지만, mute된채로 재생하고, 현재 보이는 cell 영상의 소리만 재생하면 괜찮지 않을까? 생각하게 되었습니다.

현재 index를 currentIndex변수로 만들고, print를 찍어가면서 호출 순서를 확인하고

index를 기준으로 mute와 soundOn을 호출해 현재 보이는 cell에서만 소리가 들리게 구현했습니다!

 

 

스트리밍을 최적화할 수 있는 방법을 찾아보다가 여러 자료도 보았는데  이후에 조금 더 시간내서 AVPlayer와 HLS 최적화에 대해 공부하고 구현해보며 더 개선해나가야 될 것 같습니다.

https://devmjun.github.io/archive/videos-1

https://medium.com/@hongseongho/introduction-to-hls-e7186f411a02

https://developer.apple.com/videos/wwdc2018

https://medium.com/@cafielo/optimizing-http-live-streaming-hls-for-best-user-experience-1-9b84db0de77d

https://medium.com/@cafielo/optimizing-http-live-streaming-hls-for-best-user-experience-2-c3fc7d75ed07 

 

Optimizing HTTP Live Streaming (HLS) for Best User Experience 2

Intro

medium.com

 

최종적으로 player코드를 구현했고, 캐싱때만큼은 아니지만, 용납해줄만한 딜레이의 터지지 않는 릴스화면을 구현할 수 있었습니다.

아직 앱을 완성하려면 갈 길이 많이 먼데 기간 전까지 앱을 완성할 수 있도록 화이팅하겠습니다!!

화이팅화이팅!!!