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의 앱 권한 리스트 
 

PermissionsAndroid · React Native

Project with Native Code Required

reactnative.dev

 

Protected resources | Apple Developer Documentation

Control an app’s access to protected system services and user data.

developer.apple.com

 

 

 

💡 [참고] Andriod 12 이하 버전에서 Notification 권한에 대해 문제가 발생합니다.

- Andriod 13 버전과 Andriod 12 버전에 대한 분기 알람 체크가 필요합니다 아래의 글을 참고하시면 도움이 됩니다.
 

[RN] Android 12 알림 권한 체크 및 권한 요청 방법 : react-native-permission

해당 글에서는 안드로이드 12 이하 버전에서 디바이스 알림 권한 체크가 안됨을 확인하였습니다. 이에 따라 이를 대체하는 방법에 대해 알아봅니다. 1) 현상 1. 개발 현상 파악 💡 개발 현상 파악

adjh54.tistory.com

 

 

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
 

react-native-permissions

An unified permissions API for React Native on iOS, Android and Windows. Latest version: 3.8.0, last published: 3 months ago. Start using react-native-permissions in your project by running `npm i react-native-permissions`. There are 252 other projects in

www.npmjs.com

 

 

 

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>
 

PermissionsAndroid · React Native

Project with Native Code Required

reactnative.dev

 

 

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',
]);

 

 

 

💡 [참고] 아래의 라이브러리 공식 사이트를 참고하였습니다.
 

react-native-permissions

An unified permissions API for React Native on iOS, Android and Windows. Latest version: 4.1.5, last published: 3 months ago. Start using react-native-permissions in your project by running `npm i react-native-permissions`. There are 333 other projects in

www.npmjs.com

 

💡[참고] 아래와 같은 권한들을 추가할 수 있습니다.
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>
 

Protected resources | Apple Developer Documentation

Control an app’s access to protected system services and user data.

developer.apple.com

 

 

 

 

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]);
}, []);

 

 

 

오늘도 감사합니다. 😀

 

 

 

반응형