Android 권한 (Permission) 완벽 가이드
Android 권한 (Permission) 완벽 가이드
Android 권한 시스템을 올바르게 이해하고 구현하는 방법을 알아봅니다.
권한 종류
Protection Level
- Normal: 설치 시 자동 부여, 사용자 승인 불필요
- 예:
INTERNET,VIBRATE,ACCESS_NETWORK_STATE
- 예:
- Dangerous: 런타임에 사용자 승인 필요
- 예:
CAMERA,READ_CONTACTS,ACCESS_FINE_LOCATION
- 예:
- Signature: 같은 서명의 앱만 사용 가능
- 예:
BIND_ACCESSIBILITY_SERVICE
- 예:
런타임 권한
Android 6.0 (API 23)+ 및 targetSdkVersion 23 이상에서 필요합니다.
권한 확인
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED
) {
// 권한 있음
openCamera()
} else {
// 권한 요청 필요
requestCameraPermission()
}
권한 요청
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
openCamera()
} else {
showPermissionDeniedMessage()
}
}
private fun requestCameraPermission() {
when {
shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> {
// 권한 필요 이유 설명 후 요청
showPermissionRationale()
}
else -> {
requestPermissionLauncher.launch(Manifest.permission.CAMERA)
}
}
}
여러 권한 동시 요청
private val requestMultiplePermissions = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
permissions.entries.forEach { (permission, isGranted) ->
when {
isGranted -> Log.d(TAG, "$permission granted")
shouldShowRequestPermissionRationale(permission) -> {
// 거부됨, 다시 설명 가능
}
else -> {
// 영구 거부됨
}
}
}
}
// 요청
requestMultiplePermissions.launch(
arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
)
)
권한 플로우
권한 확인 -> 없음 -> shouldShowRationale?
-> Yes: 설명 보여주기 -> 권한 요청
-> No: 권한 요청
-> 허용: 기능 사용
-> 거부:
-> shouldShowRationale = true: 재요청 가능
-> shouldShowRationale = false: 영구 거부됨
영구 거부 처리
private fun handlePermanentDenial() {
AlertDialog.Builder(this)
.setTitle("권한 필요")
.setMessage("이 기능을 사용하려면 설정에서 권한을 허용해주세요.")
.setPositiveButton("설정으로 이동") { _, _ ->
openAppSettings()
}
.setNegativeButton("취소", null)
.show()
}
private fun openAppSettings() {
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", packageName, null)
startActivity(this)
}
}
권한 그룹
같은 그룹의 권한 중 하나를 허용하면 그룹 내 다른 권한도 자동 허용됩니다.
주의: 권한 그룹은 변경될 수 있으므로 각 권한을 개별적으로 체크하세요.
하드웨어 기능
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
if (packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
// 카메라 기능 사용 가능
}
BroadcastReceiver 권한
특정 권한이 있는 앱의 브로드캐스트만 수신:
<receiver
android:name=".SmsReceiver"
android:permission="android.permission.BROADCAST_SMS"
android:exported="true">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
ADB로 권한 테스트
# 권한 부여
adb shell pm grant com.example.app android.permission.CAMERA
# 권한 해제
adb shell pm revoke com.example.app android.permission.CAMERA
Permission Helper 클래스
class PermissionHelper(private val activity: ComponentActivity) {
private var callback: ((Boolean) -> Unit)? = null
private val launcher = activity.registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val allGranted = permissions.values.all { it }
callback?.invoke(allGranted)
}
fun requestPermissions(
permissions: Array<String>,
onResult: (Boolean) -> Unit
) {
callback = onResult
val notGranted = permissions.filter {
ContextCompat.checkSelfPermission(activity, it) !=
PackageManager.PERMISSION_GRANTED
}
if (notGranted.isEmpty()) {
onResult(true)
return
}
launcher.launch(notGranted.toTypedArray())
}
fun hasPermission(permission: String): Boolean {
return ContextCompat.checkSelfPermission(activity, permission) ==
PackageManager.PERMISSION_GRANTED
}
fun shouldShowRationale(permission: String): Boolean {
return activity.shouldShowRequestPermissionRationale(permission)
}
}
사용 예시
class MainActivity : AppCompatActivity() {
private lateinit var permissionHelper: PermissionHelper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
permissionHelper = PermissionHelper(this)
}
private fun checkCameraPermission() {
permissionHelper.requestPermissions(
arrayOf(Manifest.permission.CAMERA)
) { granted ->
if (granted) {
openCamera()
} else {
handlePermissionDenied()
}
}
}
}
SMS 권한 없이 OTP 받기
SMS Retriever API 사용:
val client = SmsRetriever.getClient(this)
client.startSmsRetriever()
.addOnSuccessListener { /* 리스너 시작됨 */ }
.addOnFailureListener { /* 실패 */ }
주의사항
- 권한 그룹 변경 가능: 각 권한을 개별 체크
- 설정에서 해제 가능: 이전에 허용했어도 다시 체크 필요
- 권한 거부 시 앱 재시작:
onTerminate()호출되지 않음 - 권한 허용 후 거부: 영구 거부 기록 초기화됨
결론
런타임 권한은 사용자 프라이버시 보호를 위해 중요합니다. 권한 요청 이유를 명확히 설명하고, 필요할 때만 요청하며, 거부되었을 때의 대체 플로우를 제공하세요.
Comments