살면서 내가 모바일 권한을 직접 다뤄야 할 상황이 생길 거라고는 전혀 예상하지 못했지만, 결국 그런 상황이 찾아오고 말았다. 우리가 크로스 플랫폼인 Flutter를 선택한 이유는 기존 시스템(누OO시스템)이 일정한 유지보수 기간을 가지고 있음에도 불구하고 새로운 기능들을 추가해야 하는 상황이 있었기 때문이다.
이런 상황은 앞으로 어떤 일이 벌어질지 예측하기 어려운, 말 그대로 미지의 영역이었다. 하지만 여러 가지를 고려한 끝에, 내가 직접 이 문제를 해결해야 한다는 결론에 도달하게 되었다.
개발자로서 문제를 만났을 때, 그것을 해결하는 것은 결국 개발자의 몫이다. 그리고 이런 과정을 겪으며 내가 더욱 확실히 깨달은 것은, IT 회사에서 중요한 능력은 단순한 개발 실력이 아니라 문제 해결 능력이라는 점이다. 이는 내가 기획자로 일할 때부터도 자주 느꼈던 부분이다.
결국, 프로젝트를 진행하며 문제를 직면하고 해결해 나가는 과정은 단순한 기술적 성장만이 아니라, 문제를 바라보는 관점과 해결책을 찾는 사고방식을 더욱 탄탄하게 다져주는 귀중한 경험이 되었다.
이런 경험을 통해, 문제 해결의 중요성을 다시 한번 되새길 수 있었고, 이는 앞으로 어떤 도전이 와도 유용하게 작용할 것이라는 확신을 갖게 되었다.
아이폰 접근 권한을 설정하기 위해 내가 제일 먼저 해줫던 일은
info.plist
파일을 수정하는게 먼저였다. 아래의 코드를 적용하고 권한 문제를 차례차례 설정해나아갔다.아이폰에서 접근 권한을 설정하기 위해 내가 제일 먼저 했던 일은 info.plist 파일을 수정하는 것이었다. iOS 앱에서는 접근 권한을 요청하기 전에, 반드시
info.plist
파일에 해당 권한에 대한 설명을 추가해야 한다.아래는 내가 적용했던 코드 예시 중 하나다:
<key>NSFileProviderUsageDescription</key> <string>앱에서 파일 접근을 위해 권한이 필요합니다.</string> <key>NSMicrophoneUsageDescription</key> <string>비디오 녹화 시 마이크 접근 권한이 필요합니다.</string> <key>NSLocationAlwaysAndWhenInUseUsageDescription</key> <string>앱이 항상 위치 정보를 접근하기 위해 권한이 필요합니다.</string> <key>NSPhotoLibraryAddUsageDescription</key> <string>사진을 저장하기 위해 권한이 필요합니다.</string> <key>NSCameraUsageDescription</key> <string>앱에서 카메라 접근 권한이 필요합니다.</string> <key>NSLocationWhenInUseUsageDescription</key> <string>앱 사용 중 위치 접근이 필요합니다.</string> <key>NSPhotoLibraryUsageDescription</key> <string>사진 접근 권한이 필요합니다.</string> <key>UIApplicationSupportsIndirectInputEvents</key>
이후, 권한 요청을 직접 처리하는 코드는 Flutter 플러그인(예:
permission_handler
)을 사용해 구현했다. 권한 요청은 다음과 같이 진행되었다:// 위치, 카메라, 갤러리, 파일 저장소 접근 권한 요청 Future<bool> requestPermissions() async { final List<String> deniedPermissions = []; if (Platform.isIOS) { // 권한 상태 확인 final cameraStatus = await Permission.camera.status; final photoStatus = await Permission.photos.status; final locationStatus = await Permission.locationWhenInUse.status; // 카메라 권한 요청 if (!cameraStatus.isGranted) { final requestStatus = await Permission.camera.request(); if (!requestStatus.isGranted) { deniedPermissions.add("카메라"); } } // 사진 접근 권한 요청 if (!photoStatus.isGranted) { final requestStatus = await Permission.photos.request(); if (!requestStatus.isGranted) { deniedPermissions.add("사진"); } } // 위치 권한 요청 if (!locationStatus.isGranted) { final requestStatus = await Permission.locationWhenInUse.request(); if (!requestStatus.isGranted) { deniedPermissions.add("위치"); } else if (await Permission.locationWhenInUse.isPermanentlyDenied) { print("위치 권한이 영구적으로 거부됨."); deniedPermissions.add("위치"); } } // 제한된 권한 상태 확인 (선택 사항) if (locationStatus.isRestricted) { print("위치 권한이 제한됨."); deniedPermissions.add("위치 (제한됨)"); } // 거부된 권한이 있을 경우 사용자에게 알림 if (deniedPermissions.isNotEmpty) { print("거부된 권한: ${deniedPermissions.join(', ')}"); return false; } return true; // 모든 권한이 허용된 경우 } else if (Platform.isAndroid) { // 권한 요청 PermissionStatus locationStatus = await Permission.location.request(); PermissionStatus cameraStatus = await Permission.camera.request(); PermissionStatus storageStatus = await Permission.storage.request(); // Android 11 이상: MANAGE_EXTERNAL_STORAGE 요청 if (Platform.isAndroid && (await Permission.manageExternalStorage.isDenied)) { PermissionStatus manageStorageStatus = await Permission.manageExternalStorage.request(); print("Manage External Storage permission: $manageStorageStatus"); } // 상태 로그 출력 print("Location permission: $locationStatus"); print("Camera permission: $cameraStatus"); print("Storage permission: $storageStatus"); // 거부된 권한 체크 if (locationStatus.isDenied || cameraStatus.isDenied || storageStatus.isDenied) { print("일부 권한이 거부되었습니다."); return false; } return true; // 모든 권한이 허용된 경우 } return false; }
Android 11 이상에서는 권한 관리가 이전 버전과는 다소 달라졌으며, 특히 배경 위치 권한, 파일 접근 권한(Scope Storage) 등의 변경사항이 도입되었습니다. 이를 해결하기 위해 아래와 같은 코드를 추가하여 문제를 해결했습니다.
AndroidManifest.xml 파일 수정
Android 11 이상에서는 특정 권한을 명시적으로 추가해야 합니다. 예를 들어, 배경 위치 권한을 요청하려면 다음 코드를
AndroidManifest.xml
에 추가해야 합니다:<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Android 11 이상에서는 권한 사용 시의 명확한 설명이 요구된다고 한다 이를 위해
strings.xml
에 사용 목적을 설명하는 메시지를 추가하는 것이 좋다하여 바로 해당하는 부분을 추가했다.테스트 기기에서 권한 요청이 정상적으로 작동하는지 확인하고, 권한 거부 시의 예외 처리를 꼼꼼히 구현해야 하는데 지금 우리의 상황은 거부한 권한을 어떻게 관리할 지에 대한 부분은 미지수라 수정이 필요한 상황이다.
현재 첫 번째 문제인 권한 요청을 다시 띄우는 것은 해결하지 못하고 있다. 사용자가 앱에서 한 번 권한 요청을 거부한 경우, 해당 권한 요청 창을 다시 띄우는 것은 불가능하다.
이는 Android 및 iOS의 보안 정책상 의도된 동작으로, 사용자가 명시적으로 거부한 권한을 앱에서 다시 요청할 수 없도록 제한하고 있다. 현 상황에서 가능한 대응 방법은, 사용자가 권한을 거부한 상태에서 다시 권한이 필요할 경우, 핸들러를 통해 설정 화면으로 이동하도록 안내하는 것이다.
아래는 내가 추후 적용할 예정인 화면이다.
해결 방법: 설정 화면으로 이동
Flutter에서 사용자가 거부한 권한을 설정 화면에서 직접 변경할 수 있도록 다음과 같은 코드를 작성했다:
import 'package:permission_handler/permission_handler.dart'; Future<void> checkAndRequestPermission() async { // 특정 권한 확인 var status = await Permission.camera.status; if (status.isDenied) { // 권한이 거부된 경우 설정 화면으로 안내 print("카메라 권한이 거부되었습니다. 설정 화면으로 이동합니다."); openAppSettings(); } else if (status.isGranted) { // 권한이 이미 허용된 경우 print("카메라 권한이 허용되었습니다."); } }
주요 로직 설명
- 권한 상태 확인:
Permission.camera.status
를 사용해 현재 권한 상태를 확인한다.
- 권한 거부 상태 처리: 권한이 거부된 상태에서는
openAppSettings()
를 호출하여 앱의 설정 화면으로 이동할 수 있도록 한다.
- 사용자 안내: 권한이 필요한 이유를 사용자에게 명확히 설명하는 메시지를 UI에 추가하는 것이 중요하기에 해당 화면을 넣을 예정이다.
현재 권한 요청을 다시 띄우는 것은 정책적으로 불가능하지만, 위와 같은 방법으로 해결할 수 있는 최선의 방안을 적용할 수 있기에 해당 화면을 적용할 예정이다.
Share article