라떼는말이야

[Android] permission 권한 정리 본문

안드로이드

[Android] permission 권한 정리

MangBaam 2021. 8. 3. 22:25
반응형

안드로이드 권한의 종류

안드로이드 앱을 사용할 때 주소록, 갤러리 등의 기기의 데이터나 gps, 오디오 녹음 등 특정 기능을 활용하기 위해 사용자에게 권한을 요청할 수 있다.

 

이처럼 사용 중 필요할 때 요청되는 권한을 런타임 권한(Runtime permissions)이라고 하고,

앱이 설치되면서 자동으로 부여되는 권한은 설치 시간 권한(Install-time permissions)이라고 한다.

 

설치 시간 권한에는 일반 권한과 서명 권한 등의 여러 하위 권한이 포함된다.

일반적으로 사용자의 개인 정보나 데이터를 사용하지 않는다.

 

런타음 권한은 다음과 같이 사용자에게 권한 허가 여부를 물어본다.

runtime permission dialog

 

당연한 얘기지만 사용자가 앱을 사용할 때 반드시 필요한 권한만 요청해야한다.

 

이후 부터는 런타임 권한에 대한 이야기를 할 것이므로 편의상 권한이라고 부르겠다.

 

안드로이드 권한 요청 로직

android permission logic

사용자에게 권한을 요청하고 수행하는 과정은 위 로직과 같다.

 

1. manifest 파일에 권한을 사용할 것을 명시해야 한다.

<manifest ...>
    <uses-permission android:name="android.permission.CAMERA"/>
    <application ...>
        ...
    </application>
</manifest>

manifest.xml 파일에 <manifest> 태그 하위, <application> 태그와 동일 뎁스에 위 코드를 넣으면 된다.

위 코드의 경우 카메라에 접근하는 권한이다.

만약 특정 기기에서 카메라가 없는 경우에 해당 앱을 설치하지 못하게 하려면 다음과 같이 required 옵션을 주면 된다.

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

만약 카메라가 없지만 카메라를 사용하는 동작만 실행하지 못하게 하려면 required 옵션을 false로 주고, 다음과 같이 하면 된다.

// 기기에 전면 카메라가 있는지 확인하는 코드
if (applicationContext.packageManager.hasSystemFeature(
        PackageManager.FEATURE_CAMERA_FRONT)) {
    // 전면 카메라를 이용한 기능 수행
} else {
    // 전면 카메라가 필요한 기능 수행 못하도록 처리
}

 

 

2. 앱의 특정 작업과 일치하는 UX를 설계한다.

 

3. 사용자가 (권한이 필요한) 기능을 동작시킬 것을 기다린다.

권한은 사용자 사용 흐름 중 늦게 요청되는 것이 권장된다.

예를 들면 오디오 녹음 권한 없이 카카오톡을 사용하다가 음성 메시지를 보내려고 하는 시점에 오디오 녹음 권한을 요청하는 것이 바람직하다. 채팅만 하는데 오디오 녹음 권한을 거부했다고 앱을 사용 못하게 되면 좋은 앱이라고 할 수 없다.

 

4. 이미 이전에 권한을 허가했는지 확인한다. 허가 했다면 8a번으로, 허가한 적이 없다면 5번으로.

 

5. 사용자에게 권한이 필요한 이유를 설명하는 화면을 보여준다. (선택 사항)

설명 내용으로는 어떤 데이터에 접근하는 것인지, 권한을 허가하면 사용자가 어떤 이점을 얻을 수 있는지가 있다.

 

6. 권한을 요청하는 다이얼로그를 띄운다.

 

7. 권한이 부여됐는지 거부됐는지 확인한다. 부여됐다면 8a로, 부여되지 않았다면 8b번으로.

 

8a. 권한이 부여됐다면 주어진 권한 내에서 작업을 수행한다.

권한이 주어졌어도 카메라나 마이크와 같은 민감한 데이터나 하으뒈어에 엑세스 할 때는 앱에 지속적으로 알림을 표시해야 한다.

 

8b. 권한이 부여되지 않아도 사용자가 권한이 필요한 기능이 아닌 다른 기능들은 정상적으로 사용할 수 있도록 사용성을 제공한다.

 

 

 

좀 더 자세히...

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

val permission = ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE)

when(permission) {

	PERMISSION_GRANTED -> {
    	// 해당 권한이 부여됨
    }
    PERMISSION_DENIED -> {
    	// 해당 권한이 거부됨
    }
}

ContextCompat.checkSelfPermission() 메소드는 매개변수로 입력 되는 권한이 부여가 되었는지 아닌지 확인 후 부여가 됐다면 PERMISSION_GRANTED를, 거부됐다면 PERMISSION_DENIED를 반환한다.

 

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

ContextCompat.checkSelfPermission() 메소드가 PERMISSION_DENIED를 반환, 즉 사용자가 앱 권한을 거부했다면 shouldShowRequestPermissionRationale()true를 반환한다. shouldShowRequestPermissionRationale() 안에 사용자에게 권한이 필요한 이유를 설명하는 다이얼로그 등을 구현하는 용도로 사용된다.

 

반대로 사용자가 권한 요청을 처음 보거나, 다시 묻지 않음을 선택한 경우나, 권한을 허용한 경우 false를 반환하여 해당 메소드가 실행되지 않게 된다.

 

권한 요청

requestPermission을 사용한다.

위에서도 언급했지만 해당 메소드에서 요청하는 권한들은 Manifest에도 적어야한다.

requestPermissions(CONTEXT,
                arrayOf(Manifest.permission.REQUESTED_PERMISSION),
                REQUEST_CODE)

맨 앞 매개변수인 CONTEXT는 생략 가능하다.

두 번째 매개변수는 요청 될 권한들이 array 형태로 입력된다. 이때 null이나 공백이 아니어야 한다.

세 번째 매개변수는 REQUEST_CODE인데 이는 밑에 소개될 onRequestPermissionsResult 콜백 함수에서 사용될 것이다. 0 이상의 숫자로 적으면 된다.

 

요청 결과 처리

위에서 설명한 requestPermission 메소드의 결과를 onRequestPermissionsResult() 메소드에서 확인하고 처리할 수 있다.

override fun onRequestPermissionsResult(requestCode: Int,
        permissions: Array<String>, grantResults: IntArray) {
    when (requestCode) {
        REQUEST_CODE -> {
            // 요청이 취소된 경우 빈 결과 array를 반환한다.
            if ((grantResults.isNotEmpty() &&
                    grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                // 권한이 허가된 것이다. 이 부분에 권한이 주어졌을시 동작 작성
            } else {
            	// 권한이 거부됐을시 동작 작성
                // 권한을 거부했기에 해당 기능을 사용할 수 없음을 사용자에게 알림
                // 하지만 사용자의 결정을 존중하고 그 결정을 바꾸도록 강요하지 않도록 해야한다.
            }
            return
        }

        // 또 다른 권한이 있는 경우 처리 
        else -> {
            // 모든 권한 요청을 거부한 경우
        }
    }
}

REQUEST_CODE에는 requestPermission 메소드에서 작성한 REQUEST_CODE가 식별자가 된다.

만약 해당 권한이 주어진 경우 grantResults를 확인하면 된다. 비어있지 않으면서 첫 번째 요소가 PackageManager.PERMISSION_GRANTED라면 권한이 허가 된 것이다.

 

만약 사용자가 권한 요청을 거부했다면

사용자에게 앱에 필요한 권한이 없어 기능이 제한된 UI의 특정 부분을 강조 표시하고, 권한이 없기 때문에 어떤 기능을 사용할 수 없는지 정확하게 설명한다.

또한 권한 요청을 거부했다는 이유로 권한이 필요없는 다른 기능들도 사용하지 못하게 하면 안되고, 전체 화면 경고 메시지를 표지하지 않아야 한다.

 

 

 

전체적인 로직

private fun addPermission() {
	addPermissionButton.setOnClickListener {	// 버튼 클릭 시 권한 요청하는 코드

        when {
            ContextCompat.checkSelfPermission(
                    CONTEXT,
                    Manifest.permission.REQUESTED_PERMISSION
                    ) == PackageManager.PERMISSION_GRANTED -> {
                // 권한이 주어져있는지 확인 후 권한이 있다면 필요한 기능 수행
                performAction(...)
            }
            shouldShowRequestPermissionRationale(...) -> {
                // 권한이 없는 경우 해당 권한이 왜 필요한지를 설명하는 UI를 띄우는 부분.
                // 사용자가 권한 없이 앱을 계속해서 사용할 수 있도록 UI에 '취소' 버튼을 추가한다.
                showInContextUI(...)
            }
            else -> {
                // 직접 권한을 물을 수도 있다.
                requestPermissions(CONTEXT,
                        arrayOf(Manifest.permission.REQUESTED_PERMISSION),
                        REQUEST_CODE)
            }
        }
    }
}
override fun onRequestPermissionsResult(requestCode: Int,
        permissions: Array<String>, grantResults: IntArray) {
    when (requestCode) {
        REQUEST_CODE -> {
            // 요청이 취소된 경우 빈 결과 array를 반환한다.
            if ((grantResults.isNotEmpty() &&
                    grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                // 권한이 허가된 것이다. 이 부분에 권한이 주어졌을시 동작 작성
            } else {
            	// 권한이 거부됐을시 동작 작성
                // 권한을 거부했기에 해당 기능을 사용할 수 없음을 사용자에게 알림
                // 하지만 사용자의 결정을 존중하고 그 결정을 바꾸도록 강요하지 않도록 해야한다.
            }
            return
        }

        // 또 다른 권한이 있는 경우 처리 
        else -> {
            // 모든 권한 요청을 거부한 경우
        }
    }
}

Android11(API 30) 부터 달라진 점

Android11(API 30) 부터는 사용자가 앱이 설치된 동안 특정 권한을 두 번 이상 거부한다면 앱에서 그 권한을 다시 요청해도 사용자에게 권한 요청 대화상자가 표시되지 않는다. (다시 묻지 않음과 동일)

-> 이 경우 사용자가 해당 권한을 허가하려면 설정 - 애플리케이션 - 해당 앱에서 권한을 직접 켜줘야 한다.

일회성 권한

또한 Android11(API 30)부터 위치, 마이크, 카메라와 관련된 권한을 요청하면 이번만 허용이라는 옵션이 주어진다.

이번만 허용은 다음과 같다.

  • 앱을 사용하는 동안 데이터에 엑세스 할 수 있다.
  • 앱을 백그라운드에 보내면 짧은 시간 동안 데이터에 계속 엑세스 할 수 있다.
  • 앱을 사용하는 동안 포그라운드 서비스가 실행되고 사용자가 앱을 백그라운드로 이동하면 포그라운드 서비스가 중지될 때까지 데이터에 계속 엑세스 할 수 있다.
  • 사용자가 시스템 설정 등에서 이번만 허용 권한을 취소하면 앱에서 데이터에 엑세스할 수 없다. 또한 앱의 프로세스가 종료된다.

 


잘못 설명하고 있는 부분이나 수정할 부분이 있다면 댓글로 알려주시면 감사하겠습니다.

반응형
Comments