0Pricing
Android Academy · 강의

권한 및 런타임 요청

ActivityResultContracts를 사용해 런타임에 위험한 권한을 선언하고 요청하며, 설명 대화상자를 표시하고 영구 거부를 처리합니다.

권한 및 런타임 요청은(는) CoddyKit의 무료 Android Academy 강의입니다. 이것은 7개 중 7번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Android Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Android Academy 강의에는 총 7개의 강의가 포함되어 있습니다.

Android 권한 개요

Android 앱은 필요한 기기 기능을 선언해야 합니다. 권한은 사용자의 개인정보를 보호합니다.

  • 일반 권한 — 위험도가 낮으며 자동으로 부여됩니다(INTERNET, VIBRATE)
  • 위험한 권한 — 민감한 데이터에 접근하며 실행 중에 요청해야 합니다(CAMERA, LOCATION, READ_CONTACTS)
  • 서명 권한 — 동일한 인증서로 서명된 앱에서만 사용할 수 있습니다

매니페스트에 선언하기

모든 권한은 먼저 AndroidManifest.xml에 선언해야 합니다.

<!-- AndroidManifest.xml -->
<manifest ...>

    <!-- Normal: granted automatically -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.VIBRATE" />

    <!-- Dangerous: must request at runtime -->
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />

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

권한이 허용되었는지 확인하기

위험한 기능을 사용하기 전에 권한이 이미 허용되었는지 항상 확인해야 합니다.

import androidx.core.content.ContextCompat
import android.content.pm.PackageManager

fun isCameraPermissionGranted(): Boolean {
    return ContextCompat.checkSelfPermission(
        this,   // Context
        android.Manifest.permission.CAMERA
    ) == PackageManager.PERMISSION_GRANTED
}

ActivityResultLauncher로 권한 요청하기

ActivityResultContracts.RequestPermission()을 사용합니다. 이는 최신 방식이며, 더 이상 사용되지 않는 onRequestPermissionsResult를 사용하지 않습니다.

class CameraActivity : AppCompatActivity() {

    private val requestCameraPermission = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted ->
        if (isGranted) {
            openCamera()
        } else {
            showPermissionDeniedMessage()
        }
    }

    fun onTakePhotoClick(view: View) {
        if (isCameraPermissionGranted()) {
            openCamera()
        } else {
            requestCameraPermission.launch(android.Manifest.permission.CAMERA)
        }
    }
}

여러 권한을 한 번에 요청하기

RequestMultiplePermissions를 사용하여 여러 권한을 요청합니다.

private val requestMultiple = registerForActivityResult(
    ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
    val cameraGranted   = permissions[android.Manifest.permission.CAMERA] ?: false
    val locationGranted = permissions[android.Manifest.permission.ACCESS_FINE_LOCATION] ?: false

    if (cameraGranted && locationGranted) {
        startFeature()
    } else {
        showWhichPermissionsDenied(cameraGranted, locationGranted)
    }
}

fun requestPermissions() {
    requestMultiple.launch(arrayOf(
        android.Manifest.permission.CAMERA,
        android.Manifest.permission.ACCESS_FINE_LOCATION
    ))
}

설명: 언제 이유를 안내해야 할까요?

shouldShowRequestPermissionRationale()은 사용자가 이전에 권한을 거부했을 때 true를 반환합니다. 다시 요청하기 전에 설명을 표시합니다.

fun requestCameraWithRationale() {
    when {
        isCameraPermissionGranted() -> {
            openCamera()
        }
        shouldShowRequestPermissionRationale(android.Manifest.permission.CAMERA) -> {
            // Explain why we need it, then request
            AlertDialog.Builder(this)
                .setTitle("Camera needed")
                .setMessage("We use the camera to take profile photos.")
                .setPositiveButton("OK") { _, _ ->
                    requestCameraPermission.launch(android.Manifest.permission.CAMERA)
                }
                .show()
        }
        else -> {
            requestCameraPermission.launch(android.Manifest.permission.CAMERA)
        }
    }
}

영구적으로 거부됨 — 설정으로 이동시키기

사용자가 "다시 묻지 않음"을 누르면 shouldShowRequestPermissionRationale은 false를 반환하므로 다시 요청할 수 없습니다. 앱 설정으로 이동하도록 안내합니다.

