문법상으로는 오류가 없었던 문제; 런타임에 결정된 값을 컴파일 타임에 참조하여 컴파일 해보기 전에는 발생하지 않는 문제를 발견했습니다. 이 문제에 대한 해결 과정입니다.
IDE 상에서는 어떠한 에러메시지도 없었는데, 빌드를 시도하면 그제서야 에러가 발생하고 있었습니다. 문법상으로는 오류가 없지만 컴파일이 되지 않는 상황을 만난 것입니다.
문제 접근 프로세스는 다음과 같이 진행했습니다. 좁은 범위에서 큰 범위로 확인해보고자 합니다.
Android
lib/core/network/network_config/base_url.dart:12:28: Context: The invocation of '_url' is not allowed in a constant expression.
final String urlString = _url;
* What went wrong:
Execution failed for task ':app:compileFlutterBuildDebug'.
> Process 'command '/Users/username/development/flutter/bin/flutter'' finished with non-zero exit value 1
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 13s
Error: Gradle task assembleDebug failed with exit code 1
Exited (1).
iOS
Xcode build done. 53.7s
Failed to build iOS app
Could not build the precompiled application for the device.
Error (Xcode): lib/core/network/network_config/base_url.dart:9:3: Error: Constant evaluation error:
Error launching application on My iPhone.
에러 메시지를 보니, 플랫폼 차이로 발생한 문제가 아닙니다. 코드 중에서 _url
이라고 정의된 변수와 관련이 있어 해당 코드를 확인했습니다.
final String _url = EnvConfig.baseUrl;
enum BaseURL {
dev,
release;
final String urlString = _url;
}
_url
이라는 변수는 EnvConfig.baseUrl
을 참조하고 있습니다.class EnvConfig {
...
static String get baseUrl => dotenv.env['BASE_URL'] ?? '';
}
EnvVonfig
클래스는 detenv
라는 패키지를 통해서 파일로부터 값을 가져오고 있습니다.Future<void> main() async {
...
await dotenv.load(fileName: ...);
}
main.dart
시점에 파일을 가져와서 파싱하고 있습니다.만약 컴파일이 성공했다면, 실행 순서는 다음과 같을 것입니다:
1. 런타임에 진입한다. 제일 먼저 main()이 실행된다.
2. `dotenv.load()`를 통해 파일을 파싱한다.
3. `EnvConfig.baseUrl`이 처음 접근되는 시점에 `dotenv.env['BASE_URL']`의 값을 가져온다.
4. `_url`에 `EnvConfig.baseUrl`의 값이 할당된다.
위 케이스는 정상적으로 동작했을 때의 순서입니다. Problem에서 컴파일부터 다시 알아보도록 하겠습니다.
Approach
에서 봤던 마지막 실행순서에는 조건이 있습니다. "만약 컴파일이 성공했다면"
문법의 특성을 이해하면, 컴파일 시점에 버그 우려가 있는 코드입니다.
순서를 정리하면 다음과 같습니다:
1. 컴파일을 시작한다.
2. `_url`이 static 영역에 선언되어 있어, 컴파일 시점에 초기화를 시도한다.
3. 그러나 `EnvConfig.baseUrl`은 런타임에 결정되는 값이므로, 컴파일 시점에 상수로 평가될 수 없다.
4. 이로 인해 컴파일 에러가 발생하여 런타임에 진입하지 못한다.
_url
값은 컴파일 시점에 초기화하려고 하지만 EnvConfig.baseUrl
는 런타임에 초기화하려고 합니다. 이 부분이 문제가 됩니다.EnvConfig.baseUrl
값이 _url
에서 참조하고, 또 enum은 _url
을 참조하여 컴파일 시점에 오류가 나타납니다.그림으로 보면 다음과 같습니다:
런타임에 결정되는 값을 컴파일 타임에 참조하려고 하여 발생한 문제
문제 해결책은 간단합니다. 컴파일 타임에 결정되지 않으므로, 모두 런타임에 결정하도록 하거나 모두 컴파일 타임에 결정하도록 하면 됩니다.
1줄만 변경하면 됩니다: urlString
final String _url = EnvConfig.baseUrl; // 런타임에 결정됨
enum BaseUrl {
dev, release;
String get urlString => _url; // Getter로 수정하여 런타임에 접근하도록 수정
}
혹은 아래와 같이 수정해도 됩니다.
enum BaseUrl {
dev, release;
String get urlString => EnvConfig.baseUrl;
}