ssk@tech:~$ cat ios-review-spam-game-collection.md

iOS 심사 탈락: 단일 게임 앱을 하나로 묶었는데도 스팸으로 분류됐다

비슷한 단일 게임 앱 여러 개를 하나의 컬렉션으로 묶는 것은 4.3을 피하는 정석이지만, 그것만으로 충분하지 않다. 고전 장르의 게임이 모여 있으면 심사자는 “이미 흔한 것의 변형”이나 “템플릿으로 찍어낸 앱”으로 읽을 수 있다. 자체 엔진과 직접 만든 콘텐츠라는 사실을 증거와 함께 설명해야 한다.

왜 탈락했나

2026년 10월 6일, 자작 판타지 콘솔 위에서 도는 게임 열 개를 묶은 Mono & Watch 1.0.0(2)가 4.3(Spam)과 4.2.6(App Generation Services)으로 거절됐다. 같은 게임들은 Google Play에 열 개의 개별 앱으로 나가 있었고, App Store에는 처음부터 한 앱으로 냈다. 직전 1.0.0(1)은 ATT 팝업 문제로 거절됐었다.

메시지 형식과 아홉 가지 질문은 게임 제작 앱 사례와 같았다. 비교 대상 앱은 적혀 있지 않았다.

탈락 원인으로 추정한 신호는 다음과 같다. Apple이 명시한 것은 아니다.

  • 스네이크·퐁·소코반·행맨 같은 고전 장르 목록이 설명의 대부분을 차지했다. 4.3(b)의 “흔한 카테고리의 변형”으로 읽히기 쉽다.
  • 같은 엔진, 같은 셸, 같은 화면 규격으로 게임 열 개가 나오는 구조 자체가 템플릿 산출물처럼 보일 수 있다.
  • 같은 계정에 게임 제작 도구(Chip Toy, NES Club)가 있다. 4.2.6의 “생성 서비스” 의심과 연결될 수 있다.
  • 앱 이름이 유명 휴대용 게임기 브랜드를 연상시킨다. 이번에는 바꾸지 않았고, 남은 위험으로 기록했다.

어떻게 보강했나

Apple은 실질적인 변경 없이 재제출하지 말고, 이미 요건을 충족한다고 보면 질문에 답하라고 안내했다. 앱 자체는 직접 만든 엔진과 콘텐츠였기 때문에 빌드는 그대로 두고 답변과 심사 메모를 보강했다.

답변은 4,000자 제한에 맞춘다. App Store Connect의 회신 창은 4,000자까지만 받는다. 처음 쓴 약 5,900자 답변을 질문마다 한 단락으로 줄여 3,596자로 보냈다.

출처는 확인한 것만 적는다. 답변에 넣은 사실은 모두 저장소와 스토어에서 확인했다.

  • 엔진과 열 개 게임 저장소의 사람 커밋 작성자는 개발자 한 명뿐이고, 엔진 저장소는 비공개다.
  • 서드파티 런타임은 Lua 인터프리터(Wasmoon, MIT) 하나이고, 나머지는 Capacitor·AdMob·결제 플러그인 같은 표준 프레임워크다.
  • 소코반 퍼즐은 개발자의 2023년 Flutter 소코반 프로젝트에서 가져와 자체 솔버로 검증했다.
  • 슬라이드 퍼즐 그림은 이미지 모델 결과를 16단계 회색으로 디더링하는 자체 파이프라인으로 만들었다. 생성 도구를 쓴 사실은 숨기지 않았다.

베타는 부풀리지 않는다. iOS용 외부 TestFlight 베타는 없었다고 그대로 적었다. 대신 Google Play 개별 앱의 설치 수(1K+, 500+ 등)와 실제 반영한 사용자 신고 하나를 근거로 댔다. Missile Defense에서 “로딩이 5분째 멈춘다”는 신고가 있었고, WebGL이 없는 기기에서 화면이 검게 남는 문제를 2D 그리기로 대체해 고쳤다. 이 수정은 컬렉션의 모든 게임에 들어 있다.

계정의 다른 앱과의 관계를 먼저 밝힌다. 컬렉션은 계정에서 유일한 게임 모음이고, 엔진과 게임은 다른 iOS 앱과 공유하지 않는다고 적었다. 가장 비슷한 제작 도구는 다른 플랫폼의 ROM을 다루는 제작자용 앱이라 통합 대상이 아니라고 설명했다. 장르를 발명했다고는 주장하지 않았다.

심사 메모 첫머리에 출처를 둔다. 메모 맨 앞에 “직접 만든 콘솔과 게임, 템플릿·클라이언트 없음, Play의 앱 열 개를 하나로 묶음” 단락을 넣었다. 이어서 WATCH 모드와 화면 스타일을 어디서 보는지 적었다. 메모는 App Store Connect API(appStoreReviewDetails)로 반영하고 저장소의 텍스트 파일과 맞췄다.

검증과 제출 상태

2026년 10월 8일 회신을 보내고 같은 빌드 1.0.0(2)를 다시 제출했다. App Store Connect 화면에서 회신이 메시지 목록에 올라간 것을, API에서 버전 상태가 WAITING_FOR_REVIEW인 것을 확인했다. 최종 승인은 아직 확인되지 않았다.

반려된 제출을 API로 다시 올릴 때는 거절된 항목을 먼저 resolved: true로 표시해야 한다. 그러지 않으면 409 ENTITY_STATE_INVALID가 난다.

다음에 확인할 점

같은 질문으로 다시 거절되면 답변이 아니라 제품을 바꿔야 한다는 뜻이다. 그때는 이름에서 기존 브랜드 연상을 빼고, 첫 화면과 스토어 설명이 장르 목록 대신 고유한 기능을 앞세우도록 고친다. 이 앱에서는 AI가 대신 플레이하는 WATCH 모드와 시계·화면 스타일이 그런 기능이다.

묶음 앱을 처음 낼 때부터 설명 첫 문단에 “누가 만든 무엇인가”를 쓰고, 심사 메모에 콘텐츠 출처와 다른 앱과의 관계를 미리 적어 둔다. ATT 팝업이 나타나지 않았던 첫 거절은 ATT 사례와 같은 원인이었다.