터미널에 업로드 완료가 찍혔다. App Store Connect 의 TestFlight 탭에는 빌드가 "처리 중" 으로 올라왔다. 앱 하나를 아이폰에 올리는 길이 처음으로 끝까지 이어진 순간이었다.
그 옆 탭을 열자 빈칸이 보였다. 연령 등급, 앱이 수집하는 개인정보, 스크린샷, 심사 정보, 가격. TestFlight 는 내 아이폰까지 가는 길이고, App Store 는 남의 아이폰까지 가는 길이다. 두 번째 길에는 Apple 이 묻는 게 훨씬 많았다.
TestFlight 는 아직 심사가 아니다
빌드 번호를 겹치지 않게 매기는 일은 1편에서 정리했다. 업로드 스크립트가 App Store Connect 에 가장 높은 번호를 묻고, 방금 올라가 아직 보이지 않는 번호는 맥에 남은 archive 로 보충한다. 번호 걱정이 사라지자 그다음 질문이 보였다. 올린 빌드는 누구에게 가는가.
TestFlight 에는 두 종류의 테스터가 있다. App Store Connect 에 사용자로 등록된 내부 테스터는 빌드 처리가 끝나면 바로 설치할 수 있다. 그 밖의 외부 테스터에게 보내려면 베타 앱 설명과 피드백 이메일을 채우고 베타 앱 심사를 따로 받아야 한다. 처음 클라우드 빌드 서비스를 쓸 때는 업로드 설정 하나가 외부 테스터용 베타 심사 제출까지 같이 해 버렸다. 맥 미니 스크립트로 옮기면서 업로드까지만 하게 바꿨다. 지금 필요한 건 내부 테스트라, 심사는 내가 버튼을 누를 때만 일어나야 한다.
업로드할 때마다 걸리던 질문도 미리 답해 두었다. 수출 규정, 곧 앱이 암호화를 쓰느냐는 질문이다. 이 앱들은 HTTPS 말고 따로 암호화를 하지 않는다. Info.plist 에 ITSAppUsesNonExemptEncryption 을 false 로 넣어 두면 빌드마다 묻지 않는다. 게임은 Unity 빌드 후처리에서 같은 값을 넣는다.
버전 번호도 손봤다. App Store Connect 는 새 앱에 1.0 버전을 만들어 둔다. A/S 앱은 이걸 Android 의 versionName 과 같은 숫자로 바꿨다. 같은 앱이 두 스토어에서 다른 버전을 달고 있으면 문의가 들어왔을 때 어느 앱 이야기인지 헷갈린다. 게임은 1.8 버전으로 첫 빌드를 올렸는데, 그 빌드에는 테스트 광고가 들어 있다. 내부 테스트용이라 일부러 그렇게 했다. 업로드 스크립트도 기본은 테스트 광고 빌드이고, 옵션을 줘야 실제 광고 단위가 들어간 심사용 빌드를 만든다.
연령 등급은 앱이 여는 주소로 정해졌다
연령 등급은 설문이다. 폭력, 성적 내용, 도박, 공포, 욕설이 있는지 단계별로 고른다. 업무용 A/S 웹 서비스 앱은 고민할 게 거의 없었다. 전부 "없음" 이다.
한 항목에서 잠깐 멈췄다. 사용자가 제한 없이 웹에 접근할 수 있느냐는 질문이다. WebView 앱이니 "예" 같았다. 그런데 이 앱은 브라우저가 아니다. 2편에서 짠 URL 판단대로 앱 도메인만 앱 안에서 열고, 다른 사이트 링크는 Safari 화면으로 넘긴다. 주소를 입력할 칸도 없다. 그래서 "아니요" 로 답했다. 사용자끼리 콘텐츠를 공개로 주고받는 기능이나 채팅도 없다. 결과는 4+ 다. 연령 등급이 앱의 내용만이 아니라 앱이 어디까지 열 수 있느냐로도 정해진다는 게 재미있었다. 라우팅 규칙을 좁게 잡아 둔 덕을 여기서 봤다.
수집하는 개인정보를 스스로 신고한다
가장 오래 걸린 칸은 앱 개인정보였다. App Store 제품 페이지에 뜨는 개인정보 라벨이 여기서 나온다. 앱이 어떤 데이터를 수집하는지, 어디에 쓰는지, 사용자와 연결되는지, 추적에 쓰는지를 항목마다 답한다.
먼저 큰 질문 둘. 데이터를 수집하는가, 예. 추적하는가, 아니요. 광고 식별자도 안 쓰고 다른 회사 데이터와 결합하지도 않는다. 그다음은 A/S 앱이 실제로 다루는 것을 하나씩 짚었다.
| 데이터 유형 | 근거 |
|---|---|
| 연락처 정보(이름·이메일·전화번호) | 계정 정보 |
| 정확한 위치 | 처리 저장 때 작업 위치 1회, 동의한 경우만 |
| 사진 또는 비디오 | 현장 사진 첨부 |
| 기타 사용자 콘텐츠 | 접수·처리 내용 입력 |
| 사용자 ID | 로그인 아이디 |
| 기기 ID | 푸시용 Firebase 설치 ID(FID) |
연락처 셋을 따로 세면 여덟 가지다. 모두 용도는 "앱 기능", 사용자와 연결됨, 추적 안 함이다. 기기 ID 칸에서 4편의 결정이 돌아왔다. 등록 토큰 대신 보내기로 한 FID 는 계정과 함께 서버에 저장되니 기기 식별자로 신고하는 게 맞다. 진단 데이터는 넣지 않았다. 서버 접속 로그는 있지만 앱이 따로 충돌 보고나 사용 통계를 보내지 않는다.
신고하기 전에 개인정보 처리방침부터 고쳤다. 방침은 "웹과 Android 앱" 기준으로 쓰여 있었다. iOS 앱은 카메라와 사진 보관함 권한을 앱이 직접 요청하고, 푸시 식별자도 FCM 토큰이 아니라 FID 다. 권한 표에 iOS 행을 넣고, 권한을 끄는 방법을 iPhone 설정 경로로 적고, 푸시 항목에 FID 를 더해 먼저 게시했다. 라벨과 방침이 서로 다른 말을 하면 그게 더 문제다. 앱 개인정보 칸은 그 방침 주소를 가리킨다.
가격은 무료, 판매 국가는 대한민국만 골랐다. Apple 실리콘 Mac 에서도 iPhone 앱을 쓸 수 있게 하는 항목은 기본값으로 켜져 있었고 그대로 두었다.
스크린샷 여섯 장과 빌드 하나
스크린샷은 iPhone 6.9형 크기가 필수다. 세로 1320×2868 같은 규격으로 3장에서 10장까지 올린다. 작은 화면용은 이걸 줄여서 쓴다.
업무 앱이라 실제 화면을 찍으면 고객 이름과 연락처, 주소가 나온다. 시뮬레이터를 6.9형 기기로 띄우고, 화면에 보이는 고객 정보를 예시 값으로 바꾼 뒤 찍었다. 대시보드, 접수 목록, 처리 화면, 조치와 사진 첨부, 푸시 안내, 첨부파일 미리보기까지 여섯 장이다. 순서도 이야기처럼 짰다. 일이 들어오고, 처리하고, 알림을 받고, 자료를 본다. 업로드는 App Store Connect API 로 했다.
심사 정보 칸에는 두 가지를 신경 썼다. 하나는 심사용 계정이다. 관리자가 계정을 발급하는 업무 앱이라 앱 안에 가입이 없다. 실제 고객 정보가 보이지 않는 계정을 따로 준비해 App Store Connect 에만 적었다. 다른 하나는 가이드라인 4.2, 최소 기능 조항이다. 웹사이트를 감싸기만 한 앱은 거절될 수 있다. 심사 메모에 앱이 네이티브로 하는 일을 적었다. 푸시와 알림을 눌렀을 때의 화면 이동, 오프라인 오류 화면, 첨부파일 미리보기와 공유, 당겨서 새로고침과 밀어서 뒤로 가기, 위치 기록. 이 연재 내내 만든 것들이 이 메모 몇 줄이 됐다. 특정 조직 전용 앱으로 보고 반려되면, 같은 앱으로 미등록 배포를 신청할 수 있다는 것도 알아 두었다.
마지막으로 빌드를 골랐다. TestFlight 에 올라간 빌드 중 최종 아이콘이 들어간 것이다. 이제 남은 건 "심사에 추가" 버튼 하나다.
아직 심사 전이다
이 글을 쓰는 지금, 세 앱 중 App Store 에 나간 앱은 없다. A/S 웹 서비스 앱은 모든 칸을 채우고 빌드까지 골라 제출 버튼 앞에 있다. 식단서랍은 푸시 서버 변경과 Apple 로그인을 넣었고, 스토어 문안과 개인정보 신고, 스크린샷을 같은 방식으로 채울 차례다. 「오늘 누가 쏘냐?」는 첫 TestFlight 빌드가 처리되고 있다. 앱마다 TestFlight 실기기 확인표도 남아 있다. 앱이 켜져 있을 때와 백그라운드와 완전히 꺼졌을 때 알림이 오는지, 카카오와 Apple 로그인이 앱 안에서 끝까지 이어지는지, 사진 첨부와 다운로드가 되는지.
Android 앱을 iPhone 으로 옮기는 일은 화면을 옮기는 일인 줄 알았다. 해 보니 화면은 거의 그대로 왔다. 바뀐 건 그 앞뒤였다. 푸시는 서버가 보내는 메시지 모양부터 바꿔야 했고, 로그인은 웹에 버튼을 하나 더 달아야 했고, 출시는 앱이 무엇을 모으는지 스스로 신고하는 일이었다. 같은 웹을 담는데도 플랫폼마다 그 웹에 요구하는 것이 달랐다.
심사 결과는 아직 모른다. 반려 메일이 올 수도 있다. 그 이야기는 결과가 나오면 따로 쓴다. 「Android 앱을 iPhone으로」 연재는 여기서 마친다.