네이티브 디버깅 (iOS / Android)
핵심 원칙: 이슈 트래커 먼저, 코드는 나중
실기기에서 crash / hang / 흰·검은화면 / 첫 프레임 안 뜸 증상 만나면 코드 / Info.plist / 빌드 설정 건드리기 전에 다음 순서:
docs/platform/compat-matrix.md확인 — 동일 조합 (Framework × OS × 기기) 기록 있는지- WebSearch 로 이슈 트래커 검색 (없을 때):
<framework> <version> <os> <build-number> <증상> 예: "flutter 3.41.7 iOS 26.1 23B85 black screen" - 워크어라운드 적용 — 동일 증상 리포트 있으면 그 해결법 먼저
- compat-matrix 에 기록 — 새 조합이면 다음 사람을 위해 추가
- 이슈 없을 때만 코드 진입
Why 이 순서
2026-04-24 dartbrief 실기기 테스트에서 Flutter 3.41.7 × iOS 26.1 (23B85) 의 Metal/Skia 렌더 서피스 초기화 실패 (flutter#179592) 를 모르고 6시간 동안 Info.plist / Bonjour / signing / AppDelegate 패턴을 추측 수정. 이슈 트래커에 동일 리포트 + 해결책 ("iOS 26.1 회피") 이 이미 있었음.
Claude 의 지식 컷오프 이후 버전은 직접 알지 못함 — WebSearch 가 유일한 확인 수단.
iOS — 실기기 + 로컬 백엔드
핵심: 맥 WiFi IP 사용 (localhost X)
iPhone 에서 localhost 는 iPhone 자신을 가리킴. 실기기 iOS 에는 Android 에뮬레이터의 10.0.2.2 같은 호스트 별칭 없음.
# Makefile 이 en1(WiFi) IP 자동 감지 + dart-defines.dev.json 갱신
make run-dev
# IP 수동 지정
make run-dev IP=172.30.1.26네트워크 인터페이스
en0= 192.168.0.x (유선 랜 — iPhone 접근 불가)en1= 172.30.1.x (WiFi — iPhone 동일 네트워크)- Makefile 기본값: en1 우선, 없으면 en0 fallback
Mac WiFi 고정 IP (2026-04-28 셋업)
시스템 설정 → 네트워크 → Wi-Fi → 세부사항 → TCP/IP → 수동:
- IP:
172.30.1.100 - gateway:
172.30.1.254
재부팅해도 IP 불변 → 매번 IP 바꿀 필요 없음.
iPhone 12 Pro 실기기 ID
980ED1CB-0953-5930-B0B2-C0A61941C9EC
Info-Dev.plist ATS (현재 상태)
ios/Runner/Info-Dev.plist 에 NSAllowsArbitraryLoads: true 로 단순화 완료 (2026-04-26). 이전의 IP별 NSExceptionDomains 는 IP 변경 시마다 수정 필요해서 제거. dev 플레이버 전용이라 prod 영향 없음.
iOS 로컬 네트워크 권한 (errno=65)
폰 설정 → 개인정보 보호 → 로컬 네트워크 → 앱 권한 ON 확인.
Keychain 토큰 — uninstall 후에도 보존
flutter_secure_storage 토큰은 키체인 저장 → 앱 uninstall 해도 살아남음. 강제 로그아웃 테스트 시 주의.
Android — 실기기 + 로컬 백엔드
기기 정보
- 기기: Samsung SM-A346N
- ADB device ID:
RFCWA07VPZA - 빌드:bash또는
fvm flutter run --release --flavor dev \ --dart-define-from-file=dart-defines.dev.json \ -t lib/main_dev.dart \ -d RFCWA07VPZAmake run-dev(자동 detect)
필수 권한 (AndroidManifest.xml)
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>HTTP cleartext (dev 전용)
android/app/src/dev/res/xml/network_security_config.xml—172.30.1.100,localhost,10.0.2.2허용android/app/src/dev/AndroidManifest.xml—networkSecurityConfig참조
네트워크 에러 패턴 구분
| errno | 의미 | 원인 |
|---|---|---|
| 1 (EPERM) | Operation not permitted | INTERNET 권한 누락 |
| 65 (EHOSTUNREACH) | 네트워크 미도달 | WiFi 대역 불일치 |
| 111 (ECONNREFUSED) | Connection refused | 백엔드 다운 |
Kakao Android 로그인
- 패키지명 (dev):
com.dartbrief.app.dev - 키 해시:
i0ZN3NVNWhCz9xrxMiNrpI4CB2A=(debug keystore, 카카오 콘솔 등록됨) - debug keystore 위치:
~/.android/debug.keystore - 키 해시 재추출:bash
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -storepass android \ | openssl sha1 -binary | openssl base64 - release keystore 교체 시 새 해시 카카오 콘솔에 추가 필요
공통 — KAKAO_NATIVE_APP_KEY 누락 주의
dart-defines.dev.json 에 KAKAO_NATIVE_APP_KEY 항목 있어야 함. make run-dev 사용 시 자동 로드.
외부 망에서 Android 무선 디버깅 (ADB over Tailscale)
USB 연결 불가능한 환경 (사무실에 폰만 / Mac 원격) 에서 Tailscale 통해 ADB 무선 디버깅 + 설치.
1회 페어링 (최초)
폰: 설정 → 개발자 옵션 → 무선 디버깅 ON → "페어링 코드로 기기 페어링" 탭. 팝업에 표시되는 두 정보 필요:
- 포트 번호 (IP 부분 무시,
:뒤 숫자만) - 6자리 페어링 코드
echo "<페어링코드>" | adb pair <폰 Tailscale IP>:<페어링 포트>
# 예: echo "717224" | adb pair 100.79.15.19:40995연결 (페어링 이후 매 재연결)
페어링 팝업 닫고 무선 디버깅 메인 화면의 "IP 주소 및 포트" 표시되는 포트 사용 (페어링 포트와 다름):
adb connect <폰 Tailscale IP>:<연결 포트>
# 예: adb connect 100.79.15.19:42395
adb devices # 100.79.15.19:42395 device빌드 + 설치
flutter run -d <ID> 는 무선 ADB 기기 자동 인식 못 할 수 있어 — APK 만 빌드 후 adb install:
fvm flutter build apk --release --flavor dev \
--dart-define-from-file=dart-defines.dev.json \
-t lib/main_dev.dart
adb -s <폰 Tailscale IP>:<연결 포트> install -r build/app/outputs/flutter-apk/app-dev-release.apk주의
- 연결 포트는 무선 디버깅 토글마다 바뀜 — 끊겼다 다시 연결할 때 폰에서 새 포트 확인 필수
- Tailscale 둘 다 켜져 있어야 (Mac + 폰) —
tailscale status로 상호 reachable 확인 - 페어링 코드는 약 5분 후 만료 — 팝업 켜놓은 동안만 유효
관련 메모
- 외부 망 + Tailscale + Android 앱 ↔ 로컬 백엔드 →
network_security_config.xml에 Mac Tailscale IP cleartext 허용 필요. flutter-gotchas.md §6 참조 - 셀룰러에서 Tailscale DNS 끄기 — 동 §6