android

Android 권한

android by kotlin 2022. 10. 27. 17:38

안드로이드 권한 유형

(1) 설치 시간 권한: 앱에서 설치 시간 권한을 선언하면 시스템에서는 사용자가 앱을 설치할 때 자동으로 앱에 권한을 부여합니다. 앱스토어에서는 사용자가 앱의 세부정보 페이지를 볼 때 설치 시간 권한 안내를 표시합니다.

(2) 런타임 권한: 런타임 권한은 위험 권한이라고도 잠재적으로 민감한 정보가 포함된 특별 유형의 제한된 데이터에 액세스 하기 위해서 요구되는 권한입니다. 예를들어 위치 정보와 연락처 정보등이 있습니다.

 

 

권한의 필요성에 대한 평가

앱에서 권한을 선언하기 전에 권한의 필요성을 먼저 고려해야 합니다. 런타임 권한을 요청하는 경우 사용자가 권한을 요청하는 이유를 이해하지 못한다면 권한을 거부하거나 앱을 제거할 수도 있기 때문입니다.
그리고 이미 기기에 설치된 다른 앱이 내 앱에서 권한을 획득하여 수행하려는 기능을 대신 수행할 수 있는지도 고려해야 합니다. 만약 다른 앱에서 동일한 기능을 이미 제공하고 있다면 인텐트를 사용하여 다른 앱에 작업을 위임해야 합니다. 이렇게 하면 다른 앱에서 권한을 대신 선언하므로 내 앱에서는 권한을 선언할 필요가 없습니다.

 

권한 선언

(1) 앱에서 권한을 요청하려면 AndroidManifest 파일에 <uses-permissions> 태그를 사용하여 권한을 선언해야 합니다.
이러한 권한 선언은 앱 스토어 및 사용자가 앱이 요청할 수 있는 권한의 종류를 파악하는데 도움이 됩니다.

(2) 권한이 일반 권한 또는 서명 권한과 같이 설치 시간 권한인 경우에는 앱 설치 시 자동으로 권한이 부여되고
런타임 권한인 경우 Android 6.0(API Level 23) 이상인 기기에 설치된 경우 직접 권한을 요청해야 합니다.

(3) CAMERA 와 같은 하드웨어 관련 권한은 앱이 일부 Android 기기만 보유하고 있는 하드웨어에 액세스하도록 허용합니다. 앱에서 이러한 하드웨어 관련 권한을 선언하는 경우, 선언한 권한과 관련된 하드웨어가 없는 기기에서는 앱을 실행할 수 없는지 고려해야 합니다. 이럴 경우에는 AndroidManifest 파일에 아래의 코드 스니펫과 같이 <uses-features> 태그에 android:required 속성을 false 로 설정하여 하드웨어가 없는 기기에서도 앱이 설치될 수 있도록 선언하는 것이 좋습니다. 만약 android:required 속성을 false 로 설정하지 않으면 Android 가 앱이 실행되려면 하드웨어가 필요하다고 가정하므로 해당 하드웨어가 없는 일부 기기에서 앱을 설치하지 못하게 합니다. 그 이후 코드에서 하드웨어가 존재하는 기기인지 확인하여, 만약 하드웨어가 없다면 해당 기능을 적절하게 사용 중지해야 합니다.

<manifest>
	<application>
	...
	</application>
	<uses-features android:name="android.hardware.camera"
					android:required="false" />
</manifest>

 

if(applicationContext.packageManager.hasSystemFeature(
	packageManager.FEATURE_CAMERA_FRONT)) {
	//기기에 전면 카메라가 존재하는 경우
	//정상적으로 기능을 수행합니다.
} else {
	//기기에 전면 카메라가 없는 경우
	//적절한 알람을 띄워서 사용자에게 해당 기능을 사용하지 못함을 알려야 합니다.
}

 

권한 요청

(1) 모든 Android 앱은 샌드박스에서 실행됩니다. 앱이 자체 샌드박스 밖에 있는 리소스나 정보를 사용해야 하는 경우 권한을 선언하고 권한을 요청해야 합니다. 만약 선언한 권한이 런타임 권한(위험 권한)일 경우 런타임에 사용자에게 권한 요청을 해야합니다.

(2) 앱에서 런타임 권한을 선언하고 요청해야 한다고 판단되면 아래의 단계를 따라 완료해야 합니다.

 

더보기

1. AndroidManifest 파일에 권한을 선언합니다. 

 

2. 특정 비공개 데이터에 액세스해야 하는 작업을 사용자가 호출할 때까지 기다리고, 사용자가 해당 작업에 액세스할 때 런타임 권한을 요청해야 합니다.

 

3. 사용자가 비공개 데이터에 액세스했다면, 사용자가 이전에 이미 런타임 권한을 앱에 부여했는지 확인합니다. 부여했다면 추가적인 권한 요청없이 비공개 데이터에 액세스할 수 있습니다. 만약 사용자가 부여하지 않았다면 다음 단계로 이동합니다.(런타임 권한은 필요한 작업을 실행할 때마다 권한이 있는지 확인해야 합니다.)

 

