Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

앱 권한 요청

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

기본 원칙은 다음과 같습니다.

  • 사용자가 권한이 필요한 기능과 상호작용하기 시작할 때 컨텍스트에 따라 권한을 요청합니다.
  • 사용자를 차단하지 않습니다. 항상 권한과 관련된 교육용 UI 흐름을 취소하는 옵션을 제공합니다.
  • 사용자가 기능에 필요한 권한을 거부하거나 취소하면 권한이 필요한 기능을 사용 중지하는 등의 방법으로 앱의 성능을 단계적으로 저하시켜 사용자가 앱을 계속 사용할 수 있도록 합니다.
  • 시스템 동작을 가정하지 않습니다.

이 페이지에서는 앱에 권한을 추가하고 필요에 따라 런타임 시 이러한 권한을 요청하는 단계별 프로세스를 안내합니다.

매니페스트에 권한 추가

Android 모든 버전에서 앱에 권한이 필요하다고 선언하려면 앱 매니페스트에 최상위 <manifest> 요소의 하위 요소로 <uses-permission> 요소를 삽입합니다.

예를 들어 인터넷에 액세스해야 하는 앱은 매니페스트에 다음 줄이 있어야 합니다.

<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) 이상에서는 사용자가 언제든지 앱에서 위험 권한을 취소할 수 있습니다.

앱에 이미 권한이 부여되었는지 확인

사용자가 이미 앱에 특정 권한을 부여했는지 확인하려면 ContextCompat.checkSelfPermission() 메서드에 권한을 전달합니다. 이 메서드는 앱에 권한이 있는지에 따라 PERMISSION_GRANTED 또는 PERMISSION_DENIED를 반환합니다.

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

ContextCompat.checkSelfPermission() 메서드가 PERMISSION_DENIED를 반환하면 shouldShowRequestPermissionRationale()을 호출하세요. 이 메서드가 true를 반환하면 교육용 UI를 사용자에게 표시합니다. 이 UI에서 사용자가 사용 설정하려는 기능에 특정 권한이 필요한 이유를 설명합니다.

권한 요청

사용자에게 교육용 UI가 표시되거나 shouldShowRequestPermissionRationale()의 반환 값에서 이번에는 교육용 UI를 표시하지 않아도 된다고 나타내면 권한을 요청합니다. 사용자에게 시스템 권한 대화상자가 표시되고 사용자는 여기서 특정 권한을 앱에 부여할지 선택할 수 있습니다.

일반적으로 권한 요청의 일부로 요청 코드를 직접 관리하고 이 요청 코드를 권한 콜백 로직에 포함합니다. 또 다른 옵션은 AndroidX 라이브러리에 포함된 RequestPermission 계약을 사용하는 것으로 여기서 시스템이 권한 요청 코드를 관리하도록 허용합니다. RequestPermission 계약을 사용하면 로직이 간소화되므로 가능하면 사용하는 것이 좋습니다.

시스템이 권한 요청 코드를 관리하도록 허용

시스템이 권한 요청과 연결된 요청 코드를 관리하도록 허용하려면 모듈의 build.gradle 파일에 androidx.activity 라이브러리의 종속 항목을 추가합니다. 버전 1.2.0 이상의 라이브러리를 사용하세요.

그 후에 다음 클래스 중 하나를 사용할 수 있습니다.

다음 단계는 RequestPermission 계약을 사용하는 방법을 보여줍니다. 이 프로세스는 RequestMultiplePermissions 계약과 거의 동일합니다.

  1. 활동 또는 프래그먼트의 초기화 로직에서 ActivityResultCallback 구현을 registerForActivityResult() 호출에 전달합니다. ActivityResultCallback은 앱이 권한 요청에 대한 사용자의 응답을 처리하는 방법을 정의합니다.

    ActivityResultLauncher 유형인 registerForActivityResult()의 반환 값을 계속 참조합니다.

  2. 필요할 때 시스템 권한 대화상자를 표시하려면 이전 단계에서 저장한 ActivityResultLauncher 인스턴스에서 launch() 메서드를 호출합니다.

    launch()가 호출되면 시스템 권한 대화상자가 표시됩니다. 사용자가 선택하면 시스템은 개발자가 이전 단계에서 정의한 ActivityResultCallback 구현을 비동기적으로 호출합니다.

    참고: 앱은 launch()를 호출할 때 표시되는 대화상자를 맞춤설정할 수 없습니다. 사용자에게 더 많은 정보나 컨텍스트를 제공하려면 앱의 UI를 변경하여 사용자가 앱의 기능에 특정 권한이 필요한 이유를 더 쉽게 알 수 있도록 합니다. 예를 들어 기능을 사용 설정하는 버튼의 텍스트를 변경할 수 있습니다.

    또한 시스템 권한 대화상자의 텍스트는 개발자가 요청한 권한과 연결된 권한 그룹을 참조합니다. 이 권한 그룹은 시스템의 사용 편의성을 위해 설계되었으며 앱은 특정 권한 그룹 내부 또는 외부에 있는 권한에 의존해서는 안 됩니다.

