Flutter, 환경에 따라 다르게 빌드하는 방법
이 글은 개발 / 스테이징 / 운영으로 나뉘어진 환경에서 flutter앱을 개발하는 방법을 다룹니다.
이 글은 개발 / 스테이징 / 운영으로 나뉘어진 환경에서 flutter앱을 개발하는 방법을 다룹니다.
전체 gist 주소는 https://gist.github.com/ChangJoo-Park/48e895261475aeb2877147bbe16bbce3 입니다.
API 엔드포인트 및 기타 환경이 개발/스테이징/운영으로 나눠진 경우 환경에 맞추어 새로 앱을 빌드해야합니다. 간단히 바꿀 수 있는 정보이고 참을만한 인내심이 있으면 이 글은 도움이 안될 것 같습니다. 약간 긴 설정이 필요합니다.
이 과정은 Visual Studio Code 의 debug탭을 이용합니다.{
"version": "0.2.0",
"configurations": [{
"name": "개발 - 디버그",
"type": "dart",
"request": "launch",
"flutterMode": "debug",
"program": "lib/main.dart",
"args": [
"--flavor",
"development"
],
},
{
"name": "개발 - 릴리즈",
"type": "dart",
"request": "launch",
"flutterMode": "release",
"program": "lib/main.dart",
"args": [
"--flavor",
"development",
"--no-track-widget-creation"
],
},
{
"name": "스테이징 - 디버그",
"type": "dart",
"request": "launch",
"flutterMode": "debug",
"program": "lib/main_staging.dart",
"args": [
"--flavor",
"staging"
]
},
{
"name": "운영 - 디버그",
"type": "dart",
"request": "launch",
"flutterMode": "debug",
"program": "lib/main_production.dart",
"args": [
"--flavor",
"production"
]
},
{
"name": "운영 - 릴리즈",
"type": "dart",
"request": "launch",
"program": "lib/main_production.dart",
"flutterMode": "release",
"args": [
"--flavor",
"production",
"--no-track-widget-creation"
]
},
{
"name": "아이폰 개발",
"type": "dart",
"request": "launch",
"flutterMode": "debug",
"program": "lib/main_production.dart",
"args": [
"--no-track-widget-creation"
]
}
]
}
launch.json 전부입니다. 테스트해야할 환경이 꽤나 많습니다. 안드로이드에서 작동하면 대부분 된다고 간주하여 안드로이드만 자세하게 설정이 되어있습니다.
유심히 봐야할 부분은 “flutterMode”, “program”, “args” 입니다. flutterMode는 flutter의 로그 및 디버그 레벨을 설정하기 위한 키 입니다. program은 빌드할 소스의 진입 코드입니다. main.dart는 개발용 설정으로 시작합니다. 다른 파일이름에는 환경을 명시합니다. “args” 중 flavor 는 뒤에 있을 안드로이드 플랫폼의 gradle등의 설정에서 다룹니다.
아래는 main.dart의 기본 셋팅입니다. 개발용 설정을 설정 후 runApp 메소드로 앱을 실행합니다. AppConfig은 직접 만든 InheritedWidget을 이용한 위젯이고 중요한 부분은 child에 있습니다.import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import './app.dart';
import './app_config.dart';
import './services/http_client.dart';
void main() async {
final String apiHost = '<환경에 따른 API HOST>';
final String apiPort = '<환경에 따른 API PORT>';
AppConfig configuredApp = new AppConfig(
fbApiKey: '<파이어베이스 API키>',
fbAuthDomain: '<파이어베이스 인증 도메인>',
fbDatabaseURL: '<파이어베이스 데이터베이스 URL>',
fbProjectId: '<파이어베이스 프로젝트 ID>',
fbStorageBucket: '<파이어베이스 스토리지 버킷 URL>',
fbMessagingSenderId: "<파이어베이스 메시징 ID>",
apiHost: '<API_HOST>',
apiPort: '<API_PORT>',
child: createApp(),
);
HTTPClient client = HTTPClient();
client.setHost('$apiHost:$apiPort/api');
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('environment', 'development');
runApp(configuredApp);
}
제가 작업했던 앱은 파이어베이스와 직접 만든 API 서버 둘 다 사용해야하는 환경이라 설정을 매 테스트마다 하기가 어려웠습니다. 때문에 AppConfig이라는 위젯을 만들어 관리합니다. 아래쪽의 HTTPClient는 직접 만든 HTTP 요청 클라이언트이므로 setHost 부분만 보시면 됩니다.
AppConfig을 다른 위젯에서 사용하려면 `AppConfig.of(context).apiHost` 처럼 사용할 수 있습니다. 유용하게 사용할 수 있으므로 기억해주세요.import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
class AppConfig extends InheritedWidget {
final String fbApiKey;
final String fbAuthDomain;
final String fbDatabaseURL;
final String fbProjectId;
final String fbStorageBucket;
final String fbMessagingSenderId;
final String apiHost;
final String apiPort;
AppConfig({
@required this.fbApiKey,
@required this.fbAuthDomain,
@required this.fbDatabaseURL,
@required this.fbProjectId,
@required this.fbStorageBucket,
@required this.fbMessagingSenderId,
@required this.apiHost,
@required this.apiPort,
@required Widget child,
}) : super(child: child);
static AppConfig of(BuildContext context) {
return context.inheritFromWidgetOfExactType(AppConfig);
}
@override
bool updateShouldNotify(InheritedWidget oldWidget) => false;
}
위에서 사용한 AppConfig 클래스의 전부입니다. 여기서는 위젯을 이용해서 앱 전역에서 사용할 수 있는 설정을 담을 수 있다 정도만 이해하셔도 됩니다.
실제 앱 화면이 나오는 부분은 아래와 같습니다. AppConfig의 `child` 키에 해당합니다.// 생략
MaterialApp createApp() {
return MaterialApp(
title: '<앱 이름>',
// 로그인 상태에 따라 처음 페이지를 변경합니다.
// 예:
// 로그인 체크 중에는 스플래시 페이지를 보여줍니다.
// 로그인 정보가 없으면 로그인 페이지를 보여줍니다.
// 로그인 정보가 있으면 앱 메인 페이지를 보여줍니다.
home: _handleRootPage(),
onGenerateRoute: _getRoute
);
}
// 생략
이 내용은 Flutter 앱을 만들어보셨다면 익숙할테니 넘어가겠습니다.
이제 안드로이드 플랫폼으로 빌드할때 마다 flutter CLI의 “ — flavor” 옵션을 처리하기 위해 gradle설정을 수정합니다.... 생략
android {
compileSdkVersion 27
flavorDimensions "app"
productFlavors {
// flavor development
development {
dimension "app"
applicationId "com.app.my.dev"
versionCode 1
versionName flutterVersionName+"-"+getGitCommitHash()
}
// flavor staging
staging {
dimension "app"
applicationId "com.app.my.staging"
versionCode 1
versionName "1.0"
}
// flavor production
production {
dimension "app"
applicationId "com.app.my.production"
// versionCode 20000
// versionName "2.0"
}
... 생략
productFlavor라는 설정이 없을 것입니다. launch.json에서 만든 환경에 따라 flavor를 설정해주세요
이 과정을 마친 후 앱을 실행했을때 오류가 난다면, AndroidManifest.xml 파일에 아래 내용을 추가하세요.<application
android:name="io.flutter.app.FlutterApplication"
android:label="@string/app_name" // <-- 이 라인입니다.
android:icon="@mipmap/ic_launcher">
자세한 내용은 이 gist에서 전체 코드를 확인해주세요.
이 주제를 다루는 공식 flutter 문서에서 추천하는 블로그 목록입니다.
- https://cogitas.net/creating-flavors-of-a-flutter-app/
- https://medium.com/@salvatoregiordanoo/flavoring-flutter-392aaa875f36
- https://medium.com/flutter-community/flutter-ready-to-go-e59873f9d7de
gradle 설정에 도움을 주신 fobidlim 님께 감사를 드립니다.