Flutter 튜토리얼 79편: 빌드 모드(Debug/Profile/Release)
요약
핵심 요지
- Flutter는 Debug, Profile, Release 세 가지 빌드 모드를 제공합니다.
- Debug 모드는 개발 중 Hot Reload와 디버깅을 위해 사용합니다.
- Profile 모드는 성능 분석을 위해 사용합니다.
- Release 모드는 앱 배포 시 최적화된 빌드를 생성합니다.
문서가 설명하는 범위
Flutter 공식 문서의 Flutter’s build modes를 기반으로 각 빌드 모드의 특징, 사용 시점, 명령어를 설명합니다.
참고 자료
문제 상황
Flutter 앱을 개발하다 보면 여러 상황을 마주하게 됩니다.
- 개발 중에는 코드를 수정하고 즉시 결과를 확인하고 싶습니다.
- 앱이 느리게 동작할 때 어디서 병목이 발생하는지 파악해야 합니다.
- 스토어에 출시할 때는 가장 최적화된 버전이 필요합니다.
각 상황에 맞는 빌드 모드를 선택해야 합니다.
해결 방법
챕터 1: 빌드 모드 개요
Why: 왜 빌드 모드가 필요한가요?
개발, 테스트, 배포는 각각 다른 요구사항을 가집니다. 개발 중에는 빠른 반복 작업이 중요하고, 배포 시에는 성능과 보안이 중요합니다.
What: 세 가지 빌드 모드
Flutter는 개발 단계에 따라 세 가지 모드를 제공합니다.
| 모드 | 용도 | 특징 |
|---|---|---|
| Debug | 개발 | Hot Reload, 디버깅 도구 |
| Profile | 성능 분석 | 성능 측정, 일부 디버깅 |
| Release | 배포 | 최적화, 최소 용량 |
How: 빌드 모드 선택하기
# Debug 모드 (기본값)flutter run
# Profile 모드flutter run --profile
# Release 모드flutter run --release빌드 명령어도 모드를 지정할 수 있습니다.
# Release APK 빌드flutter build apk --release
# Release iOS 빌드flutter build ios --releaseWatch out: 주의할 점
- 에뮬레이터/시뮬레이터에서는 Debug 모드만 실행됩니다.
- Profile과 Release 모드는 실제 기기에서만 테스트하세요.
- Debug 모드의 성능으로 앱 성능을 판단하지 마세요.
챕터 2: Debug 모드
Why: 왜 Debug 모드를 사용하나요?
Debug 모드는 개발 생산성을 극대화합니다. 코드 수정 후 즉시 결과를 확인하고, 문제가 생기면 바로 디버깅할 수 있습니다.
What: Debug 모드의 특징
활성화되는 기능
- Hot Reload: 코드 변경 즉시 반영
- Assertions1:
assert()문 실행 - 서비스 확장: DevTools 연결 가능
- 소스 레벨 디버깅: 브레이크포인트, 스텝 실행
비활성화되는 최적화
- 코드 최적화 없음 (빌드 속도 우선)
- Tree Shaking2 없음
- 코드 압축(minification) 없음
How: Debug 모드 사용하기
IDE에서 실행
Android Studio에서는 Run > Debug... 메뉴를 선택하거나, 벌레 아이콘이 있는 실행 버튼을 클릭합니다.
VS Code에서는 F5 키를 누르거나 Run > Start Debugging을 선택합니다.
명령줄에서 실행
flutter run디버깅 활용
void processData(List<int> data) { // Debug 모드에서만 실행되는 검증 assert(data.isNotEmpty, 'Data must not be empty');
// assert가 false면 AssertionError 발생 assert(data.length <= 1000, 'Too many items: ${data.length}');
// 실제 로직 for (var item in data) { process(item); }}빌드 모드 확인
import 'package:flutter/foundation.dart';
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( // Debug 모드에서만 배너 표시 debugShowCheckedModeBanner: kDebugMode, home: Scaffold( body: Center( child: Text( kDebugMode ? 'Debug Build' : 'Release Build', ), ), ), ); }}Watch out: 주의할 점
- Debug 모드는 성능이 느립니다. 실제 성능 테스트는 Profile/Release 모드에서 하세요.
print()문은 Debug 모드에서만 사용하고, Release 빌드 전에 제거하세요.- 에뮬레이터에서는 실제 기기보다 느릴 수 있습니다.
챕터 3: Release 모드
Why: 왜 Release 모드를 사용하나요?
Release 모드는 사용자에게 배포할 최적화된 앱을 생성합니다. 가장 빠른 실행 속도와 가장 작은 파일 크기를 달성합니다.
What: Release 모드의 특징
활성화되는 최적화
- AOT(Ahead-of-Time) 컴파일3: 미리 기계어로 변환
- Tree Shaking: 사용하지 않는 코드 제거
- 코드 압축: 변수명, 함수명 축소
- 디버그 정보 제거
비활성화되는 기능
- Hot Reload/Hot Restart
- Assertions
- 디버깅 도구 연결
- 서비스 확장
How: Release 모드 사용하기
앱 실행
# 실제 기기에서 Release 모드로 실행flutter run --release빌드 생성
# Android APKflutter build apk --release
# Android App Bundle (권장)flutter build appbundle --release
# iOSflutter build ios --release
# Webflutter build web --releaseRelease 전용 코드 분기
import 'package:flutter/foundation.dart';
class AppConfig { static String get apiBaseUrl { if (kReleaseMode) { return 'https://api.production.example.com'; } else { return 'https://api.dev.example.com'; } }
static void log(String message) { // Release 모드에서는 로깅하지 않음 if (!kReleaseMode) { print(message); } }}조건부 import
// 환경별 설정 파일import 'config_dev.dart' if (dart.library.html) 'config_web.dart';Watch out: 주의할 점
- Release 빌드는 에뮬레이터에서 실행되지 않습니다.
print()문이 남아있으면 앱 크기와 성능에 영향을 줍니다.- 난독화(obfuscation)를 추가하면 보안이 강화됩니다.
# 난독화 적용 빌드flutter build apk --obfuscate --split-debug-info=./debug-info챕터 4: Profile 모드
Why: 왜 Profile 모드를 사용하나요?
Profile 모드는 Release에 가까운 성능을 유지하면서 성능 분석 도구를 사용할 수 있습니다. 실제 사용자 환경과 비슷한 조건에서 병목 지점을 찾을 수 있습니다.
What: Profile 모드의 특징
Release와 동일한 점
- AOT 컴파일
- 대부분의 최적화 적용
추가로 활성화되는 기능
- 성능 오버레이
- 트레이싱
- DevTools 연결
- 타임라인 이벤트 수집
How: Profile 모드 사용하기
앱 실행
flutter run --profile성능 오버레이 활성화
MaterialApp( showPerformanceOverlay: true, // 성능 오버레이 표시 home: MyHomePage(),)DevTools로 분석
# 앱 실행 후 DevTools 열기flutter run --profile# 다른 터미널에서dart devtoolsDevTools에서 확인할 수 있는 정보는 다음과 같습니다.
- Timeline: 프레임별 렌더링 시간
- CPU Profiler: 함수별 실행 시간
- Memory: 메모리 사용량 및 누수
- Network: 네트워크 요청 분석
성능 측정 코드
import 'dart:developer' as developer;
void expensiveOperation() { // 타임라인에 이벤트 기록 developer.Timeline.startSync('ExpensiveOperation');
// 무거운 작업 수행 for (var i = 0; i < 1000000; i++) { // computation }
developer.Timeline.finishSync();}Watch out: 주의할 점
- Profile 모드도 에뮬레이터에서는 실행되지 않습니다.
- 웹 앱은 Profile 모드에서 DevTools 연결이 제한됩니다. Chrome DevTools를 사용하세요.
- Profile 모드의 성능이 Release와 약간 다를 수 있습니다.
챕터 5: 플랫폼별 빌드 옵션
Why: 왜 플랫폼별로 다른 설정이 필요한가요?
각 플랫폼(iOS, Android, Web)은 고유한 빌드 시스템과 요구사항을 가집니다. 최적의 결과물을 얻으려면 플랫폼별 옵션을 이해해야 합니다.
What: 플랫폼별 특성
Android
# APK 빌드 (직접 설치용)flutter build apk --release
# App Bundle 빌드 (Play Store용)flutter build appbundle --release
# 아키텍처별 분리 빌드flutter build apk --split-per-abiiOS
# iOS 앱 빌드flutter build ios --release
# IPA 생성 (배포용)flutter build ipa --releaseWeb
# 웹 앱 빌드flutter build web --release
# 렌더러 지정flutter build web --web-renderer canvaskit # 고품질flutter build web --web-renderer html # 작은 용량How: 빌드 옵션 활용하기
build.gradle 설정 (Android)
android { buildTypes { release { signingConfig signingConfigs.release minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt') } profile { initWith debug matchingFallbacks = ['debug', 'release'] } }}환경 변수 사용
# 환경 변수로 빌드 설정flutter run --dart-define=API_KEY=your_api_keyflutter build apk --dart-define=FLAVOR=production// 코드에서 환경 변수 사용const apiKey = String.fromEnvironment('API_KEY');const flavor = String.fromEnvironment('FLAVOR', defaultValue: 'development');Flavor 설정
여러 버전(개발, 스테이징, 프로덕션)을 관리할 때 유용합니다.
# Flavor로 빌드flutter run --flavor developmentflutter run --flavor productionflutter build apk --flavor production --releaseWatch out: 주의할 점
- App Bundle(.aab)은 Play Store 업로드 전용입니다. 직접 설치하려면 APK를 사용하세요.
- iOS 빌드는 macOS에서만 가능합니다.
- 웹의 CanvasKit 렌더러는 초기 로딩이 느리지만 그래픽 품질이 좋습니다.
챕터 6: 코드에서 빌드 모드 확인
Why: 왜 코드에서 빌드 모드를 확인하나요?
빌드 모드에 따라 다른 동작이 필요할 때가 있습니다. 개발 중에만 보여줄 디버그 정보나, 프로덕션에서만 활성화할 기능 등을 제어할 수 있습니다.
What: 빌드 모드 상수
Flutter는 package:flutter/foundation.dart에서 상수를 제공합니다.
import 'package:flutter/foundation.dart';
// bool 값kDebugMode // Debug 모드인지kProfileMode // Profile 모드인지kReleaseMode // Release 모드인지How: 실전 활용 패턴
로깅 제어
class Logger { static void debug(String message) { if (kDebugMode) { print('[DEBUG] $message'); } }
static void error(String message, [Object? error]) { if (kDebugMode) { print('[ERROR] $message'); if (error != null) print(error); } // Release에서는 에러 리포팅 서비스로 전송 if (kReleaseMode) { // Crashlytics, Sentry 등에 전송 } }}환경별 설정
class Environment { static const bool isProduction = kReleaseMode;
static String get apiUrl { if (kReleaseMode) { return 'https://api.myapp.com'; } else if (kProfileMode) { return 'https://staging-api.myapp.com'; } else { return 'http://localhost:3000'; } }
static Duration get cacheTimeout { // Debug에서는 캐시 짧게 return kDebugMode ? const Duration(seconds: 10) : const Duration(hours: 1); }}디버그 전용 UI
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, // 기본 배너 숨김 home: Stack( children: [ const MainScreen(), // Debug에서만 표시되는 정보 if (kDebugMode) const Positioned( top: 50, right: 10, child: DebugInfoPanel(), ), ], ), ); }}
class DebugInfoPanel extends StatelessWidget { const DebugInfoPanel({super.key});
@override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(8), color: Colors.black54, child: const Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text('Debug Build', style: TextStyle(color: Colors.red)), Text('v1.0.0+1', style: TextStyle(color: Colors.white)), ], ), ); }}Watch out: 주의할 점
kDebugMode,kReleaseMode는 컴파일 타임 상수입니다.- 조건문 안의 코드는 해당 모드가 아니면 컴파일에서 제외됩니다.
assert()안의 코드는 Release에서 완전히 제거됩니다.
한계
- 이 문서는 기본적인 빌드 모드를 설명합니다. 고급 CI/CD 설정은 다루지 않습니다.
- Flavor 구성의 상세한 설정(iOS Scheme, Android productFlavors)은 별도 학습이 필요합니다.
- 난독화와 코드 서명에 대한 자세한 내용은 배포 가이드를 참조하세요.
Footnotes
-
Assertions는 개발 중 조건을 검증하는 문입니다.
assert(condition, 'message')로 작성하며, 조건이 false면 에러가 발생합니다. Release 모드에서는 완전히 제거됩니다. ↩ -
Tree Shaking은 사용하지 않는 코드를 빌드에서 제거하는 최적화 기법입니다. 앱에서 호출되지 않는 함수나 클래스를 자동으로 제외하여 최종 바이너리 크기를 줄입니다. ↩
-
AOT(Ahead-of-Time) 컴파일은 실행 전에 미리 기계어로 변환하는 방식입니다. JIT(Just-in-Time)보다 시작 시간이 빠르고 성능이 일관됩니다. Release 빌드에서 사용됩니다. ↩
공유
이 글이 도움이 되었다면 다른 사람과 공유해주세요!