fun openAppSettings() {
    val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
        data = Uri.fromParts("package", packageName, null)
    }
    startActivity(intent)
}

위치 권한

위치 권한에는 두 가지 수준이 있습니다. 사용 목적에 맞는 권한을 선택합니다.

  • ACCESS_COARSE_LOCATION — 도시 수준의 정확도(WiFi/기지국)
  • ACCESS_FINE_LOCATION — GPS 수준의 정확도
  • ACCESS_BACKGROUND_LOCATION — Android 10 이상에서 백그라운드 위치에 접근하려면 별도로 필요

카메라 권한 전체 예제

확인 → 설명 → 요청을 결합한 전체 흐름입니다.

class PhotoActivity : AppCompatActivity() {

    private val cameraLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { granted ->
        if (granted) openCamera() else showDenied()
    }

    private fun handleCameraClick() {
        val permission = android.Manifest.permission.CAMERA
        when {
            ContextCompat.checkSelfPermission(this, permission) ==
                    PackageManager.PERMISSION_GRANTED -> openCamera()
            shouldShowRequestPermissionRationale(permission) ->
                showRationaleDialog()
            else -> cameraLauncher.launch(permission)
        }
    }

    private fun showDenied() {
        Snackbar.make(binding.root, "Camera permission required", Snackbar.LENGTH_LONG)
            .setAction("Settings") { openAppSettings() }.show()
    }
}

권한 처리 모범 사례

원활한 권한 요청 환경을 위한 핵심 규칙입니다.

  • 필요할 때만 권한을 요청합니다(앱이 시작될 때 요청하지 않음).
  • 필요한 최소 권한만 요청합니다.
  • 거부된 경우도 항상 적절하게 처리합니다.
  • "다시 묻지 않음"을 선택한 후에는 반복해서 요청하지 않습니다.
  • 거부된 후가 아니라 시스템 대화상자가 표시되기 전에 이유를 설명합니다.

Android 13 이상에서 세분화된 미디어 권한

Android 13(API 33)부터 READ_EXTERNAL_STORAGE는 세분화된 권한으로 나뉩니다.

  • READ_MEDIA_IMAGES
  • READ_MEDIA_VIDEO
  • READ_MEDIA_AUDIO

항상 Build.VERSION.SDK_INT를 확인하고 OS 버전에 맞는 권한을 요청합니다.

빠른 확인

위험한 권한을 요청하기 전에 언제 이유를 설명하는 대화상자를 표시해야 할까요?

복습: 권한

런타임 권한을 올바르게 처리하는 방법입니다.

  • AndroidManifest.xml에 선언합니다.
  • ContextCompat.checkSelfPermission()으로 확인합니다.
  • registerForActivityResult(RequestPermission())을 통해 요청합니다.
  • shouldShowRequestPermissionRationale()이 true일 때 이유를 설명합니다.
  • 영구적으로 거부된 경우 사용자를 설정으로 안내합니다.
  • 사용자가 권한을 필요로 하기 전에는 절대 요청하지 않습니다.

다음: DataStore로 데이터를 로컬에 저장합니다.

자주 묻는 질문

“권한 및 런타임 요청” 강의는 무료인가요?

네 — “권한 및 런타임 요청” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Android Academy 강의 전체를 잠금 해제할 수 있습니다. Android Academy 강의에는 총 7개의 강의가 포함되어 있습니다.

“권한 및 런타임 요청”에서 뭘 배우나요?

ActivityResultContracts를 사용해 런타임에 위험한 권한을 선언하고 요청하며, 설명 대화상자를 표시하고 영구 거부를 처리합니다. 브라우저에서 직접 실행하는 실습 코드로 Android Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Android Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Android Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 7개 중 7번째 강의입니다.

“권한 및 런타임 요청” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Android Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Android Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 프로젝트 구조 및 매니페스트
  2. 액티비티 및 수명 주기
  3. 레이아웃 및 뷰
  4. 사용자 입력 처리
  5. 인텐트 및 탐색
  6. 프래그먼트
  7. 권한 및 런타임 요청
← Android Academy(으)로 돌아가기