하루 만에 만든 앱이 먼저 나갔다
아내의 단식 기록을 도우려고 토요일 하루를 들여 만들었다. 다음날 승인이 났고, 하루 먼저 검수를 넣은 핫딜시트를 앞질러 먼저 올라갔다.

아내가 단식을 하는데 기록을 메모장에 적고 있었다. 체중이며 혈압이며 그날 컨디션이며를 한 줄로 죽 늘어놓는 식이라, 지난번과 비교가 되지를 않았다.
마침 토스 미니앱을 하나 만들어보던 참이라 구조는 이미 손에 있었다. 토요일 하루를 통째로 넣어 만들었고, 그날 저녁에 검수를 넣었다.
다음날 승인이 났다. 핫딜시트를 하루 먼저 검수에 넣어뒀는데 이쪽이 앞질러 올라갔다.
이름은 단식수첩이다.

한 문장이 구조를 바꿨다
만들어서 보여줬더니 첫마디가 이랬다.
기록 종료 후 새로 기록할 때 기존 기록과 구분이 안 돼.
맞는 말이었고 원인은 데이터 모델에 있었다. 진행 중인 단식을 하나만 들고 있다가 끝나면 그냥 버리는 구조였다. 그러니 그 단식이 있었다는 사실 자체가 사라지고 기록만 한 뭉치로 남았다.
끝난 단식을 버리지 않고 쌓기로 했다. 그런데 기록에 단식 ID를 붙이지는 않았다. 이미 쌓인 기록에는 그 ID가 없어서 마이그레이션을 해야 하기 때문이다.
대신 시각만으로 붙였다. 기록 시각이 단식 시작과 종료 사이에 있으면 그 단식의 기록이다.
이게 뜻밖의 이득이 됐다. 기능이 생기기 전에 쌓인 기록도 알아서 제자리를 찾아갔고, 나중에 “단식 시작 시각을 과거로 당기기”를 넣었더니 그 사이 기록이 저절로 편입됐다. 따로 만든 기능이 아니다.

두 번 되돌렸다
처음엔 단식 선택을 가로 칩으로 만들었다. 바로 지적이 들어왔다. 단식 횟수가 많아지면 탭이 무한으로 늘어나는 것 아니냐고.
세로 목록으로 갈아엎었다. 개수가 늘어도 스크롤로 감당된다는 게 본래 이유였는데, 목록에서 각 단식의 기간과 기록 수와 체중 변화를 한 번에 훑게 된 게 오히려 더 컸다.
두 번째 지적은 수치였다. 체중과 혈압과 심박수와 혈당이 전부 같은 크기, 같은 검정색이었다. 저혈당 66과 멀쩡한 혈압 112/72가 시각적으로 동급이었다.

아이폰 건강 앱을 참고하라기에 열어봤는데, 거기서 가져온 건 “사용자가 중요한 걸 고르게 한다”가 아니었다. 볼 이유가 있는 것만 크게 보여준다는 쪽이었다.
그래서 고르게 하는 대신 상태가 스스로 드러나게 했다. 참고 범위를 벗어난 값만 색을 얻고 앞으로 정렬된다. 타일 배경이 색을 지니까 글자색만 바꿨을 때와 훑는 속도가 확연히 달랐다.

