https://developer.android.com/training/permissions/requesting


런타임에 권한 요청

Android 6.0(API 레벨 23)부터 사용자는 앱이 설치될 때가 아니라 앱이 실행되는 중에 앱에 권한을 부여합니다. 이 접근방식에서는 사용자가 앱을 설치하거나 업데이트할 때 권한을 부여할 필요가 없으므로 앱 설치 과정이 간소화됩니다. 또한 사용자가 앱의 기능을 더 세부적으로 제어할 수 있습니다. 예를 들어, 기기 위치가 아니라 카메라에 대한 액세스 권한을 카메라 앱에 부여하도록 선택할 수 있습니다. 사용자는 앱 설정 화면으로 이동하여 언제든지 권한을 취소할 수 있습니다.

시스템 권한은 정상 및 위험의 두 가지 범주로 구분됩니다.

  • 정상 권한은 사용자 개인정보를 직접 위험에 빠뜨리지는 않습니다. 앱이 매니페스트에 정상 권한을 나열하는 경우, 시스템은 자동으로 권한을 부여합니다.
  • 위험 권한은 사용자 기밀 데이터에 대한 액세스를 앱에 부여할 수 있습니다. 앱이 매니페스트에 정상 권한을 나열하는 경우, 시스템은 자동으로 권한을 부여합니다. 여러분이 위험 권한을 나열하는 경우, 사용자는 여러분의 앱에 대한 명시적 승인을 제공해야 합니다.

자세한 내용은 정상 권한 및 위험한 권한을 참조하세요.

권한 선언에 설명된 것처럼 모든 버전의 Android에서 여러분의 앱은 앱 매니페스트에 필요한 정상 권한 및 위험 권한을 모두 선언해야 합니다. 그러나, 이 선언의 효과는 시스템 버전과 앱의 대상 SDK 레벨에 따라 달라집니다.

  • 기기에서 Android 5.1 이하를 실행 중이거나, 또는 앱의 대상 SDK가 22 이하인 경우: 여러분이 매니페스트에 위험 권한을 나열하는 경우, 사용자는 앱을 설치할 때 권한을 부여해야 하며, 권한을 부여하지 않을 경우 시스템이 앱을 설치하지 않습니다.
  • 기기에서 Android 6.0 이상이 실행 중이고, 그리고 앱의 대상 SDK가 23 이상인 경우: 앱이 매니페스트에 권한을 나열해야 하며, 그리고 앱이 실행되는 중에 필요한 각 위험 권한을 요청해야 합니다. 사용자는 각 권한을 부여하거나 거부할 수 있으며, 사용자가 권한 요청을 거부하더라도 제한된 성능으로 앱이 계속 실행될 수 있습니다.

참고: Android 6.0(API 레벨 23)부터는, 앱이 더 낮은 API 레벨을 대상으로 하더라도 사용자가 언제든지 모든 앱에서 권한을 취소할 수 있습니다. 여러분의 앱이 대상으로 하는 API 레벨에 상관없이, 필요한 권한이 없을 때 앱이 올바로 동작하는지 테스트해야 합니다.

이 과정에서는 Android 지원 라이브러리를 사용하여 권한을 확인하고 요청하는 방법에 대해 설명합니다. Android 프레임워크에서는 Android 6.0(API 레벨 23)과 유사한 메서드를 제공합니다. 그러나 지원 라이브러리를 사용하는 것은 더 단순한데, 그 이유는 메서드를 호출하기 전에 어떤 버전의 Android가 실행 중인지를 앱이 확인하지 않아도 되기 때문입니다.

권한 확인

여러분의 앱에 위험 권한이 필요한 경우, 여러분은 해당 권한이 요구되는 작업을 실행할 때마다 이 권한의 보유 여부를 확인해야 합니다. 사용자는 언제든지 자유롭게 권한을 취소할 수 있으므로, 어제 앱이 카메라를 사용했다고 해서 오늘도 해당 권한을 가진다고 가정할 수는 없습니다.

권한 보유 여부를 확인하려면 ContextCompat.checkSelfPermission() 메서드를 호출합니다. 예를 들어, 이 스니펫은 액티비티가 달력에 쓸 수 있는 권한을 갖고 있는지 확인하는 방법을 보여줍니다.

// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
       
Manifest.permission.WRITE_CALENDAR);

