Flutter에서 권한 처리

권한은 위치, 카메라, 연락처 등의 기기 기능에 액세스해야 하는 모바일 애플리케이션을 개발할 때 중요합니다. Flutter에서 이러한 권한을 효과적으로 처리하면 앱이 개인 정보 보호 및 보안 요구 사항을 준수하면서 원활한 사용자 경험을 제공할 수 있습니다. Flutter에서 권한을 관리하는 인기 있는 패키지 중 하나는 permission_handler입니다.

본 문서에서는 permission_handler를 사용하여 Flutter에서 권한을 요청, 확인 및 처리하는 방법을 실제 예제를 통해 안내합니다.

먼저, pubspec.yaml 파일에 permission_handlerfluttertoast 패키지를 추가하십시오:

dependencies:
  permission_handler: ^11.3.1 
  fluttertoast: ^8.2.4

그런 다음 다음을 실행하십시오:

flutter pub get

permission_handler 패키지는 권한을 요청하고 상태를 확인하는 간단한 방법을 제공합니다. 아래는 전화, 연락처, 위치, SMS, 카메라/마이크 등 다양한 권한을 처리하는 방법에 대한 상세 설명입니다.

Permission Handler는 다음과 같은 유형의 권한을 처리할 수 있습니다: 캘린더, 카메라, 연락처, 위치, 미디어 라이브러리, 마이크, 전화, 사진, 알림, 센서, SMS, 음성 인식, 저장소, 배터리 최적화 무시, 알림, 미디어 위치 액세스, 활동 인식, 블루투스 권한, 외부 저장소 관리, 시스템 경고 창, 설치 패키지 요청, 앱 추적 투명성, 중요 경고, 알림 정책 액세스, 근처 Wi-Fi 장치, 오디오, 정확한 알람 일정, 항상 센서, 캘린더 읽기 전용 및 전체 액세스이지만 이 글에서는 일부를 다룰 것입니다.

아래는 각 유형의 권한을 요청하는 방법, 거부 또는 영구적 거부와 같은 시나리오를 처리하는 방법, 그리고 각 권한이 액세스를 부여하는 내용에 대한 상세 가이드입니다.


  • 권한: 캘린더, 캘린더 읽기 전용, 캘린더 전체 액세스

  • 사용 사례: 사용자의 캘린더에 액세스하여 이벤트를 읽거나 쓸 수 있습니다.

Future<void> requestCalendarPermission() async {
  var status = await Permission.calendar.request();

  if (status.isGranted) {
    // Permission granted, proceed with calendar access
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Calendar permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Calendar permission denied',
    );
  }
}

  • 권한: 카메라

  • 사용 사례: 사진이나 비디오 촬영을 위해 기기의 카메라에 액세스합니다.

Future<void> requestCameraPermission() async {
  var status = await Permission.camera.request();

  if (status.isGranted) {
    // Permission granted, proceed with camera access
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Camera permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Camera permission denied',
    );
  }
}

  • 권한: contacts

  • 사용 사례: 사용자의 연락처 목록에 대한 읽기 또는 수정 액세스

Future<void> requestContactsPermission() async {
  var status = await Permission.contacts.request();

  if (status.isGranted) {
    // Permission granted, proceed with contacts access
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Contacts permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Contacts permission denied',
    );
  }
}

  • 권한: location, locationAlways, locationWhenInUse, accessMediaLocation

  • 사용 사례: 내비게이션, 지오펜싱 또는 인근 서비스를 위해 사용자의 위치에 액세스

Future<void> requestLocationPermission() async {
  var status = await Permission.location.request();

  if (status.isGranted) {
    // Permission granted, proceed with location access
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Location permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Location permission denied',
    );
  }
}

  • 권한: mediaLibrary

  • 사용 사례: 사용자의 미디어 라이브러리에 액세스 (iOS 전용)

Future<void> requestMediaLibraryPermission() async {
  var status = await Permission.mediaLibrary.request();

  if (status.isGranted) {
    // Permission granted, proceed with media library access
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Media Library permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Media Library permission denied',
    );
  }
}

  • 권한: microphone

  • 사용 사례: 장치의 마이크에 접근하여 오디오를 녹음합니다.

Future<void> requestMicrophonePermission() async {
  var status = await Permission.microphone.request();

  if (status.isGranted) {
    // Permission granted, proceed with microphone access
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Microphone permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Microphone permission denied',
    );
  }
}

  • 권한: 전화

  • 사용 사례: 전화 걸기, 전화 상태 읽기 또는 통화 기록에 접근합니다.

Future<void> requestPhonePermission() async {
  var status = await Permission.phone.request();

  if (status.isGranted) {
    // Permission granted, proceed with phone access
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Phone permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Phone permission denied',
    );
  }
}

  • 권한: 사진, 사진추가전용

  • 사용 사례: 사용자의 사진 라이브러리에 접근하여 사진을 읽거나 추가합니다.

Future<void> requestPhotosPermission() async {
  var status = await Permission.photos.request();

  if (status.isGranted) {
    // Permission granted, proceed with photos access
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Photos permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Photos permission denied',
    );
  }
}

  • 권한: 알림

  • 사용 사례: 사용자의 장치에서 알림에 접근하고 관리합니다.

