-
iOS에서는 죽고 Android에서는 지나가는 Flutter 코드가 있습니다AI Agent/flutter 2026. 7. 9. 09:00728x90반응형

안드로이드는 통과, iOS 심사 빌드만 죽던 밤 금요일 오후였습니다.
안드로이드 QA를 전부 통과시키고, 이제 iOS 심사용 빌드만 올리면 주말이라 홀가분한 마음이었습니다.
빌드를 올리고 커피 한 잔 내리러 갔다 왔더니, 삼십 분쯤 사이에 크래시 리포트가 여러 건 쌓여 있었습니다.
심사 리뷰어가 앱을 켜자마자 특정 화면에서 앱이 닫혔다는 내용이었습니다.
같은 코드, 같은 Dart 파일인데 안드로이드에서는 개발 내내 단 한 번도 안 났던 크래시였습니다.
순간 등이 좀 서늘했습니다.
심사에서 이렇게 터지면 그대로 반려니까요.재현이 안 되는 크래시를 로그로만 쫓았다
제일 곤란했던 건, 그 크래시를 제 손에서 재현할 수가 없었다는 점입니다.
회사 테스트용 아이폰은 마침 다른 사람이 쓰고 있었고, 제 앞엔 시뮬레이터뿐이었는데 시뮬레이터에서는 아무리 눌러도 멀쩡히 돌았습니다.
리포트를 하나씩 열어보니 크래시가 전부 같은 화면, 그러니까 처음 진입할 때 캐시를 만드는 그 화면에 몰려 있었습니다.
결국 남은 단서는 리포트에 딸려 온 스택 트레이스 한 뭉치뿐이었습니다.처음엔 권한 문제일 거라고 넘겨짚었습니다.
iOS는 권한에 까다로우니까 저장소 접근이 막힌 거겠거니 하고 Info.plist부터 뒤졌죠.
근데 아무리 봐도 그 화면은 특별한 권한을 쓰지도 않았습니다.
엉뚱한 데서 한참 시간을 버리고 나서야 스택 트레이스를 처음부터 차분히 읽었습니다.
익숙한 Dart 프레임들 사이에, 파일에 뭔가를 쓰려다 예외가 난 지점이 딱 보였습니다.
앱이 문서 디렉터리 밑에 캐시 폴더를 만들고 거기에 파일을 쓰는 코드였는데, 저는 그 줄을 짤 때 폴더가 당연히 있을 거라고 생각했습니다.
안드로이드에서는 실제로 늘 있었으니까요.시뮬레이터가 멀쩡했던 진짜 이유
확인차 그 write 부분을 try/catch로 감싸 예외를 통째로 로그에 찍게 한 다음, 이번엔 시뮬레이터에서 일부러 그 캐시 폴더를 지운 채로 앱을 켰습니다.
그랬더니 시뮬레이터에서도 똑같이 터졌습니다.
그동안 시뮬레이터가 멀쩡했던 건 코드가 옳아서가 아니라, 이전 실행이 만들어둔 폴더가 그대로 남아 있었기 때문이었습니다.
재현이 안 됐던 게 아니라, 재현 조건을 제가 안 맞춰준 것뿐이었죠.앱을 새로 깐 심사 기기에는 그 폴더가 있을 리 없었습니다.
그래서 리뷰어가 앱을 처음 켜는 그 순간, 없는 경로에 바로 쓰려다 앱이 그대로 닫힌 겁니다.
개발하는 내내 제 기기에는 늘 이전에 만든 폴더가 남아 있었으니, 저는 이 크래시를 만날 기회 자체가 없었던 거고요.문자열로 조립한 경로가 화근이었다
정확히는 경로를 문자열로 직접 이어 붙인 게 문제였습니다.
흔히 보던 경로 모양을 머릿속에 그려놓고 짠 코드가, iOS 샌드박스에서는 다른 위치를 가리켰습니다.
그 자리에 디렉터리가 만들어지기도 전에 파일부터 쓰려 했으니 터질 수밖에 없었습니다.final dir = await getApplicationDocumentsDirectory(); final cacheDir = Directory('${dir.path}/thumb_cache'); if (!await cacheDir.exists()) { await cacheDir.create(recursive: true); }경로를 손으로 조립하지 않고
path_provider가 돌려주는 디렉터리에서 시작하는 것, 그리고 쓰기 전에 존재 여부를 먼저 확인하는 것.
크래시를 반나절 쫓은 끝에 실제로 고친 건 이 몇 줄이 전부였습니다.
허탈할 만큼 작은 수정이었지만, 그 작은 가정 하나가 두 OS를 갈라놓고 있었습니다.그 뒤로 저는 경로가 필요한 코드를 짤 때 절대 슬래시를 손으로 이어 붙이지 않습니다.
문서 디렉터리든 임시 디렉터리든 언제나path_provider가 주는 값에서 출발하고, 디렉터리는 쓰기 직전에create(recursive: true)로 한 번 보장하고 들어갑니다.
습관 하나 바꿨을 뿐인데, 그 뒤로 iOS와 안드로이드 사이에서 경로 때문에 앱이 닫히는 일은 없어졌습니다.안드로이드 초록불은 증거가 아니었다
이 일에서 제일 뜨끔했던 건, 제가 안드로이드에서 잘 돈다는 걸 "테스트를 통과했다"로 착각했다는 점입니다.
안드로이드 QA가 초록불이었던 건 그 플랫폼에서 그 경로가 우연히 있었기 때문이지, 코드가 옳아서가 아니었습니다.
한쪽에서 통과한 게 다른 쪽의 안전을 보장하지 않는다는 걸, 심사 반려 문턱까지 가서야 몸으로 배웠습니다.
나중에 같은 앱을 웹으로 띄웠을 때 지원 안 되는 plugin 때문에 비슷하게 한 번 더 뒤통수를 맞긴 했지만, 그건 또 다른 날의 이야기고요.두 줄을 고치고 다시 심사에 올린 날은, 커피를 내리러 가지 않았습니다.
앱을 지웠다 새로 깔고 그 화면에 처음 들어가 보기를 몇 번 반복한 다음, 리포트 알림이 안 뜨는 걸 한참 동안 그냥 지켜봤습니다.원문 기준
원문 기준: https://docs.flutter.dev/platform-integration
원문 본 날짜: 2026-07-01.
올리기 전에 원문이 그새 바뀌진 않았는지만 다시 확인합니다.728x90반응형'AI Agent > flutter' 카테고리의 다른 글
Flutter 검증은 exit 0보다 산출물 확인이 먼저입니다 (0) 2026.07.09 기기와 이미지와 센서는 이론보다 늦게 도착합니다 (0) 2026.07.09 위젯 생성은 싸지만 작업은 싸지 않습니다 (0) 2026.07.08 작은 상태에는 작은 도구가 더 낫습니다 (0) 2026.07.08 보인다는 말보다 캡처 파일이 더 강합니다 (0) 2026.07.08