앱에 권한이 있는 경우 이 메서드는 PackageManager.PERMISSION_GRANTED를 반환하고, 앱이 작업을 계속 진행할 수 있습니다. 앱에 권한이 없는 경우 이 메서드는 PERMISSION_DENIED를 반환하고, 앱이 사용자에게 명시적으로 권한을 요청해야 합니다.

권한 요청

앱 매니페스트에 나열되어 있는 위험 권한이 여러분의 앱에 필요한 경우, 사용자에게 권한 부여를 요청해야 합니다. Android에서는 여러분이 권한 요청에 사용할 수 있는 여러 가지 메서드를 제공합니다. 이들 메서드를 호출하면 사용자 지정할 수 없는 표준 Android 대화상자가 나타납니다.

앱에 권한이 필요한 이유 설명

그림 1. 권한 부여나 거부를 사용자에게 물어보는 시스템 대화상자.

어떤 경우에는 앱에 권한이 필요한 이유를 사용자에게 설명해야 할 수도 있습니다. 예를 들어, 사용자가 사진 앱을 실행하는 경우 이 사용자는 앱이 카메라 사용 권한을 요청한다고 해도 아마 놀라지 않을 것입니다. 그러나 앱이 사용자의 위치나 연락처에 액세스하려고 한다면 사용자가 이해를 못할 수도 있습니다. 여러분이 권한을 요청하기 전에 먼저 사용자에게 설명하는 것을 고려해야 합니다. 명심할 점은, 설명이 사용자에게 부담이 되어서는 안 됩니다. 너무 많은 설명을 제공할 경우 사용자가 짜증을 느끼고 앱을 제거할 수도 있습니다.

여러분이 사용할 수 있는 한 가지 접근방법은 사용자가 해당 권한 요청을 이미 거절한 경우에만 설명을 제공하는 것입니다. 권한이 요구되는 기능의 사용을 사용자가 계속 시도하면서도 계속해서 권한 요청을 거절한다면, 아마도 이 사용자는 해당 기능을 제공하기 위해 앱에 권한이 필요한 이유를 모를 수도 있습니다. 이런 상황에서는 설명을 하는 것이 좋을 수 있습니다.

사용자가 설명이 필요할 수도 있는 상황을 찾도록 Android에서는 유틸리티 메서드인 shouldShowRequestPermissionRationale()을 제공합니다. 이전에 앱이 이 권한을 요청했고 사용자가 요청을 거부한 경우, 이 메서드는 true를 반환합니다.

참고: 과거에 사용자가 권한 요청을 거절하고 권한 요청 시스템 대화상자에서 Don't ask again 옵션을 선택한 경우, 이 메서드는 false를 반환합니다. 앱이 권한을 가지지 못하도록 기기 정책에서 금지하는 경우에도 이 메서드는 false를 반환합니다.

여러분에게 필요한 권한 요청

여러분의 앱에 필요한 권한이 아직 없는 경우, 앱은 requestPermissions() 메서드 중 하나를 호출하여 적절한 권한을 요청해야 합니다. 여러분의 앱은 원하는 권한을 전달하고, 또한 이 권한 요청을 식별하기 위해 여러분이 지정하는 정수 요청 코드도 전달합니다. 이 메서드는 비동기식으로 작동합니다. 즉각적으로 반환되며, 사용자가 대화상자에 응답한 후에 시스템은 그 결과를 가지고 앱의 콜백 메서드를 호출하고, 앱이 requestPermissions()에 전달한 것과 동일한 요청 코드를 전달합니다.

다음 코드는 앱이 사용자의 연락처를 읽을 권한을 갖고 있는지 확인하고, 필요한 경우 해당 권한을 요청합니다.

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
               
Manifest.permission.READ_CONTACTS)
       
!= PackageManager.PERMISSION_GRANTED) {

   
// Should we show an explanation?
   
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
           
Manifest.permission.READ_CONTACTS)) {

       
// Show an expanation to the user *asynchronously* -- don't block
       
// this thread waiting for the user's response! After the user
       
// sees the explanation, try again to request the permission.

   
} else {

       
// No explanation needed, we can request the permission.

       
ActivityCompat.requestPermissions(thisActivity,
               
new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS
);

       
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
       
// app-defined int constant. The callback method gets the
       
// result of the request.
   
}
}

