Flutter

Flutter 업데이트 (마켓, Shorebird)

devfinger 2025. 2. 2. 19:19

마켓(Play Store, App Store) 업데이트가 필요한 경우

  • 플러그인을 업데이트하는 경우
    • 예를 들어, vibration 패키지의 새 버전에서 내부 네이티브 코드가 변경되었다면, 새 버전의 앱을 빌드하여 Play Store/App Store에 다시 업로드해야 함.
  • Android/iOS 네이티브 코드(AndroidManifest.xml, Info.plist, Kotlin/Swift) 변경 시
    • 예를 들어, 새로운 푸시 알림 기능을 추가하려고 AndroidManifest.xml 또는 Info.plist 설정을 변경했다면 마켓 업데이트 필요.

마켓 업데이트 없이 적용 가능한 경우

  • 단순한 UI 변경, 버그 수정, 서버에서 데이터 가져오는 방식 변경
  • Shorebird(코드 푸시)를 이용하는 경우

 

Shorebird Code Push

 

Shorebird는 Flutter 앱을 업데이트할 때 전체 앱을 다시 빌드하지 않고 Dart 코드만 교체할 수 있는 기술.

  • 네이티브(Android/iOS) 코드 변경 없이, Dart 코드만 업데이트 가능.
  • UI 수정, 로직 변경, 버그 수정 가능.
  • 하지만 네이티브 플러그인 업데이트나 Android/iOS SDK 변경은 불가능.

Shorebird로 가능한 것 ❌ 불가능한 것

업데이트 유형Shorebird 지원 여부

UI 변경 (Text, 버튼, 컬러 변경) ✅ 가능
비즈니스 로직 변경 (예: API 주소 변경) ✅ 가능
버그 수정 (예: 크래시 해결) ✅ 가능
새로운 화면 추가 (Dart 코드로만 구현된 경우) ✅ 가능
플러그인 업데이트 (예: firebase_messaging 버전 업) ❌ 불가능
Android/iOS 네이티브 코드 수정 (예: MainActivity, AppDelegate 변경) ❌ 불가능
AndroidManifest.xml 또는 Info.plist 변경 ❌ 불가능

 

 

미리 모든 네이티브 플러그인을 추가

 

 

  • 마켓 업데이트 없이 Shorebird 또는 서버에서 기능 활성화(Feature Flag) 방식으로 새로운 기능을 활성화할 수 있음.
  • 앱을 배포할 때, 이후 필요한 기능을 추가하는 데 마켓 승인 시간을 절약할 수 있음.
  • 예를 들어
     
dependencies:
  vibration: latest_version
  url_launcher: latest_version
  geolocator: latest_version
  camera: latest_version
  firebase_messaging: latest_version
  flutter_local_notifications: latest_version

 

 

위와 같이 모든 네이티브 기능을 미리 추가해 두면, 나중에 Shorebird를 통해 코드 수정만으로 해당 기능을 활성화할 수 있음

 

미리 네이티브 플러그인 추가의 몇 가지 문제점

1. 앱 용량이 불필요하게 커짐

  • 사용하지 않는 네이티브 플러그인까지 포함되므로 앱 크기가 증가할 수 있음.
  • 네이티브 라이브러리는 Flutter build 과정에서 포함되므로 불필요한 기능까지 앱에 탑재됨.

2. iOS의 App Store Review에서 거절될 가능성 있음

  • Apple App Store 정책상 사용하지 않는 권한(예: 카메라, 위치 정보)이 있는 경우, 심사에서 거절될 수 있음.
  • 예를 들어, camera 플러그인을 추가했지만 카메라 기능을 사용하지 않으면 "권한 요청 불필요" 등의 이유로 거절될 가능성이 있음.
  • iOS에서는 Info.plist에 권한을 정의해야 하는데, 이를 미리 추가하면 심사에서 문제될 가능성이 높음.
<key>NSCameraUsageDescription</key>
<string>이 앱은 카메라를 사용합니다.</string>

 

3. Android Play Store에서도 불필요한 권한이 감지될 수 있음

  • AndroidManifest.xml에 불필요한 권한이 남아있으면, Google Play에서 정책 위반으로 앱이 등록되지 않을 가능성이 있음
  • 예를 들어, geolocator 패키지를 추가하면 Android는 위치 권한을 자동으로 포함시키는데, 실제 사용하지 않으면 Google Play 심사에서 거부될 수 있음
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

 

 

그러면 어떻게???

방법 1: 필요한 플러그인만 최소한으로 추가

  • 처음부터 모든 네이티브 기능을 추가하지 말고, 필요할 때 추가하는 게 최선.

방법 2: 앱 권한을 동적으로 요청

  • Android에서는 AndroidManifest.xml에서 권한을 미리 추가하되, Permission Handler 플러그인을 활용해 동적으로 요청하면 Google Play 심사에서 문제될 가능성을 줄일 수 있음
     
import 'package:permission_handler/permission_handler.dart';

void requestCameraPermission() async {
  var status = await Permission.camera.request();
  if (status.isGranted) {
    print("카메라 권한 허용됨");
  } else if (status.isDenied) {
    print("카메라 권한 거부됨");
  } else if (status.isPermanentlyDenied) {
    print("카메라 권한이 영구적으로 거부됨. 설정에서 변경해야 함.");
    openAppSettings();  // 앱 설정 화면 열기
  }
}

 

