Flutter에서 릴리즈 모드 앱은 어떻게 디버깅하나요?
Flutter 릴리즈 모드 앱은 최적화되어 있고 디버그 정보가 제거되어 있어 일반적인 디버깅 방법을 사용하기 어렵습니다. 그러나 다음과 같은 방법을 통해 릴리즈 모드 앱을 디버깅할 수 있습니다.
1. 로깅(Logging) 활용
릴리즈 모드에서는 print
문이 기본적으로 출력되지 않습니다. 대신 더 강력한 로깅 시스템을 사용해야 합니다.
Flutter 로깅 패키지 사용
import 'package:logging/logging.dart';
void main() {
// 로거 설정
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
// 로그를 파일에 저장하거나 원격 서버로 전송
print('${record.level.name}: ${record.time}: ${record.message}');
});
runApp(MyApp());
}
// 앱 내에서 로깅
final logger = Logger('MyApp');
void someFunction() {
try {
// 코드 실행
logger.info('함수가 정상적으로 실행되었습니다.');
} catch (e, stackTrace) {
logger.severe('오류 발생: $e', e, stackTrace);
}
}
원격 로깅 서비스 사용
Firebase Crashlytics, Sentry 등의 서비스를 사용하여 원격으로 오류를 수집하고 분석할 수 있습니다.
import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
Future<void> main() async {
await SentryFlutter.init(
(options) {
options.dsn = 'https://example@sentry.io/example';
// 릴리즈 모드 설정
options.environment = 'production';
},
appRunner: () => runApp(MyApp()),
);
}
// 앱 내에서 사용
void someFunction() async {
try {
// 코드 실행
} catch (exception, stackTrace) {
await Sentry.captureException(
exception,
stackTrace: stackTrace,
);
}
}
2. 앱 성능 모니터링
Firebase Performance Monitoring
릴리즈 모드에서 앱의 성능을 측정하고 병목 현상을 찾아내는 데 도움이 됩니다.
import 'package:firebase_performance/firebase_performance.dart';
void trackNetworkRequest() async {
final metric = FirebasePerformance.instance
.newHttpMetric('https://api.example.com/data', HttpMethod.Get);
await metric.start();
try {
// 네트워크 요청 수행
final response = await http.get(Uri.parse('https://api.example.com/data'));
metric.httpResponseCode = response.statusCode;
metric.responsePayloadSize = response.contentLength;
} finally {
await metric.stop();
}
}
void trackCustomEvent() async {
final trace = FirebasePerformance.instance.newTrace('load_data');
await trace.start();
// 속성 추가
trace.putAttribute('user_id', '123');
try {
// 측정할 작업 수행
trace.incrementMetric('items_loaded', 10);
} finally {
await trace.stop();
}
}
커스텀 성능 측정
자체 성능 측정 코드를 작성하여 릴리즈 모드에서 특정 작업의 실행 시간을 측정할 수 있습니다.
class PerformanceMonitor {
static final Map<String, Stopwatch> _watches = {};
static void start(String operation) {
final watch = Stopwatch()..start();
_watches[operation] = watch;
}
static Duration stop(String operation) {
final watch = _watches.remove(operation);
if (watch == null) {
return Duration.zero;
}
watch.stop();
final duration = watch.elapsed;
// 로그 또는 분석 서비스로 전송
print('Operation $operation took ${duration.inMilliseconds}ms');
return duration;
}
}
// 사용 예
void loadData() {
PerformanceMonitor.start('data_loading');
// 데이터 로딩 로직
final duration = PerformanceMonitor.stop('data_loading');
if (duration.inMilliseconds > 1000) {
// 성능 이슈가 있는 경우 로깅
logger.warning('데이터 로딩에 ${duration.inSeconds}초 이상 소요됨');
}
}
3. Android에서 릴리즈 모드 앱 디버깅
ProGuard/R8 매핑 파일 활용
Android에서는 코드 축소 및 난독화가 적용되어 있을 때 ProGuard/R8 매핑 파일을 사용하여 난독화된 스택 트레이스를 디코딩할 수 있습니다.
android/app/build.gradle
파일에서:
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// 매핑 파일 저장
testProguardFile 'test-proguard-rules.pro'
}
}
난독화된 스택 트레이스를 원래 코드로 변환하려면 Android 스튜디오의 Analyze > Analyze Stack Trace
기능을 사용하고 매핑 파일을 참조하세요.
Android 로그캣으로 디버깅
릴리즈 모드 앱을 디바이스에 설치하고 Android Studio의 로그캣을 통해 앱에서 발생하는 로그를 확인할 수 있습니다. 이 경우 앱에 적절한 로깅 코드가 포함되어 있어야 합니다.
import 'package:flutter/foundation.dart';
import 'dart:developer' as developer;
void logError(String message, {Object? error, StackTrace? stackTrace}) {
// kReleaseMode는 릴리즈 모드에서 true입니다
if (kReleaseMode) {
developer.log(
message,
name: 'AppError',
error: error,
stackTrace: stackTrace,
);
} else {
print('Error: $message');
if (error != null) print(error);
if (stackTrace != null) print(stackTrace);
}
}
4. iOS에서 릴리즈 모드 앱 디버깅
심볼 파일(dSYM) 활용
iOS 릴리즈 빌드 시 생성되는 dSYM 파일을 사용하여 크래시 로그를 디코딩할 수 있습니다. 이 파일은 Xcode가 생성하며, Firebase Crashlytics나 Sentry와 같은 서비스에 업로드하여 크래시 보고서를 해석하는 데 사용됩니다.
콘솔 로그 확인
Xcode의 Console.app을 사용하여 iOS 디바이스에서 실행 중인 앱의 로그를 확인할 수 있습니다.
5. 네트워크 트래픽 분석
프록시 도구 활용
Charles, Fiddler와 같은 프록시 도구를 사용하여 앱의 네트워크 트래픽을 캡처하고 분석할 수 있습니다. 이를 통해 API 요청 및 응답을 검사하고 네트워크 관련 이슈를 디버깅할 수 있습니다.
디바이스에서:
- 프록시 설정 구성(Wi-Fi 설정에서)
- Charles/Fiddler를 컴퓨터에서 실행
- 릴리즈 모드 앱 실행
- 네트워크 트래픽 모니터링
앱 내 네트워크 로깅
앱 내에서 모든 네트워크 요청과 응답을 로깅하는 인터셉터를 구현할 수 있습니다.
import 'package:dio/dio.dart';
Dio createDioWithLogging() {
final dio = Dio();
dio.interceptors.add(
InterceptorsWrapper(
onRequest: (options, handler) {
logger.info('API 요청: ${options.method} ${options.uri}');
logger.info('요청 헤더: ${options.headers}');
logger.info('요청 데이터: ${options.data}');
return handler.next(options);
},
onResponse: (response, handler) {
logger.info('API 응답: ${response.statusCode} ${response.requestOptions.uri}');
logger.info('응답 데이터: ${response.data}');
return handler.next(response);
},
onError: (DioError e, handler) {
logger.severe('API 오류: ${e.message}', e, e.stackTrace);
logger.severe('요청 정보: ${e.requestOptions.method} ${e.requestOptions.uri}');
return handler.next(e);
},
),
);
return dio;
}
6. 사용자 피드백 메커니즘
앱 내에서 사용자가 문제를 보고할 수 있는 기능을 추가하면 릴리즈 환경에서 발생하는 이슈를 파악하는 데 도움이 됩니다.
Future<void> reportIssue(String description, {List<String>? logs}) async {
try {
// 디바이스 정보 수집
final deviceInfo = await DeviceInfoPlugin().androidInfo;
final packageInfo = await PackageInfo.fromPlatform();
// 보고서 작성
final report = {
'description': description,
'app_version': packageInfo.version,
'device_model': deviceInfo.model,
'android_version': deviceInfo.version.release,
'logs': logs,
'timestamp': DateTime.now().toIso8601String(),
};
// 서버로 전송
await http.post(
Uri.parse('https://your-api.com/bug-reports'),
body: jsonEncode(report),
headers: {'Content-Type': 'application/json'},
);
} catch (e) {
// 오류 처리
}
}
7. 플레이버/빌드 설정으로 디버깅 지원
릴리즈 빌드이지만 추가 디버깅 정보를 포함하는 특별한 빌드 구성을 만들 수 있습니다.
enum BuildType { debug, profile, release, releaseWithLogs }
// 앱 설정
class AppConfig {
static late final BuildType buildType;
static bool get isDebug => buildType == BuildType.debug;
static bool get isReleaseWithLogs => buildType == BuildType.releaseWithLogs;
static void init() {
// 빌드 환경에 따라 설정
const buildTypeString = String.fromEnvironment('BUILD_TYPE');
switch (buildTypeString) {
case 'debug':
buildType = BuildType.debug;
break;
case 'profile':
buildType = BuildType.profile;
break;
case 'releaseWithLogs':
buildType = BuildType.releaseWithLogs;
break;
default:
buildType = BuildType.release;
}
// 로깅 설정
if (isDebug || isReleaseWithLogs) {
Logger.root.level = Level.ALL;
} else {
Logger.root.level = Level.SEVERE; // 릴리즈에서는 심각한 오류만 로깅
}
}
}
요약
Flutter 릴리즈 모드 앱을 디버깅하는 주요 방법:
- 강력한 로깅 시스템 구축: 기본
print
대신 구조화된 로깅 시스템 사용 - 원격 모니터링 서비스 통합: Firebase Crashlytics, Sentry 등을 활용
- 성능 측정 도구 사용: Firebase Performance Monitoring 또는 커스텀 성능 측정
- 플랫폼별 도구 활용:
- Android: ProGuard/R8 매핑 파일과 로그캣
- iOS: dSYM 파일과 Console.app
- 네트워크 트래픽 분석: 프록시 도구와 네트워크 인터셉터 활용
- 사용자 피드백 수집: 앱 내 문제 보고 메커니즘 구현
- 특수 릴리즈 빌드 구성: 더 많은 디버깅 정보를 포함하는 특별 릴리즈 빌드 만들기
이러한 방법들을 조합하여 릴리즈 모드에서도 앱의 문제를 효과적으로 진단하고 해결할 수 있습니다.