Flutter, 환경에 따라 다르게 빌드하는 방법

이 글은 개발 / 스테이징 / 운영으로 나뉘어진 환경에서 flutter앱을 개발하는 방법을 다룹니다.

Flutter, 환경에 따라 다르게 빌드하는 방법
flavor 설정하는 심정은..

이 글은 개발 / 스테이징 / 운영으로 나뉘어진 환경에서 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 문서에서 추천하는 블로그 목록입니다.

gradle 설정에 도움을 주신 fobidlim 님께 감사를 드립니다.

Subscribe to Half-Built Life

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe