Flutter 오류 대응법 가이드
Flutter 앱을 개발하면서 다양한 오류에 직면할 수 있습니다. 이 문서에서는 자주 발생하는 Flutter 오류들과 그 해결 방법을 소개합니다.
개발 환경 오류
Section titled “개발 환경 오류”Flutter Doctor 오류
Section titled “Flutter Doctor 오류”Flutter SDK를 설치한 후에는 항상 flutter doctor
명령어를 실행하여 개발 환경을 확인하는 것이 좋습니다.
오류 메시지 | 원인 | 해결 방법 |
---|---|---|
Flutter requires Android SDK | Android SDK가 설치되지 않았거나 경로가 설정되지 않음 | Android Studio를 설치하거나, Android SDK 경로를 Flutter 환경 변수에 추가 |
Android licenses not accepted | Android 라이선스 동의가 필요함 | flutter doctor --android-licenses 실행 후 동의 |
Xcode not installed | iOS 개발을 위한 Xcode가 설치되지 않음 (macOS만 해당) | App Store에서 Xcode 설치 |
CocoaPods not installed | iOS 종속성 관리 도구가 설치되지 않음 | sudo gem install cocoapods 명령어로 설치 |
pubspec.yaml 관련 오류
Section titled “pubspec.yaml 관련 오류”Because every version of flutter_package from sdk depends on intl ^0.17.0 and app depends on intl ^0.18.0, flutter_package from sdk is forbidden.
원인: 패키지 간 의존성 충돌
해결 방법: 패키지 버전을 호환되는 범위로 조정하고 flutter pub upgrade --major-versions
실행
Gradle 관련 오류
Section titled “Gradle 관련 오류”Gradle task assembleDebug failed with exit code 1
원인: 다양한 Gradle 구성 문제 발생 가능 해결 방법:
- 안드로이드 폴더에서
./gradlew clean
실행 flutter clean
실행 후 다시 빌드- Gradle 버전 확인 및 업데이트
iOS 빌드 오류
Section titled “iOS 빌드 오류”[!] CocoaPods could not find compatible versions for pod "Firebase/Core"
원인: CocoaPods 의존성 충돌 해결 방법:
- iOS 폴더에서
pod repo update
실행 pod install --repo-update
실행Podfile.lock
삭제 후pod install
다시 실행
코드 서명 오류
Section titled “코드 서명 오류”No provisioning profile matches the specified entitlements
원인: iOS 앱 서명 설정 문제 해결 방법:
- Xcode를 열고 팀 설정 확인
- 프로비저닝 프로필 업데이트
- 앱 ID와 번들 ID가 일치하는지 확인
런타임 오류
Section titled “런타임 오류”위젯 빌드 오류
Section titled “위젯 빌드 오류”setState() 오류
Section titled “setState() 오류”setState() called after dispose(): _MyWidgetState#a7c89(lifecycle state: defunct, not mounted)
원인: 위젯이 제거된 후 setState 호출 해결 방법:
// 수정 전void fetchData() async { final data = await apiService.getData(); setState(() { this.data = data; });}
// 수정 후void fetchData() async { final data = await apiService.getData(); if (mounted) { setState(() { this.data = data; }); }}
RenderFlex 오버플로우
Section titled “RenderFlex 오버플로우”A RenderFlex overflowed by 20 pixels on the bottom
원인: 자식 위젯이 부모 위젯의 제약 조건을 초과 해결 방법:
Expanded
또는Flexible
위젯 사용SingleChildScrollView
로 스크롤 가능하게 만들기ConstrainedBox
를 사용하여 최대 크기 제한
// 오류 발생 코드Column( children: [ LargeWidget(), AnotherLargeWidget(), ],)
// 해결 방법 (스크롤 적용)SingleChildScrollView( child: Column( children: [ LargeWidget(), AnotherLargeWidget(), ], ),)
// 또는 Expanded 사용 (부모가 Row/Column일 경우)Column( children: [ Expanded(child: LargeWidget()), AnotherWidget(), ],)
상태 관리 오류
Section titled “상태 관리 오류”Provider 관련 오류
Section titled “Provider 관련 오류”Error: Could not find the correct Provider<MyModel> above this Consumer Widget
원인: Provider가 위젯 트리의 상위에 없음 해결 방법: 적절한 위치에 Provider 배치
// 수정 전 (문제가 되는 구조)Widget build(BuildContext context) { return Consumer<MyModel>( builder: (context, model, child) { return Text(model.data); }, );}
// 수정 후 (올바른 구조)Widget build(BuildContext context) { return ChangeNotifierProvider( create: (_) => MyModel(), child: Consumer<MyModel>( builder: (context, model, child) { return Text(model.data); }, ), );}
Riverpod 관련 오류
Section titled “Riverpod 관련 오류”ProviderNotFoundException: No provider found for providerHash:xxx
원인: ProviderScope 범위 밖에서 provider에 접근 시도 해결 방법: 앱 루트에 ProviderScope 추가
void main() { runApp( ProviderScope( child: MyApp(), ), );}
네트워크 관련 오류
Section titled “네트워크 관련 오류”CORS 오류 (웹)
Section titled “CORS 오류 (웹)”Access to XMLHttpRequest has been blocked by CORS policy
원인: 웹 버전에서 CORS 정책 위반 해결 방법:
- 서버 측에서 적절한 CORS 헤더 설정
- 개발 시에는 Chrome을
--disable-web-security
옵션으로 실행 - 프록시 서버 사용
인증서 오류
Section titled “인증서 오류”HandshakeException: Handshake error in client
원인: SSL 인증서 문제 해결 방법:
// 주의: 프로덕션 앱에서는 사용하지 않는 것이 좋습니다HttpClient client = HttpClient() ..badCertificateCallback = (X509Certificate cert, String host, int port) => true;
비동기 처리 오류
Section titled “비동기 처리 오류”FutureBuilder 에러
Section titled “FutureBuilder 에러”type 'Future<dynamic>' is not a subtype of type 'Future<List<String>>'
원인: Future의 타입 불일치 해결 방법: 명시적 타입 지정
// 수정 전Future fetchData() async { // ... return data;}
// 수정 후Future<List<String>> fetchData() async { // ... return data;}
스트림 오류
Section titled “스트림 오류”Bad state: Stream has already been listened to
원인: 단일 구독 스트림에 여러 번 구독 시도
해결 방법: Stream.asBroadcastStream()
사용 또는 StreamController
에서 broadcast: true
설정
플랫폼별 오류
Section titled “플랫폼별 오류”Android 특정 오류
Section titled “Android 특정 오류”다중 DEX 문제
Section titled “다중 DEX 문제”Execution failed for task ':app:mergeDebugResources'. > java.lang.OutOfMemoryError
원인: 메서드 수가 DEX 제한을 초과
해결 방법: android/app/build.gradle
에 멀티덱스 활성화
android { defaultConfig { multiDexEnabled true }}
dependencies { implementation 'androidx.multidex:multidex:2.0.1'}
권한 관련 오류
Section titled “권한 관련 오류”PlatformException: Permission denied
원인: 필요한 권한이 설정되지 않음
해결 방법: AndroidManifest.xml
에 필요한 권한 추가 및 런타임 권한 요청 구현
<manifest> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CAMERA" /> <!-- 필요한 권한 추가 --></manifest>
iOS 특정 오류
Section titled “iOS 특정 오류”Info.plist 관련 오류
Section titled “Info.plist 관련 오류”This app has crashed because it attempted to access privacy-sensitive data without a usage description.
원인: 필요한 권한 설명이 Info.plist에 없음
해결 방법: ios/Runner/Info.plist
에 관련 설명 추가
<key>NSCameraUsageDescription</key><string>이 앱은 프로필 사진 등록을 위해 카메라에 접근합니다.</string><key>NSPhotoLibraryUsageDescription</key><string>이 앱은 사진 업로드를 위해 갤러리에 접근합니다.</string>
미니멈 iOS 버전 오류
Section titled “미니멈 iOS 버전 오류”The iOS deployment target is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.0.
원인: 지원되지 않는 iOS 최소 버전 설정
해결 방법: ios/Podfile
및 Xcode 프로젝트 설정에서 최소 버전 업데이트
# Podfileplatform :ios, '12.0'
Web 특정 오류
Section titled “Web 특정 오류”자바스크립트 오류
Section titled “자바스크립트 오류”TypeError: Cannot read property 'X' of undefined
원인: 웹용 플러그인이 올바르게 초기화되지 않음 해결 방법: 플랫폼 조건부 코드 사용
import 'package:flutter/foundation.dart' show kIsWeb;
if (kIsWeb) { // 웹 전용 코드} else { // 모바일 전용 코드}
웹 리소스 로딩 오류
Section titled “웹 리소스 로딩 오류”Failed to load resource: net::ERR_BLOCKED_BY_CLIENT
원인: 광고 차단기가 리소스 로딩 차단 해결 방법: 리소스 URL 패턴 변경 또는 필수 리소스임을 사용자에게 알림
내부적인 패키지 오류
Section titled “내부적인 패키지 오류”플러그인 호환성 문제
Section titled “플러그인 호환성 문제”MissingPluginException(No implementation found for method X on channel Y)
원인: 플러그인 초기화 문제 또는 플랫폼 지원 부재 해결 방법:
- 앱 재시작
flutter clean
실행 후 다시 빌드- 플러그인 호환성 확인 및 조건부 코드 작성
패키지 버전 충돌
Section titled “패키지 버전 충돌”Undefined name 'X'. (The name X isn't a type and can't be used in a declaration)
원인: API 변경으로 인한 코드 호환성 문제 해결 방법:
- 패키지 버전 확인 및 호환되는 버전 사용
- 최신 API에 맞게 코드 업데이트
- 코드 마이그레이션 가이드 참조
메모리 관련 오류
Section titled “메모리 관련 오류”메모리 누수
Section titled “메모리 누수”컨트롤러 누수
Section titled “컨트롤러 누수”A Timer still exists even after the widget tree was disposed.
원인: dispose 메서드에서 타이머, 컨트롤러 등을 해제하지 않음 해결 방법:
class _MyWidgetState extends State<MyWidget> { AnimationController _controller; Timer _timer;
@override void initState() { super.initState(); _controller = AnimationController(vsync: this); _timer = Timer.periodic(Duration(seconds: 1), (_) => update()); }
@override void dispose() { _controller.dispose(); // 컨트롤러 해제 _timer.cancel(); // 타이머 취소 super.dispose(); }}
대용량 이미지 메모리 문제
Section titled “대용량 이미지 메모리 문제”Out of memory: Bytes allocation failed
원인: 과도한 메모리 사용 해결 방법:
- 이미지 캐싱 라이브러리 사용 (cached_network_image)
- 적절한 크기의 이미지 로드 (ResizeImage 또는 서버 측 리사이징)
- 메모리에 저장하는 데이터 제한
코드 품질 오류
Section titled “코드 품질 오류”The parameter 'onPressed' is required
원인: 필수 매개변수 누락 해결 방법: 코드 분석 도구가 지적한 문제 해결
과도한 빌드 호출
Section titled “과도한 빌드 호출”원인: 불필요한 위젯 리빌드로 인한 성능 저하 해결 방법:
const
생성자 사용- 상태 변경을 더 작은 위젯으로 분리
RepaintBoundary
를 사용하여 리페인트 영역 제한
// 개선 전ListView.builder( itemBuilder: (context, index) { return MyListItem(data: items[index]); })
// 개선 후ListView.builder( itemBuilder: (context, index) { return RepaintBoundary( child: const MyListItem(data: items[index]), ); })
디버깅 도구
Section titled “디버깅 도구”Flutter DevTools
Section titled “Flutter DevTools”Flutter DevTools는 Flutter 앱 개발 시 강력한 디버깅 도구입니다. 다음과 같은 기능을 제공합니다:
- Inspector: 위젯 트리 분석 및 레이아웃 디버깅
- Performance: 프레임 드롭 분석
- Memory: 메모리 사용량 및 누수 확인
- Network: 네트워크 요청 모니터링
- Logging: 로그 확인 및 필터링
효과적인 로깅 전략
Section titled “효과적인 로깅 전략”효과적인 로깅은 문제를 빠르게 파악하는 데 도움이 됩니다:
-
로깅 레벨 구분:
import 'package:logger/logger.dart';final logger = Logger(printer: PrettyPrinter(),);// 다양한 로깅 레벨 사용logger.v("Verbose log");logger.d("Debug log");logger.i("Info log");logger.w("Warning log");logger.e("Error log"); -
예외 처리:
try {// 위험한 작업} catch (e, stackTrace) {logger.e("오류 발생", error: e, stackTrace: stackTrace);}
Flutter 앱 개발 중 발생하는 대부분의 오류는 체계적인 접근 방식으로 해결할 수 있습니다. 문제를 정확히 이해하고, 검색 엔진이나 Stack Overflow 같은 자료를 활용하며, 필요하다면 Flutter 이슈 트래커나 커뮤니티에 도움을 요청하세요.
오류 메시지를 무시하지 말고, 로그를 주의 깊게 읽고 분석하는 습관을 들이면 문제 해결 능력이 크게 향상됩니다. 또한 정기적으로 Flutter와 종속성을 최신 상태로 유지하는 것도 많은 문제를 예방할 수 있는 좋은 방법입니다.