React & React Native/라이브러리 활용
[RN] React Native 앱 접근 권한 관리 이해하고 설정하기: react-native-permissions
adjh54
2023. 6. 15. 22:35
반응형
해당 글에서는 앱의 접근 권한을 관리하는 라이브러리인 react-native-permissions에 대해 이해를 돕기 위해 작성한 글입니다.
1) react-native-permissions
💡 react-native-permissions
- React Native 앱에서 권한을 요청하고 처리하는 라이브러리입니다.
- 사용자가 앱에서 필요로 하는 권한을 요청하면 이 라이브러리는 사용자에게 해당 권한을 요청하는 대화상자를 보여줍니다. 사용자가 권한을 허용하면 라이브러리는 이를 처리하고, 거부하면 사용자에게 알림을 표시합니다.
- 이 라이브러리는 iOS와 Android 플랫폼 모두에서 작동합니다. iOS에서는 사용자가 권한을 승인할 때마다 대화 상자가 표시됩니다. Android에서는 일부 권한은 앱 설치 시점에 사용자에게 요청되고 다른 권한은 앱이 실행되는 동안 사용자에게 요청됩니다.
1. permission의 종류
💡 react-native-permissions: 3.8.0 기준의 권한 관리 종류들입니다.
종류 | 설명 | Andriod 키 값 | iOS 키 값 |
camera | 카메라 사용 권한 | CAMERA | NSCameraUsageDescription |
mic | 마이크 사용 권한 | RECORD_AUDIO | NSMicrophoneUsageDescription |
photo | 사진첩 사용 권한 | READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE | NSPhotoLibraryUsageDescription |
contacts | 연락처 사용 권한 | READ_CONTACTS | NSContactsUsageDescription |
event | 캘린더 이벤트 사용 권한 | READ_CALENDAR, WRITE_CALENDAR | NSCalendarsUsageDescription |
backgroundRefresh | 백그라운드 리프레시 사용 권한 | ACCESS_BACKGROUND_LOCATION | NSLocationBackgroundUsageDescription |
notification | 푸시 알림 사용 권한 | POST_NOTIFICATIONS | None |
bluetooth | 블루투스 사용 권한 | BLUETOOTH, BLUETOOTH_ADMIN | NSBluetoothAlwaysUsageDescription, NSBluetoothPeripheralUsageDescription |
location | 위치 정보 사용 권한 | ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION | NSLocationUsageDescription |
motion | 모션 사용 권한 | BODY_SENSORS | NSMotionUsageDescription |
AppTrackingTransparency | 앱 추적 투명성 사용 권한 | None | NSUserTrackingUsageDescription |
BluetoothPeripheral | 블루투스 Peripheral 사용 권한 | BLUETOOTH, BLUETOOTH_ADMIN | NSBluetoothAlwaysUsageDescription, NSBluetoothPeripheralUsageDescription |
Calendars | 캘린더 사용 권한 | READ_CALENDAR, WRITE_CALENDAR | NSCalendarsUsageDescription |
Contacts | 연락처 사용 권한 | READ_CONTACTS | NSContactsUsageDescription |
FaceID | Face ID 사용 권한 | None | NSFaceIDUsageDescription |
LocationAccuracy | 정확한 위치 정보 사용 권한 | ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION | NSLocationUsageDescription |
LocationAlways | 항상 위치 정보 사용 권한 | ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION | NSLocationAlwaysUsageDescription |
LocationWhenInUse | 사용 중일 때 위치 정보 사용 권한 | ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION | NSLocationWhenInUseUsageDescription |
MediaLibrary | 미디어 라이브러리 사용 권한 | READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE | NSAppleMusicUsageDescription, NSPhotoLibraryUsageDescription |
Microphone | 마이크 사용 권한 | RECORD_AUDIO | NSMicrophoneUsageDescription |
Motion | 모션 사용 권한 | BODY_SENSORS | NSMotionUsageDescription |
Notifications | 알림 사용 권한 | VIBRATE | None |
PhotoLibrary | 사진첩 사용 권한 | READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE | NSPhotoLibraryUsageDescription |
PhotoLibraryAddOnly | 사진첩 추가만 사용 권한 | READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE | NSPhotoLibraryAddUsageDescription |
Reminders | 리마인더 사용 권한 | READ_REMINDERS, WRITE_REMINDERS | NSRemindersUsageDescription |
Siri | Siri 사용 권한 | None | NSSiriUsageDescription |
SpeechRecognition | 음성 인식 사용 권한 | RECORD_AUDIO | NSSpeechRecognitionUsageDescription |
💡 [참고] Android, iOS의 앱 권한 리스트
💡 [참고] Andriod 12 이하 버전에서 Notification 권한에 대해 문제가 발생합니다.
- Andriod 13 버전과 Andriod 12 버전에 대한 분기 알람 체크가 필요합니다 아래의 글을 참고하시면 도움이 됩니다.
2. react-native-permissions 앱 권한 상태(App Permission Status)
반환 값 | 설명 |
RESULTS.UNAVAILABLE | 이 기능은 사용할 수 없습니다 라는 상태 |
RESULTS.DENIED | 권한이 요청되지 않았거나 요청 가능하지만 거부되었습니다 라는 상태 |
RESULTS.GRANTED | 권한이 허용되었습니다 라는 상태 |
RESULTS.LIMITED | 권한이 제한적으로 허용되었습니다 라는 상태 |
RESULTS.BLOCKED | 권한이 거부되었으며 더 이상 요청할 수 없습니다 라는 상태 |
3. react-native-permissions 메서드
메서드 | 설명 |
check(permission: string): Promise<string> | 지정된 권한이 허용되는지 확인합니다. 확인된 권한은 문자열 형식으로 반환됩니다. |
request(permission: string): Promise<string> | 지정된 권한을 요청합니다. 요청된 권한은 문자열 형식으로 반환됩니다. |
requestMultiple(permissions: string[]): Promise<{[permission: string]: string}> | 여러 권한을 동시에 요청합니다. 요청된 권한은 객체 형태로 반환됩니다. |
checkNotifications(): Promise<string> | 현재 알림 권한 상태를 확인합니다. 확인된 권한은 문자열 형식으로 반환됩니다. |
requestNotifications(options?: { ios?: { provisional?: boolean } }): Promise<string> | 알림 권한을 요청합니다. 요청된 권한은 문자열 형식으로 반환됩니다. |
checkMultiple(permissions: string[]): Promise<{[permission: string]: string}> | 여러 권한이 허용되는지 확인합니다. 확인된 권한은 객체 형태로 반환됩니다. |
2) 환경설정 및 구성하기
💡 이번 예시를 위해 사용하려는 권한들입니다.
종류 | 설명 | Andriod 키 값 | iOS 키 값 |
Calendars | 캘린더 사용 권한 | READ_CALENDAR, WRITE_CALENDAR | NSCalendarsUsageDescription |
Camera | 카메라 사용 권한 | CAMERA | NSCameraUsageDescription |
PhotoLibrary | 사진첩 사용 권한 | READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE | NSPhotoLibraryUsageDescription |
Microphone | 마이크 사용 권한 | RECORD_AUDIO | NSMicrophoneUsageDescription |
💡 라이브러리를 설치합니다.
$ npm install --save react-native-permissions
# --- or ---
$ yarn add react-native-permissions
3) Android 설정
1. [AndroidManifest.xml] 권한을 추가합니다.
💡 해당 예시에 대해서 권한에 대해서만 추가하였습니다.
💡 다른 권한 추가를 원하시면 위에 표를 참고하시거나 아래의 링크를 참고하시면 도움이 됩니다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.xxxxx">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
2. [터미널] Gradle을 재 빌드를 수행합니다.
# gradle Clean을 수행합니다.
$ ./gradlew clean
4) iOS 설정
1. [Podfile] react-native 0.72+ 이상인 경우 권한을 추가합니다
💡 권한을 추가합니다 -1 : react-native 0.72 +
- react-native 버전이 0.72 버전 이상인 경우에는 아래와 같이 Podfile 내에 추가를 해줍니다.
######### 기존에 있는 해당 내용을 삭제합니다.
# Resolve react_native_pods.rb with node to allow for hoisting
# require Pod::Executable.execute_command('node', ['-p',
# 'require.resolve(
# "react-native/scripts/react_native_pods.rb",
# {paths: [process.argv[1]]},
# )', __dir__]).strip
######## 해당 내용을 추가합니다.
def node_require(script)
# Resolve script with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
"require.resolve(
'#{script}',
{paths: [process.argv[1]]},
)", __dir__]).strip
end
######## 해당 내용을 추가합니다.
node_require('react-native/scripts/react_native_pods.rb')
node_require('react-native-permissions/scripts/setup.rb')
platform :ios, '13.0'
prepare_react_native_project!
######## 해당 내용을 추가합니다 : 사용할 권한을 추가합니다.
setup_permissions([
'Camera',
]);
💡 [참고] 아래의 라이브러리 공식 사이트를 참고하였습니다.
💡[참고] 아래와 같은 권한들을 추가할 수 있습니다.
setup_permissions([
# 'AppTrackingTransparency',
# 'Bluetooth',
# 'Calendars',
# 'CalendarsWriteOnly',
# 'Camera',
# 'Contacts',
# 'FaceID',
# 'LocationAccuracy',
# 'LocationAlways',
# 'LocationWhenInUse',
# 'MediaLibrary',
# 'Microphone',
# 'Motion',
# 'Notifications',
# 'PhotoLibrary',
# 'PhotoLibraryAddOnly',
# 'Reminders',
# 'Siri',
# 'SpeechRecognition',
# 'StoreKit',
])
권한 | 설명 |
AppTrackingTransparency | 앱이 사용자 활동을 추적할 수 있는 권한입니다. |
Bluetooth | 블루투스 장치와의 연결을 허용하는 권한입니다. |
Calendars | 사용자의 캘린더 정보를 읽을 수 있는 권한입니다. |
CalendarsWriteOnly | 사용자의 캘린더에 쓰기만 할 수 있는 권한입니다. |
Camera | 카메라를 사용할 수 있는 권한입니다. |
Contacts | 사용자의 연락처 정보를 읽을 수 있는 권한입니다. |
FaceID | Face ID를 사용할 수 있는 권한입니다. |
LocationAccuracy | 높은 정확도의 위치 정보를 사용할 수 있는 권한입니다. |
LocationAlways | 앱이 항상 위치 정보를 사용할 수 있는 권한입니다. |
LocationWhenInUse | 앱이 사용 중일 때만 위치 정보를 사용할 수 있는 권한입니다. |
MediaLibrary | 사용자의 미디어 라이브러리에 접근할 수 있는 권한입니다. |
Microphone | 마이크를 사용할 수 있는 권한입니다. |
Motion | 모션 센서를 사용할 수 있는 권한입니다. |
Notifications | 사용자에게 알림을 보낼 수 있는 권한입니다. |
PhotoLibrary | 사용자의 사진 라이브러리에 접근할 수 있는 권한입니다. |
PhotoLibraryAddOnly | 사용자의 사진 라이브러리에 쓰기만 할 수 있는 권한입니다. |
Reminders | 사용자의 리마인더 정보를 읽을 수 있는 권한입니다. |
Siri | Siri를 사용할 수 있는 권한입니다. |
SpeechRecognition | 음성 인식을 사용할 수 있는 권한입니다. |
StoreKit | 앱 내 구매를 처리할 수 있는 권한입니다. |
2. [Podfile] react-native 0.72+ 미만 인 경우 권한을 추가합니다
💡 react-native 0.72+ 이상 인 경우 권한을 추가합니다.
- 아래의 내용을 추가하여 권한을 설정합니다.
# with react-native < 0.72
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
####### 해당 부분을 추가합니다.
require_relative '../node_modules/react-native-permissions/scripts/setup'
platform :ios, '13.0'
prepare_react_native_project!
######## 해당 내용을 추가합니다 : 사용할 권한을 추가합니다.
setup_permissions([
'Camera',
]);
3. [터미널] Podfile을 갱신합니다.
💡 Podfile을 갱신합니다.
- 위에서 변경한 Podfile에 대해서 이를 업데이트를 시켜줍니다.
$ npx pod-install
# or
$ cd ios && pod install
4. [info.plist] 추가 권한을 추가합니다
💡 추가 권한을 추가합니다
- 이전에는 Podfile에서 필요로 하는 권한만 추가하였습니다. 이와 관련되어 다른 내용이 궁금하시면 아래의 링크를 참고하시면 도움이 됩니다.
<dict>
<key>NSCalendarsUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSCameraUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSMicrophoneUsageDescription</key>
<string>YOUR TEXT</string>
</dict>
5) 코드 작성하기
1. 단일 권한 요청
💡 react-native-permissions의 request() 함수를 이용하여서 지정된 권한을 요청하는 함수를 호출하여 결과값(Permission State)이 GRANTED가 되었을 경우 사용자가 권한에 대한 허용을 하였습니다.
import { Alert, Platform } from "react-native";
import { PERMISSIONS, RESULTS, request } from "react-native-permissions";
/**
* '앱의 권한'을 공통으로 관리하는 유틸입니다.
*/
class PermissionUtil {
/**
* [필수] 모든 권한에 대해서 기본적으로 디바이스 플랫폼을 체크합니다.
* @returns {boolean} true : 사용 가능 디바이스 플랫폼, false : 사용 불가능 디바이스 플랫폼
*/
cmmDevicePlatformCheck = (): boolean => {
let isUseDevice = true;
if (Platform.OS !== "ios" && Platform.OS !== "android") !isUseDevice;
return isUseDevice;
}
/**
* 카메라 권한
* @return
*/
cmmReqCameraPermission = async (): Promise<void> => {
// 모든 권한에 대해 디바이스 플랫폼을 체크합니다. (해당 되지 않는 경우 종료합니다.)
if (!this.cmmDevicePlatformCheck()) return;
const platformPermissions = Platform.OS === "ios" ? PERMISSIONS.IOS.CAMERA : PERMISSIONS.ANDROID.CAMERA;
try {
// Request Permission
const result = await request(platformPermissions);
if (result == RESULTS.GRANTED) {
console.log("권한이 허용되었습니다.")
}
} catch (err) {
Alert.alert("Camera permission err");
console.warn(err);
}
}
}
export default PermissionUtil();
2. 결과 확인
3. 다중 권한 요청
1. 함수 선언부
💡 해당 기능에서는 PermissionUtil로 구성을 하였습니다.
1. cmmAccessDevicePlatformCheck()는 앱 권한을 가지는 디바이스 플랫폼인지 여부를 체크를 하는 함수입니다.
2. cmmPermsArrCheck()는 권한 체크에서 모두 다 ‘허용’ 했는지 여부를 체크하는 함수입니다.
3. cmmReqPermis()는 실제로 권한 요청을 위해 데이터를 파라미로 전달받으면 1,2번이 수행되고 권한 요청을 수행합니다.
import { APP_PERMISSION_CODE } from "common/codes/CommonCode";
import { Alert, Linking, Platform } from "react-native";
import { PERMISSIONS, RESULTS, request, requestMultiple, Permission, check } from "react-native-permissions";
/**
* '앱의 권한'을 공통으로 관리하는 유틸입니다.
*/
class PermissionUtil {
/**
* 접근 가능한 디바이스 플랫폼을 체크합니다.
*
* @returns {boolean} true : 사용 가능 디바이스 플랫폼, false : 사용 불가능 디바이스 플랫폼
*/
private cmmAccessDevicePlatformCheck = (): boolean => {
let isUseDevice = true;
if (Platform.OS !== "ios" && Platform.OS !== "android") !isUseDevice;
return isUseDevice;
}
/**
* 권한 체크에서 모두 '허용(granted)'을 하였는지 여부를 체크합니다.
*
* @param {Permission[} permsCodeArr
* @returns {Promise<Permission[]>} 권한 중 '허용'을 모두 한 경우 빈 배열이며 '허용'을 하지 않으면 배열로 반환합니다.
*/
private cmmPermsArrCheck = async (permsCodeArr: Permission[]): Promise<Permission[]> => {
let notGrantedArr: Permission[] = [];
// [STEP1] 전달 받은 배열을 순회하면서 권한을 허용했는지 체크합니다.
for (let permsItem of permsCodeArr) {
// [STEP2] 동일한 플랫폼(andriod, ios)의 코드가 아니면 undefined가 발생합니다. 이를 제외하고 수행합니다.
if (permsItem != undefined) {
//[STEP3] react-native-permissions 함수를 이용해 권한을 체크합니다.
let permsCheck = await check(permsItem);
switch (permsCheck) {
// [CASE1] 권한 상태가 수락(granted) 상태인 경우
case "granted":
break;
// [CASE2] 권한 상태가 수락되지 않은 상태 : 배열을 저장합니다.
case "blocked":
case "denied":
case "limited":
case "unavailable":
notGrantedArr.push(permsItem);
break;
}
}
}
return notGrantedArr;
}
/**
* 권한 체크를 수행할 것을 배열로 전달받습니다.
*
* @param {Permission[]} permsArr
* @returns
*/
cmmReqPermis = async (permsArr: Permission[]): Promise<void> => {
console.log("[+] 함수 실행완료")
// [CASE1] 모든 권한에 대해 디바이스 플랫폼을 체크합니다. (해당 되지 않는 경우 종료합니다.)
if (!this.cmmAccessDevicePlatformCheck()) return;
// [CASE2] 허용되지 않은 권한을 확인합니다.
const notGrantedArr = await this.cmmPermsArrCheck(permsArr);
const notGrantedArrLen = notGrantedArr.length;
/**
* [CASE3] 허용되지 않은 권한이 있는지 체크하여 분기 처리를 합니다.
* - 허용되지 않은 권한이 없을 경우 - 종료
* - 허용되지 않은 권한이 있는 경우 - 권한요청
*/
if (notGrantedArrLen == 0) return;
else {
await requestMultiple(notGrantedArr)
.then((statues) => {
let permsCnt = 0; // 허용되지 않은 권한의 종류
notGrantedArr.map((permsItem) => statues[permsItem] === RESULTS.GRANTED ? permsCnt += 1 : 0);
if (notGrantedArrLen === permsCnt) return;
else {
Linking.openSettings(); // 핸드폰 상 설정 페이지
Alert.alert("권한을 모두 허용해주세요");
}
})
.catch((e) => {
console.log("[-] 권한 요청에서 에러가 발생하였습니다.", e)
})
}
}
}
export default new PermissionUtil();
2. 코드 선언부
💡 APP_PERMISSION_CODE이라는 코드를 구성하여서 각각 안드로이드, iOS 별로 앱 권한을 요청할 수 있도록 구성하였습니다.
/**
* Andriod, IOS 앱 권한 코드
* - calendar : 캘린더 접근 권한
* - camera : 카메라 접근 권한
* - microphone: 마이크 접근 권한
* - mediaLibaray : 외부 저장소 접근 권한
*/
export const APP_PERMISSION_CODE = {
"calendar": [PERMISSIONS.ANDROID.READ_CALENDAR, PERMISSIONS.ANDROID.WRITE_CALENDAR, PERMISSIONS.IOS.CALENDARS],
"camera": [PERMISSIONS.ANDROID.CAMERA, PERMISSIONS.IOS.CAMERA],
"microphone": [PERMISSIONS.ANDROID.RECORD_AUDIO, PERMISSIONS.IOS.MICROPHONE],
"mediaLibaray": [PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE, PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE, PERMISSIONS.IOS.MEDIA_LIBRARY],
}
3. 호출부
💡 특정 페이지에서 호출을 하는 경우 권한 요청을 위해서 처리를 함수를 호출합니다.
useEffect(() => {
PermissionUtil.cmmReqPermis([...APP_PERMISSION_CODE.camera, ...APP_PERMISSION_CODE.calendar, ...APP_PERMISSION_CODE.mediaLibaray]);
}, []);
오늘도 감사합니다. 😀
반응형