색 이름을 함께 적은 건 접근성 때문이다. 색만으로 정보를 전달하면 안 되니까 체중 · 주의처럼 단계를 글자로도 남겼다.
그래프에서는 한 번 더 데였다. 지표마다 다른 색을 줬더니 혈당 선이 주황이라 이상치 점이 그 안에 묻혔다. 경고색은 예약어라는 걸 그때 알았다. 선은 브랜드색 한 벌로 통일하고 주황과 빨강은 이상치 전용으로 비워뒀다.
브라우저에서 되던 게 실기기에서 다 깨졌다
이게 하루의 절반을 먹었다.
iOS 스와이프 뒤로가기가 안 먹었다. 상세 화면을 React 상태로만 띄워서 히스토리에 아무것도 안 쌓였다. 스와이프는 웹뷰 히스토리를 되돌리는 동작인데 되돌릴 게 없었던 것이다.
pushState로 얹어서 고쳤더니 이번엔 토스 상단의 <가 미니앱을 통째로 닫았다. 그건 히스토리를 안 건드리기 때문이다. 그래서 backEvent를 구독해 history.back()으로 흘려보냈다. 다만 구독하면 기본 동작인 종료가 막히므로, 떠 있는 화면이 있는 동안에만 구독한다. 첫 화면에서 뒤로가기는 나가는 게 맞으니까.
그리고 상세 화면에 내가 만든 < 버튼은 지웠다. 출시 가이드에 토스 뒤로가기와 자체 뒤로가기가 동시에 보이면 안 된다고 적혀 있더라.
삭제가 처음부터 동작하지 않았다. 아내가 최근 기록을 지웠는데 반영이 안 된다고 했고, 파보니 원인이 두 개였다. 디자인 시스템의 목록 행 오른쪽 슬롯에 넣은 아이콘 버튼이 클릭을 아예 안 받았고, 확인 다이얼로그는 기록 카드에서만 뜨지 않은 채 멈춰 있었다. 설정 화면에서는 같은 훅이 멀쩡히 동작했다.
휴지통 아이콘을 걷어내고 텍스트 버튼으로 바꿨다. 확인도 다이얼로그 대신 두 번 눌러 지우는 방식으로 갈았다. 남의 레이어에 기대지 않는 쪽이 결국 편했다.
덤으로 하나 더 나왔다. 지워지지 않는다던 그 카드는 사실 값이 하나도 없는 기록이었다. 아무 칸도 안 채우고 저장하면 그대로 저장되고 있었다.
마지막은 좀 황당했다. 메모 여백이 아무리 고쳐도 안 먹었다. 실측하니 padding이 0이었다. 디자인 시스템의 텍스트 컴포넌트에 className을 주면 emotion이 나중에 주입돼서 같은 특이성에서 이겨버린다. 감싸는 div에 클래스를 주니 바로 됐다.
여하튼 브라우저에서 됐다고 된 게 아니다. 웹뷰와 디자인 시스템과 네이티브 브리지가 각자 규칙이 따로 있고, 그건 실기기에 올려봐야 나온다.
분석을 붙이면서 선을 하나 그었다
설정 화면에 “기록은 이 기기에만 저장돼요”라고 써뒀다. 그래놓고 분석 이벤트를 심으려니 마음에 걸렸다.
체중·혈압·심박수·혈당 값과 경고 발생 여부와 메모는 어떤 이벤트에도 싣지 않기로 하고, 그 문장을 주석으로 코드에 박아뒀다. 남기는 건 화면 진입과 단식 시작·종료, 그리고 기록을 저장할 때 몇 칸을 채웠는지 정도다.
배포하고 콘솔에서 실제 수집된 파라미터를 확인했다. 수치는 한 건도 올라가지 않았다.
그러다 이상한 걸 하나 봤다. 단식 시작 이벤트만 파라미터가 비어 있었다. 이 로그만 직후에 화면을 닫는데, 같은 tick에서 이동하니 브리지가 파라미터를 싣기 전에 끊겼던 것이다. 화면 이동을 한 tick 미루니 정상으로 붙었다.
앱인토스에서 배운 것들
검수를 네 번 넣었고 번들을 아홉 개 올렸다. 그러면서 알게 된 것 몇 가지.
- 검수를 한 번 취소하면 그 번들은 재요청이 영구 불가다. 새로 빌드해야 한다
- 승인이 곧 출시가 아니다. 라이브 전환은 콘솔 웹에서만 된다
- 스토어 스크린샷은 세로 636×1048, 가로 썸네일 1932×828이고 1px만 달라도 거부된다
- 앱정보 갱신은 전체 페이로드를 다시 보내는 방식이라, 기존 이미지 URL을 빠뜨리면 그 이미지가 사라진다
스크린샷 규격은 아이폰 비율과 아예 다르다. 그래서 390px 폭으로 렌더해서 캡처한 뒤 규격 캔버스에 배경과 함께 합성했다. 이 글의 커버 이미지를 그렇게 만들었다.
남은 것
이 앱은 의료기기가 아니다. 참고 범위를 벗어났다는 안내일 뿐 진단이 아니고, 아무 표시가 없다고 안전한 것도 아니다. 앱 안에도 그렇게 못박아 뒀다.
입력값 범위 검증은 아직 없다. 심박수에 1245를 넣어도 그냥 들어간다. 다음에 할 일이다.
토스 미니앱 목록에는 80위로 올라 있다. 위아래로 색칠하기와 스트레스 쉑쉑 같은 것들이 있다.

아무래도 하루 만에 만든 것이라 빠뜨린 게 더 있겠지. 여하튼 아내가 이번 단식에 실제로 쓰는지가 이 앱의 유일한 지표다. 안 쓰면 그걸로 끝인 거고.
핫딜시트는 정산 승인까지 다 났고 광고도 붙였는데, 하루 먼저 넣은 검수가 아직 안 끝났다. 순서가 이렇게 될 줄은 몰랐다.