ErrorNote, Flutter, 문법상으론 오류가 없지만 잘못된 참조로 인해 런타임에 결정되는 값을 컴파일에 참조하여 발생한 버그

Uno·2024년 8월 29일
0

ErrorNote

목록 보기
8/8
post-thumbnail

Summary

문법상으로는 오류가 없었던 문제; 런타임에 결정된 값을 컴파일 타임에 참조하여 컴파일 해보기 전에는 발생하지 않는 문제를 발견했습니다. 이 문제에 대한 해결 과정입니다.

Approach

IDE 상에서는 어떠한 에러메시지도 없었는데, 빌드를 시도하면 그제서야 에러가 발생하고 있었습니다. 문법상으로는 오류가 없지만 컴파일이 되지 않는 상황을 만난 것입니다.

문제 접근 프로세스는 다음과 같이 진행했습니다. 좁은 범위에서 큰 범위로 확인해보고자 합니다.

  1. 에러 메시지 분석
  2. 에러 메시지에 해당하는 소스코드 수정
  3. 에러 메시지에 관련된 프로젝트 세팅 수정

Error Message

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.

Analysis

에러 메시지를 보니, 플랫폼 차이로 발생한 문제가 아닙니다. 코드 중에서 _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에서 컴파일부터 다시 알아보도록 하겠습니다.

Problem

Approach 에서 봤던 마지막 실행순서에는 조건이 있습니다. "만약 컴파일이 성공했다면"

문법의 특성을 이해하면, 컴파일 시점에 버그 우려가 있는 코드입니다.

순서를 정리하면 다음과 같습니다:

1. 컴파일을 시작한다.
2. `_url`이 static 영역에 선언되어 있어, 컴파일 시점에 초기화를 시도한다.
3. 그러나 `EnvConfig.baseUrl`은 런타임에 결정되는 값이므로, 컴파일 시점에 상수로 평가될 수 없다.
4. 이로 인해 컴파일 에러가 발생하여 런타임에 진입하지 못한다.
  • _url 값은 컴파일 시점에 초기화하려고 하지만 EnvConfig.baseUrl는 런타임에 초기화하려고 합니다. 이 부분이 문제가 됩니다.
  • 게다가 enum은 컴파일 타임에 값이 모두 결정됩니다. 그런데 아직 결정되지 않은 EnvConfig.baseUrl 값이 _url에서 참조하고, 또 enum은 _url을 참조하여 컴파일 시점에 오류가 나타납니다.

그림으로 보면 다음과 같습니다:

  • 그림을 보면 아직 런타임에 도착하지 않았는데, 그 값을 참조하고 있어서 문제가 발생하고 있습니다.

런타임에 결정되는 값을 컴파일 타임에 참조하려고 하여 발생한 문제

Solution

문제 해결책은 간단합니다. 컴파일 타임에 결정되지 않으므로, 모두 런타임에 결정하도록 하거나 모두 컴파일 타임에 결정하도록 하면 됩니다.

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; 
}

Reflection

  • 코드 작성 시점에 발견하지 못한 에러가 빌드할 때 나타나는 점이 흔하지 않은 경우라 조금 신기했습니다. 아마 대부분의 경우 컴파일 에러로 바로 알려주어 잡아내지만, 이렇게 잡아내지 못하는 경우도 있다는 것이 흔치 않은 경험이라 더 그렇게 느꼈습니다.
  • 전역 변수에 특정 값을 할당할 때, const 값이 아니라면 정말 유의해야 함을 느꼈습니다.
profile
iOS & Flutter

0개의 댓글