앱을 실행할 때 권한을 동적으로 요청하면 심사에서 "불필요한 권한 추가" 문제를 방지할 수 있음

 

여러개 권한 요청

void requestMultiplePermissions() async {
  Map<Permission, PermissionStatus> statuses = await [
    Permission.camera,
    Permission.microphone,
    Permission.location
  ].request();

  print("카메라 권한: ${statuses[Permission.camera]}");
  print("마이크 권한: ${statuses[Permission.microphone]}");
  print("위치 권한: ${statuses[Permission.location]}");
}

 

방법 3: Feature Flag(기능 플래그) 활용

  • Firebase Remote Config 또는 자체 API를 활용해 특정 기능을 서버에서 활성화/비활성화하는 방법.
  • 예를 들어, 앱에는 camera 플러그인이 포함되어 있지만, 앱 UI에서 버튼을 숨겨놓고 Firebase Remote Config를 통해 카메라 기능을 나중에 활성화할 수 있음.
import 'package:firebase_remote_config/firebase_remote_config.dart';

void checkFeatureFlag() async {
  final remoteConfig = FirebaseRemoteConfig.instance;
  await remoteConfig.fetchAndActivate();
  bool isCameraEnabled = remoteConfig.getBool("enable_camera");

  if (isCameraEnabled) {
    print("카메라 기능 활성화됨");
  } else {
    print("카메라 기능 비활성화됨");
  }
}

 

방법 4: 네이티브 기능을 MethodChannel로 직접 호출

  • 모든 네이티브 기능을 플러그인으로 추가하지 않고, Flutter → 네이티브(Android/iOS)로 직접 요청하는 방식(MethodChannel 사용).
  • 플러그인이 아니라 네이티브 코드에서 기능을 처리하므로, 필요할 때만 기능을 활성화할 수 있음.

예: Flutter에서 Android 네이티브 진동 기능 호출

 

import 'package:flutter/services.dart';

const platform = MethodChannel('com.example.vibrate');

void triggerVibration() async {
  try {
    await platform.invokeMethod('vibrate');
  } on PlatformException catch (e) {
    print("에러 발생: ${e.message}");
  }
}
 
 
 

Android 네이티브 코드(Java)

 

public class MainActivity extends FlutterActivity {
    private static final String CHANNEL = "com.example.vibrate";

    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);
        new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
                .setMethodCallHandler(
                        (call, result) -> {
                            if (call.method.equals("vibrate")) {
                                Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
                                if (vibrator != null && vibrator.hasVibrator()) {
                                    vibrator.vibrate(500);
                                    result.success("Vibration started");
                                } else {
                                    result.error("UNAVAILABLE", "Vibrator not available", null);
                                }
                            } else {
                                result.notImplemented();
                            }
                        }
                );
    }
}

 

이렇게 하면 Shorebird로 Flutter 코드만 변경하면서도, 네이티브 기능을 필요할 때만 활성화할 수 있음.

 

 

위의 방법 2의 동적으로 권한 요청 (안드로이드 AndroidManifest.xml 만 가능)

서버에서 권한 활성화 여부를 설정 (Feature Flag)

  • Firebase Remote Config 또는 자체 API를 사용하여 앱에서 특정 권한을 요청할지 여부를 서버에서 결정.
  • 이렇게 하면 앱 코드 자체는 변경하지 않고, 서버에서 설정만 변경하여 기능 활성화 가능.

예제: Firebase Remote Config를 활용하여 카메라 권한 활성화 여부를 서버에서 제어

import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:permission_handler/permission_handler.dart';

void checkFeatureFlag() async {
  final remoteConfig = FirebaseRemoteConfig.instance;
  await remoteConfig.fetchAndActivate();
  bool isCameraEnabled = remoteConfig.getBool("enable_camera");

  if (isCameraEnabled) {
    print("카메라 기능 활성화됨. 권한 요청 시작.");
    requestCameraPermission();
  } else {
    print("카메라 기능 비활성화됨. 권한 요청 안 함.");
  }
}

 

서버에서 enable_camera 값을 false로 설정하면, 앱은 카메라 권한을 요청하지 않음

 

Android에서 AndroidManifest.xml을 동적으로 설정하는 방법

AndroidManifest.xml을 직접 변경할 수는 없지만, Flutter 앱 빌드 시 권한을 동적으로 추가하는 방법이 있음.

  1. Android Gradle에서 AndroidManifest.xml을 서버에서 가져오기
  2. Flutter에서 앱을 빌드할 때 manifestPlaceholders로 동적으로 추가

예: Android Gradle에서 권한을 동적으로 추가

 

android {
    defaultConfig {
        manifestPlaceholders = [
            ENABLE_CAMERA: "true"
        ]
    }
}

 

예: AndroidManifest.xml에서 동적으로 적용

<uses-permission android:name="android.permission.CAMERA" android:required="${ENABLE_CAMERA}"/>

'Flutter' 카테고리의 다른 글

Flutter 이벤트 사용법  (0) 2025.02.02
Flutter 기본 정리  (0) 2025.02.02