모바일 앱 개발시 개발 환경과 운영 환경의 세팅을 일일이 수동으로 변경한다면 매우 번거로울것이다. 또한 실수로 운영 빌드시 개발 환경 세팅으로 앱이 배포된다면 정말 아찔한 경험을 할 수 있다.
Flutter 의 flavor 를 이용한다면 개발/운영 환경 설정을 깔끔하게 분리할 수 있다. 덤으로 한 디바이스에서 개발/운영 앱을 동시에 설치 할 수 있어 테스트 시에 앱을 지우고 재설치하는 번거로움을 줄일 수 있다.
사실 flavor 는 Android 에서 동일한 소스로 다른 버전의 앱을 빌드 할 수 있도록 해주는 개념이다.
위와 같은 값들을 하드코딩 하는게 아닌 build 시 설정에 따라 적용하게 된다.
iOS 의 경우 build scheme 을 이용해서 구현할 수 있다. Flutter 의 경우 구글의 플랫폼이다 보니 좀 더 Android 의 향기가 짙게 나는게 사실이다.
android/app/src/build.gradle 에서 android.productFlavors 와 android.flavorDimensions 에서 설정할 수 있다.
android {
// ... all existing things like `sourceSets`, ...
flavorDimensions "app"
productFlavors {
dev {
dimension "app"
applicationId "com.example.flavor_test.dev"
resValue "string", "app_name", "DEV flavor test"
}
product {
dimension "app"
applicationId "com.example.flavor_test"
resValue "string", "app_name", "flavor test"
}
}
}
flavor 에 맞게 app 이름을 변경 될 수 있도록 label 값을 변경하자
android:label="@string/app_name"
build.gradle 에 의해 설정된 값을 method channels 를 통해 Flutter 에서 사용할 수 있도록 전달 하자.
package com.example.flavor_test
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "flavor").setMethodCallHandler {
call, result -> result.success(BuildConfig.FLAVOR)
}
}
}
iOS 의 설정은 xCode 에서 하는것이 편하다.
iOS workspace 를 xCode 에서 오픈하고 아래와 같은 설정을 해주자
이제 위에서 설정한 configuration 을 build scheme 에 연결 시켜 보자
이제 scheme 설정이 끝났다. 설정이 잘 마무리 되었다면, xCode 상단에 아래와 같이 선택이 가능할 것이다. 사실 여기서 run 버튼 (플레이 화살표) 을 통해서도 해당 scheme 으로 실행이 가능하다. 하지만 CLI 형태로 설정한다면 CI/CD 설정을 간편히 할 수 있어 CLI 를 사용하는것을 추천한다.
이제 각 빌드 scheme 별로 다른 설정 값을 추가 해주자
Identifier 수정
Android 에 applicationId 을 변경해주었다면, iOS 에서는 Identifier 값을 수정해주면 된다. build Setting 창에서 identifier 라고 검색하면 Product Bundle identifier 내용이 나올 것이다. 이 내용을 dev, product 환경에 맞게 변경 해주자
(dev 빌드시 identifier 뒤에 .dev 를 붙여 줄 것이다.)
app name 추가
이제 각 빌드마다 앱 이름을 다르게 설정하기 위해 App_Name 값을 추가 해 준다.
좌측 상단에 + 버튼을 눌러 Add User-defined setting 을 선택하면 사용자가 custom 하게 설정을 추가 할 수 있다.
APP_NAME 이라고 추가하고 각 빌드 scheme 별 이름을 설정하자.
이제 info.plist 에 Bundle display name 을 $(APP_NAME) 로 변경 시켜주면 된다.
Android 마찬가지로 build 설정을 flutter 에 전달하기 위해서 AppDelefate.swift 에서 Method channel 을 이용하여 flavor 를 전달하자
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let controller = window.rootViewController as! FlutterViewController
let flavorChannel = FlutterMethodChannel(
name: "flavor",
binaryMessenger: controller.binaryMessenger)
flavorChannel.setMethodCallHandler({(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
// Note: this method is invoked on the UI thread
let flavor = Bundle.main.infoDictionary?["App-Flavor"]
result(flavor)
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
flutter code 에서는 flavor 에 따른 설정을 가지고 오는 부분이 구현 되어야 한다.
main.dart 에서 실행시 아래와 같이 flavor 값을 전달 받은 다음 설정을 해주자
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// App flavor 값 조회
// flavor = 'dev' | 'product'
String? flavor = await const MethodChannel('flavor')
.invokeMethod<String>('getFlavor');
Config(flavor);
runApp(MyApp());
}
class Config{
final String baseUrl;
final String token;
Config._dev():
baseUrl = '', // dev url
token = ''; // dev token
Config._product():
baseUrl = '', // product url
token = ''; // product token
factory Config (String? _flavor) {
if (_flavor == 'dev'){
instance = Config._dev();
(_flavor == 'product'){
instance = Config._product();
}else {
throw Exception("Unknown flaver : $_flavor}");
}
return instance;
}
static late final Config instance;
}
위에서 잠깐 말했듯이 Android Studio 나 xCode 에서 flavor 를 선택후 실행을 할 수 있다. CLI 를 이용한다면 CI/CD 를 활용할 수 있고, 무엇보다 터미널 창에서 command 로 실행하는것이 개발자로서 간지(?) 가 난다. 쉽다
flutter run --flavor [flavor 명]
위 명령어를 실행하면 아래와 같이 다른 이름의 앱이 설치 되는것을 볼 수 있다. flavor 에 따라 앱 아이콘도 변경 가능하지만 이는 이번 글에서 소개하지 않겠다