Future<void> requestRemindersPermission() async {
  var status = await Permission.reminders.request();

  if (status.isGranted) {
    // Permission granted, proceed with reminders access
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Reminders permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Reminders permission denied',
    );
  }
}

  • 권한: 센서, 센서항상

  • 사용 사례: 가속도계 및 자이로스코프와 같은 장치의 센서에 접근합니다.

Future<void> requestSensorsPermission() async {
  var status = await Permission.sensors.request();

  if (status.isGranted) {
    // Permission granted, proceed with sensors access
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Sensors permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Sensors permission denied',
    );
  }
}

  • 권한: sms

  • 사용 사례: SMS 메시지를 읽거나 전송하는 등 SMS 기능에 접근합니다.

Future<void> requestSmsPermission() async {
  var status = await Permission.sms.request();

  if (status.isGranted) {
    // Permission granted, proceed with SMS access
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'SMS permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'SMS permission denied',
    );
  }
}

  • 권한: speech

  • 사용 사례: 음성-텍스트 변환 기능에 접근합니다.

Future<void> requestSpeechPermission() async {
  var status = await Permission.speech.request();

  if (status.isGranted) {
    // Permission granted, proceed with speech recognition
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Speech recognition permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Speech recognition permission denied',
    );
  }
}

  • 권한: storage, manageExternalStorage

  • 사용 사례: 사용자의 내부 또는 외부 저장소에 접근하여 파일을 읽거나 씁니다.

Future<void> requestStoragePermission() async {
  var status = await Permission.storage.request();

  if (status.isGranted) {
    // Permission granted, proceed with storage access
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Storage permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Storage permission denied',
    );
  }
}

  • 권한: ignoreBatteryOptimizations

  • 사용 사례: 시스템에 앱을 배터리 최적화 조치에서 제외하도록 요청합니다.

Future<void> requestIgnoreBatteryOptimizationsPermission() async {
  var status = await Permission.ignoreBatteryOptimizations.request();

  if (status.isGranted) {
    // Permission granted, proceed with ignoring battery optimizations
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Battery optimization permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Battery optimization permission denied',
    );
  }
}

  • 권한: notification

  • 사용 사례: 앱에서 알림을 보낼 수 있도록 허용합니다.

Future<void> requestNotificationPermission() async {
  var status = await Permission.notification.request();

  if (status.isGranted) {
    // Permission granted, proceed with notifications access
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Notification permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Notification permission denied',
    );
  }
}

  • 권한: bluetooth, bluetoothScan, bluetoothAdvertise, bluetoothConnect

  • 사용 사례: 블루투스 장치에 연결하거나 관리하기 위해 블루투스 기능에 접근합니다.

Future<void> requestBluetoothPermission() async {
  var status = await Permission.bluetooth.request();

  if (status.isGranted) {
    // Permission granted, proceed with Bluetooth access
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'Bluetooth permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'Bluetooth permission denied',
    );
  }
}

  • 권한: appTrackingTransparency

  • 사용 사례: 사용자로부터 앱 추적을 위한 트래킹 권한 요청, iOS에서의 필수 사항입니다.

Future<void> requestAppTrackingTransparencyPermission() async {
  var status = await Permission.appTrackingTransparency.request();

  if (status.isGranted) {
    // Permission granted, proceed with app tracking
  } else if (status.isPermanentlyDenied) {
    ShowToast.showErrorText(
      text: 'App tracking permission permanently denied. Please enable it in the app settings.',
    );
    openAppSettings();
  } else {
    ShowToast.showErrorText(
      text: 'App tracking permission denied',
    );
  }
}

권한은 사용자에게 원활한 경험을 제공하고 동시에 개인 정보를 존중하는 데 중요한 역할을 합니다. 이러한 권한을 세련되게 처리하고 사용자에게 앱이 해당 권한을 요청하는 이유에 대해 교육하는 것이 중요합니다.

permission_handler 패키지를 사용하면 Flutter 애플리케이션에서 Android 및 iOS 플랫폼 모두에서 필요한 모든 권한을 요청, 확인 및 관리할 수 있습니다. 사용자에게 의미 있는 피드백을 제공하고 권한이 거부되거나 취소된 경우 대체 흐름을 추가하는 것을 고려해야 합니다.

Android 및 iOS 구성

필요한 Android 및 iOS 구성을 포함하는 것이 중요합니다. Android의 경우 AndroidManifest.xml을 올바른 권한 선언으로 업데이트해야 합니다. iOS의 경우 권한 사용 설명을 Info.plist 파일에 추가하여 사용자에게 특정 권한이 요청되는 이유를 알려주어야 합니다.