4. 사용자에게 런타임 권한이 필요한 근거를 표시해야 합니다. 여기서 앱이 사용자에게 런타임 권한을 요청하는 이유를 설명합니다. 만약 시스템에서 앱이 근거를 표시하지 않아야 한다고 판단하면 별도의 UI 를 표시하지 않고 다음 단계로 이동하고, 근거를 표시해야 한다고 판단하면 사용자에게 UI 요소로 근거를 표시합니다.
이러한 UI 를 통해 사용자에게 런타임 권한이 필요한 이유를 명확히 설명하고, 사용자가 설명을 확인한 후 다음 단계를 진행합니다.

 

5. 런타임 권한을 요청합니다. 이때 권한 요청을 위한 시스템 다이얼로그가 표시됩니다.

 

6. 사용자가 앱에 런타임 권한 부여를 선택했는지 또는 거부를 선택했는지 사용자의 응답을 확인합니다

 

7. 사용자가 앱에 런타임 권한을 부여했다면 비공개 데이터에 액세스할 수 있고, 사용자가 런타임 권한을 거부했다면 비공개 데이터 없이도 사용자에게 기능을 제공하도록 합니다.


(3) 앱에 이미 권한이 부여되어있는지 확인
ContextCompat.checkSelfPermission() 메서드에 권한을 전달하면 됩니다. 이 메서드는 앱에 권한이 있는지 여부에 따라 PERMISSION_GRANTED 또는 PERMISSION_DENIED 를 반환합니다.

(4) 앱에 권한이 필요한 이유 설명
requestPermissions() 메서드를 호출하면 시스템에서 보여주는 권한 다이얼로그에 앱에서 사용하는 권한이 무엇인지는 표시되지만 필요한 이유는 표시되지 않습니다. 그렇기 때문에 requestPermissions() 를 호출하기 전에 앱에서 권한을 요청하는 이유를 사용자에게 설명하는 것이 좋습니다. 만약 ContextCompat.checkSelfPermission() 메서드가 PERMISSION_DENIED 를 반환하면 shouldShowRequestPermissionRationale() 을 호출하세요. 만약 이 메서드가 true 를 반환하면 권한이 필요한 이유를 설명하는 UI 를 사용자에게 표시합니다. 이 UI 특정 기능을 사용하는데 권한이 필요한 이유를 설명합니다.

(5) 시스템이 권한 요청 코드를 관리하도록 하는 방법

시스템이 권한 요청 코드를 관리하도록 하려면 아래의 코드 스니펫 처럼 작성합니다.
컴포넌트가 STARTED 이전일 때 registerForActivityResult() 메서드를 사용하여 사용자가 권한 요청 다이얼로그를 클릭한 후 호출될 콜백을 등록하고
사용자가 버튼을 클릭했을 때 위에서 얻어온 Launcher 를 통해 권한 요청을 하는 방식입니다.

	private val requestPermissionLauncher = registerForActivityResult(ActivityResultContract.RequestPermission()) { isGrant ->
		if(isGrant) {
			...
		} else {
			...
		}
	}
		
	override fun onCreate(savedInstanceState?: Bundle) {
		super.onCreate(savedInstanceState)
		setContentView(binding.root)
		
		
		init()
	}
	
	private fun init() {
		binding.requestPermissionButton.setOnClickListener {
			requestPermissionLauncher.launch(Manifest.permission.CAMERA)
		}
	}


(7) 권한 요청 코드 직접 관리하는 방법
시스템이 권한 요청 코드를 관리하도록 하는 대신 직접 권한 요청 코드를 관리할 수 있습니다. 이렇게 하려면 아래와 같은 코드 스니펫을 사용하여 할 수 있습니다.

더보기

1. executeRequestPermission 메서드는 매개변수로 요청할 권한과 요청 코드를 받으며 현재 앱에 권한이 있는지 확인 

 

2. 없으면 권한 설명 UI 가 필요한지 확인

 

3. 필요 없으면 requestPermissions() 메서드로 권한 요청하는 순서로 동작합니다.

	private fun executeRequestPermission(val permission: Int, val requestCode: Int) {
		when {
			ContextCompat.checkSelfPermission(
				this,
				permission
			) == PackageManager.PERMISSION_GRANTED -> {
				//사용자가 이전에 이미 권한을 부여한 상태
			}
		
			shouldShowRequestPermissionRationale(
				this,
				permission
			) -> {
				//사용자에게 앱에 권한이 필요한 이유를 설명하는 다이얼로그를 띄웁니다,
				showRationaleDialog()
			} else -> {
				//현재 앱에 권한이 없는 상태이고 
				//앱에 권한이 필요한 이유를 설명하는 다이얼로그를 띄울 필요가 없는 경우 
				//requestPermissions 메서드를 호출하여 권한 요청을 합니다.
				requestPermissions(
					this,
					arrayOf(permission),
					requestCode
				)
			}
		}
	}
	
	override fun onRequestPermissionResult(requestCode: Int, permissions: Array< String, grantResults: IntArray) {
		when(requesCode) {
			요청 코드 -> {
				if((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
					//사용자가 권한을 부여했습니다.
				} else {
					//사용자가 권한을 거부했습니다.
				}
				return
			}
			
			else -> {
			
			}
		}
	}


(8) Android 11(API Level 30) 부터 사용자가 앱이 기기에 설치된 기간 동안 특정 권한에 대해 두번 이상 거부하면 
앱에서 해당 권한을 다시 요청하는 경우 권한 요청을 위한 시스템 다이얼로그가 표시되지 않습니다.