live · service
IntervalMate
인터벌 러닝 / 트레이닝 타이머. 워치 진동, Live Activity, 백그라운드 오디오 — 운동 중 화면을 보지 않아도 다음 신호가 손목과 귀로 도착합니다.
서비스 소개
IntervalMate 는 인터벌 러닝 / 인터벌 트레이닝을 하면서 — 화면을 보지 않고도 다음 단계로 넘어가는 순간을 정확히 알 수 있게 만드는 타이머입니다. 한 세션은 짧게는 20초, 길게는 30분 — 그 사이에서 사용자가 핸들 / 폰을 꺼내지 않고도 운동에 집중할 수 있도록 워치 진동, Live Activity, 백그라운드 오디오 신호를 한꺼번에 보냅니다.
iOS 앱은 SwiftUI + MVVM 으로 구현하고, Watch 앱과 Widget / Live Activity 익스텐션을 함께 묶어 한 번 시작하면 모든 표면에 동기화됩니다. 서버는 PHP 로 가볍게 짜고, APNS 푸시 / Live Activity 업데이트는 별도 sandbox / production 엔드포인트로 분기. 데이터(루틴 / 챌린지 / 워크아웃 로그 / 마일리지)는 MySQL 에 저장합니다. 서비스 자체는 다른 서버에서 운영 중이라 jiny.shop 도메인은 따로 두지 않았습니다.
- 인터벌 타이머여러 단계의 운동 / 휴식을 시간으로 쪼개 자동 진행. 루틴 템플릿으로 저장 가능.
- 워치 앱 + 진동아이폰 없이도 워치 단독 진행. 단계 전환마다 손목으로 진동 신호.
- Live Activity잠금화면 / 다이내믹 아일랜드에서 남은 시간이 실시간으로 갱신됩니다.
- 백그라운드 오디오음악 / 팟캐스트 와 섞이는 형태로 단계 전환음을 재생.
- 위젯오늘의 챌린지 / 다음 운동을 홈 화면에 노출. 워치용 위젯도 함께.
- 루틴 공유본인이 만든 루틴을 코드로 공유 → 친구 / 동료가 같은 인터벌로 운동 가능.
- 챌린지 · 마일리지일정 기간 동안 함께 채우는 챌린지, 그 보상으로 쌓이는 마일리지.
- 다국어한국어 / 영어 — 워치 화면까지 로케일 반영.
Architecture
다른 서비스들과 달리 PHP 기반의 가벼운 서버를 별도 호스팅에서 운영합니다. APNS 와 Live Activity 푸시는 sandbox / production 엔드포인트를 분리해 호출.
Behind the scenes
만들면서 가장 힘 주었던 두 가지 — 화면을 보지 않게 만드는 일, 그리고 실제 운동 환경에서 정확히 동작하게 하는 일.
-
01
화면을 보지 않게 만드는 일
인터벌 타이머의 진짜 가치는 "사용자가 화면을 안 보게 만드는 데" 있습니다. 그래서 단계 전환을 알리는 신호를 세 곳에 동시에 띄웠습니다 — 워치 진동(WatchKit), 잠금화면 / 다이내믹 아일랜드(Live Activity), 그리고 백그라운드 오디오. 한 시점의 신호가 어느 하나라도 늦으면 사용자는 멈춰서 폰을 꺼내야 했고, 그게 운동 흐름을 끊어버렸습니다. 특히 Live Activity 는 한 번 설정하고 끝이 아니라 — 매 단계마다 ActivityKit 으로 갱신을 보내야 하고, 그 갱신이 누락되면 잠금화면의 시간이 멈춰 보입니다. "쌓이는 문제" / "업데이트 안 되는 버그" 같은 초기 커밋들은 모두 이 과정의 흔적입니다.
-
02
백그라운드 카운트는 다른 게임이다
앱이 활성 상태일 때는 단순한
Timer한 개로 되는 일이, 백그라운드로 들어가는 순간부터 완전히 다른 문제가 됩니다. iOS 는 일정 시간 후 타이머를 멈추기 때문에 "절대 시각" 으로 만료 시점을 박아두고, 복귀 / 워치 깨우기 / 백그라운드 오디오 / APNS 알림 중 어느 경로로든 도착하게 만들어야 했습니다. 서버에서는 단계 전환 시점에 맞춰 무음 푸시를 쏘아 워치를 깨우는 방식까지 동원했고, sandbox / production APNS 의 차이로 디버그 단계에서 한참 헤맸습니다. "타이머 하나" 처럼 보이는 기능 뒤에 — 결국 모바일 OS 의 백그라운드 모델 전체와 정면으로 마주해야 한다는 걸 만들면서 배웠습니다.
Screenshots
카드를 누르면 큰 이미지로 볼 수 있습니다.
More
지나온 버전과 앞으로 들어갈 항목을 별도 페이지에서 확인할 수 있습니다.