다음 코드 스니펫은 권한 응답을 처리하는 방법을 보여줍니다.

Kotlin

// Register the permissions callback, which handles the user's response to the
// system permissions dialog. Save the return value, an instance of
// ActivityResultLauncher. You can use either a val, as shown in this snippet,
// or a lateinit var in your onAttach() or onCreate() method.
val requestPermissionLauncher =
    registerForActivityResult(RequestPermission()
    ) { isGranted: Boolean ->
        if (isGranted) {
            // Permission is granted. Continue the action or workflow in your
            // app.
        } else {
            // Explain to the user that the feature is unavailable because the
            // features requires a permission that the user has denied. At the
            // same time, respect the user's decision. Don't link to system
            // settings in an effort to convince the user to change their
            // decision.
        }
    }

자바

// Register the permissions callback, which handles the user's response to the
// system permissions dialog. Save the return value, an instance of
// ActivityResultLauncher, as an instance variable.
private ActivityResultLauncher<String> requestPermissionLauncher =
    registerForActivityResult(new RequestPermission(), isGranted -> {
        if (isGranted) {
            // Permission is granted. Continue the action or workflow in your
            // app.
        } else {
            // Explain to the user that the feature is unavailable because the
            // features requires a permission that the user has denied. At the
            // same time, respect the user's decision. Don't link to system
            // settings in an effort to convince the user to change their
            // decision.
        }
    });

이 코드 스니펫은 권한을 확인하고 필요할 때 사용자에게 권한을 요청하는 권장 프로세스를 보여줍니다.

Kotlin

when {
    ContextCompat.checkSelfPermission(
            CONTEXT,
            Manifest.permission.REQUESTED_PERMISSION
            ) == PackageManager.PERMISSION_GRANTED -> {
        // You can use the API that requires the permission.
    }
    shouldShowRequestPermissionRationale(...) -> {
        // In an educational UI, explain to the user why your app requires this
        // permission for a specific feature to behave as expected. In this UI,
        // include a "cancel" or "no thanks" button that allows the user to
        // continue using your app without granting the permission.
        showInContextUI(...)
    }
    else -> {
        // You can directly ask for the permission.
        // The registered ActivityResultCallback gets the result of this request.
        requestPermissionLauncher.launch(
                Manifest.permission.REQUESTED_PERMISSION)
    }
}

자바

if (ContextCompat.checkSelfPermission(
        CONTEXT, Manifest.permission.REQUESTED_PERMISSION) ==
        PackageManager.PERMISSION_GRANTED) {
    // You can use the API that requires the permission.
    performAction(...);
} else if (shouldShowRequestPermissionRationale(...)) {
    // In an educational UI, explain to the user why your app requires this
    // permission for a specific feature to behave as expected. In this UI,
    // include a "cancel" or "no thanks" button that allows the user to
    // continue using your app without granting the permission.
    showInContextUI(...);
} else {
    // You can directly ask for the permission.
    // The registered ActivityResultCallback gets the result of this request.
    requestPermissionLauncher.launch(
            Manifest.permission.REQUESTED_PERMISSION);
}

권한 요청 코드 직접 관리

시스템이 권한 요청 코드를 관리하도록 허용하는 대신 권한 요청 코드를 직접 관리할 수 있습니다. 이렇게 하려면 requestPermissions() 호출에 요청 코드를 포함합니다.

다음 코드 스니펫은 요청 코드를 사용하여 권한을 요청하는 방법을 보여줍니다.

Kotlin

