-
[Flutter] iOS에서 마이크 권한 팝업이 안 뜨는 이유와 해결 방법프로그래밍/flutter로 앱만들기 2025. 5. 15. 13:29반응형
최근 Flutter로 음성 녹음 기능을 구현하면서 아주 흥미롭고 애매한 상황을 겪었다.
iOS에서 마이크 권한 팝업이 어떤 때는 잘 뜨고, 어떤 때는 전혀 안 뜨는 문제가 있었고,
덕분에permission_handler
,record
패키지, 그리고 iOS 자체의 권한 처리 구조를 탈탈 털게 됐다.이 글은 그 삽질 기록을 정리한 것이다.
혹시 나처럼 마이크 팝업이 안 떠서 당황하고 있는 Flutter 개발자가 있다면 도움이 될 수 있을 거라고 본다.상황 설명
앱에서 음성 녹음을 시작하는 기능을 구현 중이었다.
사용한 주요 패키지는 다음과 같다:record
: 실제 녹음 기능permission_handler
: 권한 요청/상태 확인
앱 흐름은 간단했다.
녹음 버튼을 누르면 마이크 권한을 요청하고, 승인되면 녹음을 시작하는 구조였다.그런데 iOS에서 이상한 현상이 발생했다.
- 어떤 기기에서는 마이크 권한 팝업이 잘 뜨고
- 어떤 기기에서는 아예 팝업이 안 뜨며,
PermissionStatus.permanentlyDenied
가 반환됨
처음에는 Info.plist 설정 문제인가 싶었지만, 당연히
NSMicrophoneUsageDescription
은 잘 들어가 있었고,Background Modes
도Audio
로 활성화된 상태였다.원인 분석
결론부터 말하자면 이 문제는 코드 로직, 정확히는 마이크에 실제 접근하는 시점과 권한 요청 타이밍의 미묘한 차이에서 발생했다.
iOS의 권한 구조는 다음과 같다:
- 시스템이 처음 마이크 접근을 감지하면
- Info.plist에 설명이 있을 경우 팝업을 띄움
- 사용자가 거절하면 더 이상 자동으로 묻지 않음
- 이후 권한 상태는
denied
또는permanentlyDenied
로 고정됨
문제는 Flutter 환경에서 이 "처음 마이크 접근"이 iOS가 인식할 수 있을 만큼 확실하게 일어나지 않는 경우가 있다는 것.
예를 들어 이런 코드:
final status = await Permission.microphone.request(); if (!status.isGranted) { // 권한 거부 처리 }
이 코드는 실제로 iOS에서 마이크에 접근하는 게 아니라, 그냥 권한 상태만 확인한다.
그렇기 때문에 iOS는 "얘가 마이크를 쓰려는지 아닌지 아직 모르겠네?" 하고 팝업을 생략한다.결국 팝업이 안 뜨고, 상태만 permanentlyDenied로 떨어져버리는 것.
해결 방법
iOS가 마이크 접근을 감지하도록 명확한 시도를 먼저 해주는 게 핵심이다.
그걸 위해 나는 아래처럼AudioRecorder().start()
를 잠깐 실행하는 코드를 넣었다.Future<void> triggerIOSMicrophoneRequest() async { final tempDir = await getTemporaryDirectory(); final dummyPath = '${tempDir.path}/dummy_check.m4a'; final dummyRecorder = AudioRecorder(); try { await dummyRecorder.start( const RecordConfig(), path: dummyPath, ); await Future.delayed(const Duration(milliseconds: 300)); await dummyRecorder.stop(); } catch (_) { // 무시 } }
이 코드를 앱 초기화 시점이나 권한 요청 전에 한 번 실행하면,
iOS는 "이 앱이 진짜로 마이크를 쓰려는구나"라고 판단해서 팝업을 띄운다.이 이후부터는
permission_handler
에서 권한 상태를 확인해도 정상적으로 granted가 뜬다.왜 이런 문제가 Flutter에서만 발생할까?
Swift로 iOS 네이티브 개발을 할 때는 이런 문제가 없다.
그 이유는 간단하다. 네이티브에서는 내가AVAudioSession.sharedInstance().requestRecordPermission()
를 직접 호출하고,
실제 마이크 접근 타이밍도 완전히 제어할 수 있다.하지만 Flutter는 cross-platform 프레임워크다 보니 내부에서 어떤 순서로 native bridge가 실행되는지 정확히 알기 어렵다.
그래서 실제 마이크 접근 이전에 권한 상태를 먼저 물어보는 식의 흐름이 생기면, iOS는 아무 반응도 안 보이고 끝나는 것이다.그래서 팝업이 어떨 땐 뜨고, 어떨 땐 안 떴던 이유
테스트 중 권한 팝업이 뜰 때도 있고, 안 뜰 때도 있었다.
그건 결국 녹음 버튼을 눌렀을 때 실제로 iOS가 마이크 접근을 감지할 수 있었냐 없었냐의 차이였다.- 어떤 시점엔
_recorder.start()
가 먼저 실행돼서 팝업이 떴고 - 어떤 시점엔
permission_handler
가 상태부터 물어보는 바람에 팝업이 무시됐다
요약하자면, iOS에서 마이크 권한 팝업이 Flutter 환경에서 예측 불가능하게 동작한다면,
실제로 마이크 접근을 시도해서 iOS의 관심을 먼저 끄는 게 유일한 해결책이다.record
나permission_handler
패키지가 문제가 아니라,
그 내부 동작 방식이 iOS의 권한 시스템과 정확히 맞물리지 않는 게 문제였던 셈이다.덕분에 iOS와 Flutter 사이의 동기화 타이밍이 얼마나 중요한지 뼈저리게 느끼게 됐다.
728x90반응형'프로그래밍 > flutter로 앱만들기' 카테고리의 다른 글