콘텐츠로 이동

운동 기록 앱을 45일 만에 App Store에 올리기까지 — 혼자, 그리고 여섯 에이전트와

운동 기록 앱을 여러 개 써 봤습니다. 결론은 늘 같았습니다 — 운동은 하는데 기록이 안 쌓입니다. 세트가 끝나면 무게·횟수를 타이핑해야 하고, 그 폼을 여는 순간 이미 귀찮습니다. 3일 치를 몰아서 넣다가, 어느 날부터는 안 넣습니다. 기록이 없으니 스트릭도 없고, 스트릭이 없으니 동기도 없습니다.

그래서 요구사항 1번을 이렇게 적었습니다. "3초 안에 기록한다." 이 한 줄이 이후 45일의 모든 결정을 심판했습니다. 어떤 기능이든 이 줄을 해치면 버렸습니다.

7월 20일, 백로그 첫 줄을 썼습니다. 이 글은 그날부터 App Store 출시, 그리고 출시 후 2주까지의 기록입니다. 기술 개념의 정리는 위키의 출시 여정 페이지에, 여기에는 사람이 내린 결정과 넘어진 자리만 남깁니다.

운동기록 앱의 홈·카운트·수량 조정 화면 — 실제 앱 화면

1. 팀을 먼저 만들었다

혼자 하는 프로젝트지만 혼자 하지 않기로 했습니다. Claude Code에 여섯 역할을 정의했습니다.

역할 하는 일
어드바이저 범위·설계 결정, 문서 정합, "열린 결정" 확정 — 코드는 쓰지 않음
앱 개발자 도메인·엔진·DB·네이티브(워치·헬스)
UI 개발자 화면·디자인 토큰·접근성
QA 리뷰어 머지 전 게이트, 블로커 0건까지 반복
테스터 회귀 시나리오 실행·버그 재현 — 코드는 고치지 않음
릴리스 매니저 버전·체인지로그·제출 Go/No-Go

핵심 규칙은 두 개였습니다. 판단과 구현을 분리한다 — 어드바이저는 코드를 쓰지 않고, 개발자는 범위를 정하지 않습니다. 그리고 게이트를 생략하지 않는다 — 엔진이나 DB를 건드린 변경은 리뷰와 테스트 없이 "완료"라고 보고할 수 없습니다.

하나 더. 문서가 진실이다. 요구사항·개념 모델·아키텍처·스키마 문서를 먼저 쓰고, 코드가 문서와 어긋나면 코드가 아니라 문서부터 고칩니다. 에이전트가 여섯이면 "어디까지가 맞는 건지"를 사람 대신 문서가 답해야 합니다.

하네스 엔지니어링 관점의 정리는 이전 글들 — Karpathy의 하네스, Fable 어드바이저 전략 — 에 있습니다. 이 글은 그것의 실전편입니다.

2. 결정들 — 무엇을 안 하기로 했나

만든 것보다 안 만든 것이 이 앱을 정의합니다.

  • 계정·서버 없음. 모든 읽기·쓰기는 폰 안 DB에서 즉시. 클라우드 없이 완전 동작. 동기화는 있어도 백그라운드.
  • 기록 수정 없음. 기록은 추가만 하고, 삭제는 표시만 남깁니다. 스트릭·진행률·뱃지는 저장하지 않고 기록에서 매번 계산합니다. 저장하는 순간 어긋나기 시작하니까요.
  • 타이머는 하나. 세트 휴식, 루틴 인터벌, 달리기 타이머가 하나의 상태기계를 씁니다. 처음엔 세 개였고, 세 번째를 만들다 멈췄습니다.
  • GPS 지도 없음, 랭킹 없음, 푸시 없음. 전문 러닝 앱과 싸우지 않습니다. 워치로 뛴 기록은 건강 앱에서 가져옵니다.

그리고 8월 초, 가장 큰 결정. "기록은 행위의 산물이다 — 임의 수동 입력은 반칙." 숫자를 타이핑해서 채워 넣는 UI를 전부 지웠습니다. 탭 카운트, 타이머, 센서, 건강 앱 가져오기 — 실제 수행만이 기록을 만듭니다. 불편해 보이지만, 그래서 스트릭이 거짓말을 안 합니다. 이 결정 하나로 화면 절반을 다시 그렸습니다.

팔굽혀펴기 카운트 링 — 화면을 탭할 때마다 +1, 목표에 닿으면 자동 저장. 실제 앱 화면

로드맵도 두 번 뒤집었습니다. 코칭(루틴·햅틱)을 헬스 연동보다 앞으로, 다시 헬스 연동을 워치보다 앞으로. 둘 다 "지금 내가 매일 쓰는 데 뭐가 먼저 필요한가"로 정했습니다.

3. 워치 — 가장 많이 넘어진 곳

