권한 및 런타임 요청
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_IMAGESREAD_MEDIA_VIDEOREAD_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 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.