LogoSkills

config-agent

Environment configuration management specialist. Used for Envied annotations and flavor switching pattern implementation

ํ•ญ๋ชฉ๋‚ด์šฉ
Invoke/shared:config
Aliases/env:create, /config:setup
ToolsRead, Edit, Write, Glob, Grep
Modelsonnet

/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

์•ˆ์—์„œ ๋ฌด์Šจ ์ผ์ด ๋ฒŒ์–ด์ง€๋‚˜์š”#

  1. ํ™˜๊ฒฝ๋ณ„ ์„ค์ • ํด๋ž˜์Šค 3์ข…(dev/stg/prod)์„ ๋งŒ๋“ค์–ด, ๊ฐ ํ™˜๊ฒฝ์˜ .env ํŒŒ์ผ์—์„œ ๊ฐ’์„ ์ฝ์–ด์˜ค๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  2. ์ „ํ™˜ ์Šค์œ„์น˜(EnvConfig) ๋ฅผ ๋งŒ๋“ค์–ด, ๋นŒ๋“œํ•  ๋•Œ ์ง€์ •ํ•œ ํ™˜๊ฒฝ(--dart-define=FLAVOR=...)์— ๋”ฐ๋ผ ์•Œ๋งž์€ ๊ฐ’์„ ์ž๋™์œผ๋กœ ๊ณจ๋ผ ์“ฐ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  3. ๋ฏผ๊ฐํ•œ ๊ฐ’์€ ๋‚œ๋…ํ™” ์ฒ˜๋ฆฌํ•˜๊ณ , ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ํŒŒ์ผ์€ ์‹ค์ˆ˜๋กœ ๊ณต์œ ๋˜์ง€ ์•Š๋„๋ก .gitignore์— ๋„ฃ๋„๋ก ์•ˆ๋‚ดํ•ฉ๋‹ˆ๋‹ค.
  4. ํ”Œ๋žซํผ๋ณ„ ๋ถ„๊ธฐ ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด, iOSยท์•ˆ๋“œ๋กœ์ด๋“œยท์›น ๋“ฑ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•ด์•ผ ํ•  ๋ถ€๋ถ„์„ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  5. ๋งˆ์ง€๋ง‰์œผ๋กœ ์ฝ”๋“œ ์ƒ์„ฑ์„ ๋Œ๋ฆฌ๊ณ , ๋น ๋œจ๋ฆฐ ๊ฒŒ ์—†๋Š”์ง€ ์ฒดํฌ๋ฆฌ์ŠคํŠธ๋กœ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

โš™๏ธ ์ƒ์„ธ ์˜ต์…˜ยท์‹คํ–‰ ๋ช…์„ธ (๊ฐœ๋ฐœ์ž / 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:config Activated when command is invoked
  • Invoked during environment variable and config file work

Parameters#

ParameterRequiredDescription
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=443

Build 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-outputs

Envied Annotation Types#

AnnotationPurposeExample
@EnviedEnvironment class definition@Envied(path: 'env/.env.dev')
@EnviedFieldEnvironment variable field@EnviedField(varName: 'API_KEY')
obfuscate: trueValue obfuscationUsed for API keys, secrets
defaultValueDefault value settingUsed for optional variables
useConstantCase: trueAuto constant case conversionapiBaseUrl -> 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.dart

Checklist#

  • 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