하나의 코드로 두 플랫폼
화면과 흐름은 한 벌로 씁니다. 대신 사용자가 손으로 아는 부분은 갈라 놓습니다. 위 화면에서 플랫폼을 바꿔 보면 헤더 정렬, 뒤로 가기, 탭바, 스위치, 되물음(액션 시트 ↔ 다이얼로그)이 바뀝니다. 이걸 통일해 버리면 "웹을 씌운 앱" 소리를 듣습니다.
지도·블루투스·백그라운드 위치처럼 기기에 깊이 붙는 기능은 네이티브 모듈로 뺍니다. 처음부터 전부 네이티브로 갈 이유는 거의 없습니다.
같은 자리를 둘이 누르면
정원 8명에 마지막 한 자리. 두 사람이 동시에 누르면 화면에서는 둘 다 성공처럼 보입니다. 자리 확정은 서버 한 곳에서 트랜잭션으로 처리하고, 앱에는 Idempotency-Key 를 들려 보냅니다. 네트워크가 끊겨 같은 요청이 두 번 가도 예약은 하나만 생깁니다.
- 낙관적 표시
- 누르는 즉시 예약된 것처럼 보여 주되, 서버가 거절하면 되돌리고 대기 신청을 권합니다.
- 대기 승격
- 앞사람이 취소하면 대기 1번에게 푸시를 보내고 15분만 자리를 잡아 둡니다.
끊겨도 쓸 수 있게
지하 스튜디오에서는 신호가 자주 끊깁니다. 예약·취소·체크인은 큐에 넣고 연결되면 순서대로 보냅니다. 조회 화면은 마지막으로 받은 값을 그대로 보여 주고, 언제 받은 것인지 같이 적습니다.
비행기 모드를 켜고 예약해 보면 위에서 그대로 볼 수 있습니다.
결제는 스토어 밖에서
회원권은 스튜디오에서 실제로 받는 수업입니다. 두 스토어 모두 이런 실물 서비스는 인앱결제 의무 대상이 아니라서, 국내 PG 로 받고 수수료를 아낍니다. 반대로 앱 안에서만 쓰는 이용권이라면 인앱결제를 붙여야 하고, 이 판단이 원가를 크게 바꿉니다.
심사에서 자주 걸리는 것
- 계정 삭제
- 가입이 앱에서 되면 삭제도 앱에서 끝나야 합니다. 메일 문의로 넘기면 반려됩니다.
- 권한 사유 문구
- "카메라 권한이 필요합니다"는 부족합니다. 무엇에 쓰는지 한 문장으로 적습니다.
- 심사용 계정
- 번호 인증이 필요한 앱은 심사자가 못 들어옵니다. 고정 계정과 고정 인증번호를 같이 냅니다.
- 개인정보 항목
- 실제로 수집하는 것만 적습니다. 쓰지도 않는 항목을 적어 두면 그것도 지적받습니다.
버전을 막는 기준
모두를 막는 화면은 마지막 수단입니다. 서버가 minSupported 를 내려 주고, 앱은 그보다 낮을 때만 게이트를 띄웁니다. API 규격이 바뀐 배포에만 씁니다. 그 외에는 "새 버전이 있습니다" 정도로 권하고 넘어갑니다.
이 데모에 없는 것
서버가 없어 모든 것이 브라우저 안에서 돕니다. 새로고침하면 처음 값으로 돌아갑니다. 실제 프로젝트에서는 이 자리에 예약 API, 푸시 발송 서버, PG 연동, 스토어 배포 파이프라인이 들어갑니다.