iOS 네이티브 플러그인을 사용하는 Flutter 앱에서 싱글톤 패턴을 쓸 때 흔히 저지르는 실수가 있다. 플러그인 인스턴스를 클래스 필드에서 즉시 생성하는 것이다.
문제가 되는 패턴
class CloudSyncService {
CloudSyncService._();
static final CloudSyncService instance = CloudSyncService._();
// ❌ 클래스 필드에서 즉시 생성
final _iCloudSync = IcloudStorageSync();
}
static final instance = CloudSyncService._() 는 Dart에서 클래스가 처음 참조되는 시점에 실행된다. main.dart 상단에 import만 해도 static field initializer가 돌 수 있다.
이 시점은 WidgetsFlutterBinding.ensureInitialized() 이전일 수 있고, Flutter 엔진의 플러그인 채널 등록이 완료되기 전이다. 이 상태에서 IcloudStorageSync() 같은 네이티브 플러그인 인스턴스를 생성하면 플랫폼 채널을 찾지 못해 크래시가 발생한다.
해결: Lazy Initialization
플러그인 인스턴스를 nullable로 선언하고, 실제로 사용하는 시점에 처음 생성한다.
class CloudSyncService {
CloudSyncService._();
static final CloudSyncService instance = CloudSyncService._();
// ✅ nullable로 선언, 처음 사용할 때 생성
IcloudStorageSync? _iCloudSync;
Future<void> upload(String filePath, String destination) async {
// ??= 연산자로 lazy init
_iCloudSync ??= IcloudStorageSync();
await _iCloudSync!.upload(
containerId: 'iCloud.com.example.myapp',
filePath: filePath,
destinationRelativePath: destination,
);
}
}
main()에서 WidgetsFlutterBinding.ensureInitialized()가 완료된 이후에 upload()가 호출되므로, 그 시점에는 플러그인 채널이 이미 등록된 상태다.
왜 디버그에서는 안 터지나
디버그 빌드는 JIT 컴파일 + 느린 실행 속도 때문에 Flutter 엔진 초기화와 싱글톤 생성 사이에 시간 여유가 생긴다. 릴리즈 빌드(AOT)는 실행이 빠르기 때문에 타이밍 충돌이 드러난다.
TestFlight 빌드에서만 크래시가 나고 시뮬레이터에서는 정상이라면 이 패턴을 의심해볼 것.
적용 범위
이 문제는 icloud_storage_sync 외에 iOS 네이티브 플러그인을 래핑하는 모든 패키지에 해당한다.
local_authflutter_secure_storagepermission_handlersign_in_with_apple- 기타 플랫폼 채널을 사용하는 패키지
싱글톤 서비스에서 이런 패키지를 쓴다면 모두 lazy init으로 바꾸는 것이 안전하다.

💬 댓글
비밀번호를 기억해두면 나중에 내 댓글을 삭제할 수 있어요.