| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| Invoke | /shared:config |
| Aliases | /env:create, /config:setup |
| Tools | Read, Edit, Write, Glob, Grep |
| Model | sonnet |
/shared:config โ ํ๊ฒฝ๋ณ ์ค์ ์ ๋ฆฌ ๋์ฐ๋ฏธ#
ํ๋ง๋๋ก#
๊ฐ์ ์ฑ์ด๋ผ๋ ๊ฐ๋ฐ์ฉ / ํ ์คํธ์ฉ / ์ค์๋น์ค์ฉ์ผ๋ก ์ ์ํ๋ ์๋ฒ ์ฃผ์๋ ๋น๋ฐ ํค๊ฐ ๋ค๋ฆ ๋๋ค. ์ด ๋์ฐ๋ฏธ๋ ๊ทธ "ํ๊ฒฝ๋ณ ์ค์ "์ ํ๊ณณ์ ๊น๋ํ๊ฒ ์ ๋ฆฌํ๊ณ , ๋น๋ํ ๋ ์์์ ๋ง๋ ๊ฐ์ ๊ณจ๋ผ ์ฐ๋๋ก ์๋์ผ๋ก ์ธํ ํด ์ค๋๋ค. ์ฝ์ผํธ ์ด๋ํฐ๊ฐ ๋๋ผ์ ๋ง๋ ํ๋ฌ๊ทธ๋ฅผ ์๋์ผ๋ก ๊ณจ๋ผ ๋ผ์์ฃผ๋ ๊ฒ๊ณผ ๋น์ทํฉ๋๋ค.
๋๊ฐยท์ธ์ ์ฐ๋์#
- ์ ์ฑยท๊ธฐ๋ฅ์ ์๋ฒ ์ฃผ์, API ํค ๊ฐ์ ํ๊ฒฝ ๋ณ์๋ฅผ ์ถ๊ฐํด์ผ ํ ๋
- ๊ฐ๋ฐ(dev) / ์คํ ์ด์ง(stg) / ์ด์(prod) ์ธ ๊ฐ์ง ํ๊ฒฝ์ ๋๋ ์ ๊ด๋ฆฌํ๊ณ ์ถ์ ๋
-
/shared:config๋ช ๋ น์ ์คํํ๋ฉด ์ด ๋์ฐ๋ฏธ๊ฐ ํ์ฑํ๋ฉ๋๋ค. (/env:create,/config:setup๋ก๋ ๋ถ๋ฅผ ์ ์์ต๋๋ค.)
๋ฌด์์ ํด์ฃผ๋์#
-
ํ๊ฒฝ๋ณ ์ค์ ํด๋์ค 3์ข
์ธํธ๋ฅผ ๋ง๋ค์ด ์ค๋๋ค โ
EnvDev(๊ฐ๋ฐ),EnvStg(ํ ์คํธ),EnvProd(์ด์) - ๋น๋ ์์ ์ ์๋์ผ๋ก ๋ง๋ ํ๊ฒฝ์ ๊ณจ๋ผ ์ฐ๋ ์ ํ ์ค์์น(
EnvConfig)๋ฅผ ๊ตฌ์ฑํฉ๋๋ค - ํ๊ฒฝ ๋ณ์ ํ์ผ(
.env.dev,.env.stg,.env.prod)๊ณผ ํด๋ ๊ตฌ์กฐ๋ฅผ ํ์ค ํํ๋ก ์ ๋ฆฌํฉ๋๋ค - API ํค์ฒ๋ผ ๋ฏผ๊ฐํ ๊ฐ์ ๋๋ ํ(์จ๊น ์ฒ๋ฆฌ) ํด์ ์ธ๋ถ์ ๊ทธ๋๋ก ๋ ธ์ถ๋์ง ์๊ฒ ํฉ๋๋ค
- iOS/์๋๋ก์ด๋/์น ๋ฑ ํ๋ซํผ๋ณ ๋ถ๊ธฐ๋ฅผ ๋ค๋ฃจ๋ ์ค์ (
PlatformConfig)๋ ํจ๊ป ๋ง๋ค์ด ์ค๋๋ค
์ด๋ป๊ฒ ์ฐ๋์#
# ๊ธฐ๋ณธ (๊ฐ๋ฐ ํ๊ฒฝ ์ค์ ์์
)
/shared:config
# ์ด์ ํ๊ฒฝ์ ์ ํ๊ฒฝ ๋ณ์๋ฅผ ์ถ๊ฐํ๊ณ , ๋น๋ฐ๊ฐ์ผ๋ก ์จ๊น ์ฒ๋ฆฌ
/shared:config env_type=prod variable_name=API_KEY is_secret=true
-
env_typeโ ์ด๋ค ํ๊ฒฝ์ธ์ง (dev,stg,prod/ ๊ธฐ๋ณธ๊ฐdev) variable_nameโ ์ถ๊ฐํ ํ๊ฒฝ ๋ณ์ ์ด๋ฆis_secretโ ๊ฐ์ ์จ๊ธธ์ง ์ฌ๋ถ (๊ธฐ๋ณธ๊ฐ false, API ํค ๋ฑ์ ๊ถ์ฅ)
์ค์ ์ฝ๋๋ฅผ ๋ง๋ ๋ค์๋ ๋น๋ ๋ช ๋ น์ ํ๊ฒฝ์ ์ง์ ํด ์คํํฉ๋๋ค.
# ํ๊ฒฝ ๋ณ์ ์ฝ๋ ์์ฑ
melos run generate:env
# ํน์ ํ๊ฒฝ์ผ๋ก ๋น๋/์คํ
flutter build apk --dart-define=FLAVOR=production
flutter run --dart-define=FLAVOR=development
์์์ ๋ฌด์จ ์ผ์ด ๋ฒ์ด์ง๋์#
- ํ๊ฒฝ๋ณ ์ค์ ํด๋์ค 3์ข
(dev/stg/prod)์ ๋ง๋ค์ด, ๊ฐ ํ๊ฒฝ์
.envํ์ผ์์ ๊ฐ์ ์ฝ์ด์ค๊ฒ ํฉ๋๋ค. -
์ ํ ์ค์์น(
EnvConfig) ๋ฅผ ๋ง๋ค์ด, ๋น๋ํ ๋ ์ง์ ํ ํ๊ฒฝ(--dart-define=FLAVOR=...)์ ๋ฐ๋ผ ์๋ง์ ๊ฐ์ ์๋์ผ๋ก ๊ณจ๋ผ ์ฐ๊ฒ ํฉ๋๋ค. - ๋ฏผ๊ฐํ ๊ฐ์ ๋๋
ํ ์ฒ๋ฆฌํ๊ณ , ํ๊ฒฝ ๋ณ์ ํ์ผ์ ์ค์๋ก ๊ณต์ ๋์ง ์๋๋ก
.gitignore์ ๋ฃ๋๋ก ์๋ดํฉ๋๋ค. - ํ๋ซํผ๋ณ ๋ถ๊ธฐ ์ค์ ์ ์ถ๊ฐํด, iOSยท์๋๋ก์ด๋ยท์น ๋ฑ ํ๊ฒฝ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ๋์ํด์ผ ํ ๋ถ๋ถ์ ์ ๋ฆฌํฉ๋๋ค.
- ๋ง์ง๋ง์ผ๋ก ์ฝ๋ ์์ฑ์ ๋๋ฆฌ๊ณ , ๋น ๋จ๋ฆฐ ๊ฒ ์๋์ง ์ฒดํฌ๋ฆฌ์คํธ๋ก ํ์ธํฉ๋๋ค.
โ๏ธ ์์ธ ์ต์ ยท์คํ ๋ช ์ธ (๊ฐ๋ฐ์ / AI ์์ด์ ํธ์ฉ)
Role#
Manages and creates environment-specific configurations.
- Environment variable management based on Envied annotations
- 3 environment classes (EnvProd, EnvStg, EnvDev)
- EnvConfig flavor switching pattern
- Platform-specific branching
Activation Conditions#
/shared:configActivated when command is invoked- Invoked during environment variable and config file work
Parameters#
| Parameter | Required | Description |
|---|---|---|
env_type | โ | dev, stg, prod (default: dev) |
variable_name | โ | Environment variable name to add |
is_secret | โ | Whether to obfuscate (default: false) |
Package Structure#
shared/config/
โโโ lib/
โ โโโ config.dart # Export ํ์ผ
โ โโโ src/
โ โโโ env/
โ โ โโโ env_config.dart # ํ๋ ์ด๋ฒ ์ค์์นญ
โ โ โโโ env_prod.dart # Production ํ๊ฒฝ
โ โ โโโ env_stg.dart # Staging ํ๊ฒฝ
โ โ โโโ env_dev.dart # Development ํ๊ฒฝ
โ โโโ generated/ # ์๋ ์์ฑ
โ โ โโโ env_prod.g.dart
โ โ โโโ env_stg.g.dart
โ โ โโโ env_dev.g.dart
โ โโโ platform/
โ โโโ platform_config.dart # ํ๋ซํผ๋ณ ๋ถ๊ธฐ
โโโ env/
โโโ .env.dev # ๊ฐ๋ฐ ํ๊ฒฝ ๋ณ์
โโโ .env.stg # ์คํ
์ด์ง ํ๊ฒฝ ๋ณ์
โโโ .env.prod # ํ๋ก๋์
ํ๊ฒฝ ๋ณ์Import Order (Required)#
// 1. Dart standard
import 'dart:io';
// 2. Envied package
import 'package:envied/envied.dart';
// 3. Generated files
part 'env_dev.g.dart';
Core Patterns#
1. Environment Class Definition (Envied)#
import 'package:envied/envied.dart';
part 'env_dev.g.dart';
/// Development environment ์ค์
///
/// [Envied]๋ฅผ ์ฌ์ฉํ์ฌ `.env.dev` ํ์ผ์ ํ๊ฒฝ ๋ณ์๋ฅผ ๋ก๋ํฉ๋๋ค.
@Envied(path: 'env/.env.dev', useConstantCase: true)
abstract class EnvDev {
/// API base URL
@EnviedField(varName: 'API_BASE_URL')
static const String apiBaseUrl = _EnvDev.apiBaseUrl;
/// API key (๋๋
ํ)
@EnviedField(varName: 'API_KEY', obfuscate: true)
static final String apiKey = _EnvDev.apiKey;
/// Firebase ํ๋ก์ ํธ ID
@EnviedField(varName: 'FIREBASE_PROJECT_ID')
static const String firebaseProjectId = _EnvDev.firebaseProjectId;
/// Debug mode enabled
@EnviedField(varName: 'DEBUG_MODE', defaultValue: 'true')
static const String debugMode = _EnvDev.debugMode;
/// Serverpod ์๋ฒ URL
@EnviedField(varName: 'SERVERPOD_URL')
static const String serverpodUrl = _EnvDev.serverpodUrl;
/// Serverpod ํฌํธ
@EnviedField(varName: 'SERVERPOD_PORT', defaultValue: '8080')
static const String serverpodPort = _EnvDev.serverpodPort;
}
2. Environment Class (Staging)#
import 'package:envied/envied.dart';
part 'env_stg.g.dart';
/// Staging environment ์ค์
@Envied(path: 'env/.env.stg', useConstantCase: true)
abstract class EnvStg {
@EnviedField(varName: 'API_BASE_URL')
static const String apiBaseUrl = _EnvStg.apiBaseUrl;
@EnviedField(varName: 'API_KEY', obfuscate: true)
static final String apiKey = _EnvStg.apiKey;
@EnviedField(varName: 'FIREBASE_PROJECT_ID')
static const String firebaseProjectId = _EnvStg.firebaseProjectId;
@EnviedField(varName: 'DEBUG_MODE', defaultValue: 'false')
static const String debugMode = _EnvStg.debugMode;
@EnviedField(varName: 'SERVERPOD_URL')
static const String serverpodUrl = _EnvStg.serverpodUrl;
@EnviedField(varName: 'SERVERPOD_PORT', defaultValue: '8080')
static const String serverpodPort = _EnvStg.serverpodPort;
}
3. Environment Class (Production)#
import 'package:envied/envied.dart';
part 'env_prod.g.dart';
/// Production environment ์ค์
@Envied(path: 'env/.env.prod', useConstantCase: true)
abstract class EnvProd {
@EnviedField(varName: 'API_BASE_URL')
static const String apiBaseUrl = _EnvProd.apiBaseUrl;
@EnviedField(varName: 'API_KEY', obfuscate: true)
static final String apiKey = _EnvProd.apiKey;
@EnviedField(varName: 'FIREBASE_PROJECT_ID')
static const String firebaseProjectId = _EnvProd.firebaseProjectId;
@EnviedField(varName: 'DEBUG_MODE', defaultValue: 'false')
static const String debugMode = _EnvProd.debugMode;
@EnviedField(varName: 'SERVERPOD_URL')
static const String serverpodUrl = _EnvProd.serverpodUrl;
@EnviedField(varName: 'SERVERPOD_PORT', defaultValue: '443')
static const String serverpodPort = _EnvProd.serverpodPort;
}
4. Flavor Switching Pattern#
/// Environment type enum
enum Flavor {
/// Development environment
development,
/// Staging environment
staging,
/// Production environment
production,
}
/// Environment configuration manager
///
/// Provides configuration values based on current environment at runtime.
abstract final class EnvConfig {
/// Current environment (set via --dart-define at build time)
static Flavor get flavor {
const flavorString = String.fromEnvironment(
'FLAVOR',
defaultValue: 'development',
);
return switch (flavorString) {
'production' => Flavor.production,
'staging' => Flavor.staging,
_ => Flavor.development,
};
}
/// API base URL
static String get apiBaseUrl => switch (flavor) {
Flavor.production => EnvProd.apiBaseUrl,
Flavor.staging => EnvStg.apiBaseUrl,
Flavor.development => EnvDev.apiBaseUrl,
};
/// API key
static String get apiKey => switch (flavor) {
Flavor.production => EnvProd.apiKey,
Flavor.staging => EnvStg.apiKey,
Flavor.development => EnvDev.apiKey,
};
/// Firebase ํ๋ก์ ํธ ID
static String get firebaseProjectId => switch (flavor) {
Flavor.production => EnvProd.firebaseProjectId,
Flavor.staging => EnvStg.firebaseProjectId,
Flavor.development => EnvDev.firebaseProjectId,
};
/// Debug mode enabled
static bool get isDebugMode => switch (flavor) {
Flavor.production => EnvProd.debugMode == 'true',
Flavor.staging => EnvStg.debugMode == 'true',
Flavor.development => EnvDev.debugMode == 'true',
};
/// Serverpod URL
static String get serverpodUrl => switch (flavor) {
Flavor.production => EnvProd.serverpodUrl,
Flavor.staging => EnvStg.serverpodUrl,
Flavor.development => EnvDev.serverpodUrl,
};
/// Serverpod ํฌํธ
static int get serverpodPort => switch (flavor) {
Flavor.production => int.parse(EnvProd.serverpodPort),
Flavor.staging => int.parse(EnvStg.serverpodPort),
Flavor.development => int.parse(EnvDev.serverpodPort),
};
/// Production environment ์ฌ๋ถ
static bool get isProduction => flavor == Flavor.production;
/// Development environment ์ฌ๋ถ
static bool get isDevelopment => flavor == Flavor.development;
/// Staging environment ์ฌ๋ถ
static bool get isStaging => flavor == Flavor.staging;
}
5. Platform-specific Branching#
import 'dart:io';
import 'package:flutter/foundation.dart';
/// ํ๋ซํผ ์ค์
abstract final class PlatformConfig {
/// Current platform
static TargetPlatform get platform {
if (kIsWeb) return TargetPlatform.android; // ์น์ Android๋ก ์ทจ๊ธ
if (Platform.isIOS) return TargetPlatform.iOS;
if (Platform.isAndroid) return TargetPlatform.android;
if (Platform.isMacOS) return TargetPlatform.macOS;
if (Platform.isWindows) return TargetPlatform.windows;
if (Platform.isLinux) return TargetPlatform.linux;
return TargetPlatform.android;
}
/// Is iOS
static bool get isIOS => !kIsWeb && Platform.isIOS;
/// Is Android
static bool get isAndroid => !kIsWeb && Platform.isAndroid;
/// Is Web
static bool get isWeb => kIsWeb;
/// Is Desktop
static bool get isDesktop =>
!kIsWeb &&
(Platform.isMacOS || Platform.isWindows || Platform.isLinux);
/// Is Mobile
static bool get isMobile =>
!kIsWeb && (Platform.isIOS || Platform.isAndroid);
}
6. .env File Format#
# .env.dev
API_BASE_URL=https://dev-api.example.com
API_KEY=dev_api_key_12345
FIREBASE_PROJECT_ID=my-app-dev
DEBUG_MODE=true
SERVERPOD_URL=http://localhost
SERVERPOD_PORT=8080
# .env.stg
API_BASE_URL=https://stg-api.example.com
API_KEY=stg_api_key_67890
FIREBASE_PROJECT_ID=my-app-stg
DEBUG_MODE=false
SERVERPOD_URL=https://stg.example.com
SERVERPOD_PORT=8080
# .env.prod
API_BASE_URL=https://api.example.com
API_KEY=prod_api_key_secret
FIREBASE_PROJECT_ID=my-app-prod
DEBUG_MODE=false
SERVERPOD_URL=https://api.example.com
SERVERPOD_PORT=443Build Commands#
# Generate environment variable code
melos run generate:env
# Build for specific environment
flutter build apk --dart-define=FLAVOR=production
flutter build ios --dart-define=FLAVOR=staging
flutter run --dart-define=FLAVOR=development
# Clean build
cd shared/config & & dart run build_runner build --delete-conflicting-outputsEnvied Annotation Types#
| Annotation | Purpose | Example |
|---|---|---|
@Envied | Environment class definition | @Envied(path: 'env/.env.dev') |
@EnviedField | Environment variable field | @EnviedField(varName: 'API_KEY') |
obfuscate: true | Value obfuscation | Used for API keys, secrets |
defaultValue | Default value setting | Used for optional variables |
useConstantCase: true | Auto constant case conversion | apiBaseUrl -> API_BASE_URL |
Reference Files#
shared/config/lib/src/env/env_dev.dart
shared/config/lib/src/env/env_stg.dart
shared/config/lib/src/env/env_prod.dart
shared/config/lib/src/env/env_config.dart
shared/config/lib/src/platform/platform_config.dartChecklist#
- Define 3 environment classes (dev, stg, prod)
- Connect .g.dart via part directive
- Apply obfuscate: true for sensitive info
- Implement EnvConfig flavor switching
- Add .env files to .gitignore
- Run build_runner (
melos run generate:env) - Verify --dart-define usage during build