간단히 말하면: 네, Apple은 일정 범위 내에서 라이브 업데이트를 허용합니다. 앱의 본질이 바뀌지 않는 한, 새로운 App Store 심사 없이 iOS 앱에 새 JavaScript, HTML, CSS를 푸시할 수 있습니다. 단, 네이티브 code 다운로드는 불가능합니다.
Capgo 사용 시: Capgo Live Update는 Capacitor 앱의 웹 레이어(JavaScript, HTML, CSS, 에셋)만 배포합니다. 앱의 주요 목적이 유지되고, code 스토어프론트를 만들지 않으며, OS 보안을 우회하지 않는 경우 Apple 규칙이 이를 허용합니다. 네이티브 변경 사항은 여전히 스토어 릴리스를 거쳐야 합니다.
대부분의 혼란은 두 가지를 혼동해서 생깁니다. 인터프리트되는 code의 라이브 업데이트(허용됨)와 실행 가능한 새 code 다운로드(허용되지 않음)입니다. 이 페이지에서는 규정을 인용하고, 경계가 어디인지 보여 주며, Apple이 이를 어떻게 집행하는지 설명합니다.
이 내용은 법적 조언이 아닙니다. 현재 규정이 어떻게 되어 있는지, 수천 개 앱이 프로덕션에서 이를 어떻게 활용하는지 설명합니다.
Apple 규칙의 실제 내용
두 가지 텍스트가 중요합니다. 첫 번째는 App Store 심사 가이드라인 2.5.2:
앱은 번들 내에서 자체적으로 완결되어야 하며, 지정된 컨테이너 영역 외부의 데이터를 읽거나 쓸 수 없고, 다른 앱을 포함해 앱의 기능이나 동작을 도입하거나 변경하는 code 를 다운로드, 설치 또는 실행할 수 없습니다.
단독으로 읽으면 금지처럼 들립니다. 예외는 두 번째 텍스트인 Apple Developer Program 라이선스 계약, 모든 개발자가 서명하는:
해석된 code는 애플리케이션에 다운로드될 수 있지만, 해당 code가 다음 조건을 충족하는 경우에만 가능합니다: (a) App Store에 제출된 애플리케이션의 의도 및 홍보된 목적과 일치하지 않는 기능이나 동작을 제공하여 애플리케이션의 주요 목적을 변경하지 않아야 하며, (b) 다른 code 또는 애플리케이션을 위한 스토어나 스토어프론트를 만들지 않아야 하고, (c) OS의 서명, 샌드박스 또는 기타 보안 기능을 우회하지 않아야 합니다.
즉, 규칙은 “다운로드된 code 금지”가 아닙니다. “다운로드된 네이티브 code 금지, 그리고 세 가지 조건에서만 해석되는 code”입니다.
Capacitor 앱의 웹 레이어는 JavaScript, HTML, CSS가 실행되는 WKWebView, 이는 Apple 자체 WebKit 엔진입니다. code 방식으로 해석되므로 예외에 해당합니다. 가이드라인 4.7도 같은 취지입니다. Apple은 바이너리에 포함되지 않은 HTML5 및 JavaScript 콘텐츠를 앱에서 실행하는 것을 명시적으로 허용합니다.
허용 기준
| live update 사용 시 허용 | live update 사용 시 허용 안 됨 |
|---|---|
| JavaScript, HTML, CSS 버그 수정 | Swift, Objective-C 또는 컴파일된 바이너리 다운로드 |
| 기존 웹 스택의 UI 변경, 문구 변경, 새 화면 추가 | 앱을 다른 제품으로 바꾸기 (예: 메모 앱이 카지노 앱으로 바뀜) |
| 웹 code의 A/B 테스트 및 점진적 배포 | App Review에서 숨긴 기능 잠금 해제 |
| 이미지, 번역, 설정 업데이트 | 다른 앱이나 code를 설치하는 스토어 만들기 |
| 잘못된 릴리스를 롤백하는 경우 | iOS 샌드박스, 서명, 권한 우회 |
Capgo은 왼쪽 열만 배포합니다. 앱 내부의 웹 번들을 교체합니다. 새 Capacitor 플러그인, 새 권한, SDK 업그레이드 같은 네이티브 항목은 여전히 일반 App Store 릴리스가 필요합니다. Capgo의 버전 타겟팅 새 네이티브 code이 필요한 번들이 오래된 바이너리에 도달하지 않도록 보장합니다.
Apple이 이를 강제하는 방식
Apple은 기술이 아니라 실질적 내용을 판단합니다. 오타 수정을 위해 Live Update를 사용한다고 앱이 삭제되지는 않습니다. Apple이 문제 삼는 패턴은 리뷰를 통과할 때와 이후에 앱의 성격이 달라지는 경우입니다. 이는 2.5.2와 마찬가지로 2.3.1(숨겨지거나 문서화되지 않은 기능 금지)에 해당합니다.
실제 사례:
- 제출할 내용을 그대로, 더 빠르게 배포하세요.
- 모든 업데이트가 App Store 리스팅에 명시된 목적 범위 내에 있도록 유지하세요.
- 큰 기능 출시 시에는 App Review를 거치세요. 릴리스 사이의 수정 및 반복 개발에는 Live Update를 사용하세요.
- 리뷰어가 확인하지 못한 동작을 활성화하기 위해 live update를 사용하지 마세요.
이 방식을 따르면 라이브 업데이트는 iOS 앱 출시의 자연스러운 일부입니다. React Native 팀은 CodePush와 Expo Updates로 수년간 그래 왔고, Capacitor 팀은 Capgo로 그렇게 합니다.
iOS에서 어떤 스택이 가능한가요?
iOS에서 OTA 업데이트를 배포할 수 있는지는 단 하나의 질문에 달려 있습니다. 앱 로직이 런타임에 인터프리트되나요? JavaScript와 기타 인터프리트되는 code는 가능합니다. Swift, Objective-C, C++처럼 머신 code로 컴파일되는 언어는 불가능합니다. Flutter 같은 AOT 컴파일 프레임워크는 인터프리터를 통해서만 가능합니다.
스택별 iOS live update 호환성
| 스택 | iOS에서 code가 실행되는 방식 | Live updates 허용 여부 | 도구 |
|---|---|---|---|
| Capacitor | WKWebView(WebKit) 내 JavaScript, HTML, CSS | 지원 | Capgo |
| Ionic (Capacitor에서) | Capacitor와 동일 | 지원 | Capgo |
| Cordova | WKWebView의 JavaScript | 지원 | Capacitor로 마이그레이션한 후의 Capgo, 레거시 Cordova 플러그인 |
| React Native / Expo | Hermes 또는 JavaScriptCore의 JavaScript bundle | 네, JavaScript만 지원 | Expo Updates, 자체 호스팅 CodePush 대안 |
| NativeScript | 임베디드 엔진 기반 JavaScript | 네, JavaScript만 지원 | 제한적, 주로 자체 구축 방식 |
| Flutter | 머신 code로 AOT 컴파일된 Dart | 인터프리터로만 가능 | Shorebird (패치용 Dart 인터프리터) |
| 네이티브 Swift / Objective-C | 머신 code | 네이티브 code는 불가, WebKit 또는 JavaScriptCore의 JavaScript는 가능 | 리모트 구성 및 서버 주도 UI |
| .NET MAUI / Xamarin | 머신 code으로 AOT 컴파일된 C# | 아니요 | 원격 설정만 |
| Kotlin / Compose Multiplatform | 머신으로 컴파일된 Kotlin/Native code | 아니요 | 원격 설정만 |
| Unity / Unreal | IL2CPP 또는 C++로 컴파일된 C# | code는 안 되지만, 콘텐츠는 가능합니다 | 에셋 번들, 스크립트 콘텐츠 (Lua) |
| 웹 래퍼 (원격 URL) | WebView에서 로드되는 웹사이트 | 항상 라이브이지만, 심사 리스크 있음 | 필요 없음, 아래 노트 참조 |
Capacitor, Ionic 및 Cordova: 완벽히 지원
앱의 웹 UI와 JavaScript 비즈니스 로직은 다음에서 실행되며 WKWebView, 네이티브 셸과 플러그인은 네이티브로 유지됩니다. Apple Developer Program License Agreement 3.3.1(B) 조항에 따르면, 다운로드된 인터프리터 방식의 code는 앱의 주요 목적을 유지하고, code 스토어프런트를 만들지 않으며, OS 보안을 우회하지 않는 경우에만 허용됩니다. 가이드라인 4.7은 HTML5 및 JavaScript 미니 앱을 제공하는 앱을 별도로 규정하며 추가 요구 사항을 둡니다.
live update는 웹 번들을 교체합니다. 네이티브 셸, 플러그인, 권한은 Apple이 검토한 그대로 유지됩니다. 이것이 Capgo이 설계상 규정을 준수하는 이유입니다. 원하더라도 네이티브 code는 변경할 수 없기 때문입니다. 새로운 네이티브 플러그인이나 권한은 여전히 App Store 출시가 필요합니다.
React Native 및 Expo: JavaScript는 가능, 네이티브 모듈은 불가
React Native는 Hermes 또는 JavaScriptCore에서 JavaScript 번들을 실행합니다. 이 번들은 인터프리터 방식의 code이며 OTA로 업데이트할 수 있습니다. Expo Updates와 Microsoft CodePush(2025년 종료)가 바로 이를 위한 것이었습니다. CodePush를 떠나는 팀은 대체 수단이 필요합니다.
네이티브 모듈, React Native 버전 또는 Info.plist 새로운 바이너리가 필요합니다.
Flutter: 인터프리터를 통해서만
iOS에서 Flutter는 Dart를 ARM 기계어 code로 AOT 컴파일합니다. 새 기계어 code 다운로드는 금지되어 있어 기본 Flutter로는 라이브 업데이트를 쓸 수 없습니다. Shorebird는 패치된 Dart code를 인터프리터로 실행해 해석된 code 예외에 해당하도록 만드는 방식으로 해결합니다. 자세한 내용은 Flutter 앱용 live updates.
Native Swift, .NET MAUI, Kotlin Multiplatform: 네이티브 code 업데이트 불가
이러한 스택은 네이티브 기계어 code를 생성합니다. 대체 네이티브 code는 다운로드할 수 없습니다. 네이티브 앱이라도 Apple의 조건을 충족하면 JavaScriptCore를 통한 JavaScript처럼 인터프리트되는 code를 다운로드해 실행할 수 있습니다. OTA로 할 수 있는 또 다른 일은 데이터 변경입니다: remote config, 이미 검토된 기능의 feature flag, 서버 주도 UI, 이미지와 문구.
Unity 및 게임 엔진: 콘텐츠는 가능, code는 불가
게임 code는 컴파일됩니다. 에셋 번들(레벨, 아트, 밸런싱 데이터)은 콘텐츠이며 다운로드할 수 있습니다. Lua와 같은 내장 스크립팅 언어는 인터프리트되는 code이며 JavaScript와 동일한 규칙을 따릅니다.
Remote URL wrapper: 실시간 반영은 되지만 심사에서 리스크가 있음
WebView로 웹사이트만 불러오는 앱은 사이트를 배포할 때마다 '업데이트'되는 셈입니다. 문제는 가이드라인 2.5.2가 아니라 4.2(최소 기능)입니다. Apple은 웹사이트를 프레임에 담기만 한 앱을 거절합니다. Capacitor로 웹 앱을 번들링하고, 네이티브 기능을 추가하고, Live Updates를 사용하면 이 문제를 피할 수 있습니다.
iOS에서 어떤 스택으로도 불가능한 것
어떤 프레임워크를 쓰든 live update로는 다음을 할 수 없습니다:
- 네이티브 code, 프레임워크 또는 동적 라이브러리 다운로드
- 새 권한 또는 엔타이틀먼트 추가
- App Store 등록 정보에 설명되지 않은 앱으로 변경
- App Review에서 숨겼던 기능 활성화
다음 단계
- Android의 답변: Google은 Play Store 리뷰 없이 Live Updates를 허용하나요?
- 적용 방식 자세히: Apple의 OTA 업데이트 제한 적용 방식
- 승인 워크플로: Capacitor OTA 업데이트: App Store 승인 가이드
- 실제 거절 사례: App Store 거절 공포 스토리
심사 대기 없이 오늘 바로 수정 사항 배포
Capgo Live Update 사용하기 채널, 롤백, 델타 업데이트, 종단 간 암호화를 통해 Capacitor 앱에 JavaScript, HTML, CSS 변경 사항을 몇 분 만에 배포하세요. 오픈 소스이며 셀프 호스팅할 수 있습니다.
bunx @capgo/cli@latest init
다음 빠른 시작 첫 업데이트를 보내려면.