
??? : Rationale이 뭐야?
안드로이드 앱을 개발하다 보면 권한 처리 는 피할 수 없는 과정입니다.
특히 Android 6.0 (Marshmallow, API 23) 부터는 위험 권한(Dangerous Permissions)을 사용하기 위해 런타임(Runtime)에 사용자로부터 명시적으로 권한을 부여받아야 합니다. 즉, 위치나 카메라 등 사용자의 민감한 정보 또는 하드웨어에 접근하려면 반드시 권한 요청 절차를 거쳐야 합니다. 또한 이 과정에서 버전별 권한 정책 차이, 사용자의 거부/허용 상태에 따른 분기 처리 등 다양한 시나리오도 고려해야 합니다.
이 때 활용될 수 있는 것이 Rationale입니다. 이 글에서는 Rationale을 활용하는 이유, 관련 메서드의 동작 방식과 한계 그리고 그 해결 방법을 알아보겠습니다.
안드로이드 측에서는 특정 상황에서 교육용 UI를 통해 해당 권한이 필요한 이유(Rationale)를 사용자에게 설명하는 것을 권장합니다. 이것을 다이얼로그를 비롯한 여러 종류의 UI 컴포넌트로 사용자에게 알릴 수 있습니다. 아래 예시는 네이버 앱에 ‘위치’ 권한을 부여하지 않은 상태로 처음 진입하였을 때의 모습입니다.
Rationale을 보여주고자 하는 상황은 구현하는 프로젝트 혹은 기능의 성격에 따라 다를 수 있습니다. 다만 안드로이드 공식문서에 따르면 다음과 같은 상황과 순서로 요청하는 방식을 제시하고 있음을 알 수 있습니다.

이 권장 사항에서 다음과 같은 의문이 생길 수 있습니다.
설명을 보여주어야 하는 경우는 어떤 경우인가?
이 질문의 답을 찾기 위해서는 ActivityCompat의 멤버 중 하나인 shouldShowRequestPermissionRationale() 메서드를 살펴볼 필요가 있습니다.
쉽게 말해, 이 함수는 권한 요청 흐름에서 플로우차트의 5a의 여부(Rationale을 보여줄지)를 결정합니다. 함수의 반환값이 true라면 사용자에게 권한이 필요한 이유를 설명하는 UI(Rationale)를 보여주어야 하고, false라면 곧바로 시스템 권한 다이얼로그를 띄우면 됩니다.
해당 함수의 반환값은 다음 기준에 따라 결정됩니다.
shouldShowRequestPermissionRationale()의 반환값은 시스템 내부의 권한 플래그 상태에 따라 결정됩니다. AOSP의 PermissionManagerService의 내부 구현에 의해 다음과 같은 조건으로 구체화됩니다.
@Override
public boolean shouldShowRequestPermissionRationale(
String permName,
String packageName,
int userId
) {
// ...
// 권한 허용이 되어 있는 경우 false 반환
if (checkPermission(permName, packageName, userId)
== PackageManager.PERMISSION_GRANTED) {
return false;
}
// FLAG_PERMISSON_USER_FIXED
// 유저가 시스템 다이얼로그 상에서 2번 거절하거나 '다시 묻지 않음'을 선택했을 때 설정됨
final int flags = getPermissionFlagsInternal(permName, packageName, callingUid, userId);
final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
| PackageManager.FLAG_PERMISSION_POLICY_FIXED
| PackageManager.FLAG_PERMISSION_USER_FIXED;
if ((flags & fixedFlags) != 0) {
return false;
}
// ...
// 이외 상황에서 유저가 한 번이라고 권한을 명시적으로 허용 혹은 거부했을 때 true 반환
return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
}
표의 내용을 요약하면 다음과 같습니다.
| 상황 | FLAG_PERMISSION_USER_FIXED | FLAG_PERMISSION_USER_SET | shouldShowRequestPermissionRationale() | 설명 |
|---|---|---|---|---|
| 처음 요청 | ❌ | ❌ | false | 아직 사용자가 응답하지 않음 |
| 1번 거부 | ❌ | ✅ | true | Rationale 표시 권장 시점 |
| 2번 거부 (Android 11+) | ✅ | ✅ | false | fixedFlags 체크에서 걸림 |
| "다시 묻지 않음" 선택 (Android 10 이하) | ✅ | ✅ | false | fixedFlags 체크에서 걸림 |
| 권한 허용됨 | - | - | false | 첫 번째 체크에서 반환 |
공식문서를 살펴보면, 곳곳에 권한 관련 동작이 사용자의 앱 활용을 방해하지 않아야 한다는 문구를 확인할 수 있습니다. 안드로이드 측에서 가지고 있는 권한 설정 관련 철학을 엿볼 수 있습니다.
사용자 인터페이스를 차단하지 않습니다. 즉, 사용자가 앱을 계속 사용하는 것을 막는 전체 화면 경고 메시지를 표시하지 않습니다.
사용자를 차단하지 않습니다. 교육용 UI 흐름(예: 권한 요청의 근거를 설명하는 흐름)을 취소하는 옵션을 항상 제공합니다.
동시에 앱은 권한을 거부하겠다는 사용자의 결정을 존중해야 합니다. Android 11(API 수준 30)부터 사용자가 앱이 기기에 설치된 전체 기간 동안 특정 권한에 관해 거부를 두 번 이상 탭하면 앱에서 그 권한을 다시 요청하는 경우 사용자에게 시스템 권한 대화상자가 표시되지 않습니다.
안드로이드 권한 가이드라인에서는 shouldShowRequestPermissionRationale()와 FLAG_PERMISSION_USER_FIXED, FLAG_PERMISSION_USER_SET 등의 상태를 기반으로, 사용자가 권한 요청을 거부했을 때 적절한 안내를 하도록 권장하고 있습니다. 하지만 실제 서비스에서는 몇 가지 현실적인 고민이 발생합니다.
이러한 이유로, 많은 프로젝트에서는 서비스 특성에 맞춰 가이드라인을 그대로 따르기보다는 유연하게 대응하고 있습니다. 실제로 정말 중요한 기능이라면, 사용자가 2번 이상 권한을 허용하지 않았을 때, 시스템 다이얼로그는 더 이상 보여주지 못하더라도 설정 화면으로 이동할 수 있는 버튼이 포함된 커스텀 다이얼로그를 표출하여 사용자가 권한을 허용하도록 유도할 수도 있습니다.
예를 들어, 안드로이드 프레임워크에서 제공하는 권한 관련 메서드만 활용하기 보다는, SharedPreference나 DataStore를 활용하여 사용자의 권한 거부 횟수나 안내 표시 여부를 기기에 기록하면, 상황에 맞는 안내를 제공할 수 있습니다.
결국, 권한 요청 전략은 단순히 가이드라인을 따르는 것을 넘어, 사용자 경험과 서비스 특성에 맞게 설계하는 것이 중요합니다. 사용자가 권한 요청으로 불편을 겪지 않도록 하면서도, 앱의 핵심 기능을 안전하게 제공할 수 있어야 할 것입니다.
안드로이드의 권한 처리 관련 메서드 중 shouldShowRequestPermissionRationale()은 안드로이드의 철학이 반영된 유용한 메서드이지만, 모든 서비스의 요구사항을 완벽하게 충족시키기는 어려울 수 있습니다. 따라서 서비스의 특성과 사용자의 맥락을 고려하여 적절한 권한 요청 전략을 수립해야 할 것입니다.
https://developer.android.com/training/permissions/requesting?hl=ko