// blog post
VibeShell 앱스토어 심사에서 10번 거절당한 이야기
거절 알림이 일상이 된 3주
앱스토어 심사 제출 후 하루이틀 지나면 App Store Connect에 알림이 옵니다. 처음에는 두근두근하면서 열었는데, 열 번쯤 되니까 알림만 보고 "아, 또 떨어졌구나" 하게 되더라고요.
VibeShell을 처음 제출한 날부터 최종 승인까지 약 3주, 거절만 10번쯤 받았습니다. 엘스 산책시키면서 폰으로 App Store Connect 앱 열어서 거절 사유 확인하고, 산책 끝나자마자 수정하고 재제출하는 게 루틴이 됐습니다.
거절 사유는 거의 하나였습니다. 터널링 관련.
SSH 터널링은 포그라운드에서만 됩니다
VibeShell의 핵심 기능 중 하나가 SSH 터널링입니다. 원격 서버의 개발 중인 웹페이지를 모바일에서 바로 확인할 수 있게 해주는 기능이에요.
문제는 iOS의 백그라운드 정책입니다. 앱이 포그라운드에 있을 때만 네트워크 연결이 유지됩니다. 사용자가 사파리를 열어서 localhost:3000을 확인하려고 앱을 전환하는 순간, SSH 연결이 끊기고 터널도 같이 죽습니다.
VibeShell에서 터널링 활성화
↓
사파리로 전환 (localhost:3000 확인하려고)
↓
iOS가 VibeShell을 백그라운드로 보냄
↓
SSH 연결 끊김 → 터널 사망
↓
사파리에서 "연결할 수 없음"
터널링 기능이 있는데 실제로는 쓸 수 없는 상황이었습니다.
위치정보로 백그라운드 유지를 시도했습니다
iOS에서 앱을 백그라운드에서 계속 실행하는 방법이 몇 가지 있습니다. 오디오 재생, VoIP, 위치정보 업데이트 등. 이 중에서 위치정보(Background Location Updates)가 가장 현실적으로 보였습니다.
원리는 간단합니다. 위치정보 권한을 받아서 백그라운드 위치 업데이트를 활성화하면 iOS가 앱을 죽이지 않습니다. 그 사이에 SSH 연결을 유지하는 거죠.
위치정보 백그라운드 모드 활성화
↓
iOS가 앱을 살려둠 (위치 추적 중이니까)
↓
SSH 터널 유지
↓
사파리에서 localhost:3000 접속 성공!
기술적으로는 동작했습니다. 그래서 이대로 제출했습니다.
애플은 바보가 아니었습니다
첫 번째 거절 알림이 왔습니다.
위치정보 기능이 앱의 핵심 기능과 직접적인 관련이 없습니다.
SSH 터미널 앱이 왜 사용자의 위치를 추적해야 하는지 설명할 수 없었습니다. 당연하죠. 실제로 위치를 쓰는 게 아니라 백그라운드 유지를 위해 우회한 거니까요.
그래도 포기가 안 돼서 심사 노트에 이유를 설명하고 재제출했습니다. "개발자가 이동 중에 서버 상태를 확인해야 하므로 위치 기반 알림이 필요합니다" 같은 논리를 붙여봤는데, 돌아온 건 같은 거절 사유였습니다.
접근 방식을 조금씩 바꿔가면서 재제출을 반복했습니다.
- 위치정보 사용 목적을 더 상세하게 기술 → 거절
- 위치 기반 기능을 실제로 추가해봄 → 거절
- 위치 권한 요청 타이밍을 변경 → 거절
- 심사 노트에 기술적 필요성을 장문으로 설명 → 거절
매번 미묘하게 다른 시도를 했지만, 핵심은 같았습니다. 위치정보를 백그라운드 유지 수단으로 쓰고 있다는 것. 애플 리뷰어는 이걸 정확히 알고 있었습니다.
방향을 완전히 바꿨습니다
10번쯤 거절당하고 나서야 깨달았습니다. 이 방향으로는 안 되겠다.
문제를 다시 정의했습니다.
원래 접근: 터널링을 백그라운드에서 유지하고 싶다 → 외부 브라우저에서 확인 새로운 접근: 터널링이 포그라운드에서만 된다면, 브라우저도 앱 안에 넣자
인앱브라우저를 만들기로 했습니다. VibeShell 안에서 터널링도 하고, 그 결과를 바로 확인할 수 있는 브라우저를 내장하는 거예요.
VibeShell 포그라운드 상태
├── SSH 터미널 (터널링 활성화)
└── 인앱브라우저 (localhost:3000 표시)
앱을 떠나지 않으니 포그라운드가 유지됩니다. 터널도 안 끊깁니다. 위치정보 권한도 필요 없습니다.
위치정보 관련 코드를 전부 제거하고, 인앱브라우저를 구현해서 제출했습니다.
승인
제출하고 이틀 뒤, App Store Connect에 알림이 왔습니다. 이번에는 느낌이 달랐습니다.
승인.
3주간 10번 거절당하고, 위치정보 코드를 넣었다 뺐다 하며 매일 재제출하던 게 끝났습니다.
엘스한테 "형 앱 통과됐어" 하고 말했는데 관심도 없더라고요. 간식을 줬더니 그제야 꼬리를 흔들었습니다.
돌아보면
인앱브라우저가 오히려 더 나은 UX였습니다. 사파리로 전환하는 것보다 앱 안에서 바로 확인하는 게 훨씬 편하거든요. 터미널과 브라우저를 빠르게 오가면서 개발 결과를 확인할 수 있으니까요.
거절당할 때는 "왜 이걸 안 받아주지" 했는데, 결과적으로 애플의 거절이 더 좋은 제품을 만들게 해준 셈입니다.
이 경험에서 얻은 교훈은 하나입니다.
플랫폼 정책과 싸우지 말 것. 정책 안에서 해결책을 찾을 것.
위치정보 우회는 설령 통과하더라도 언제든 정책 변경으로 막힐 수 있는 방법이었습니다. 인앱브라우저는 정책을 완전히 준수하면서도 문제를 해결한 방법이고요.
3주가 길게 느껴졌지만, 앱을 출시한 지금 돌아보면 그 3주가 VibeShell을 더 좋은 앱으로 만들어준 시간이었습니다.