애플워치 앱은 SwiftUI로 따로 썼습니다. 폰이 진실이고 워치는 스냅샷 캐시와 미전송 큐만 가진 얇은 클라이언트입니다.

넘어진 기록입니다.

  • 시뮬레이터에서 폰↔워치 세션이 안 붙습니다. 며칠을 썼고, 결국 워치 앱에 시뮬레이터 전용 데모 스냅샷을 내장해 스토어 스크린샷을 찍었습니다.
  • 실기기 설치가 굳습니다. 폰의 Watch 앱 설치와 CLI 설치를 동시에 하면 서로 끊어먹습니다. 워치 재부팅 직후 1분이 골든타임이었습니다.
  • 무료 팀 7일 서명 만료. 일주일마다 앱이 죽었습니다. 유료 개발자 등록으로 끝.

8월 14~16일, 실제 손목에서 써 본 피드백 여섯 건(탭 지연, 나갔다 오면 카운트 유실, 아이콘 크롭…)을 이틀 안에 다 고쳤습니다. "즉시 반영되네"가 그 주말의 가장 좋은 말이었습니다.

4. 8월 17일 — 가장 긴 하루

개념 모델을 확정하고 그에 맞춰 스키마·목표 폼·홈 카드를 전부 재구현했습니다. 같은 날 애플 개발자 등록이 승인됐고, 번들 ID를 전면 교체했고(기존 실기록은 날아갔습니다 — 헬스 가져오기로 복구), App Store Connect에 앱을 만들고 폰 스크린샷 8장을 올렸습니다.

19일, 업로드 반려 두 건 — iPad 멀티태스킹 방향 요건은 iPhone 전용 지정으로, 헬스 플러그인이 쓰기 API를 참조해 미사용이어도 권한 문구가 필수인 것은 문구 추가로 — 을 고치고 19시 54분 심사 제출. 첫 배포였습니다.

5. 출시 후 — 시뮬레이터가 못 보는 것들

1차 심사는 "정보 요청"으로 돌아왔습니다. 기능 위반은 아니고, 무엇을 하는 앱인지 스크린 레코딩과 설명을 달라는 것이었습니다. 콜드 스타트부터 찍은 영상과 일곱 항목 영문 회신으로 21일 승인.

그 뒤 2주가 진짜 개발이었습니다. 시뮬레이터에선 절대 안 보이는 것들입니다.

  • 카운트 중에 화면이 자동으로 잠깁니다. 폰을 내려놓고 탭하는 흐름이 끊깁니다.
  • 큰 글씨 설정에서 버튼 라벨이 두 줄로 꺾여 깨집니다.
  • 세트 하다 나갔다 오면 0부터 시작합니다. 사용자가 원한 "이어하기"는 남은 양을 다시 계산하는 게 아니라 "4/20이 화면에 그대로 보이는 것"이었습니다 — 이 오해 하나로 두 번을 다시 만들었습니다.

세트 모드 휴식 화면 — 아쿠아색 카운트다운, 끝나기 3초 전 진동. 실제 앱 화면

버그 있는 빌드가 심사 중일 때는 취소하고 수정판으로 통합 제출했습니다. 라이브에 버그를 내보내는 것보다 하루 늦는 게 낫습니다. Android는 Play의 테스터 관문(비공개 테스트 후 프로덕션)을 지나는 중입니다.

6. 배운 것 세 가지

  1. "안 만든다"가 가장 비싼 결정이다. 요구사항 1번(3초)과 원칙 6번(행위가 기록을 만든다)이 기능 요청의 절반을 걸러 줬습니다. 걸러진 것들이 이 앱의 정체성입니다.
  2. 에이전트 팀은 문서가 있어야 돌아간다. 여섯 역할이 같은 문서를 읽기 때문에 "그건 어디에 쓰여 있나"가 모든 논쟁의 끝이었습니다. 문서 없이 에이전트를 늘리면 소음만 늘어납니다.
  3. 실기기와 실사용자는 대체 불가. 골든 테스트 수백 개가 통과해도 손에 쥐면 다른 앱입니다. 제출 전 일주일은 시뮬레이터가 아니라 손목과 주머니에 써야 합니다.

홈 화면 — 목표 진행 링과 종목 카드, 카드 탭이 곧 카운트 시작. 실제 앱 화면

앱은 App Store에 무료로 올라가 있습니다(iOS + Apple Watch). 써 보시고 "이건 이상하다" 싶은 게 있으면 알려 주세요 — 지금까지의 개선 대부분이 그렇게 들어왔습니다.

💡 원스의 인사이트: 사이드 프로젝트의 적은 시간이 아니라 범위다

45일이 가능했던 건 빨라서가 아니라 Non-goal 목록이 길어서입니다. GPS·랭킹·서버·수동 입력 — 넣지 않은 것의 목록이 넣은 것보다 길고, 그 목록은 첫 주에 썼습니다.

참고 자료 및 출처