The Android Developer Challenge is back! Submit your idea before December 2.

앱 권한 요청

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

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

manifest에 권한 추가

Android 모든 버전에서 앱에 권한이 필요하다고 선언하려면 앱 manifest에 최상위 <manifest> 요소의 하위 요소로 <uses-permission>을 삽입합니다. 예를 들어, 인터넷에 액세스해야 하는 앱은 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
    }
    

Java

    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를 반환합니다.

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

앱 권한 권장사항을 참조하면 권한 요청 시 좋은 사용자 환경을 만드는 방법에 대한 조언을 얻을 수 있습니다.

필요 시 기본 핸들러로 요청

일부 앱은 통화 기록 및 SMS 메시지와 관련된 민감한 사용자 정보에 액세스해야 합니다. 통화 기록 및 SMS 메시지에 특정한 권한을 요청하고 Play 스토어에 앱을 게시하려면 런타임 권한을 요청하기 전에 사용자에게 허용 여부 메시지를 표시하여 앱을 핵심 시스템 기능의 기본 핸들러로 설정하도록 해야 합니다.

기본 핸들러 및 기본 핸들러 허용 여부 메시지를 사용자에게 표시하는 방법에 관한 자세한 정보는 기본 핸들러에서만 사용되는 권한에 관한 가이드를 참조하세요.

앱에 필요한 권한 요청

앱에 필요한 권한이 아직 없다면 앱은 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
    }
    

Java

    // 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.
            }
        }
    }
    

Java

    @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 릴리스에서는 권한 그룹화도 변경될 수 있습니다. 코드에서 특정 권한이 동일한 그룹에 있다고 혹은 없다고 가정해서는 안 됩니다.

예를 들어, READ_CONTACTSWRITE_CONTACTS를 앱 manifest에 나열한다고 가정합니다. 앱이 READ_CONTACTS를 요청하고 사용자가 권한을 부여한 다음 WRITE_CONTACTS를 요청하면 시스템은 사용자와의 상호작용 없이 즉시 앱에 권한을 부여합니다.

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

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

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

API 레벨별 권한 선언

런타임 권한을 지원하는 기기, 즉 Android 6.0(API 레벨 23) 이상을 실행하는 기기에만 권한을 선언하려면 uses-permission 태그 대신 uses- permission-sdk-23 태그를 포함해야 합니다.

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

참고 자료

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

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