티스토리 뷰
외부 요인에 맞게 빌드 환경이 필요할 때 이를 보통 dev, staging, prod 등으로 나누는데요,
flutter에서는 flavor란 기능으로 이를 지원하고 있습니다.
저는 프로젝트 중 서버가 불안정할때도 원활하게 UI개발을 하고 싶어 flavor를 이용해 환경을 분리하게 되었습니다.
빌드 환경을 나누고 프로젝트 내부에서 환경에 따라 DI하는 법까지 소개해보겠습니다.
1. iOS 세팅
조금 번거롭지만, flutter에서 빌드 환경을 나누려면 각 플랫폼 별에서 별도로 작업을 미리 해주어야합니다.
iOS는 빌드 환경을 'Scheme'라고 하는데 이를 먼저 세팅해주겠습니다.
1. 먼저, ios > Runner.xcworkspace 파일을 통해 Xcode를 열어줍니다.
2. Project > Info 에서 Configurations을 설정합니다.
기본적으로 Debug, Release, Profile이 되어 있는데, 여기서 원하는대로 설정을 변경해주시면 됩니다.
적당한 configuration을 복사하고, 이름을 변경합니다.
저는 다음과 같은 목적으로 나누었습니다.
- dev: 개발 서버 사용
- local: 서버 X (오프라인)
- prod: 배포 서버 사용
Debug, Release, Profile을 일일이 설정하는게 번거로워서 아래처럼 임의로 설정도 해보았는데요,
정확한 원인은 모르겠지만 이런 오류를 계속 마주쳤습니다.
분명 맞게 스키마 설정도 해주었는데 말이죠...
정확히는 모르겠지만 아마 내부적으로 configuration과 scheme를 연결하는데 있어서 어긋나는게 있었던 것 같습니다.
예를 들어 debug 스키마의 release 부분을 링크하려고 하는데 debug 설정이 걸려있다던지... 잘은 모르겠습니다 ㅎㅎ..
정석대로 iOS에서 사용하는 Debug, Release, Profile 버전을 모두 생성해 스키마를 설정해주었더니 성공했습니다!
3. 설정에 따라 스키마를 만들어줍니다.
Xcode 상단에 프로젝트 이름 부분을 클릭하고 New Scheme를 선택해줍니다.
스키마 이름을 지정해주면, 새로운 스키마가 생성됩니다.
해당 스키마가 선택된 상태에서 Edit Scheme를 누르면 아래와 같이 뜨는데요,
Run, Test, Profile 등에서 스키마와 Build Configuration이 일치하도록 설정해줍니다.
나머지 스키마들에 대해서도 반복적으로 진행해줍니다.
4. 환경별로 Bundle Identifier와 Display Name을 변경해주겠습니다.
빌드 환경을 바꿀 때마다 앱을 재설치하지 않고 환경에 따라 다른 앱으로 설치될 수 있게 번들 고유 아이디와 앱 이름을 다르게 지정해주는 과정입니다.
Build Settings > + 버튼 > Add User-Defined Setting을 눌러 새로운 변수를 추가합니다.
저는 APP_SUFFIX로 변수명을 정의한 뒤 각 환경별로 문자열을 지정해주었습니다.
(공식문서에서는 'Product Bundle Identifier'라는 이미 있는 Setting 값을 설정하는데 저는 먹히지가 않아서 User-Defined 방식으로 진행했습니다.)
그리고 Info.plist에서 설정한 접미사가 붙을 수 있도록 아래와 같이 설정해주었습니다.
- Bundle display name: 앱을 설치하면 앱 아이콘 밑에 보여지는 이름
- Bundle identifier: 앱의 고유 아이디. 이 아이디에 의해 환경별로 별도의 앱이 설치되게 됩니다.
2. 안드로이드 세팅
1. andriod > app > build.gradle에서 다음 설정을 추가해줍니다.
iOS와 동일한 구성으로 설정해주었습니다.
- flavorDimensions: flavor를 그룹화 하기 위한 속성입니다. 어떤 속성에 따라 빌드 환경을 구분한 것인지 정의할 수 있습니다. ex)version, mode, region
- demension : flavorDemensions에 정의한 속성과 일치시켜줍니다.
- resValue: 앱 이름을 마지막 인자로 오는 인자("sample") 로 설정합니다.
- applicationIdSuffix: application id에 접미사를 붙여 각각의 앱으로 설치될 수 있게 합니다.
android {
...
flavorDimensions "environment"
productFlavors {
dev {
dimension "environment"
resValue "string", "app_name", "sample.dev"
applicationIdSuffix ".dev"
}
local {
dimension "environment"
resValue "string", "app_name", "sample.local"
applicationIdSuffix ".local"
}
prod {
dimension "environment"
resValue "string", "app_name", "sample.prod"
applicationIdSuffix ".prod"
}
}
}
2. andriod > app > src > main > AndriodManifest.xml 에서 앱 이름이 변경될 수 있도록 다음과 같이 변경해줍니다.
android:label="@string/app_name"
3. Flutter 빌드
이제 설정한 flavor에 맞게 아래 명령어를 터미널에 입력해 빌드할 수 있습니다.
flutter run --flavor [environment name]
flutter run --flavor local // dev, local, prod 가능
각 환경별로 한번씩 빌드하면 이렇게 환경에 따라 구분되는 앱이 설치됩니다.
4. get_it 라이브러리를 이용한 오프라인 환경 구성
https://pub.dev/packages/get_it
환경에 따라 DI하기 위해 사용할 라이브러리입니다! Installing 부분을 참고하셔서 설치해주세요.
1. 빌드 명령어로 환경 변수 설정
내부적으로 빌드 환경을 구분하는 방법은 main파일을 분리하는 등 여러 방법이 있겠지만 저는 실행 시 명령어로 인자를 전달하는 방법을 택했습니다.
--dart-define을 이용해서 FLAVOR라는 인자에 빌드 환경을 나타내는 문자열을 포함시켰습니다.
flutter run --flavor local -t lib/main.dart --dart-define=FLAVOR=local
이렇게 받은 문자열은 flutter 내에서 아래처럼 접근할 수 있습니다.
String flavor = const String.fromEnvironment('FLAVOR');
2. 환경을 구분할 객체 생성
이를 편하게 활용하기 위해 빌드 환경을 나타내는 객체를 만들어주겠습니다.
앱 전역에서 편하게 접근할 수 있도록 싱글턴으로 구성하였습니다.
enum BuildType {
dev,
local,
prod
}
class Environment {
static Environment? _instance;
static Environment get instance {
assert(_instance != null, 'Environment instance has not been initialized yet.');
return _instance!;
}
final BuildType _buildType;
static BuildType get buildType => instance._buildType;
static String get apiURL {
switch (buildType) {
case BuildType.local:
return ""; // If in local mode, inject mock repository in the DI.
default:
return "https://api.smartelectric.kr";
}
}
Environment._internal(this._buildType);
factory Environment.newInstance(BuildType buildType) {
_instance ??= Environment._internal(buildType);
return _instance!;
}
// 플레이버 기반의 환경 설정 초기화 메서드
static void initialize(String flavor) {
BuildType buildType;
switch (flavor) {
case 'dev':
buildType = BuildType.dev;
break;
case 'local':
buildType = BuildType.local;
break;
case 'prod':
buildType = BuildType.prod;
break;
default:
buildType = BuildType.dev; // 기본값 설정
}
Environment.newInstance(buildType);
}
}
그리고 앱이 시작되는 부분에서 FLAVOR 인자를 받아 Environment 객체를 초기화해줍니다.
void main() async {
// Initializes environment settings based on the value of the "FLAVOR" environment variable.
// The FLAVOR value is set during compilation, such as `--dart-define=FLAVOR=debug`.
String flavor = const String.fromEnvironment('FLAVOR');
Environment.initialize(flavor);
// ...
3. get_it을 이용한 Mock 객체 주입
이제 get_it을 이용해보겠습니다. DI를 위한 함수를 만듭니다.
제 목적은 서버를 사용하지 않고 UI를 빌드하기 위한 것이기 때문에 MockRepository를 주입해줄 예정입니다.
아까 초기화해둔 Environment 객체를 이용해서 빌드 환경에 따라 다른 Repository가 주입되도록 설정했습니다.
void setupDI() {
// Inject mock in local environment.
if(Environment.buildType == BuildType.local) {
GetIt.I.registerSingleton<AuthRepositoryInterface>(AuthMockRepository());
//...
} else {
GetIt.I.registerSingleton<AuthRepositoryInterface>(AuthRepository());
//...
}
}
이 setupDI 함수 또한 main에서 호출해주면 됩니다.
void main() async {
// Initializes environment settings based on the value of the "FLAVOR" environment variable.
// The FLAVOR value is set during compilation, such as `--dart-define=FLAVOR=debug`.
String flavor = const String.fromEnvironment('FLAVOR');
Environment.initialize(flavor);
// Set up DI
setupDI();
// ...
GetIt으로 주입받은 객체를 사용할때는 아래처럼 인터페이스를 이용해 선언해주면 됩니다.
이렇게 하면 상황에 맞게 해당 인터페이스와 일치하는 구현 객체를 Resolve해줍니다!
final authRepository = GetIt.I.get<AuthRepositoryInterface>();
이로써 서버에 의존하지 않는 빌드 환경을 만들 수 있었습니다!
5. IDE로 빌드 명령어 세팅
매번 터미널로 빌드하는 것은 번거롭기 때문에 IDE에 설정을 추가해주었습니다.
저는 안드로이드스튜디오를 사용하고 있습니다.
아래처럼 run args와 flavor부분에 명령어를 미리 입력해두면 Run cofigurations을 이용해 간단히 빌드할 수 있습니다.
더 다양하게 flavor를 이용할 경우가 생기면 추가적으로 포스팅해보도록 하겠습니다.
감사합니다.
ref.
'Flutter' 카테고리의 다른 글
[Flutter] WebView 사용하기 (0) | 2022.11.21 |
---|---|
[Flutter][Error] DioError [DioErrorType.other]: type 'String' is not a subtype of type 'Map<String, dynamic>?' in type cast With Retrofit+Freezed (2) | 2022.09.22 |
[Flutter][GetX] GetX로 BottomNavigationBar 만들기 (1) | 2022.08.18 |
[Flutter] BottomNavigationBar 커스텀하기 (0) | 2022.08.16 |
[Flutter] 하단탭바, BottomNavigationBar 만들기 (0) | 2022.08.10 |
- Total
- Today
- Yesterday
- SwiftUI
- Swift Concurrency
- DocC
- ios
- coordinator pattern
- Flux
- 아키텍쳐 패턴
- TestCode
- notion
- Architecture Pattern
- 코디네이터 패턴
- MVVM
- 비동기/동기
- GetX
- design pattern
- SWM
- 프로그래머스
- 리액티브 프로그래밍
- combine
- reactive programming
- Bloking/Non-bloking
- programmers
- swift
- 노션
- Flutter
- MVI
- healthkit
- RX
- 소프트웨어마에스트로
- MVC
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |