사전 런타임 권한
개념

런타임 권한

gabury1고친 사람 github-actions[bot]

런타임 권한은 앱이 민감한 기능을 쓰기 직전에 사용자에게 허락을 묻게 합니다. 설치할 때 한꺼번에 받아 두지 않고 카메라를 켜기 직전 같은 순간에 하나씩 받습니다. 사용자는 거절할 수도 있고 준 허락을 나중에 거둘 수도 있습니다. 주로 안드로이드에서 쓰는 이름입니다.

쉽고 빠른 이해

런타임 권한은 앱이 민감한 기능을 쓰는 순간에 사용자의 허락을 받게 합니다. 사진 찍기 버튼을 누르자 「카메라를 쓰도록 허용할까요」 창이 뜨는 것이 그것입니다.

이게 없으면 사용자는 설치 화면에서 권한 목록 전체에 동의하거나 설치를 포기해야 합니다. 목록만 봐서는 앱이 그 권한을 어디에 쓰는지 알 수 없습니다.

  1. 앱은 쓸 권한을 설정 파일에 미리 적어 둡니다
  2. 기능을 쓰기 직전에 허락이 있는지 봅니다. 없으면 요청합니다
  3. 시스템이 창을 띄우고 사용자의 답을 앱에 돌려줍니다

대가는 앱이 할 일이 늘어난다는 것입니다. 거절당한 경우와 나중에 허락을 거둔 경우까지 앱이 전부 다뤄야 합니다.

상세

이사 업체를 부르면 계약할 때 집 열쇠를 모두 넘기지 않습니다. 짐을 나르던 기사는 안방에 들어가야 할 때가 되면 먼저 묻습니다. 집주인은 왜 들어가려는지 보고 들여보낼지 정합니다.

앱이 어떤 데이터나 기능에 손대도 되는지를 정해 둔 허가를 권한이라고 합니다. 사진·연락처 같은 데이터와 카메라·마이크 같은 장치를 앱이 함부로 쓰지 못하게 막으려고 둡니다.

런타임은 프로그램이 실행 중인 때를 가리킵니다. 런타임 권한은 권한 가운데 앱이 실행 중일 때 사용자에게 직접 받아야 하는 권한입니다. 설치할 때 받는 권한과 맞세워 붙은 이름입니다.

메신저 앱의 「연락처로 친구 찾기」가 대표적인 경우입니다. 사용자가 그 버튼을 누른 직후에 「연락처에 접근하도록 허용할까요」 창이 뜹니다. 버튼을 누르기 전까지 앱은 연락처를 읽지 못합니다.

설치할 때 받는 권한과의 차이

안드로이드가 모든 권한을 사용자에게 묻지는 않습니다. 권한마다 위험한 정도가 매겨져 있습니다. 그 등급이 언제 누가 허락을 내주는지를 정합니다. 등급은 넷입니다.

권한마다 시스템이 붙여 둔 위험 등급을 보호 수준이라고 부릅니다. 권한을 쓰는 앱이 고르는 값이 아닙니다. 권한을 만든 쪽이 정해 둡니다.

권한은 대개 운영체제가 만듭니다. 카메라나 위치 권한이 그렇습니다. 앱도 자기 권한을 새로 만들어 다른 앱에 내줄 수 있습니다.

종류 보호 수준 받는 때 사용자에게 묻나 예
일반 권한 normal 설치할 때 ✗ 인터넷 연결 · 진동
서명 권한 signature 설치할 때 ✗ 앱이 직접 만들어 자기 회사 앱에만 여는 권한
런타임 권한 dangerous 앱이 요청할 때 ✓ 카메라 · 위치 · 연락처 · 마이크
특수 권한 appop 사용자가 설정 화면에서 켤 때 설정 화면에서 직접 다른 앱 위에 화면 띄우기

일반 권한은 사용자의 개인 정보나 다른 앱에 주는 영향이 아주 작습니다. 그래서 시스템이 설치할 때 말없이 내줍니다.

서명 권한은 앱에 찍힌 전자 서명을 봅니다. 전자 서명은 앱을 만든 쪽이 앱에 찍는 표시입니다. 누가 만든 앱인지를 이것으로 가립니다.

서명한 쪽이 누구인지를 밝히는 파일을 인증서라고 합니다. 서명 권한은 권한을 만든 앱과 같은 인증서로 서명한 앱에만 내줍니다. 한 회사가 만든 앱들끼리만 쓰는 기능을 이렇게 묶습니다.

런타임 권한은 개인 정보에 닿거나 시스템과 다른 앱에 크게 영향을 줍니다. 그래서 사용자가 직접 정합니다. 보호 수준 이름을 따서 위험 권한이라고도 부릅니다.

특수 권한은 성격이 또 다릅니다. 앱 안의 창이 아니라 설정 화면의 전용 스위치로 켭니다. 보호 수준 칸의 appop 은 앱 동작(app operation)의 줄임입니다. 앱 하나의 동작을 이 스위치로 따로 켜고 끈다는 표시입니다.

쓰는 순간에 묻게 된 까닭

예전 안드로이드는 권한을 설치 화면에서 한꺼번에 받았습니다. 사용자가 고를 수 있는 것은 목록 전체에 동의하거나 설치를 그만두는 것 둘뿐이었습니다.

목록은 앱이 그 권한을 어디에 쓰는지 말해 주지 않습니다. 손전등 앱이 연락처 권한을 달라고 해도 설치 화면에서는 왜 필요한지 알 길이 없습니다. 목록이 길면 읽지 않고 넘기기도 쉽습니다.

쓰는 순간에 물으면 맥락이 함께 보입니다. 지도에서 「내 위치」 버튼을 누른 직후라면 위치 권한이 왜 필요한지 설명이 없어도 압니다.

권한마다 따로 답할 수 있다는 것도 달라진 점입니다. 사용자는 카메라는 허락하고 연락처는 거절한 채 앱을 계속 씁니다. 일에 꼭 필요한 만큼만 권한을 주자는 최소 권한 원칙을 사용자가 직접 고르게 된 셈입니다.

요청이 오가는 순서

앱 하나가 카메라 권한을 받는 과정을 따라갑니다. 등장하는 쪽은 앱과 시스템과 사용자 셋입니다.

요청보다 먼저 할 일이 있습니다. 앱은 쓸 권한을 매니페스트에 미리 적어 둡니다. 매니페스트는 앱의 이름과 구성 요소와 필요한 권한을 적는 설정 파일입니다. 여기에 없는 권한은 요청해도 받지 못합니다.

실행 중인 앱은 먼저 허락이 이미 있는지 확인합니다. 있으면 요청 없이 바로 카메라를 엽니다. 없으면 요청을 보냅니다. 시스템이 창을 띄워 사용자의 답을 받아 앱에 돌려줍니다.

sequenceDiagram
    participant 앱
    participant 시스템
    participant 사용자
    앱->>시스템: 카메라 허락이 있나
    Note over 앱,시스템: 시스템이 있다고 답하면 여기서 카메라를 연다
    시스템-->>앱: 없다
    앱->>시스템: 카메라 권한 요청
    시스템->>사용자: 허용할까요 창
    사용자-->>시스템: 허용 또는 거절
    시스템-->>앱: 답을 전달

창을 띄우는 쪽은 앱이 아니라 시스템입니다. 앱은 창의 문구도 모양도 바꾸지 못합니다. 그래서 사용자는 어느 앱에서든 같은 창을 보고 답합니다.

허락은 시스템 창에서 누른 답으로만 기록됩니다. 앱이 비슷한 창을 흉내 내 사용자가 「허용」을 눌러도 권한은 생기지 않습니다.

코드로 본 요청

카메라 권한을 요청하는 가장 짧은 코드는 매니페스트 한 줄과 코틀린 코드 한 덩이입니다.

매니페스트는 XML(Extensible Markup Language) 파일입니다. 권한 한 줄은 아래처럼 생겼습니다. android.permission.CAMERA 가 권한의 이름입니다.

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

요청은 앱 코드에서 합니다. 결과를 받을 함수를 먼저 등록해 둡니다. 사용자가 촬영 버튼을 누르면 허락부터 확인합니다. 없을 때만 요청을 띄웁니다.

Kotlin
val cameraLauncher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { granted ->
    if (granted) openCamera() else openGallery()
}

// 촬영 버튼을 눌렀을 때
val camera = Manifest.permission.CAMERA
if (checkSelfPermission(camera) == PERMISSION_GRANTED) {
    openCamera()                  // 이미 허락이 있다
} else {
    cameraLauncher.launch(camera) // 창이 뜬다
}

코드 앞부분의 중괄호 블록은 사용자가 답한 뒤에 불리는 함수입니다. 자바의 람다와 같은 것입니다. 코틀린은 마지막 인자인 람다를 괄호 밖에 씁니다. granted 에는 허락했으면 참, 거절했으면 거짓이 들어옵니다.

openCamera 와 openGallery 는 앱이 직접 만든 함수입니다. 거절당하면 저장된 사진을 고르는 화면처럼 카메라 없이 갈 수 있는 길을 엽니다.

Manifest.permission.CAMERA 는 매니페스트에 적은 android.permission.CAMERA 를 코드에서 부르는 이름입니다. 둘은 같은 권한을 가리킵니다.

코드 뒷부분은 버튼을 누를 때마다 checkSelfPermission 으로 허락이 이미 있는지 먼저 봅니다. 허락이 있으면 PERMISSION_GRANTED 가 돌아옵니다. 그때는 요청 없이 바로 카메라를 엽니다. 창은 허락이 없을 때만 뜹니다.

거절과 철회

허락은 한 번 정해지고 끝나지 않습니다. 권한 하나가 거치는 상태는 아래와 같습니다.

stateDiagram-v2
    state "아직 안 물음" as 미정
    state "허용" as 허용
    state "거절" as 거절
    state "더 물을 수 없음" as 막힘
    [*] --> 미정
    미정 --> 허용: 사용자가 허용
    미정 --> 거절: 사용자가 거절
    거절 --> 허용: 다시 물어 허용
    거절 --> 막힘: 거절을 되풀이
    허용 --> 거절: 설정에서 거둠
    막힘 --> 허용: 사용자가 설정에서 켬

거절당한 앱은 권한 없이 할 수 있는 일을 계속합니다. 카메라가 막히면 저장된 사진 고르기로 가는 식입니다. 권한 하나가 없다고 앱 전체가 멈추면 사용자는 허락하거나 앱을 지우는 것밖에 고를 수 없습니다.

한 번 거절당한 권한을 다시 물을 때는 이유를 먼저 보여 줄 수 있습니다. shouldShowRequestPermissionRationale 는 사용자가 이 권한을 거절한 적이 있으면 참을 돌려줍니다. 앱은 그때 「사진을 찍으려면 카메라가 필요합니다」 같은 설명 화면을 먼저 띄우고 요청합니다.

거절을 되풀이하면 시스템은 그 권한 요청에 더는 창을 띄우지 않습니다. 요청하는 즉시 거절이 돌아옵니다. 남은 길은 사용자를 앱의 설정 화면으로 안내해 거기서 직접 켜게 하는 것입니다.

사용자는 허락한 권한을 설정에서 언제든 거둘 수 있습니다. 그래서 앱은 허락 여부를 한 번 확인해 저장해 두지 않습니다. 기능을 쓸 때마다 다시 확인합니다.

언제 요청하나

어떤 권한이 런타임 권한인지는 앱이 고르지 않습니다. 앱이 고르는 것은 언제 요청하느냐입니다.

앱을 켜자마자 권한을 몰아서 물으면 사용자는 기능을 보지도 못한 채 답해야 합니다. 설치 화면에서 한꺼번에 받던 것과 다를 바가 없어집니다. 쓰는 순간에 묻는 이점이 사라집니다.

그래서 요청은 그 권한이 필요한 기능을 사용자가 부른 직후에 합니다. 기능에 권한이 꼭 필요하지 않으면 요청하지 않습니다. 묻지 않은 권한은 거절될 일도 없습니다.

요청이 잦으면 다른 문제가 생깁니다. 창을 너무 자주 본 사용자는 내용을 읽지 않고 버튼부터 누릅니다. 이를 권한 피로라고 부릅니다.

서버에서 보이는 흔적

런타임 권한은 앱 안의 일 같지만 서버로 오는 데이터에도 흔적을 남깁니다. 허락한 사용자와 거절한 사용자가 보내는 요청의 모양이 다릅니다.

지도 앱의 주변 검색 요청이라면 위치를 허락한 사람과 거절한 사람이 섞여 있습니다. 그래서 위치 필드가 빈 요청이 늘 함께 들어옵니다. 서버는 이 값이 없는 요청도 정상 요청으로 받아야 합니다.

허락은 중간에 바뀌기도 합니다. 어제까지 위치를 보내던 사용자가 설정에서 허락을 거두면 오늘부터 위치가 비어서 옵니다.

다른 플랫폼의 같은 방식

쓰는 순간에 묻는 방식은 안드로이드에만 있지 않습니다. iOS와 웹 브라우저도 같은 생각으로 권한을 다룹니다. 아래 표는 셋이 무엇을 미리 적고 언제 묻는지를 견줍니다.

플랫폼 미리 적는 것 묻는 때
안드로이드 매니페스트의 권한 이름 앱이 요청 함수를 부를 때
iOS 설정 파일의 사용 목적 문구 앱이 그 기능을 처음 쓰려 할 때
웹 브라우저 없음 페이지가 위치나 카메라 기능을 부를 때

iOS 앱은 권한마다 왜 쓰는지 적은 문구를 Info.plist라는 설정 파일에 넣습니다. 시스템 창에 그 문구가 함께 뜹니다. 문구를 빠뜨린 채 그 기능에 손대면 앱이 강제로 종료됩니다.

웹 페이지가 위치를 달라고 하면 브라우저가 허용 창을 띄웁니다. 사용자의 답은 사이트마다 따로 기억됩니다.

관련 항목

런타임 권한이 속하는 상위 분류

권한 · 접근 제어 · 인가 · 최소 권한 · 개인정보 보호

런타임 권한과 맞세워지는 권한 종류

설치 시점 권한 · 일반 권한 · 서명 권한 · 특수 권한 · 보호 수준

런타임 권한을 요청하고 확인하는 안드로이드 함수

registerForActivityResult · ActivityResultContracts · checkSelfPermission · shouldShowRequestPermissionRationale

런타임 권한이 기대는 안드로이드 보안 구조

Android · 매니페스트 · 앱 샌드박스 · 샌드박스 · UID · SELinux · 앱 서명

런타임 권한이 지키는 데이터와 장치

카메라 · 마이크 · 위치 정보 · 연락처 · 백그라운드 위치

같은 방식으로 권한을 묻는 플랫폼

iOS · Info.plist · 웹 브라우저 · Permissions API · Geolocation API

권한 요청에서 생기는 문제

권한 피로 · 과잉 권한 · 권한 상승 · 다크 패턴

다른 이름: runtime permission · 위험 권한 · dangerous permission · 런타임 퍼미션