17가지 예시의 각 권한에 대해 <uses-permission> 태그를 AndroidManifest.xml에 추가해야 합니다. 다음은 권한이 permission_handler 패키지와 어떻게 일치하는지에 대한 내용입니다:

  1. 카메라 권한

     <uses-permission android:name="android.permission.CAMERA" />
    
  2. 연락처 권한

     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
    
  3. 위치 권한

     <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" />
    
  4. 미디어 라이브러리/저장소 권한

     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage"/>
     <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
     <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
     <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
    
  5. 마이크 권한

     <uses-permission android:name="android.permission.RECORD_AUDIO" />
    
  6. 전화 권한

     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    
  7. 사진 권한

     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    
  8. 알림 권한 (명시적인 Android 권한 없음)

  9. 센서 권한

     <uses-permission android:name="android.permission.BODY_SENSORS" />
    
  10. SMS 권한

    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    
  11. 음성 인식 권한

    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    
  12. 배터리 최적화 무시

    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
    
  13. 알림 권한 (안드로이드 13 이상에서 기본 처리)

  14. 미디어 위치 액세스 권한

    <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
    
  15. 활동 인식 권한

    <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
    
  16. 블루투스 권한

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    
  17. 앱 추적 투명성 권한 (iOS 전용)


iOS에서는 사용자에게 앱이 특정 권한을 필요로 하는 이유를 알려주기 위해 Info.plist 파일에 설명적인 키를 제공해야 합니다. 아래는 각 권한 예제에 대한 구성입니다:

  1. 카메라 권한

     <key>NSCameraUsageDescription</key>
     <string>사진 촬영을 위해 카메라 접근이 필요합니다.</string>
    
  2. 연락처 권한

     <key>NSContactsUsageDescription</key>
     <string>더 나은 소통을 위해 연락처 접근이 필요합니다.</string>
    
  3. 위치 권한

     <key>NSLocationWhenInUseUsageDescription</key>
     <string>위치 기반 서비스를 제공하기 위해 귀하의 위치에 액세스해야 합니다.</string>
     <key>NSLocationAlwaysUsageDescription</key>
     <string>앱이 비활성 상태일 때도 귀하의 이동을 추적하기 위해 귀하의 위치에 액세스해야 합니다.</string>
    
  4. 미디어 라이브러리/저장소 권한

     <key>NSPhotoLibraryUsageDescription</key>
     <string>미디어를 공유하거나 업로드하기 위해 사진에 액세스해야 합니다.</string>
    
  5. 마이크 권한

     <key>NSMicrophoneUsageDescription</key>
     <string>음성 녹음을 위해 마이크에 액세스해야 합니다.</string>
    
  6. 전화 권한

     <key>NSPhoneUsageDescription</key>
     <string>전화 서비스에 액세스해야 전화를 사용할 수 있습니다.</string>
    
  7. 사진 권한

     <key>NSPhotoLibraryAddUsageDescription</key>
     <string>사진을 라이브러리에 추가할 수 있는 권한이 필요합니다.</string>
    
  8. 알림 권한

     <key>NSRemindersUsageDescription</key>
     <string>작업 관리를 위해 알림에 접근할 수 있는 권한이 필요합니다.</string>
    
  9. 센서 권한

     <key>NSSensorsUsageDescription</key>
     <string>피트니스 추적을 위해 센서 접근이 필요합니다.</string>
    
  10. SMS 권한 (SMS 서비스 요청 시 iOS에서 자동으로 처리됨)

  11. 음성 인식 권한

<key>NSSpeechRecognitionUsageDescription</key>
<string>We need access to speech recognition for voice commands.</string>
  1. 배터리 최적화 무시 (iOS에서 적용되지 않음)

  2. 알림 권한

<key>NSUserNotificationUsageDescription</key>
<string>We need permission to send notifications.</string>
  1. 미디어 위치 접근 권한 (iOS에서 자동으로 처리됨)

  2. 활동 인식 권한

<key>NSMotionUsageDescription</key>
<string>We need access to motion data for fitness tracking.</string>
  1. 블루투스 권한
<key>NSBluetoothPeripheralUsageDescription</key>
<string>We need access to Bluetooth for device connectivity.</string>
  1. 앱 추적 투명성
<key>NSUserTrackingUsageDescription</key>
<string>We need permission to track your activity across apps and websites for personalized ads.</string>

귀하의 앱을 완전히 기능적이고 최신 개인 정보 보호 규정을 준수하는 것은 권한을 적절하게 관리하는 것이 중요합니다. 플러터 앱에서 올바른 권한 처리 논리를 구현함으로써 AndroidManifest.xmlInfo.plist을 모두 업데이트하여 민감한 기능에 대한 액세스 요청 시 사용자에게 원활하고 투명한 경험을 제공합니다.

언제나 두 플랫폼에서 권한을 테스트하고 앱 스토어 검토 중 거부를 피하기 위해 사용자에게 권한이 필요한 이유에 대해 의미 있는 설명을 제공하세요.

보다 심층적인 연구를 위해 공식 권한 처리기 설명서를 확인하여 다른 권한 유형을 탐색하십시오. 또한 각 권한 유형에 대한 필요한 선언을 포함하여 플랫폼별 파일(예: AndroidManifest.xmlInfo.plist)을 업데이트하십시오.

Source:
https://atuoha.hashnode.dev/permission-handling-in-flutter