when {
    ContextCompat.checkSelfPermission(
            CONTEXT,
            Manifest.permission.REQUESTED_PERMISSION
            ) == PackageManager.PERMISSION_GRANTED -> {
        // You can use the API that requires the permission.
        performAction(...)
    }
    shouldShowRequestPermissionRationale(...) -> {
        // In an educational UI, explain to the user why your app requires this
        // permission for a specific feature to behave as expected. In this UI,
        // include a "cancel" or "no thanks" button that allows the user to
        // continue using your app without granting the permission.
        showInContextUI(...)
    }
    else -> {
        // You can directly ask for the permission.
        requestPermissions(CONTEXT,
                arrayOf(Manifest.permission.REQUESTED_PERMISSION),
                REQUEST_CODE)
    }
}

자바

if (ContextCompat.checkSelfPermission(
        CONTEXT, Manifest.permission.REQUESTED_PERMISSION) ==
        PackageManager.PERMISSION_GRANTED) {
    // You can use the API that requires the permission.
    performAction(...);
} else if (shouldShowRequestPermissionRationale(...)) {
    // In an educational UI, explain to the user why your app requires this
    // permission for a specific feature to behave as expected. In this UI,
    // include a "cancel" or "no thanks" button that allows the user to
    // continue using your app without granting the permission.
    showInContextUI(...);
} else {
    // You can directly ask for the permission.
    requestPermissions(CONTEXT,
            new String[] { Manifest.permission.REQUESTED_PERMISSION },
            REQUEST_CODE);
}

사용자가 시스템 권한 대화상자에 응답하면 시스템은 앱의 onRequestPermissionsResult() 구현을 호출합니다. 시스템은 다음 코드 스니펫과 같이 사용자 응답을 권한 대화상자에 전달하고 개발자가 정의한 요청 코드도 전달합니다.

Kotlin

override fun onRequestPermissionsResult(requestCode: Int,
        permissions: Array<String>, grantResults: IntArray) {
    when (requestCode) {
        PERMISSION_REQUEST_CODE -> {
            // If request is cancelled, the result arrays are empty.
            if ((grantResults.isNotEmpty() &&
                    grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                // Permission is granted. Continue the action or workflow
                // in your app.
            } else {
                // Explain to the user that the feature is unavailable because
                // the features requires a permission that the user has denied.
                // At the same time, respect the user's decision. Don't link to
                // system settings in an effort to convince the user to change
                // their decision.
            }
            return
        }

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

자바

@Override
public void onRequestPermissionsResults(int requestCode, String[] permissions,
        int[] grantResults) {
    switch (requestCode) {
        case PERMISSION_REQUEST_CODE:
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 &&
                    grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission is granted. Continue the action or workflow
                // in your app.
            }  else {
                // Explain to the user that the feature is unavailable because
                // the features requires a permission that the user has denied.
                // At the same time, respect the user's decision. Don't link to
                // system settings in an effort to convince the user to change
                // their decision.
            }
            return;
        }
        // Other 'case' lines to check for other
        // permissions this app might request.
    }
}

권한 거부 처리

사용자가 권한 요청을 거부하면 앱에서는 사용자가 권한 거부에 따른 영향을 이해하도록 지원해야 합니다. 특히 앱은 권한이 없어서 작동하지 않는 기능을 사용자가 인식하도록 해야 합니다. 이때 다음 권장사항에 유의하세요.

  • 사용자의 주의를 유도합니다. 앱에 필요한 권한이 없어서 기능이 제한된 앱 UI의 특정 부분을 강조표시합니다. 다음과 같은 작업을 실행할 수 있습니다.

    • 기능의 결과나 데이터가 나타난 메시지를 표시합니다.
    • 오류 아이콘과 색상이 포함된 다른 버튼을 표시합니다.
  • 자세히 설명합니다. 일반적인 메시지를 표시하지 않습니다. 대신 앱에 필요한 권한이 없어서 어떤 기능을 사용할 수 없는지 언급합니다.

  • 사용자 인터페이스를 차단하지 않습니다. 즉, 사용자가 앱을 계속 사용하는 것을 막는 전체 화면 경고 메시지를 표시하지 않습니다.

어떤 상황에서는 사용자의 조치가 없어도 권한이 자동으로 거부될 수 있습니다. 마찬가지로 권한이 자동으로 부여될 수도 있습니다. 자동 동작에 관해 어떤 가정도 하지 않는 것이 중요합니다. 앱에서 권한이 필요한 기능에 액세스해야 할 때마다 앱에 여전히 권한이 부여되어 있는지 확인해야 합니다.

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

필요 시 기본 핸들러로 요청

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

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

API 수준별 권한 선언

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

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

추가 리소스

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

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