Flutter 앱에 환경 변수를 주입하는 3가지 방법 비교 — dart-define, 쉘 래퍼, flutter_dotenv
이 글에서 다루는 내용
Flutter 앱이 실행/빌드될 때 SUPABASE_URL이나 API 서버 주소 같은 환경별 값을 코드에 주입하는 세 가지 방식(--dart-define, .env + 쉘 스크립트 래퍼, flutter_dotenv 패키지)의 차이와 선택 기준이다.
왜 이런 고민이 필요한가
하드코딩은 개발 · 스테이징 · 운영 환경을 분리할 수 없고 보안상 문제도 생긴다. 그렇다고 Swift/Kotlin 네이티브 BuildConfig 방식은 Flutter에 없다. 대안이 셋이며 각각 타이밍과 조작 방식이 다르다.
1. --dart-define — 컴파일 타임 상수
가장 기본적이고 공식적인 방법이다. Flutter CLI가 제공하는 --dart-define 플래그로 빌드 시 값을 주입하고, Dart 코드에서 String.fromEnvironment(...)로 읽는다.
flutter run \
--dart-define=SUPABASE_URL=https://xxx.supabase.co \
--dart-define=SUPABASE_ANON_KEY=sb_publishable_...
class ApiConfig {
static const String baseUrl = String.fromEnvironment(
'API_BASE_URL',
defaultValue: 'http://localhost:8081',
);
}
장점
- 외부 의존성 0. Dart 언어/Flutter CLI 공식 기능만 사용
- 상수(const)로 주입. 컴파일러가 최적화 가능, 런타임 체크 불필요
- 타입 안정성.
String.fromEnvironment,int.fromEnvironment,bool.fromEnvironment세 종류로 명시적 타입 - 기본값 제공. 미주입 시 fallback으로 앱이 죽지 않음
단점
- IDE Run 버튼에 친화적이지 않다. Android Studio / IntelliJ는 Run Configuration의
Additional run args필드에 매번 긴 문자열을 넣어야 한다. VS Code는.vscode/launch.json의toolArgs배열로 관리 - 값이 바뀌면 전부 리빌드. 상수라 hot reload로는 변경되지 않음
- 긴 명령어가 번거롭다. 값이 4~5개만 되어도 커맨드가 한눈에 안 읽힘
언제 쓰나
- 공식 문서 예제, 대부분의 플러그인 README가 이 방식을 안내한다
- CI/CD 파이프라인에서 환경 변수를 그대로 주입하기에 가장 자연스럽다
2. .env + 쉘 래퍼 스크립트 — 1번의 사용성 개선
1번 방식은 그대로 두되, 긴 명령어를 .env 파일과 스크립트로 감싸는 하이브리드 방식이다. 코드는 여전히 String.fromEnvironment를 사용한다.
.env 예시:
API_BASE_URL=http://10.0.2.2:8081
SUPABASE_URL=https://xxx.supabase.co
SUPABASE_ANON_KEY=sb_publishable_...
scripts/run.sh:
#!/usr/bin/env bash
set -euo pipefail
ENV_FILE="${1:-.env}"
DEFINES=()
while IFS='=' read -r key value || [[ -n "$key" ]]; do
[[ -z "$key" || "$key" =~ ^[[:space:]]*# ]] && continue
DEFINES+=("--dart-define=$key=$value")
done < "$ENV_FILE"
exec flutter run "${DEFINES[@]}"
./scripts/run.sh # 기본 .env
./scripts/run.sh .env.prod # 운영 빌드
장점
- 명령이 한 줄로 짧다. 매번
--dart-define나열 안 함 - 환경별 파일 분리 가능.
.env.dev,.env.staging,.env.prod각각 유지 - 코드는 그대로.
String.fromEnvironment사용하므로 1번의 장점 모두 유지 - CI 친화적. 배포 스크립트에서
.env.prod만 교체하면 됨 - 패키지 의존성 없음. 쉘 스크립트와 표준 Flutter만으로 동작
단점
- IDE Run 버튼과 궁합이 나쁘다. Android Studio에서는 여전히
Additional run args를 수동 설정해야 한다 - 팀원에게 스크립트 사용을 강제해야 효과가 있다. 누군가는 그냥
flutter run으로 실행하면 값이 비어버림 .env파일 분실 위험. gitignore 대상이라 신규 개발자는.env.example참고해 직접 만들어야 함
언제 쓰나
- CI/CD, 배포 자동화, 터미널·VS Code 개발자가 섞여있는 팀에 적합
- 1인 프로젝트에서 나중의 자동화를 대비한 투자
3. flutter_dotenv — 런타임 asset 로딩
.env 파일을 앱 번들에 asset으로 포함시키고, 앱 시작 시점에 Dart 코드로 읽어들이는 방식이다.
dependencies:
flutter_dotenv: ^5.2.1
flutter:
assets:
- .env
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load(fileName: '.env');
runApp(...);
}
final url = dotenv.env['SUPABASE_URL'] ?? '';