앱을 개발하다 보면 카메라 앱을 통해 사진이나 동영상 촬영 기능을 필요로 하는 경우가 있다.
이때, 카메라 앱 구동에 앞서 해당 앱에 대한 권한 설정이 필요하고, 권한이 설정되었을 때 카메라 앱을 구동시켜야 한다.
앱 권한 설정하기
앞서 언급한 대로 앱에서 카메라 앱에 접근하려면 접근 권한을 허용받아야 한다.
이를 위해 react-native-permission 라이브러리를 사용하면 보다 편하게 권한 설정을 요청하고 확인할 수 있다. 카메라 앱 외에도 다양한 권한을 설정하고 추가할 수 있기 때문에 많은 사람들이 사용하고 있다.
1. Dependency 설치
먼저 개발 환경의 build package에 따라 설치를 해준다.
$ npm install --save react-native-permissions
# --- or ---
$ yarn add react-native-permissions
2. IOS 설정
다음은 ios 폴더의 Podfile 파일에 접근해 다음과 같이 설정을 변경한다.
# react-native 0.72 버전 이상일 경우, 아래의 코드를 삭제
- # 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
# react-native 0.72 버전 이상일 경우, 아래의 코드를 추가
+ 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')
# 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'
react-native 버전의 분기에 따른 설정 방법이 다르니 본인의 프로젝트에서 사용되는 react-native의 버전을 잘 확인해야 한다.
버전 확인하는 방법은 stackoverflow 포스팅을 참고하자.
이후 같은 파일 (Podfile)에 아래와 같이 필요한 권한에 따른 코드를 추가해 준다.
# …
platform :ios, min_ios_version_supported
prepare_react_native_project!
# ⬇️ 필요한 코드를 주석해제하여 사용
setup_permissions([
# 'AppTrackingTransparency',
# 'Bluetooth',
# 'Calendars',
# 'CalendarsWriteOnly',
# 'Camera',
# 'Contacts',
# 'FaceID',
# 'LocationAccuracy',
# 'LocationAlways',
# 'LocationWhenInUse',
# 'MediaLibrary',
# 'Microphone',
# 'Motion',
# 'Notifications',
# 'PhotoLibrary',
# 'PhotoLibraryAddOnly',
# 'Reminders',
# 'Siri',
# 'SpeechRecognition',
# 'StoreKit',
])
# …
카메라 앱의 경우 Camera, Microphone, PhotoLibrary (또는 PhotoLibraryAddOnly) 권한이 필요하다.
Microphone은 마이크 권한에 대한 설정인데, 동영상 촬영 시 필요하기 때문에 설정해 두는 것이 좋다.
다음은 ios > app(앱 이름) > info.plist 파일에 대한 설정이 필요하다.
마찬가지로 필요로 하는 코드만을 추가해 사용하면 된다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- 아래 내용 중 필요로 하는 코드만 사용 -->
<key>NSAppleMusicUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSCalendarsFullAccessUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSCalendarsWriteOnlyAccessUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSCameraUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSContactsUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSFaceIDUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
<key>YOUR-PURPOSE-KEY</key>
<string>YOUR TEXT</string>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSMicrophoneUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSMotionUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSRemindersFullAccessUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSSiriUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSUserTrackingUsageDescription</key>
<string>YOUR TEXT</string>
<!-- … -->
</dict>
</plist>
사진 촬영과 마찬가지로 NSCameraUsageDescription, NSMicrophoneUsageDescription, NSPhotoLibraryUsageDescription에 대한 권한 요청 시 메시지를 <string> 태그 사이에 입력해 주면 된다.
간단한 예시를 들자면, Android는 별도의 메시지 설정이 되지 않기 때문에 Android와 내용을 동일하게 설정할 수 있다. "앱 이름(이)가 사진 권한을 필요로 합니다. 허용하시겠습니까?"와 같은 느낌으로, Android 구동 화면을 참고하면 된다.
앱 심사를 문제없이 통과하기 위해서는(반려 사유가 될 수 있고, 실제로 반려되었던 적이 있었다) 서비스와 연관되어 명확한 안내 문구를 적어주는 것이 좋다. "프로필 설정을 촬영한 사진으로 설정하기 위해 카메라 권한을 필요로 합니다."와 같은 느낌으로 적어주면 앱 심사를 반려되지 않을 수 있다.
3. Android 환경 설정
Android 환경은 설정이 더 간편하다.
android > app > src > main > AndroidManifest.xml 파일에 아래 코드 중 원하는 부분을 추가해 주면 된다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 아래 코드 중 원하는 코드만 사용 (필요없는 코드는 삭제) -->
<uses-permission android:name="android.permission.ACCEPT_HANDOVER" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BODY_SENSORS" />
<uses-permission android:name="android.permission.BODY_SENSORS_BACKGROUND" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_MMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.USE_SIP" />
<uses-permission android:name="android.permission.UWB_RANGING" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- … -->
</manifest>
사진과 동영상을 촬영하기 위해서는 android.permission.CAMERA, android.permission.READ_EXTERNAL_STORAGE, android.permission.WRITE_EXTERNAL_STORAGE 권한을 필요로 한다.
이때 sdk 33 버전 이상을 타깃으로 한다면, 또는 33 버전 이상도 타깃에 포함된다면 android.permission.READ_MEDIA_AUDIO, android.permission.READ_MEDIA_IMAGES, android.permission.READ_MEDIA_VIDEO에 대한 권한을 요청해야 한다.
최종적으로 아래와 같이 권한을 요청할 수 있다.
<!-- ... -->
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- SDK >= 33 permissions for media -->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
<!-- ... -->
이제 기본적인 설정이 끝났다(!)
해당 설정에 대한 부분은 공식 문서를 따르기만 해도 충분히 구현이 가능하니 반드시 공식문서를 잘 읽고 필요한 권한을 설정하도록 하자.
권한 요청하기
기본적인 설정을 마쳤으니, 이제는 원하는 기능 사용에 앞서 권한을 요청하기 위한 함수를 정의해야 한다.
react-native-permissions 라이브러리를 사용하면 requestMultiple이라는 내장 함수를 통해 여러 개의 권한을 한 번에 요청할 수 있다.
간단한 예시로, 카메라 권한을 요청하기 위한 코드는 아래와 같이 작성할 수 있다. (react-native, typescript 기반)
// 카메라 권한 요청
const requestCameraPermission = async (): Promise<boolean> => {
try {
const results = await requestMultiple([
Platform.OS === 'ios' ? PERMISSIONS.IOS.CAMERA : PERMISSIONS.ANDROID.CAMERA,
]);
return results[Platform.OS === 'ios' ? PERMISSIONS.IOS.CAMERA : PERMISSIONS.ANDROID.CAMERA] === RESULTS.GRANTED;
} catch (error) {
console.error(error);
return false; // 예외 발생 시 false 반환
}
};
ios, Android 환경에 맞게 분기를 설정하고, 이에 따라 카메라 권한을 요청한다.
// 비디오 권한 요청
const requestVideoPermission = async (): Promise<boolean> => {
try {
let permissions: Permission[];
if (Platform.OS === 'android') {
permissions =
Platform.Version >= 33
? [
PERMISSIONS.ANDROID.CAMERA,
PERMISSIONS.ANDROID.READ_MEDIA_IMAGES,
PERMISSIONS.ANDROID.READ_MEDIA_VIDEO,
PERMISSIONS.ANDROID.READ_MEDIA_AUDIO,
]
: [PERMISSIONS.ANDROID.CAMERA, PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE];
} else if (Platform.OS === 'ios') {
permissions = [PERMISSIONS.IOS.CAMERA, PERMISSIONS.IOS.MICROPHONE, PERMISSIONS.IOS.PHOTO_LIBRARY];
} else {
return false;
}
const results: Record<Permission, PermissionStatus> = await requestMultiple(permissions);
return permissions.every(permission => results[permission] === RESULTS.GRANTED);
} catch (error) {
console.error(error);
return false;
}
};
영상 촬영의 경우 좀 더 많은 권한을 요청해야 하는데, 방법은 근본적으로 같다.
권한 요청과 별도로, 사진 또는 영상을 기록할 때 권한이 올바르게 설정되어 있는지 추가적으로 확인해 주는 것이 반드시 좋다.
이를 위해 react-native-permissions 라이브러리의 checkMultiple 함수를 호출하여 기존 방법과 비슷하게 각 기능에 필요로 하는 권한을 확인하면 된다. 아래 코드를 참고해서 필요에 따라 수정하여 권한을 요청하면 된다.
// Camera 권한 확인
export const checkCameraPermission = async (): Promise<boolean> => {
const results = await checkMultiple([Platform.OS === 'ios' ? PERMISSIONS.IOS.CAMERA : PERMISSIONS.ANDROID.CAMERA]);
if (results[Platform.OS === 'ios' ? PERMISSIONS.IOS.CAMERA : PERMISSIONS.ANDROID.CAMERA] === RESULTS.GRANTED) {
return true;
} else {
return await requestCameraPermission();
}
};
// Video 권한 확인 및 필요 시 요청
const checkVideoPermission = async (): Promise<boolean> => {
let permissions: Permission[];
if (Platform.OS === 'android') {
permissions =
Platform.Version >= 33
? [
PERMISSIONS.ANDROID.CAMERA,
PERMISSIONS.ANDROID.READ_MEDIA_IMAGES,
PERMISSIONS.ANDROID.READ_MEDIA_VIDEO,
PERMISSIONS.ANDROID.READ_MEDIA_AUDIO,
]
: [PERMISSIONS.ANDROID.CAMERA, PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE];
} else if (Platform.OS === 'ios') {
permissions = [PERMISSIONS.IOS.CAMERA, PERMISSIONS.IOS.PHOTO_LIBRARY];
} else {
console.warn('Unsupported platform');
return false;
}
const results: Record<Permission, PermissionStatus> = await checkMultiple(permissions);
const allPermissionsGranted = permissions.every(permission => results[permission] === RESULTS.GRANTED);
if (allPermissionsGranted) {
return true;
} else {
return await requestVideoPermission();
}
};
위 코드는 예시일 뿐이니, 정확히 어떻게 코드가 작동하는지 알고 필요로 하는 부분만을 사용하는 것이 좋을 것 같다.
권한 요청 함수 호출 및 카메라 구동
이제 정의된 Custom Hook 함수를 호출하고 사용하면 된다!
함수 호출에 앞서 기본 카메라를 사용하기 위한 라이브러리를 설치하여 사용하도록 하자.
react-native-image-picker를 추천한다. 주간 다운로드 수도 많고, 기본 카메라 앱을 불러와 촬영 결과물의 데이터를 사용하기 용이한 편이라고 생각한다.
카메라 앱의 구동과 관련된 다양한 라이브러리가 있기 때문에 앱 서비스의 방향성과 기능성에 따라 프로젝트에 맞는 라이브러리를 사용하도록 하자. 예를 들어 카메라의 직접적인 기능에 관여하고 촬영에 필요로 하는 필터 등을 사용하려면 다른 라이브러리가 더 유용할 수 있다.
아래 코드는 react-native-image-picker를 기반으로 프로젝트에서 사용했던 코드 내용의 일부를 사용한 예시다.
// recordMedia.ts
export const takePhoto = async ({ navigation, route }: ITakePhoto) => {
const granted = await checkCameraPermission();
if (granted) {
await launchCamera({
mediaType: 'photo',
maxWidth: 1920,
maxHeight: 1280,
quality: 0.8,
cameraType: 'back',
includeBase64: false,
}).then(() => {
const asset = response?.assets?.[0];
navigation.navigate('FinalConfirmation', { type: route.params.type, uri: asset.uri as string });
}
});
} else {
Alert.alert('접근 권한 에러', '현재 카메라 사용에 대한 접근 권한이 없습니다. 접근 권한을 허용해 주세요.', [
{
text: '확인',
onPress: () => openSettings(),
},
]);
}
};
기본적으로 권한을 확인한 후 권한 확인 여부를 boolean 형태로 반환받아 해당 여부에 따라 카메라를 구동시키는데, 이때 앞서 언급한 react-native-image-picker 라이브러리의 내장 함수 launchCamera를 사용하여 촬영을 할 수 있다.
launchCamera는 mediaType 매개변수가 'video' 또는 'photo'로 설정하여 동영상 또는 사진 촬영을 진행할 수 있고, 동영상의 경우 최대길이도 지정해 줄 수 있다.
openSettings 함수는 react-native-permissions 라이브러리의 내장 함수로 사용자의 기기에 해당 앱에 대한 설정을 열어 사용자에게 직접적으로 권한 설정을 조정하게 할 수도 있다.
위 코드는 단순한 예시일 뿐, 필요에 따라 store값 저장, navigator의 route에 필요한 정보를 포함시키는 등 앱 서비스의 방향성과 기능성에 따라 코드를 수정하여 권한이 승인되면 카메라를 구동시키고 해당 결괏값을 어떻게 다룰지 코드를 통해 구현하면 된다.
'Front-End > React + Native' 카테고리의 다른 글
[React-Query]리액트 쿼리 Intro (0) | 2024.06.21 |
---|---|
[React-Typescript] 프로젝트 기본 설정 및 DaisyUI 드롭다운 구현하기 (2) | 2024.06.08 |
[React-Native] Splash 화면 / 앱 아이콘 설정하기 (IOS) (0) | 2024.03.29 |
[React Native] Xcode에서 자주 보는 에러 및 해결 방법 (0) | 2023.10.24 |
[React Native] CodePush 설정 및 유의사항 (0) | 2023.09.24 |
댓글