Shorebird를 활용한 Flutter 앱 실시간 핫픽스(CodePush) 배포 및 무중단 라이브 패치

프로덕션 환경에 배포된 모바일 앱에서 결제 오류나 화면 이탈 같은 크리티컬 버그가 발견되었을 때, 기존의 앱 마켓 업데이트 방식(Google Play / Apple App Store)은 심사 검토 및 승인까지 짧게는 몇 시간, 길게는 수일이 소요된다. 그동안 발생하는 유저 이탈과 매출 손실은 서비스 운영팀에 막대한 타격을 준다.
React Native 생태계의 AppCenter CodePush와 달리, Flutter는 Dart AOT(Ahead-Of-Time) 네이티브 바이너리로 직접 컴파일되는 특성 때문에 오랫동안 실시간 코드 푸시(CodePush) 도입이 불가능한 영역으로 여겨졌다.
그러나 Flutter 창시자 중 한 명인 Eric Seidel이 설립한 Shorebird가 Dart VM 런타임을 자체 개조한 엔진을 선보이면서, Flutter 개발자들도 앱 심사 재제출 없이 Dart 코드 변경사항을 유저 기기에 즉시 OTA(Over-The-Air)로 라이브 패치할 수 있게 되었다.
이 글에서는 Shorebird의 Dart VM 인터프리터 패치 작동 원리, App Store / Google Play 정책 준수 여부, shorebird CLI를 이용한 릴리스 및 핫픽스 패치 워크플로우, 그리고 제한사항을 상세히 다룬다.
핵심 요약
- Dart 런타임 개조 기술: Shorebird는 앱 설치 바이너리에 개조된 Dart VM을 탑재하여, 수정된 Dart 코드만 변경 델타(Delta Patch)로 다운로드받아 인터프리팅/JIT 방식으로 교체 실행한다.
- 스토어 정책 준수: Apple App Store 가이드라인 3.3.2 섹션(해석되는 코드 실행 정책) 및 Google Play 정책 범주 내에서 작동하도록 설계되었다.
- 실행 속도: 패치된 실행 블록만 최소한으로 해석하고 나머지 대부분의 네이티브 코드는 기존 AOT로 작동하므로 일반 네이티브 성능에 영향을 주지 않는다.
- 제한 사항: C++/Java/Swift 등 네이티브 플러그인 코드 수정이나
pubspec.yaml에 새로운 Asset/네이티브 딜펜던시가 추가된 경우는 마켓 정식 업데이트가 필요하며, 순수 Dart 코드 수정에 최적화되어 있다.
1. Shorebird 작동 원리: Dart AOT와 인터프리터의 결합
Flutter 앱은 빌드 시 Dart 코드를 ARM 64비트 기계어로 AOT 컴파일한다. 따라서 바이너리 자체를 직접 고치는 것은 iOS 시스템 보안(Code Signing)에 위배된다.
Shorebird는 이를 해결하기 위해 다음과 같은 이중 구조를 채택한다:
- Base Release 빌드:
shorebird release명령으로 앱을 빌드하면, 링커 단계에서 Dart 함수들의 주소 표(Symbol Table)와 바이너리 서명을 저장한다. - Patch 생성: 코드 수정 후
shorebird patch를 실행하면, 변경된 Dart 함수만 조각내어 Patch Delta File로 변환한다. - 런타임 실행: 사용자 기기의 Shorebird 런타임 엔진이 백그라운드에서 패치 파일을 받아, 변경되지 않은 99% 코드는 기존 AOT 기계어로 실행하고, 변경된 1% 함수만 개조된 VM 인터프리터로 우회 실행한다.
2. Shorebird CLI 설치 및 프로젝트 설정
1) Shorebird CLI 설치
# macOS / Linux 터미널
curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/shorebirdtech/install/main/install.sh | sh
# 설치 확인
shorebird --version
2) 프로젝트 초기화 (shorebird init)
Flutter 프로젝트 루트 디렉터리에서 다음 명령어를 실행한다.
shorebird init
이 명령어는 프로젝트 루트에 shorebird.yaml 파일을 자동 생성하며, 앱 고유 식별자(app_id)를 발급한다.
# shorebird.yaml
app_id: 8f3a9b12-3c4d-5e6f-7a8b-9c0d1e2f3a4b
3. 실전 프로덕션 배포 및 핫픽스 패치 워크플로우
Shorebird의 기본 철학은 **“릴리스 바이너리를 마켓에 올린 후, 문제가 생기면 핫픽스를 쏜다”**이다.
Step 1: 최초 마켓 제출용 릴리스 빌드 생성
기존 flutter build appbundle 대신 Shorebird CLI를 통해 릴리스를 생성하고 스토어에 업로드한다.
# Android App Bundle 릴리스 생성 (Google Play 제출용)
shorebird release android
# iOS IPA 릴리스 생성 (App Store 제출용)
shorebird release ios
이 과정에서 릴리스 바이너리의 심볼 및 베이스 상태가 Shorebird 클라우드 서버에 안전하게 핑거프린팅된다.
Step 2: 긴급 버그 수정 및 실시간 핫픽스 패치 배포
마켓 배포 후 결제 버그나 널 포인트 에러가 발견되었다면, Dart 코드를 수정한 뒤 마켓 재심사 없이 아래 명령어 단 한 줄로 라이브 패치를 쏜다!
# Android 앱용 긴급 핫픽스 패치 배포
shorebird patch android
# iOS 앱용 긴급 핫픽스 패치 배포
shorebird patch ios
패치 명령을 실행하면 수 초 내로 글로벌 CDN으로 패치 파일이 전파되며, 사용자가 앱을 다음 번에 실행하거나 재부팅할 때 무중단으로 최신 수정 코드가 반영된다.
4. Flutter 클라이언트 코드 내 패치 체크 제어
Shorebird 패치는 기본적으로 앱 시작 시 백그라운드에서 자동으로 다운로드되지만, 필요 시 Dart 코드로 직접 패치 다운로드 및 재시작 상태를 컨트롤할 수 있다.
shorebird_code_push 패키지 활용 예시:
import 'package:flutter/material.dart';
import 'package:shorebird_code_push/shorebird_code_push.dart';
final _shorebirdCodePush = ShorebirdCodePush();
class AppUpdateChecker extends StatefulWidget {
const AppUpdateChecker({super.key});
@override
State<AppUpdateChecker> createState() => _AppUpdateCheckerState();
}
class _AppUpdateCheckerState extends State<AppUpdateChecker> {
bool _isChecking = false;
Future<void> _checkForUpdate() async {
setState(() => _isChecking = true);
// Shorebird 서버에 이용 가능한 핫픽스 패치가 있는지 확인
final isUpdateAvailable = await _shorebirdCodePush.isNewPatchAvailableForDownload();
if (isUpdateAvailable) {
// 패치 차분 데이터 다운로드 실행
await _shorebirdCodePush.downloadUpdateIfAvailable();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('새로운 패치가 다운로드되었습니다. 앱을 재시작하면 적용됩니다.')),
);
}
}
setState(() => _isChecking = false);
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _isChecking ? null : _checkForUpdate,
child: const Text('핫픽스 패치 점검'),
);
}
}
결론
Shorebird의 도입은 Flutter 개발팀에 있어 마켓 심사 대기시간이라는 거대한 불안 요소를 제거해 준 혁신적인 도구이다.
C++ / Native 코드나 빌드 설정 변경이 없는 순수 Dart 비즈니스 로직의 크리티컬 버그를 몇 분 만에 전 세계 사용자 기기에 무중단으로 적용할 수 있으므로, 안정적인 라이브 앱 운용을 위해 반드시 도입을 고려해볼 만하다.