개발 slecs

수면 앱에 오프라인 ambient BGM과 트랙 선택 UI 추가

목차

수면 관련 앱에 ambient BGM 트랙들과 selector UI를 붙였다.

왜 이 타이밍에 오디오 에셋을 직접 번들링했나

사실 처음부터 번들링 방식으로 가려던 건 아니었다. 스트리밍 방식(CDN에서 끌어오기)도 테이블 위에 있었고, 팀 내에서도 의견이 갈렸다. 그런데 bedtime 기능 특성상 오프라인 환경에서도 끊김 없이 재생이 보장돼야 한다는 점이 결정타였다. 잠들려는 순간에 버퍼링이 걸리면 UX가 박살 난다. 당연한 얘기 같아도 실제로 이 트레이드오프를 팀 전체가 공유하고 결정하는 게 중요했다.

번들 사이즈 증가는 명확한 단점이었다. 그래서 트랙 선정 기준도 나름 엄격하게 잡았다.

  • rain.mp3 — 백색소음 계열, 수면 유도 효과 검증된 정석 트랙
  • fire.mp3 — 모닥불 크래클링, rain과 함께 가장 수요 많은 ambient 카테고리
  • birds.mp3 — 이른 아침 기상 쪽 UX와 연결될 수 있는 자연음
  • bell.mp3 — 명상/마음챙김 계열 톤
  • calm.mp3 — 그 외 채울 수 없는 분위기 레이어

8 트랙 전체를 한 번에 커밋한 건 에셋 관리 측면에서 일부러 묶었다. 반쪽짜리 상태로 feature branch에 살아있는 게 더 리스크다.

selector chips UI 설계 포인트

chips 방식을 택한 이유가 있다. 탭이나 드롭다운 대비 현재 선택 상태가 시각적으로 명확하고, 한 화면에서 전체 옵션을 한눈에 볼 수 있다. bedtime 화면은 사용자가 어둠 속에서 반쯤 눈 감고 조작하는 상황이라 터치 영역과 피드백이 특히 중요하다.

// selector chip 구성 예시 패턴
const AMBIENT_TRACKS = [
  { id: 'rain',  label: '빗소리', src: 'rain.mp3' },
  { id: 'fire',  label: '모닥불', src: 'fire.mp3' },
  { id: 'birds', label: '새소리', src: 'birds.mp3' },
  { id: 'bell',  label: '벨',    src: 'bell.mp3' },
  { id: 'calm',  label: 'Calm',  src: 'calm.mp3' },
  // ...
];

// chip 선택  이전 트랙 정지   트랙 로드  재생
function selectTrack(id) {
  stopCurrent();
  loadAndPlay(AMBIENT_TRACKS.find(t => t.id === id));
  setSelected(id);
}

트랙 전환 시 크로스페이드 처리 여부도 잠깐 논의됐는데, 이건 일단 심플하게 끊고 새로 시작하는 방식으로 갔다. 크로스페이드는 구현 복잡도 대비 bedtime 컨텍스트에서 체감 차이가 크지 않다는 판단이었다.

CREDITS.md — 에셋 라이선스 관리

app/assets/audio/ambient/CREDITS.md 이 파일이 사실 이번 작업에서 제일 신경 쓰인 부분이다. 오디오 에셋을 번들에 포함할 때 라이선스 처리를 흐지부지하고 넘어가는 경우를 꽤 봤는데, 그게 나중에 법무/컴플라이언스 이슈로 튀어오르면 수습이 훨씬 힘들다. 초기에 CREDITS 문서 하나 제대로 만들어두는 게 팀 전체 리스크 관리 차원에서 맞다.

파일 용도 비고
CREDITS.md 출처·라이선스 명시 필수
rain.mp3 빗소리 ambient -
fire.mp3 모닥불 크래클링 -
birds.mp3 자연음/새소리 -
bell.mp3 명상 톤 -
calm.mp3 복합 calm -

앞으로 트랙이 추가될 때도 CREDITS 업데이트를 PR 체크리스트에 박아두는 게 맞다고 생각해서 팀 내 PR 템플릿에도 항목 추가했다. 에셋 종류가 뭐든 외부 소스에서 가져오는 거라면 항상 같은 기준을 적용하는 게 낫다.

회고

사실 이 피처 자체가 코드보다 결정의 연속이었다. 번들 vs 스트리밍, 트랙 수와 용량, chip vs 다른 UI 패턴, 크로스페이드 여부. 각각은 작은 결정이지만 어느 하나 근거 없이 넘어간 게 없다. 이런 판단들을 코드 리뷰 때 설명할 수 있어야 하고, 팀원이 나중에 이 코드를 수정할 때도 왜 이렇게 짰는지 이해할 수 있어야 한다.

8 트랙 + selector 조합이 실제로 사용자 수면 경험에 얼마나 기여할지는 지표를 봐야 알겠지만, 적어도 이 작업이 왜 이 방식으로 들어갔는지는 팀 안에서 충분히 정리됐다.

끝.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.