Flutter 튜토리얼 48편: 디버깅 도구 활용
요약
핵심 요지
- 문제 정의: 앱이 예상대로 동작하지 않을 때 원인을 찾아야 한다. 로그만으로는 복잡한 문제를 파악하기 어렵다.
- 핵심 주장: Flutter는 IDE 디버거, DevTools1, 코드 기반 디버깅 플래그를 제공해서 다양한 문제를 진단할 수 있다.
- 주요 근거: 위젯 트리, 렌더 트리, 레이어 트리를 덤프해서 레이아웃 문제를 찾고, 성능 오버레이로 프레임 드랍을 확인할 수 있다.
- 실무 기준: 간단한 문제는 로그로, 복잡한 레이아웃 문제는 인스펙터로, 성능 문제는 DevTools로 진단한다.
- 한계: 디버그 모드에서만 작동하는 기능이 많아서 릴리스 빌드의 문제는 별도 접근이 필요하다.
문서가 설명하는 범위
- DevTools 개요와 주요 기능
- IDE 내장 디버거와 브레이크포인트
- 로깅 (print, log, debugPrint)
- 위젯/렌더/레이어/포커스/시맨틱 트리 덤프
- 레이아웃 디버깅 플래그
- 성능 오버레이
읽는 시간: 20분 | 난이도: 초급
참고 자료
- Debug Flutter apps - 디버깅 도구 개요
- Debug Flutter apps from code - 코드 기반 디버깅
- Use a native language debugger - 네이티브 디버거 사용
문제 상황
앱 개발 중 다양한 문제가 발생합니다.
자주 겪는 디버깅 상황
// 문제 1: 위젯이 원하는 크기로 표시되지 않음Container( width: 100, // 왜 100px이 아니지? child: Text('Hello'),)
// 문제 2: 버튼 탭이 동작하지 않음GestureDetector( onTap: () => print('탭!'), // 왜 호출이 안 되지? child: Container(color: Colors.blue),)
// 문제 3: 앱이 버벅거림ListView.builder( itemCount: 1000, itemBuilder: (context, index) { // 어디서 성능 병목이 발생하지? return ExpensiveWidget(); },)이런 문제들은 코드만 봐서는 원인을 찾기 어렵습니다. 디버깅 도구가 필요합니다.
해결 방법
Flutter는 다양한 디버깅 도구를 제공합니다.
챕터 1: IDE 디버거와 브레이크포인트
Why
NOTEIDE 디버거는 가장 기본적인 디버깅 도구입니다. 코드 실행을 중단하고 변수 값을 검사할 수 있습니다.
지원 IDE:
- VS Code (Flutter/Dart 확장 필요)
- Android Studio / IntelliJ (Flutter 플러그인 필요)
What
NOTE브레이크포인트 설정 방법:
- IDE에서 설정: 라인 번호 왼쪽 클릭
- 코드에서 설정:
debugger()함수 사용import 'dart:developer';void someFunction(double offset) {// offset이 30을 초과하면 중단debugger(when: offset > 30);// 이후 코드...}
How
TIPVS Code 디버깅 단축키:
단축키 동작 F5 디버그 시작 F9 브레이크포인트 토글 F10 Step Over (다음 줄) F11 Step Into (함수 내부) Shift+F11 Step Out (함수 밖으로) 변수 검사:
브레이크포인트에서 중단되면 Debug 패널에서 변수 값을 확인할 수 있습니다. Watch 패널에 표현식을 추가해서 복잡한 값도 검사할 수 있습니다.
Watch out
WARNING디버그 모드 주의: 디버그 모드는 릴리스 모드보다 느립니다. 성능 문제를 진단할 때는 프로필 모드(
--profile)를 사용하세요.
챕터 2: 로깅 기법
Why
NOTE로깅은 가장 간단한 디버깅 방법입니다. 코드 실행 흐름과 변수 값을 확인할 수 있습니다.
What
NOTE로깅 함수 비교:
함수 용도 라이브러리 print()기본 출력 dart debugPrint()긴 출력 (잘림 방지) foundation log()상세 로깅 dart stderr.writeln()에러 출력 dart import 'dart:developer' as developer;// 기본 로깅print('간단한 메시지');// 긴 메시지 (잘리지 않음)debugPrint('아주 긴 JSON 데이터...');// 상세 로깅 (DevTools에서 필터링 가능)developer.log('사용자 로그인',name: 'auth.login',error: jsonEncode({'userId': 123}),);
How
TIPlog() 함수의 장점:
import 'dart:developer' as developer;void fetchUserData() {developer.log('API 호출 시작',name: 'api.user', // 카테고리 (필터링 가능)time: DateTime.now(), // 타임스탬프);try {// API 호출...developer.log('API 호출 성공', name: 'api.user');} catch (e) {developer.log('API 호출 실패',name: 'api.user',error: e.toString(), // 에러 정보 (JSON 권장)level: 1000, // 에러 레벨);}}DevTools Logging 뷰에서
name으로 로그를 필터링할 수 있습니다.
Watch out
WARNING릴리스 빌드에서 로그 제거:
print()는 릴리스 빌드에서도 출력됩니다. 성능과 보안을 위해 조건부 출력을 사용하세요.import 'package:flutter/foundation.dart';if (kDebugMode) {print('디버그에서만 출력');}또는
debugPrint()를 사용하면 됩니다. 이 함수는 디버그 모드 체크 없이도 릴리스에서 무시됩니다.
챕터 3: DevTools 활용
Why
NOTEDevTools는 Flutter 앱을 분석하는 웹 기반 도구입니다. 위젯 구조, 성능, 메모리, 네트워크 등을 시각적으로 분석할 수 있습니다.
What
NOTEDevTools 주요 기능:
탭 기능 Flutter Inspector 위젯 트리, 레이아웃 시각화 Performance 프레임 타이밍, CPU 프로파일링 Memory 메모리 사용량, 누수 감지 Network HTTP 요청/응답 모니터링 Logging 로그 뷰어, 필터링 Debugger 소스 레벨 디버깅
How
TIPDevTools 실행 방법:
방법 1: VS Code
- 앱 디버그 모드로 실행
- 명령 팔레트 (Cmd/Ctrl + Shift + P)
- “Dart: Open DevTools” 선택
방법 2: 커맨드 라인
Terminal window # 앱 실행flutter run# 콘솔에 표시된 URL에서 DevTools 열기# 예: http://127.0.0.1:9100방법 3: Android Studio
- 앱 디버그 모드로 실행
- 하단 “Flutter Inspector” 탭 클릭
- “Open DevTools” 버튼 클릭
챕터 4: 위젯 인스펙터
Why
NOTE위젯 인스펙터는 위젯 트리를 시각적으로 탐색할 수 있게 해줍니다. 레이아웃 문제, 예상치 못한 위젯 구조를 파악할 때 유용합니다.
What
NOTE인스펙터 기능:
- Select Widget Mode: 앱 화면에서 위젯을 탭해서 선택
- Widget Tree: 위젯 계층 구조 표시
- Layout Explorer: 위젯 크기와 제약 조건 시각화
- Details Tree: 선택한 위젯의 속성 표시
How
TIP코드에서 위젯 트리 덤프:
DevTools 없이도 위젯 트리를 확인할 수 있습니다.
import 'package:flutter/material.dart';class AppHome extends StatelessWidget {@overrideWidget build(BuildContext context) {return Material(child: Center(child: TextButton(onPressed: () {// 위젯 트리 덤프debugDumpApp();},child: const Text('Dump Widget Tree'),),),);}}콘솔에 전체 위젯 트리가 출력됩니다.
출력 예시:
MyApp└TextButton(dirty, dependencies: [MediaQuery, _InheritedTheme])└Text('Dump Widget Tree')
dirty는 위젯이 다시 빌드되어야 함을 의미합니다.
챕터 5: 렌더 트리와 레이어 트리
Why
NOTE레이아웃 문제는 위젯 트리만으로 파악하기 어려울 때가 있습니다. 렌더 트리는 실제 레이아웃 계산 결과를 보여줍니다.
What
NOTE트리 덤프 함수:
함수 용도 debugDumpApp()위젯 트리 덤프 debugDumpRenderTree()렌더 트리 덤프 (크기, 제약조건) debugDumpLayerTree()레이어 트리 덤프 (합성 정보) debugDumpFocusTree()포커스 트리 덤프 debugDumpSemanticsTree()시맨틱 트리 덤프 (접근성)
How
TIP렌더 트리로 레이아웃 디버깅:
TextButton(onPressed: () {debugDumpRenderTree();},child: const Text('Dump Render Tree'),)출력에서 확인할 정보:
RenderPositionedBox#dc1df│ constraints: BoxConstraints(w=800.0, h=600.0) // 제약조건│ size: Size(800.0, 600.0) // 실제 크기│ alignment: Alignment.center└─child: RenderPadding#8455f│ constraints: BoxConstraints(56.0<=w<=800.0, 28.0<=h<=600.0)│ size: Size(100.0, 48.0)│ padding: EdgeInsets(8.0, 0.0, 8.0, 0.0)
constraints: 부모가 전달한 제약조건size: 위젯이 결정한 크기- 제약조건이 예상과 다르면 레이아웃 문제의 원인
relayoutBoundary: 이 값이 크면 작은 변경에도 많은 위젯이 다시 레이아웃됩니다. 성능 최적화 포인트입니다.
챕터 6: 레이아웃 디버깅 플래그
Why
NOTE코드 기반 플래그는 앱 전체에 시각적 디버깅 정보를 오버레이합니다. 빠르게 레이아웃 문제를 파악할 수 있습니다.
What
NOTE주요 디버깅 플래그:
플래그 효과 debugPaintSizeEnabled모든 박스에 테두리 표시 debugPaintBaselinesEnabled텍스트 기준선 표시 debugPaintPointersEnabled탭 영역 강조 debugPaintLayerBordersEnabled레이어 경계 표시 debugRepaintRainbowEnabled리페인트 영역 색상 표시
How
TIP레이아웃 디버깅 활성화:
import 'package:flutter/rendering.dart';void main() {debugPaintSizeEnabled = true; // 박스 테두리 표시runApp(const MyApp());}표시되는 정보:
- 청록색 테두리: 모든 박스의 경계
- 파란색 영역: 패딩
- 노란색 화살표: 정렬 방향
- 회색 영역: 자식이 없는 Spacer
탭 영역 디버깅:
버튼이 눌리지 않을 때 유용합니다.
debugPaintPointersEnabled = true;탭하면 탭 가능한 영역이 청록색으로 표시됩니다. 표시되지 않으면 위젯이 히트 테스트 영역 밖에 있는 것입니다.
챕터 7: 성능 오버레이
Why
NOTE성능 오버레이는 실시간으로 프레임 렌더링 성능을 보여줍니다. UI 버벅거림(jank) 원인을 빠르게 파악할 수 있습니다.
What
NOTE성능 오버레이 구성:
- 상단 그래프: GPU 스레드 (래스터라이저)
- 하단 그래프: UI 스레드 (Dart 코드)
녹색 선: 16ms (60fps 목표) 선을 넘으면 프레임 드랍 발생
How
TIP코드에서 성능 오버레이 활성화:
import 'package:flutter/material.dart';class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(showPerformanceOverlay: true, // 성능 오버레이 표시title: 'My App',home: const MyHomePage(),);}}DevTools에서 활성화:
Flutter Inspector에서 “Performance Overlay” 버튼을 클릭합니다.
해석 방법:
- UI 스레드 느림: Dart 코드 최적화 필요 (빌드 메서드, 애니메이션)
- GPU 스레드 느림: 복잡한 그래픽, 큰 이미지, 과도한 클리핑
챕터 8: 애니메이션 디버깅
Why
NOTE애니메이션 문제는 정상 속도에서 파악하기 어렵습니다. 애니메이션을 느리게 해서 문제를 찾을 수 있습니다.
What
NOTE애니메이션 속도 조절:
방법 1: DevTools
Flutter Inspector에서 “Slow Animations” 버튼 클릭 (20% 속도로 재생)
방법 2: 코드
import 'package:flutter/scheduler.dart';void main() {// 50배 느리게 (50.0 = 1/50 속도)timeDilation = 50.0;runApp(const MyApp());}
How
TIPtimeDilation 사용 주의:
// 앱 시작 시 한 번만 설정void main() {timeDilation = 5.0; // 5배 느리게runApp(const MyApp());}// 실행 중 변경하지 마세요!// 특히 줄이면 시간이 역행하는 것처럼 보여서 문제 발생기본값 복원:
디버깅이 끝나면
timeDilation = 1.0으로 복원하거나 코드를 제거하세요.
챕터 9: 프레임 타이밍 디버깅
Why
NOTE특정 작업이 프레임 예산(16ms)을 초과하는지 확인해야 할 때가 있습니다. 프레임 배너를 출력해서 확인할 수 있습니다.
What
NOTE프레임 타이밍 플래그:
import 'package:flutter/scheduler.dart';void main() {debugPrintBeginFrameBanner = true; // 프레임 시작debugPrintEndFrameBanner = true; // 프레임 끝runApp(const MyApp());}출력 예시:
I/flutter : ▄▄▄▄▄▄▄▄ Frame 12 30s 437.086ms ▄▄▄▄▄▄▄▄I/flutter : Debug print: 내 로그 메시지I/flutter : ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
How
TIPTimeline API로 상세 성능 측정:
import 'dart:developer';void expensiveOperation() {Timeline.startSync('expensive_operation');// 측정할 코드for (int i = 0; i < 1000000; i++) {// 복잡한 계산}Timeline.finishSync();}DevTools Performance 탭에서
expensive_operation구간을 확인할 수 있습니다.
한계
디버깅 도구에는 몇 가지 제한이 있습니다.
디버그 모드 한정: 대부분의 디버깅 플래그와 기능은 디버그 모드에서만 작동합니다. 릴리스 빌드의 문제는 로깅과 크래시 리포팅 서비스에 의존해야 합니다.
성능 영향: 디버그 모드는 릴리스 모드보다 훨씬 느립니다.
정확한 성능 측정은 프로필 모드(flutter run --profile)에서 수행하세요.
네이티브 코드 디버깅: Flutter 디버거는 Dart 코드만 디버깅합니다. 네이티브 코드(Kotlin, Swift)는 Android Studio나 Xcode의 네이티브 디버거를 사용해야 합니다.
다음 튜토리얼에서는 에러 처리와 보고 방법을 알아봅니다.
Footnotes
-
DevTools(데브툴즈): Flutter 앱의 성능, 메모리, 레이아웃을 분석하는 웹 기반 개발 도구다. ↩
공유
이 글이 도움이 되었다면 다른 사람과 공유해주세요!