1.1 권한 명세와 기능 명세
설정 파일에 작성하는 명세에는 권한 명세와 기능 명세 두 가지가 있습니다. 권한 명세는 해당 데이터나 기능의 사용여부를 설정하고, 기능 명세는 해당 기능이 있는 안드로이드폰에서만 내려받을 수 있도록 플레이 스토어에서 내려받는 것을 방지합니다.
권한 명세
권한 명세를 설정하는 AndroidManifest.xml 파일은 [app] - [manifests] 디렉터리 밑에 있습니다.
예시로 와이파이 권한과 인터넷 접근 권한을 부여하는 코드입니다.
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
기능 명세
권한 이외에도 기능에 대한 명세가 필요할 때가 있는데 기능 명세는 AndroidManifest.xml파일에 따로 추가하지 않아도 해당 기능을 사용할 때 시스템이 자동으로 부여합니다. 이때 사용하는 태그가 <uses-feature/>태그입니다.
예를 들어 카메라를 사용하는 앱을 개발하면 카메라 기능을 사용하는 순간 자동으로 <uses feature android:name="android.hardware.camera" android:required="true"/>가 AndroidManifest.xml 파일에 명세됩니다.
이렇게 개발한 앱을 플레이 스토어에 올렸을 때 카메라가 없는 스마트폰에서 플레이 스토어에 접속했을 때는 이 앱이 검색되지 않습니다.
그렇다면 카메라 기능이 없어도 해당 앱을 다운받으려면 기능 명세에서 required 옵션을 false로 작성하면 카메라가 없어도 검색되고 설치도 할 수 있습니다. 단, 예외 처리를 해주어야 합니다.(특별한 경우가 아니라면 false x)
1.2 권한의 보호 수준
권한은 일반 권한(Normal Permission), 위험 권한(Dangerous Permission), 서명 권한(Signature Permission) 세 가지의 보호 수준으로 나뉩니다.
일반 권한(Normal Permission)
앱 설치 시 사용자에게 권한 승인을 묻는 팝업창을 보여줍니다. 인터넷 사용, 알람 설정 등이 일반권한에 포함됩니다.
권한 | 설명 |
ACCESS_NETWORK_STATE | 네트워크 연결 상태 확인 |
ACCESS_WIFI_STATE | 와이파이 상태 확인 |
BLUETOOTH | 블루투스 상태 확인 |
INTERNET | 네트워크 및 인터넷 사용 |
NFC | 기기 간 근거리 통신 사용 |
SET_ALARM | 알람 설정 |
VIBRATE | 진동 설정 |
위험 권한(Dangerous Permission)
앱이 사용자의 개인정보와 관련된 데이터나 기능을 액세스하거나 다른 앱 및 기기의 작동에 영향을 줄 우려가 있는 권한입니다. 위험 권한은 [Gradle Scripts]의 build.gradle (Module: app) 파일의 targetSdkVersion이 23(API 23) 이상으로 설정되어야지 정상으로 동작합니다.
권한 그룹 | 권한 | 설명 |
CALENDAR | READ_CALENDAR WRITE_CALENDAR |
캘린더 읽기 캘린더 쓰기 |
CAMERA | CAMERA | 카메라 |
CONTACTS | READ_CONTACTS WRITE_CONTACTS GET_ACCOUNTS |
주소록 읽기 주소록 쓰기 계정 정보 가져오기 |
LOCATION | ACCESS_FINE_LOCATION ACCESS_COARSE_LOCATION |
위치 정보 사용 위치 정보 사용 |
MICROPHONE | RECORD_AUDIO | 마이크 녹음 |
PHONE | READ_PHONE_STATE READ_PHONE_NUMBERS CALL_PHONE ANSWER_PHONE_CALLS READ_CALL_LOG WRITE_CALL_LOG ADD_VOICEMAIL USE_SIP PROCESS_OUTGOING_CALLS |
폰 상태 정보 전화번호 가져오기 발신하기 응답하기 전화 로그 읽기 전화 로그 쓰기 음성메일 추가 SIP 사용 통화 관련 Brodcast 수신 |
SENSORS | BODY_SENSORS | 바디센서 |
SMS | SEND_SMS RECEIVE_SMS READ_SMS RECEIVE_WAP_PUSH RECEIVE_MMS |
SMS 보내기 SMS 받기 SMS 읽기 WAP 수신 MMS 받기 |
STORAGE | READ_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE |
안드로이드 공용 저장소 읽기 안드로이드 공용 저장소 쓰기 |
서명 권한(Signature Permission)
서명 권한은 권한을 사용하려는 앱이 권한을 전의하는 앱과 동일한 인증서로 서명된 경우 시스템은 권한을 자동으로 부여합니다. 쉽게말해 구글에서 만든 앱은 권한이 자동으로 부여되는 것과 같다고 생각하면 됩니다.
1.3 권한 그룹
각각의 권한은 그룹 단위로 구성됩니다. 파일에 대해 읽기/쓰기 권한이 있으면 이 2개의 권한은 하나의 그룹에 속합니다. 권한에 대한 요청은 그룹 단위로 처리되며 동일한 권한 그룹 내에서 다른 권한이 이미 부여된 경우 시스템은 즉시 권한을 부여합니다. 예를 들어 앱에서 동일한 권한 그룹 CONTACTS에 있는 READ_CONTACTS와 WRITE_CONTACTS를 사용해야 하는 앱에서 READ_CONTACTS 요청에 대한 승인이 있었다면 , 시스템은 사용자에게 다시 물어보지 않고 WRITE_CONTACTS 권한에 대한 사용을 허가합니다. 권한 그룹으로 묶인 권한은 모두 한 번에 처리한다고 보면 됩니다.
- 미니 퀴즈 6-1 -
1. 권한의 유형에는 사용자에게 권한 부여 여부만 물어보고 앱의 매니페스트에 공개하면 자동으로 부여되는 ( ① ) 권한과 권한이 부여되면 사용자 데이터 및 특정 시스템 기능에 액세스 할 수 있는 ( ② ) 권한이 있습니다.
답 : ① 일반권한, ② 위험권한
2. 안드로이드 6.0 버전부터는 앱의 매니페스트에서 위험 권한을 공개하고 권한 요청을 코드로도 처리해야 합니다. 액티비티에서 권한을 요청하고 처리하는 메서드는 무엇입니까?
답 :
2. 위험한 권한 처리하기
위험한 권한에 대해서는 AndroidManifest.xml 파일을 수정한 다음 소스 코드에도 추가로 처리해야 합니다.
예제로 카메라 권한을 추가해보겠습니다.
1. AndroidManifest.xml파일을 열고 해당 코드를 작성합니다.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hellow.permission_6">
<uses-permission android:name="android.permission.CAMERA"/> // 카메라 권한
<application
android:allowBackup="true"
...
</application>
</manifest>
2. 권한 요청을 위한 버튼을 생성합니다.
3. 위험 권한을 처리하기 위해 소스 코드 수정 시 다음의 3단계를 거칩니다.
- 권한에 대한 사용자 승인 확인(이전에 승인하였는지)
- 사용자에게 승인 요청
- 사용자 승인 후 처리
[1단계] : 권한에 대한 사용자 승인 요청
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
fun checkPermission() {
// Manifest 클래스에서 카메라 권한 가져옴
val cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
// cameraPermission에 저장된 값의 승인 여부에 따라 분기
if (cameraPermission == PackageManager.PERMISSION_GRANTED) {
// 상태가 승인일 경우 코드 진행
startProcess()
} else {
// 승인되지 않았다면 권한 요청 프로세스 진행
requestPermission()
}
fun startProcess() {
Toast.makeText(this, "카메라를 실행합니다.", Toast.LENGTH_SHORT).show()
}
}
[2단계] : 사용자에게 승인 요청
onCreate() 메서드 안에서 checkPermission()을 호출한 후, 앱을 실행하면 애뮬레이터에 권한 승인을 묻는 팝업이 나타나고 두 번째 입력되는 권한 배열의 개수만큼 2개의 권한을 요청하면 두 번의 팝업이 뜹니다.
fun startProcess() {
Toast.makeText(this, "카메라를 실행합니다.", Toast.LENGTH_SHORT).show()
}
// 미승인일 때 권한 요청하는 메서드
fun requestPermission() {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 99)
}
[3단계] : 사용자 승인 후 처리
권한을 묻는 팝업창에 사용자가 DENY(거부) 또는 ALLOW(수락)을 클릭하면 액티비티의 onRequestPermissionResult() 메서드가 호출됩니다. 메서드 안에서 승인 후 처리하면 됩니다.
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
onRequestPermissionsResult() 파라미터 requestCode : 요청한 주체를 확인하는 코드, requestPermissions() 메서드의 세 번째 파라미터로 전달됩니다. permissions : 요청한 권한 목록. requestPermissions() 메서드의 두 번째 파라미터로 전달됩니다. grantResults : 권한 목록에 대한 승인 미승인 값, 권한 목록의 개수와 같은 수의 결괏값이 전달됩니다. |
override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when(requestCode) {
99 -> {
// 권한 결괏값 확인 후 실행 내용 결정
// 결괏값을 체크해서 승인 여부를 체크하고 승인이면 startProcess() 메서드 실행
// 미승인일 경우 finish() 메서드 실행하여 앱 종료
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startProcess()
} else {
finish()
}
}
}
}
마지막으로 onCrate()메서드 안에 버튼 클릭 시 checkPermission() 메서드 호출하도록 코드 작성
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnCamera : Button = findViewById<Button>(R.id.btnCamera)
btnCamera.setOnClickListener {
checkPermission()
}
}
[실행 결과 및 전체 코드]
(참고로 한번 권한을 수락하면 이후 애플리케이션을 동작 할 때마다 일일이 권한 여부를 물어보지 않습니다.)
[MainActivity.kt]
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnCamera : Button = findViewById<Button>(R.id.btnCamera)
btnCamera.setOnClickListener {
checkPermission()
}
}
fun checkPermission() {
// Manifest 클래스에서 카메라 권한 가져옴
val cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
// cameraPermission에 저장된 값의 승인 여부에 따라 분기
if (cameraPermission == PackageManager.PERMISSION_GRANTED) {
// 상태가 승인일 경우 코드 진행
startProcess()
} else {
// 승인되지 않았다면 권한 요청 프로세스 진행
requestPermission()
}
}
fun startProcess() {
Toast.makeText(this, "카메라를 실행합니다.", Toast.LENGTH_SHORT).show()
}
// 미승인일 때 권한 요청하는 메서드
fun requestPermission() {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 99)
}
override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when(requestCode) {
99 -> {
// 권한 결괏값 확인 후 실행 내용 결정
// 결괏값을 체크해서 승인 여부를 체크하고 승인이면 startProcess() 메서드 실행
// 미승인일 경우 finish() 메서드 실행하여 앱 종료
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startProcess()
} else {
finish()
}
}
}
}
}
- 미니 퀴즈 6-2 -
1. 이 권한이 부여되면 민감한 개인정보 및 기능에 접근할 수 있으며 안드로이드 6.0 버전부터는 앱 매니패스트에 공개하고 코틀린 코드에 권한 요청 및 처리로 액세스가 가능한 권한은 무엇인가요?
답 : 위험 권한
2. 사용자에게 권한을 요청하는 팝업을 표시하는 메서드의 이름은 무엇인가요?
답 : ActivityCompat.requestPermissions(Activity activity, String[] permissions, int requestCode)
3. 사용자에게 권한을 요청한 결과를 알려주는 메서드의 이름은 무엇인가요?
답: ContextCompat.checkSelfPermission(Context context, String permission)
이상 마치겠습니다. :)
'책 요약하기 > 이것이 안드로이드다' 카테고리의 다른 글
#5.5 탭 메뉴로 화면 구성 ViewPager와 TabLayout 2021-03-14 (0) | 2021.03.14 |
---|---|
#5-3. 프래그먼트 2021-03-12 (0) | 2021.03.12 |
#5-2. 컨테이너 2021-03-12 (0) | 2021.03.12 |
#5-1. 화면 구성하기 2021-03-12 (0) | 2021.03.12 |
#4. 위젯과 리소스 다루기 2021-03-06 (0) | 2021.03.06 |