내가 직접 만든 오디오 플레이어
(nexo.sh)- 2025년에도 iPhone에서 MP3 음악을 자유롭게 재생하는 것에는 제한이 많음
- Apple 및 써드파티 앱은 대부분 유료 서비스이거나 사용자 편의성이 부족함
- 직접 만든 앱은 풀텍스트 검색, iCloud 지원, 로컬 우선 환경 등을 제공함
- React Native 등 크로스플랫폼 접근은 파일 시스템 제약과 안정성 문제로 한계가 있었음
- SwiftUI와 SQLite 기반 설계로, 독립적·확장성 높은 음악 관리 경험 구현함
개요
2025년에도 iPhone에서 사용자가 직접 소유한 MP3 파일을 자유롭게 재생하기 어렵다는 현실에 대한 불편함을 직접 해결하고자, 개발자가 스스로 음악 플레이어 앱을 만들게 된 과정과 결과를 소개함. Apple의 음악 서비스와 외부 앱 모두 다양한 제한과 유료화 모델을 가진 반면, 직접 구현한 앱은 사용자 음악 파일 관리 및 재생에 최적화된 경험을 제공함.
음악 플레이어를 스스로 만든 이유
- Apple Music과 iCloud Music Library 등 클라우드 기반 동기화 기능이 유료 서비스 구독을 해야만 동작함
- 구독을 중단할 경우 자동 동기화 및 음악 폴더 접근이 전부 차단됨
- 기존의 음악 라이브러리 통합, 범용 기기 활용성에 대한 소유자 권한 부재를 체감함
- "구매한 스마트폰을 활용해 꼭 필요한 기능은 스스로 만들 수도 있다"는 자기결정성을 실현하고자 함
Apple과 타사 음악 재생 솔루션 분석
Apple 기본 앱
- Files 앱을 이용해 iCloud 폴더에서 음악 파일을 재생할 순 있으나, 재생목록 관리, 메타데이터 정렬, 큐 조작 등 기본적 기능이 부실함
- 음악 감상에 최적화되지 않은 사용자 경험 제공함
서드파티 앱
- 앱스토어에는 다양한 외부 앱이 있으나, 구독 기반 과금 모델이 많고 앱마다 기능 수준이 상이함
- Doppler 등 일부 유료(단품 결제) 앱도 있으나, 대규모 iCloud 폴더에서의 검색·임포트 경험이 매끄럽지 못함
“빌더 모드”로 나아간 기술적 여정
- 직접 음악 플레이어 앱을 만들어야겠다고 결심함
- 요구 기능: iCloud 폴더 전체에 대한 빠른 풀텍스트 검색, 공식 음악 앱 수준의 음악 관리 기능(큐, 재생목록, 정렬 등)과 직관적 인터페이스
React Native 첫 시도
- Swift 경험상의 불편함(이전에는 async/await 미지원 등)으로 React Native/Expo를 선호함
- 오픈소스 템플릿 활용 등으로 UI 구현은 무난했으나, 파일 시스템(iCloud 폴더) 접근 및 동기화 처리에서 앱 크래시와 기능적 한계를 느꼈음
- iOS 샌드박스 정책상 명시적 권한 없이 외부 폴더 접근이 불가능함을 깨닫고 Swift로 전환함
SwiftUI 선택 및 이유
- SwiftUI의 선언적 UI 패러다임, 현대적 async/await 및 Swift Actor 지원 활용
- UI와 데이터 로직 철저 분리로, 클린한 데이터 플로우 및 도메인 동시성 처리 구현
- LLM(OpenAI o1, DeepSeek 등) 활용 최적화 효과, 생성된 UI 코드의 의존성 최소화에 기여함
앱 아키텍처 및 데이터 모델
-
SQLite를 데이터 저장소로 사용, 풀텍스트 검색(FTS5) 기능을 바로 쓸 수 있어 CoreData 대신 채택함
-
앱의 3개 주요 화면/모드:
- 라이브러리 임포트: iCloud 폴더별로 오디오 파일 경로를 DB에 일괄 저장, 유연한 검색/관리 지원
- 라이브러리 관리: 플레이리스트, 곡 분류, 편집 등
- 음악 재생: 큐 관리(반복, 셔플 등)와 기본 곡 컨트롤
-
라이브러리 임포트 시 유저 흐름:
- 초기 빈 상태에서 "Add iCloud Source"를 통해 폴더 선택 및 트리 스캔
- 인덱싱이 완성되면 라이브러리 탭으로 이동, 키워드별 리스트 및 미니 플레이어 제공
- 새로운 곡 추가 시 백그라운드에서 자동 병합됨
백엔드 스타일 논리 계층
- 웹/클라우드 기반 스타트업 개발 경험을 바탕으로, 논리계와 UI/ViewModel을 철저히 분리
- 도메인 레이어(Swift actors) 가 주요 비즈니스 로직(임포트, 검색, 큐 등)을 보유, 스레드 안전성 확보
- UI 업데이트는 ViewModel 통해 깔끔히 분할, 파일동기화·재생·UI 간 의존도 최소화로 유지보수성 향상
SQLite로 풀텍스트 검색 구현
- iOS 11 이상부터 별도 설정 없이 FTS5 지원 SQLite 사용 가능
- 외부 검색 인덱스/서버 필요 없이 빠른 검색 지원
- SQLite.swift 라이브러리로 일반 쿼리 처리, FTS 쿼리에는 직접 SQL 문 사용
FTS 테이블 구조
-
songs_fts
: 곡의 artist, title, album, albumArtist 등 인덱싱 -
source_paths_fts
: 파일 경로, 파일명 인덱싱 - 메인 테이블과 나란히 존재하며, UI에선 검색(READ) 전용 사용
- 인덱스 업데이트는 DB 트랜잭션으로 처리해 데이터 일관성 유지
퍼지(Fuzzy) 검색 및 랭킹
- 입력값 뒤에 와일드카드 자동 추가, BM25 기반 관련도 순 정렬
- 전체적으로, 네트워크 의존성 없는 예측 가능 데이터 구조와 강력한 로컬 검색 환경이 실현됨
iOS 파일 시스템 및 북마크(Bookmarks) 활용
- iOS는 macOS와 달리 security-scoped bookmarks를 지원하지 않아, 외부 파일 확장 접근 지속성 부족함
- 경로 정보만 저장되어 재접근 시 사용자가 다시 권한을 승인해야 함
- 해결책: 접근 허용 시점에 파일 자체를 앱 샌드박스로 사본 저장
- 백그라운드에서 자동 복사해 파일 인덱싱 속도 개선
- 기기 리부팅 후 외부 파일 직접 재생은 여전히 어렵지만, iOS의 파일 접근 제약이 심각함을 보여줌
AVFoundation 기반 재생 및 인터페이스 설계
- AVFoundation 프레임워크와 AVURLAsset 사용해 오디오 파일 메타데이터 분석
- track number 등 일부 필드는 수동 파싱(ID3 태그 활용)
- 오디오 재생에는 AVAudioPlayer 사용, 컨트롤 센터 연동 위해 MPRemoteCommandCenter·Delegate 프로토콜 구현
개발 후 느낀 점 및 Apple 정책에 대한 고찰
불편했던 점
- Xcode의 제약적 환경: SwiftUI 실시간 프리뷰는 발전적이나, VSCode·Flutter의 편의성에 미치지 못함
- 에디터 융통성 부족: Neovim, VSCode에서 Swift LSP 활용하려면 추가 설정 필요 및 완성도 낮음
- 일부 SDK 구석은 Objective-C 위주: 메타데이터 검색 등에서 현대 Swift 친화적 API 부족
- iCloud 연동 디버깅 번거로움: SwiftUI 프리뷰에서 클라우드 기능 완전 구현 불가, 직접 목업 구성 필요
긍정적인 점
- Async/await로 I/O 바운드 동시성 코드 작성이 현저히 수월해짐
- 풍부한 네이티브 라이브러리: 오픈소스 바인딩 한계에서 벗어나 더 다양한 기능 개발 가능
- SwiftUI의 선언적 UI 설계: React 방식 강점과 높은 생산성 실감
결론: 개발이 더 쉬워져야 하지 않을까
- 1.5주 개발로 로컬/오프라인 음악 플레이어 목적 달성
- 실제론 앱 자체 배포에도 제한 존재: 개발자 인증서 없이 7일 후 사용 불가, 연간 $99 개발자 등록 필요
- EU DMA Act 이후에도 사이드로딩 완전 개방 아님, 개인·취미 개발자에겐 여전히 제약 지속
- PWAs 역시 iOS에선 주요 API 미지원 등 제한(Web Bluetooth/USB/NFC, Background Sync 등)
- AI로 개발 장벽은 낮아졌으나, iOS만은 인위적 규정으로 진입 장벽 높음
- 독립 개발자·사용자 권한 제한은 여전하며, iOS의 폐쇄성은 여전히 혁신에 장애 요인임
Hacker News 의견
- 25년 동안 FLAC 포맷으로 내 음악 컬렉션을 구축한 경험자임, 작년에 안드로이드 폰과 1TB MicroSD 카드를 구입해서 모든 음악을 주머니에 넣을 수 있게 된 점에 큰 만족감 느낌, 음악을 임대하거나 통제권을 잃고 산업이 밀어주는 곡을 스트리밍하거나 광고를 다루고 싶지 않은 사람들이 확실히 자신뿐만이 아닐 것이라는 생각, 직접 애플리케이션을 개발하는 사례를 보면 멋짐 느낌
- 기술은 이미 몇 년 전부터 충분히 따라왔지만 내가 적합하지 않은 포맷을 고집하고 있을 뿐이라는 의견, 좋은 재인코딩을 활용하면 음질 차이를 느낄 수 없을 만큼 투명한 음질로 음악 전부를 훨씬 작은 용량에 담을 수 있음, 백업용으로 데스크탑에 FLAC 파일을 두는 방식 추천
- 내가 정말 수집을 잘하고 있는 사람이라는 칭찬, 본인의 음악 컬렉션은 FLAC/APE/ALAC/WavePack 같은 무손실 포맷이 25% 정도이고 용량이 3TB가 넘어감, 이런 이유로 이동하면서 음악을 듣는 데 어려움이 있음 — 어떤 음악을 모바일 기기에 옮길지 미리 선택하기 힘든 상황 공유
- 안드로이드에서 앨범 커버나 타이틀 정보가 제대로 반영되지 않거나 랜덤하게 바뀌는 문제를 계속 겪음, 이것이 안드로이드 버그로 보이는데 혹시 해결 경험이 있는지 궁금증 전달
- 나도 FLAC만으로 개인 컬렉션을 모으고 있는데 아직 25년은 안 됨, 1TB를 넘었고 Navidrome 서버와 Symfonium 클라이언트를 사용하면서 매우 만족하는 중, 2TB microSD 카드가 요즘 나오기 시작해서 가격이 더 내려가면 아마 장만할 예정
- 예전 winamp 시절부터 음악을 들었고, 지금도 스트리밍 시대에도 로컬 음악 라이브러리를 폴더별로 정리해서 사용 중, 나도 다른 댓글러들처럼 오프라인 음악 감상을 위해 직접 올드스쿨 음악 플레이어를 취미로 제작함, 1페이지짜리 html/js 앱이고 전체 키보드 제어와 간단한 큐(재생목록) 기능이 있음, https://nobsutils.com/mp 구경 추천
- 나도 27년 전부터 winamp UI가 정말 훌륭했다고 생각하는 사람, 폴더별 파일 모음과 전체 랜덤 재생, 특정 디렉토리만 재생하는 단순함이 핵심 강점임 강조
- 직접 만든 앱이 정말 잘 동작한다는 피드백 제공
- 나에게는 foobar2000이 최애 음악 플레이어였음, 현재는 Cog 앱으로 대체 사용
- 내가 직접 웹앱을 개발해서 전체 앨범을 들으면서 기기를 바꿔가며 중단한 위치에서 계속 들을 수 있는 기능을 구현함, 앨범을 처음부터 끝까지 들으면서도 YouTube Music 같은 서비스는 재생 위치 기억을 제대로 못 하거나 기기 전환이 불편함을 경험, 내가 만든 웹앱은 URL 붙여넣기만 하면 yt-dlp로 서버에 다운로드되고 거기서 스트리밍 가능, 항상 재생 위치를 기억해서 자동차에서 듣던 위치를 그대로 직장 노트북에서 이어듣기 가능, NTS Radio 등 다른 소스 믹스 추가도 굉장히 잘됨
- YouTube Music에서 큐 저장과 기기 간 원활한 전환이 안 되는 것에 공감, 개발자가 만든 웹앱을 한 번 직접 써보고 싶음
- 기사에서 물리적 기기뿐 아니라 이를 관리하고 재생하는 소프트웨어 이야기도 다뤘길 바람, 몇 년 전 10살 아들에게 mp3 플레이어를 사주고 싶었는데 적합한 제품이 거의 없어 충격 받음, Apple이 iPod을 단종하며 시장에 큰 빈 공간이 생겼지만 아직 누구도 제대로 채우지 못한 상황, iPod shuffle(USB 스틱 형태)이 내가 써본 최고의 mp3 플레이어였는데 작고 플러그인식이고 배터리도 오래가서 만족스러웠음, 화면 없이 셔플만 되는 콘셉트도 오히려 장점, 이런 단순한 기기도 현재는 하드웨어 시장에서 복제되지 않음, 소프트웨어/DRM 문제로 받아들이는 사람도 있지만, 아쉽게 생각하며 음악 재생만 해주는 저렴하고 휴대성 좋은 기기가 있었으면 하는 바람
- 주요 변화의 원인은 iPod의 사라짐이 아니라 Spotify와 스마트폰의 보급이라고 생각, 이 둘이 시장 대부분을 차지하며 다른 선택지를 모두 밀어냈다는 의견
- Fiio에서 해당 카테고리의 상품 여러 개를 내놓고 있다는 정보 공유, 예시1 예시2
- 하드웨어나 소프트웨어 문제가 아니라 수요 문제라고 생각함, 중국 제조사에서 Mini iPhone 16, Mini S24처럼 스마트폰의 기능을 담고 음악 재생도 되는 미니 기기를 $50~$100에 판매 중, 대부분의 부모가 mp3 플레이어보다는 이런 기기들을 자녀에게 구매할 가능성이 높고, 14세가 될 때까지 휴대폰을 안 주려는 부모가 많지 않아 이에 맞춰 시장 수요가 형성됨을 지적
- Sony에서 아직도 "walkman" 브랜드로 좋은 플레이어를 계속 내고 있음, 공식 링크 10살 아이에게는 다소 비쌀 수 있으니 중고로 이베이에서 구입 추천
- SanDisk Clip 같은 mp3 플레이어가 집 어딘가에 있을 것 같다는 데서 소환된 추억
- 이 글 재미있게 읽었고 아직 다 읽지는 못함, 개발자가 작고 세밀한 결정들을 내리는 과정과 그 배경을 읽는 부분이 좋음, 거의 모든 음악 앱에서 UX와 레이아웃이 비슷하게 보여서 나는 항상 이 모든 음악 앱들과 '권투'하는 느낌, 새로운 시도에 도전하는 사람들에게 응원
- 나는 여전히 Apple Music 앱에서 로컬 파일만 사용함, Apple Music 스트리밍 서비스를 꺼두고 macOS의 Apple Music 앱에 모든 음원을 로드한 뒤, 폰을 노트북에 연결해서 2007년처럼 동기화해서 씀, 내 음악이 자주 바뀌지 않기 때문에 이 방식이 문제없으며, 유선 동기화에서 느껴지는 향수도 동시에 가짐
- iTunes로 자동 wi-fi 동기화도 여전히 잘 작동함을 언급
- "혁신적인 IT 기업이 왜 민주적 애플리케이션 개발에 오히려 장애물을 두는지"라는 의문에 대해, Disney 전 CEO Michael Eisner의 인용문을 들어, 결국 기업의 본질은 이윤 추구이고, Apple은 혁신적이거나 민주적인 회사가 아니라 수익 추구 성향임을 주장, 더 많은 수익을 보장하지 않는 한 개발자의 진입장벽 완화나 민주적 개방은 공식 스토어의 '황금 거위' 수익을 포기하는 셈, 즉 이익을 최우선하는 논리 강조
- 오프라인 음악 라이브러리를 보유한 Android 사용자를 위해 Musicolet을 강력 추천, 완벽하게 동작함
- Plex, Jellyfin, WebDAV, SMB 등 광범위한 지원이 가능한 Symfonium도 매우 훌륭함 강조
- 깊이 있는 기술 분석을 재미있게 읽었고, React Native에서 SwiftUI로 전환하며 네이티브 코드가 iCloud 접근 및 최적화에서 얼마나 더 쉬워지는지 실감, SQLite FTS5 검색 트릭도 인상적이어서 내 라이브러리 앱에 참고할 생각임
- Swift를 처음엔 어려워해서 피했지만, 후에 async/await이 추가되면서 동시성 코드 작성이 쉬워졌다는 주장에 동의하지 않음, async가 코드를 작성하는 데는 편의성을 주지만, 규모가 커지면 코드 흐름과 동시성 파악이 훨씬 더 어려워지는 경험, 풀리지 않은 문제가 있을 때 green lightweight threads 같은 대안이 있다고 생각, 장기적으로는 async 기반이 오히려 유지 비용을 높일 수 있다는 우려
- 동시성 컨셉 자체보다는 async/await 추상화의 한계가 더 문제라고 봄, 좋은 동시성은 코드 확장 시 더 이해하기 쉽고 관리되도록 해야 하며, 프로세스/서비스 중심 캡슐화가 큰 이점이라는 생각
- 본인의 단순 음악 플레이어 목적이라면 async로 인한 복잡도 증가는 거의 문제되지 않을 듯함