Register now for Android Dev Summit 2019!

앱 권한 요청

모든 Android 앱은 액세스가 제한된 샌드박스에서 실행됩니다. 자체 샌드박스 밖에 있는 리소스나 정보를 앱이 사용해야 하는 경우에는 앱이 적절한 권한을 요청해야 합니다. 앱에 권한이 필요함을 선언하려면 해당 권한을 앱 manifest에 나열한 후 사용자가 런타임에 각 권한을 승인하도록 요청합니다(Android 6.0 이상).

이 페이지에서는 Android 지원 라이브러리를 사용하여 권한을 확인하고 요청하는 방법을 설명합니다. Android 프레임워크에서는 Android 6.0(API 레벨 23)과 유사한 메서드를 제공하지만 지원 라이브러리를 사용하여 더욱 쉽게 이전 Android 버전과의 호환성을 지원할 수 있습니다.

manifest에 권한 추가

Android에서는 버전에 상관없이 앱에 권한이 필요함을 선언하려면 앱 manifest에 <uses-permission> 요소를 최상위 <manifest> 요소의 하위로 지정합니다. 예를 들어 인터넷에 액세스해야 하는 앱은 manifest에 다음 줄이 있어야 합니다.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
            package="com.example.snazzyapp">

        <uses-permission android:name="android.permission.INTERNET"/>
        <!-- other permissions go here -->

        <application ...>
            ...
        </application>
    </manifest>
    

권한 선언 후의 시스템 동작은 해당 권한이 민감한 정도에 따라 달라집니다. 어떤 권한은 '정상' 권한으로 간주되어 시스템이 설치 시에 곧바로 부여합니다. 어떤 권한은 '위험' 권한으로 간주되어 사용자가 명시적으로 앱 액세스 권한을 부여해야 합니다. 다양한 종류의 권한에 관한 자세한 정보는 보호 수준을 참조하세요.

권한 확인

앱에 위험 권한이 필요한 경우 해당 권한이 요구되는 작업을 할 때마다 권한이 있는지 확인해야 합니다. Android 6.0(API 레벨 23)부터는 앱이 더 낮은 API 레벨을 타겟팅하더라도 사용자가 언제든 앱의 권한을 취소할 수 있습니다. 따라서 앱이 어제 카메라를 사용했다고 해서 오늘도 권한이 있다고 가정할 수는 없습니다.

권한이 있는지 확인하려면 ContextCompat.checkSelfPermission() 메서드를 호출합니다. 예를 들어 다음 스니펫에서는 캘린더에 쓸 수 있는 권한이 활동에 있는지 확인하는 방법을 보여줍니다.

Kotlin

    if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR)
            != PackageManager.PERMISSION_GRANTED) {
        // Permission is not granted
    }
    

자바

    if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR)
            != PackageManager.PERMISSION_GRANTED) {
        // Permission is not granted
    }
    

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

권한 요청

앱이 checkSelfPermission()에서 PERMISSION_DENIED를 수신하면 사용자에게 해당 권한을 요청하는 메시지를 표시해야 합니다. 아래 코드 스니펫에 나와 있는 것처럼, Android는 requestPermissions()와 같이 권한을 요청하는 데 사용할 수 있는 여러 메서드를 제공합니다. 이러한 메서드를 호출하면 표준 Android 대화상자가 나타납니다. 이 대화상자는 맞춤설정할 수 없습니다.

사용자에게 표시되는 방식은 권한 개요에 설명된 대로 기기 Android 버전과 애플리케이션의 대상 버전에 따라 다릅니다.

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

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

권한 설명에서 사용할 수 있는 한 가지 방법은 사용자가 해당 권한 요청을 이미 거절한 경우에만 설명을 제공하는 것입니다. Android에서는 이를 위해 유틸리티 메서드 shouldShowRequestPermissionRationale()를 제공합니다. 이 메서드는 사용자가 전에 해당 요청을 거부한 경우에는 true를 반환하고, 사용자가 해당 권한을 거부했으며 권한 요청 대화상자에서 다시 묻지 않음 옵션을 선택했거나 기기 정책에서 해당 권한을 금지하는 경우에는 false를 반환합니다.