참고: 앱이 requestPermissions()를 호출하는 경우, 시스템은 표준 대화상자를 사용자에게 표시합니다. 앱은 이 대화상자를 구성하거나 변경할 수 없습니다앱에 권한이 필요한 이유 설명에 나오는 것처럼, 사용자에게 정보나 설명을 제공해야 하는 경우에는 requestPermissions()를 호출하기 전에 제공해야 합니다.

권한 요청 응답 처리

여러분의 앱이 권한을 요청하면 시스템이 사용자에게 대화상자를 표시합니다. 사용자가 응답하면 시스템은 앱의 onRequestPermissionsResult() 메서드를 호출하여 사용자 응답에 전달합니다. 권한이 부여되었는지 여부를 확인하려면 여러분의 앱이 이 메서드를 재정의해야 합니다. 이 콜백에는 여러분이 requestPermissions()에 전달한 것과 동일한 요청 코드가 전달됩니다. 예를 들어, 앱이 READ_CONTACTS 액세스를 요청하는 경우 다음과 같은 콜백 메서드를 가질 수 있습니다.

@Override
public void onRequestPermissionsResult(int requestCode,
       
String permissions[], int[] grantResults) {
   
switch (requestCode) {
       
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
           
// If request is cancelled, the result arrays are empty.
           
if (grantResults.length > 0
               
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {

               
// permission was granted, yay! Do the
               
// contacts-related task you need to do.

           
} else {

               
// permission denied, boo! Disable the
               
// functionality that depends on this permission.
           
}
           
return;
       
}

       
// other 'case' lines to check for other
       
// permissions this app might request
   
}
}

시스템이 표시하는 대화상자에서는 여러분의 앱이 액세스해야 하는 권한 그룹에 대해 설명하지만, 특정 권한을 나열하지는 않습니다. 예를 들어, 여러분이 READ_CONTACTS 권한을 요청하는 경우, 앱이 기기의 연락처에 액세스해야 한다는 메시지만 시스템 대화상자에 나타납니다. 사용자는 각 권한 그룹에 대해 한 번만 권한을 부여해야 합니다. 여러분의 앱이 해당 그룹에 있는 다른 권한을 요청하는 경우(앱 매니페스트에 나열된 권한), 시스템이 자동으로 권한을 부여합니다. 여러분이 권한을 요청하는 경우, 시스템은 사용자가 시스템 대화상자를 통해 여러분의 요청을 명시적으로 부여했을 때와 동일한 방식으로 onRequestPermissionsResult() 콜백 메서드를 호출하고 PERMISSION_GRANTED를 전달합니다.

참고: 사용자가 또 다른 권한을 동일 그룹에 이미 부여했다고 하더라도, 여러분의 앱은 필요한 모든 권한을 명시적으로 요청해야 합니다. 또한, 권한을 그룹으로 묶는 것은 향후 Android 릴리스에서 변경될 수도 있습니다. 여러분의 코드에서는 특정 권한이 동일한 그룹에 있거나 없다는 가정을 해서는 안 됩니다.

예를 들어, 여러분이 READ_CONTACTS 및 WRITE_CONTACTS를 둘 다 앱 매니페스트에 나열한다고 가정해 봅시다. 여러분이 READ_CONTACTS를 요청하고 사용자가 권한을 부여했는데 여러분이 다시 WRITE_CONTACTS를 요청하면, 시스템은 사용자와의 상호작용 없이 즉시 여러분에게 해당 권한을 부여합니다.

사용자가 권한 요청을 거부하는 경우, 앱은 적절한 동작을 취해야 합니다. 예를 들어, 해당 권한이 필요한 사용자가 요청한 작업을 앱이 수행할 수 없는 경우, 앱이 그 이유를 설명하는 대화상자를 표시할 수도 있습니다.

시스템이 사용자에게 권한을 허용하도록 요청하면 사용자에게는 시스템에게 해당 권한을 다시 요청하지 말라고 지시할 선택권이 있습니다. 그런 경우, 앱이 해당 권한을 요청하기 위해 requestPermissions()를 사용할 때마다 시스템이 즉시 이 요청을 거부합니다. 시스템은 사용자가 명시적으로 여러분의 요청을 다시 거절했을 때와 동일한 방식으로 onRequestPermissionsResult() 콜백 메서드를 호출하고 PERMISSION_DENIED를 전달합니다. 즉, 여러분이 requestPermissions()를 호출하는 경우, 사용자와의 어떠한 직접적 상호작용이 발생했다고 가정할 수 없습니다.