Flutter 튜토리얼 75편: 디버거와 로깅 - DevTools로 앱 디버깅하기
요약
핵심 요지
- 문제 정의: Flutter 앱에서 버그가 발생했을 때 원인을 빠르게 파악하기 어렵다.
- 핵심 주장: DevTools의 디버깅 도구들을 활용하면 코드 실행 흐름을 추적하고 문제를 효율적으로 해결할 수 있다.
- 주요 근거: DevTools는 브레이크포인트, 로그 뷰, 네트워크 모니터링 등 종합적인 디버깅 기능을 제공한다.
문서가 설명하는 범위
- Debugger로 브레이크포인트 설정 및 코드 스테핑
- Console에서 출력 확인 및 표현식 평가
- Network 뷰로 HTTP 트래픽 모니터링
- Logging 뷰로 앱 이벤트 추적
읽는 시간: 20분 | 난이도: 초급
참고 자료
- Use the debugger - Flutter 공식 Debugger 가이드
- Debug console view - Flutter 공식 Console 가이드
- Use the Network View - Flutter 공식 Network 가이드
- Use the Logging view - Flutter 공식 Logging 가이드
문제 상황
앱에서 예상치 못한 동작이 발생했을 때, print 문만으로는 원인을 파악하기 어렵습니다.
코드 실행 흐름을 따라가면서 변수 값을 확인하고 싶지만 방법을 모릅니다.
기존 방식의 한계
// print 문으로만 디버깅하는 방식Future<void> loadUserData(String userId) async { print('loadUserData 시작: $userId'); // 어디서 호출됐는지 모름
final response = await api.getUser(userId); print('응답: $response'); // 응답 전체 구조 파악 어려움
if (response.data != null) { print('데이터 있음'); user = response.data; } else { print('데이터 없음'); // 왜 없는지 알 수 없음 }
print('loadUserData 끝');}문제는 다음과 같습니다.
- 코드 실행을 중간에 멈출 수 없다
- 변수 값을 실시간으로 확인하기 어렵다
- 호출 스택을 추적하기 어렵다
- print 문을 계속 추가/삭제해야 한다
해결 방법
챕터 1: DevTools Debugger 시작하기
Why
NOTE
Debugger1는 코드 실행을 원하는 지점에서 멈추고, 변수 값을 확인하며, 한 줄씩 실행할 수 있게 해줍니다. print 문 없이도 코드의 동작을 상세히 분석할 수 있습니다.
What
NOTEDevTools Debugger의 주요 기능입니다.
flowchart TD subgraph Debugger["Debugger 기능"] BP[Breakpoints] ST[Stepping] VAR[Variables] CS[Call Stack] end subgraph Actions["동작"] BP --> P[일시 정지] ST --> N[한 줄씩 실행] VAR --> I[값 검사] CS --> T[호출 추적] end
기능 설명 단축키 Continue 다음 브레이크포인트까지 실행 F5 Step Over 현재 줄 실행 후 다음 줄로 F10 Step Into 함수 내부로 진입 F11 Step Out 현재 함수 빠져나가기 Shift+F11
How
TIPDebugger를 시작하는 방법입니다.
Terminal window # 1. 디버그 모드로 앱 실행flutter run# 2. DevTools 열기# VS Code: Ctrl+Shift+P → "Dart: Open DevTools"# 또는 터미널에 표시된 DevTools URL 클릭// DevTools Debugger 탭에서:// 1. 소스 파일 탐색 (Libraries 버튼 또는 Ctrl+P)// 2. 줄 번호 클릭하여 브레이크포인트 설정// 3. 앱에서 해당 코드 실행되면 자동 일시 정지소스 파일 탐색 방법입니다.
파일 찾기: 1. Debugger 탭 열기2. 우측 상단 "Libraries" 클릭 (또는 Ctrl+P)3. 파일명 검색4. 원하는 파일 선택
Watch out
WARNINGVS Code를 사용하면 DevTools의 Debugger 탭이 숨겨집니다. VS Code에 내장 디버거가 있기 때문입니다.
VS Code 사용 시:- DevTools Debugger 탭 숨김- VS Code 내장 디버거 사용 권장- 동일한 기능 제공Android Studio/IntelliJ 사용 시:- DevTools Debugger 탭 표시- DevTools 또는 IDE 디버거 선택 가능
결론: Debugger를 사용하면 코드 실행을 멈추고 변수 값을 확인하며 단계별로 실행할 수 있다.
챕터 2: 브레이크포인트 설정과 활용
Why
NOTE
브레이크포인트2는 코드의 특정 지점에서 실행을 멈추게 하는 표시입니다. 문제가 의심되는 코드에 브레이크포인트를 설정하면 해당 시점의 상태를 확인할 수 있습니다.
What
NOTE브레이크포인트의 종류와 사용법입니다.
flowchart LR subgraph Types["브레이크포인트 유형"] L[Line Breakpoint] E[Exception Breakpoint] end subgraph States["상태"] A[활성화] D[비활성화] H[조건부] end Types --> States
유형 설명 사용 시점 Line 특정 줄에서 멈춤 일반적인 디버깅 Exception 예외 발생 시 멈춤 에러 추적 Conditional 조건 충족 시만 멈춤 특정 조건 디버깅
How
TIP브레이크포인트 설정 방법입니다.
// 브레이크포인트 설정 예시class UserRepository {Future<User> getUser(String id) async {// 여기에 브레이크포인트 설정 (줄 번호 클릭)final response = await _api.fetchUser(id);// 응답 처리 로직에도 설정if (response.statusCode == 200) {return User.fromJson(response.body);} else {throw ApiException(response.statusCode);}}}브레이크포인트 관리 방법입니다.
Breakpoints 패널 사용: 1. 좌측 하단 Breakpoints 영역 확인2. 체크박스로 개별 활성화/비활성화3. X 버튼으로 제거4. 더블클릭으로 해당 위치로 이동예외에서 멈추기 설정입니다.
// Debugger 상단의 드롭다운에서 예외 처리 설정//// Ignore: 예외 발생해도 멈추지 않음// Stop on Unhandled: 처리되지 않은 예외에서만 멈춤// Stop on All: 모든 예외에서 멈춤 (try-catch 포함)try {await riskyOperation();} catch (e) {// "Stop on All" 설정 시 여기서도 멈춤handleError(e);}
Watch out
WARNING
Hot Restart3를 하면 모든 브레이크포인트가 초기화됩니다. 중요한 브레이크포인트 위치를 기억해두세요.Hot Restart 영향:- 브레이크포인트 초기화됨- 변수 값 초기화됨- 앱 상태 초기화됨Hot Reload:- 브레이크포인트 유지됨- 변수 값 유지됨 (가능한 경우)
결론: 브레이크포인트를 설정하면 원하는 지점에서 코드 실행을 멈추고 상태를 확인할 수 있다.
챕터 3: 변수 검사와 스테핑
Why
NOTE브레이크포인트에서 멈췄을 때, 현재 상태의 변수 값을 확인하고 코드를 한 줄씩 실행하며 변화를 추적할 수 있습니다. 이를 통해 버그의 정확한 원인을 찾을 수 있습니다.
What
NOTEVariables 패널과 스테핑 기능입니다.
flowchart TD subgraph Inspection["변수 검사"] V[Variables 패널] H[Hover 검사] W[Watch 표현식] end subgraph Stepping["스테핑"] SI[Step Into] SO[Step Over] SOU[Step Out] C[Continue] end
스테핑 명령 동작 사용 시점 Step Into 함수 내부로 진입 함수 내부 확인 필요 Step Over 함수 건너뛰고 다음 줄 함수 내부 불필요 Step Out 현재 함수 완료까지 실행 함수 빠져나가기 Continue 다음 브레이크포인트까지 특정 지점까지 건너뛰기
How
TIPVariables 패널 사용법입니다.
// 브레이크포인트에서 멈췄을 때void processOrder(Order order) {// 여기서 멈춤 - Variables 패널에서 확인 가능:// - order: Order 객체// - order.items: List<Item>// - order.totalPrice: doublefinal discount = calculateDiscount(order);// Step Over 후: discount 값 확인 가능final finalPrice = order.totalPrice - discount;// Step Over 후: finalPrice 값 확인 가능if (finalPrice < 0) {// 이 조건이 왜 true인지 확인 가능throw InvalidPriceException();}}객체 내부 탐색입니다.
Variables 패널에서:1. 객체 옆의 화살표 클릭하여 펼치기2. 중첩된 객체도 계속 펼치기 가능3. 객체 위에 마우스 올리면 toString() 결과 표시예시:order (Order)├── id: "123"├── items (List<Item>)│ ├── [0]: Item│ │ ├── name: "Product A"│ │ └── price: 10000│ └── [1]: Item└── totalPrice: 25000Call Stack 활용입니다.
// Call Stack 패널에서 호출 경로 확인//// processOrder (order_service.dart:42)// handleCheckout (checkout_page.dart:128)// onPressed (checkout_page.dart:85)// ...//// → 각 스택 프레임 클릭 시 해당 위치로 이동// → 해당 스코프의 변수 확인 가능
Watch out
WARNINGVariables 패널에서 객체를 펼치면
toString()이 호출됩니다.toString()내에 부수효과가 있으면 주의가 필요합니다.// 주의: toString()에 부수효과가 있는 경우class Counter {int value = 0;@overrideString toString() {value++; // 부수효과 - 디버깅 시 값이 변함!return 'Counter: $value';}}// 권장: toString()은 순수 함수로 작성class Counter {int value = 0;@overrideString toString() {return 'Counter: $value'; // 부수효과 없음}}
결론: Variables 패널과 스테핑 기능으로 코드 실행 흐름을 따라가며 변수 값의 변화를 추적할 수 있다.
챕터 4: Debug Console 활용
Why
NOTE
Debug Console4은 앱의 출력을 확인하고, 실시간으로 코드를 실행할 수 있는 도구입니다. 브레이크포인트 없이도 앱 상태를 확인할 수 있습니다.
What
NOTEDebug Console의 주요 기능입니다.
flowchart LR subgraph Console["Debug Console"] O[stdout/stderr 출력] E[표현식 평가] H[힙 스냅샷] end subgraph Usage["활용"] O --> L[로그 확인] E --> T[실시간 테스트] H --> M[메모리 분석] end
기능 설명 예시 stdout 표시 print 출력 확인 print('debug')표현식 평가 코드 실시간 실행 user.name입력힙 브라우징 객체 탐색 스냅샷 열기
How
TIPConsole에서 출력 확인하기입니다.
// 앱 코드에서 출력void main() {print('앱 시작');debugPrint('디버그 메시지'); // 긴 출력도 잘림 없이 표시runApp(MyApp());}// Console에서 확인:// 앱 시작// 디버그 메시지표현식 평가 기능입니다.
브레이크포인트에서 멈춘 상태에서:1. Console 하단 입력창에 표현식 입력2. Enter로 실행3. 결과 확인예시:> user.name"John Doe"> user.orders.length5> user.orders.where((o) => o.status == 'pending').length2> calculateTotal(user.orders)150000힙 스냅샷 브라우징입니다.
힙 스냅샷 열기:1. Memory 뷰에서 스냅샷 캡처2. 스냅샷에서 특정 인스턴스 우클릭3. "Browse in Console" 선택4. Console에서 해당 객체 탐색 가능Console에서:> instance.field1> instance.method()
Watch out
WARNINGConsole에서 실행한 코드는 앱 상태를 변경할 수 있습니다. 테스트 목적으로만 사용하고, 의도치 않은 변경에 주의하세요.
// Console에서 실행 시 주의> user.name = "Modified" // 실제로 변경됨!> list.add(item) // 실제로 추가됨!> api.deleteUser(id) // 실제로 삭제됨!// 안전한 사용> user.name // 읽기만> list.length // 읽기만> user.toJson() // 메서드 호출 (부수효과 없는 것만)
결론: Debug Console에서 앱 출력을 확인하고 표현식을 실시간으로 평가할 수 있다.
챕터 5: Network 뷰로 HTTP 트래픽 분석
Why
NOTEAPI 호출이 제대로 동작하는지 확인하려면 실제 HTTP 요청과 응답을 봐야 합니다.
Network 뷰5에서 앱의 모든 네트워크 트래픽을 모니터링할 수 있습니다.
What
NOTENetwork 뷰가 기록하는 정보입니다.
flowchart LR subgraph Request["요청 정보"] M[Method] U[URL] H1[Headers] B1[Body] end subgraph Response["응답 정보"] S[Status] H2[Headers] B2[Body] T[Timing] end Request --> Response
컬럼 설명 Method HTTP 메서드 (GET, POST 등) URL 요청 URL Status 응답 상태 코드 Type 컨텐츠 타입 (json, html 등) Duration 요청 소요 시간
How
TIPNetwork 뷰 사용법입니다.
기본 사용: 1. DevTools > Network 탭 선택2. 앱에서 네트워크 요청 발생시키기3. 요청 목록에서 항목 확인4. 요청 선택하여 상세 정보 보기상세 정보 탭:- General: URL, 메서드, 상태 코드- Request Headers: 요청 헤더- Request Body: 요청 본문 (POST 등)- Response Headers: 응답 헤더- Response Body: 응답 본문- Timing: 타이밍 정보필터링으로 원하는 요청만 보기입니다.
필터 문법:- method, m: HTTP 메서드- status, s: 상태 코드- type, t: 컨텐츠 타입예시:- "m:get t:json" → GET 요청 중 JSON만- "s:404" → 404 에러만- "api/users" → URL에 api/users 포함검색:- 입력창에 텍스트 입력- URL, 메서드, 상태 등 전체 검색앱 시작 시점부터 네트워크 기록하기입니다.
Terminal window # 앱 시작 시점의 네트워크도 기록하려면:# 1. 앱을 일시 정지 상태로 시작flutter run --start-paused# 2. DevTools Network 탭 열기 (Recording 확인)# 3. 앱 재개# DevTools에서 Resume 버튼 클릭 또는# 터미널에서 'r' 입력
Watch out
WARNINGNetwork 뷰는
dart:io기반 HTTP 클라이언트만 지원합니다. 웹 앱은 브라우저 개발자 도구를 사용하세요.// 지원되는 HTTP 클라이언트import 'dart:io';final client = HttpClient(); // 지원// dio 패키지 - 지원// http 패키지 - 지원// 웹 앱에서는// Chrome DevTools > Network 탭 사용
결론: Network 뷰에서 앱의 HTTP 요청과 응답을 모니터링하여 API 통신 문제를 디버깅할 수 있다.
챕터 6: Logging 뷰 활용
Why
NOTE
Logging 뷰6는 앱에서 발생하는 다양한 이벤트를 시간순으로 보여줍니다. Dart 런타임, Flutter 프레임워크, 앱 로그를 한 곳에서 확인할 수 있습니다.
What
NOTELogging 뷰가 표시하는 이벤트 유형입니다.
flowchart TD subgraph Sources["로그 소스"] D[Dart Runtime] F[Flutter Framework] A[Application] end subgraph Events["이벤트 유형"] D --> GC[GC 이벤트] F --> FR[프레임 이벤트] A --> ST[stdout/stderr] A --> CL[Custom Logs] end
소스 이벤트 설명 Dart GC 가비지 컬렉션 발생 Flutter Frame 프레임 생성 App stdout print() 출력 App Custom developer.log()
How
TIPLogging 뷰 기본 사용법입니다.
// 다양한 로깅 방법// 1. print - 기본 출력print('Simple log message');// 2. debugPrint - 긴 메시지도 잘림 없이debugPrint('Very long message that would normally be truncated...');// 3. developer.log - 구조화된 로깅import 'dart:developer' as developer;developer.log('User logged in',name: 'AuthService',error: jsonEncode({'userId': '123'}),);커스텀 로깅 구현입니다.
// 앱 전용 로거 클래스import 'dart:developer' as developer;class AppLogger {static void debug(String message, {String? tag}) {developer.log(message,name: tag ?? 'DEBUG',level: 500,);}static void info(String message, {String? tag}) {developer.log(message,name: tag ?? 'INFO',level: 800,);}static void error(String message, {Object? error, StackTrace? stackTrace}) {developer.log(message,name: 'ERROR',level: 1000,error: error,stackTrace: stackTrace,);}}// 사용 예시AppLogger.debug('Loading user data', tag: 'UserRepository');AppLogger.info('User loaded successfully');AppLogger.error('Failed to load user', error: e, stackTrace: stack);로그 필터링입니다.
Logging 뷰 필터:- 검색창에 텍스트 입력하여 필터링- 특정 소스만 보기 가능유용한 필터:- "ERROR" - 에러 로그만- "AuthService" - 특정 태그만- "gc" - GC 이벤트만
Watch out
WARNING릴리스 빌드에서는 로그가 제거되거나 무시될 수 있습니다. 프로덕션 로깅이 필요하면 별도 로깅 서비스를 사용하세요.
// 릴리스 빌드에서 로그 제어import 'package:flutter/foundation.dart';void log(String message) {if (kDebugMode) {print(message); // 디버그 빌드에서만 출력}}// 프로덕션 로깅 - 외부 서비스 사용// Firebase Crashlytics, Sentry 등
결론: Logging 뷰에서 앱의 모든 로그와 이벤트를 시간순으로 확인하고 필터링할 수 있다.
챕터 7: 효과적인 디버깅 전략
Why
NOTE도구를 아는 것과 효과적으로 사용하는 것은 다릅니다. 체계적인 디버깅 전략을 적용하면 버그를 더 빨리 찾을 수 있습니다.
What
NOTE효과적인 디버깅 프로세스입니다.
flowchart TD subgraph Process["디버깅 프로세스"] R[재현] I[격리] A[분석] F[수정] V[검증] end R -->|"버그 재현"| I I -->|"범위 좁히기"| A A -->|"원인 분석"| F F -->|"코드 수정"| V V -->|"재테스트"| R
How
TIP단계별 디버깅 전략입니다.
// 1. 재현 (Reproduce)// - 버그가 발생하는 정확한 조건 파악// - 재현 단계 기록// 2. 격리 (Isolate)// - 문제 범위를 좁혀나감// - 브레이크포인트를 넓은 범위에서 좁은 범위로// 3. 분석 (Analyze)// - 변수 값 확인// - 호출 스택 추적// - 예상값 vs 실제값 비교// 4. 수정 (Fix)// - 근본 원인 해결// - 임시 방편 피하기// 5. 검증 (Verify)// - 수정 후 재테스트// - 다른 부분에 영향 없는지 확인자주 발생하는 버그 패턴입니다.
// 1. Null 참조 에러// 브레이크포인트: null 발생 직전// 확인: 어디서 null이 되는지 역추적// 2. 상태 불일치// 브레이크포인트: setState 호출 지점// 확인: 상태 변경 타이밍과 순서// 3. 비동기 문제// 브레이크포인트: await 전후// 확인: 실행 순서, 콜백 호출 여부// 4. UI 업데이트 안 됨// 브레이크포인트: build 메서드// 확인: 위젯 재빌드 여부, 상태 값// 5. API 에러// Network 뷰: 요청/응답 확인// 확인: 상태 코드, 응답 본문디버깅 체크리스트입니다.
버그 발견 시:[ ] 버그 재현 가능?[ ] 어떤 조건에서 발생?[ ] 에러 메시지나 스택 트레이스 있음?[ ] 최근 변경한 코드와 관련?브레이크포인트 설정:[ ] 에러 발생 지점[ ] 의심되는 로직 시작점[ ] 데이터 흐름 중간 지점변수 확인:[ ] 예상값과 실제값 비교[ ] null 여부 확인[ ] 타입 확인수정 후:[ ] 버그 재현 시도[ ] 관련 기능 테스트[ ] 다른 부분 영향 확인
Watch out
WARNING디버깅에 너무 많은 시간을 쓰고 있다면, 접근 방식을 바꿔보세요.
디버깅이 오래 걸릴 때:- 잠시 휴식 후 새로운 관점으로- 동료에게 설명해보기 (러버덕 디버깅)- 문제를 더 작은 단위로 분리- 가정을 의심하기피해야 할 것:- 같은 접근법 반복- 추측만으로 코드 수정- print 문 무한 추가
결론: 체계적인 디버깅 프로세스를 따르면 버그를 더 효율적으로 찾고 해결할 수 있다.
한계
- VS Code 제한: VS Code 사용 시 DevTools Debugger 탭이 숨겨지며 VS Code 내장 디버거를 사용해야 한다
- 웹 앱 제한: Network 뷰는 웹 앱에서 동작하지 않으며, 브라우저 개발자 도구를 사용해야 한다
- 릴리스 빌드: 릴리스 빌드에서는 DevTools 연결이 불가능하다
Footnotes
-
Debugger(디버거): 코드 실행을 제어하고 상태를 검사할 수 있게 해주는 도구이다. ↩
-
Breakpoint(브레이크포인트): 코드 실행을 일시 중지시키는 지점 표시이다. ↩
-
Hot Restart(핫 리스타트): 앱 상태를 초기화하고 전체 앱을 다시 시작하는 기능이다. ↩
-
Debug Console(디버그 콘솔): 앱 출력을 확인하고 표현식을 실행할 수 있는 인터페이스이다. ↩
-
Network View(네트워크 뷰): HTTP 요청과 응답을 모니터링하는 DevTools 기능이다. ↩
-
Logging View(로깅 뷰): 앱의 로그 이벤트를 시간순으로 표시하는 DevTools 기능이다. ↩
공유
이 글이 도움이 되었다면 다른 사람과 공유해주세요!