사용자가 권한이 요구되는 기능을 계속 사용하려고 시도하면서도 권한 요청을 계속 거절한다면 아마도 이 사용자는 해당 기능을 제공하기 위해 앱에 권한이 필요한 이유를 모를 수도 있습니다. 이런 상황에서는 설명을 하는 것이 좋을 수 있습니다.

앱 권한 권장사항에는 권한을 요청할 때 좋은 사용자 환경을 만드는 방법에 관한 권장사항이 더 많이 제공되어 있습니다.

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

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

다음 코드에서는 앱이 사용자의 연락처를 읽을 권한이 있는지 확인합니다. 권한이 없으면 권한이 필요한 이유에 관한 설명을 표시해야 하는지 설명이 필요 없는지 확인하고 권한을 요청합니다.

Kotlin

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

        // Permission is not granted
        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
                Manifest.permission.READ_CONTACTS)) {
            // Show an explanation 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,
                    arrayOf(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.
        }
    } else {
        // Permission has already been granted
    }
    

자바

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

        // Permission is not granted
        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
                Manifest.permission.READ_CONTACTS)) {
            // Show an explanation 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; 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.
        }
    } else {
        // Permission has already been granted
    }
    

시스템이 표시하는 메시지에서는 특정 권한이 아니라, 앱이 액세스해야 하는 권한 그룹을 설명합니다.

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

권한 요청 응답 처리

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

Kotlin

    override fun onRequestPermissionsResult(requestCode: Int,
            permissions: Array<String>, grantResults: IntArray) {
        when (requestCode) {
            MY_PERMISSIONS_REQUEST_READ_CONTACTS -> {
                // If request is cancelled, the result arrays are empty.
                if ((grantResults.isNotEmpty() && 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
            }

            // Add other 'when' lines to check for other
            // permissions this app might request.
            else -> {
                // Ignore all other requests.
            }
        }
    }
    

자바

    @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 권한을 요청하는 경우 앱이 기기의 연락처에 액세스해야 한다는 메시지만 시스템 대화상자에 나타납니다. 사용자는 각 권한 그룹에 관해 한 번만 권한을 부여해야 합니다. 앱이 해당 그룹에 있는 다른 권한(앱 manifest에 나열된 다른 권한)을 요청하는 경우 시스템이 자동으로 권한을 부여합니다. 여러분이 권한을 요청하면 시스템은 사용자가 시스템 대화상자를 통해 명시적으로 요청을 승인했을 때와 동일한 방식으로 onRequestPermissionsResult() 콜백 메서드를 호출하고 PERMISSION_GRANTED를 전달합니다.

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

예를 들어 앱 manifest에 READ_CONTACTSWRITE_CONTACTS를 둘 다 나열한다고 가정해보겠습니다. READ_CONTACTS를 요청하여 사용자가 권한을 부여한 후 WRITE_CONTACTS를 요청하면 시스템이 사용자와 상호작용 없이 곧바로 해당 권한을 부여합니다.

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

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

앱 권한을 요청할 때 최고의 사용자 환경을 제공하려면 앱 권한 권장사항도 참조하세요.

API 레벨별 권한 선언

런타임 권한을 지원하는 기기, 즉 Android 6.0(API 레벨 23) 이상을 실행하는 기기에서만 권한을 선언하려면 uses-permission 태그가 아닌 uses- permission-sdk-23 태그를 포함하세요.

이러한 태그 중 하나를 사용하면 maxSdkVersion 속성을 설정하여 상위 버전을 실행하는 기기에서는 특정 권한이 필요하지 않음을 지정할 수 있습니다.

참고 자료

권한에 관한 자세한 정보는 다음 문서를 참조하세요.

권한 요청에 관해 자세히 알아보려면 다음 샘플 앱을 다운로드하세요.