React & React Native/라이브러리 활용
[RN] expo-sensor의 DeviceMotion 이해하고 활용하기
adjh54
2023. 12. 20. 22:54
반응형
해당 글에서는 expo-sensor를 이용하여서 실제 디바이스가 움직이는 모션을 이용하는 방법에 대해 알아봅니다.
💡 [참고] 이전에 작성한 글을 기반으로 좀 더 보기 편하게 재구성하였습니다.
1) expo-sensors
💡 expo-sensors
- Expo SDK에서 제공하는 센서 관련 기능을 사용하기 위한 라이브러리입니다. 이 라이브러리를 사용하면 Expo 앱에서 다양한 센서를 활용할 수 있습니다.
- 이러한 센서를 통해, 움직임, 방향, 압력, 자기장, 주변광, 걸음 수를 측정할 수 있는 장치 센서에 액세스 할 수 있는 다양한 API를 제공합니다.
2) expo-sensors 종류
💡 expo-sensors 종류
- expo-sensors를 이용하여서 사용이 가능한 센서들에 대해 확인해봅니다
센서 타입 | 이름 | 지원 플랫폼 | 설명 |
Accelerometer | 가속도계 | ALL platform | 장치의 가속도 측정을 통해 움직임을 감지하는 센서 |
Barometer | 기압계 | Andriod, iOS | 대기압을 측정하여 고도를 추정하는 센서 |
DeviceMotion | 디바이스 모션 | ALL platform | 장치의 모션 및 방향을 측정하는 센서 |
Gyroscope | 자이로스코프 | ALL platform | 장치의 회전 속도를 측정하는 센서 |
LightSensor | 조도 센서 | Android | 주변의 조도를 측정하는 센서 |
Magnetometer | 자력계 | Andriod, iOS | 자기장을 측정하여 방위각을 계산하는 센서 |
Pedometer | 보행 센서 | Andriod, iOS | 걸음 수를 측정하는 센서 |
3) DeviceMotion 주요 메서드
메서드 | 설명 |
addListener() | 이벤트 리스너를 추가하여 디바이스 모션 이벤트를 수신합니다. |
getListenerCount() | 현재 등록된 이벤트 리스너의 수를 반환합니다. |
getPermissionsAsync() | 디바이스 모션에 대한 액세스 권한 상태를 비동기적으로 반환합니다. |
hasListeners() | 현재 이벤트에 대한 리스너가 있는지 여부를 확인합니다. |
isAvailableAsync() | 디바이스 모션 기능이 사용 가능한지 여부를 비동기적으로 반환합니다. |
removeAllListeners() | 모든 이벤트 리스너를 제거합니다. |
removeSubscription() | 특정 이벤트 리스너를 제거합니다. |
requestPermissionsAsync() | 디바이스 모션에 대한 액세스 권한을 요청합니다. |
setUpdateInterval() | 디바이스 모션 이벤트를 업데이트하는 간격을 설정합니다. |
[ 더 알아보기 ]
💡 DeviceMotion을 사용하기 위해서는 권한허용이 필요한가?
- 안드로이드의 경우는 해당 권한이 필요하지 않지만 iOS에서는 권한이 필요하다고 합니다.
4) DeviceMotion x, y, z 축 이해하기
💡 DeviceMotion
- expo-sensors를 이용하여 ‘디바이스 장치의 동작 및 방향 센서’에 대한 정보를 제공받을 수 있습니다.
- 이러한 정보는 세 개의 축(x, y, z)으로 제공을 합니다.
1. DeviceMotion x축의 방향
💡 DeviceMotion x축의 방향
- 디바이스 모션에서 x축의 방향은 ‘왼쪽에서 오른쪽으로 이동하는 방향’을 가리킵니다.
2. DeviceMotion y축의 방향
💡 DeviceMotion x축의 방향
- 디바이스 모션에서 y축의 방향은 ‘아래에서 위로 이동하는 방향’을 가리킵니다.
3. DeviceMotion의 z축의 방향
💡 DeviceMotion의 z축의 방향
- 디바이스 모션에서 z축의 방향은 ‘화면을 뒤에서 앞으로 수직 통과하는 방향’을 가리킵니다.
4. x, y, z 축의 정리
💡 x, y, z 축의 정리
- 디바이스 모션을 이용하여 x, y, z 축에 대한 값을 반환받습니다.
- 해당 x, y, z 축의 기본값과 값이 가지는 범위에 대해서 확인해 봅니다.
값 | 설명 | 기준(기본값) | 값의 범위 |
Alpha(x축) | 디바이스의 좌우 움직임에 대한 값 | 디바이스의 위치가 북쪽(0) | -3.14 ~ 3.14 |
Beta(y축) | 디바이스의 상하 움직임에 대한 값 | 디바이스의 위치가 평지(0) | -1.57 ~ 1.57 |
Gamma(z축) | 디바이스의 회전 움직임에 대한 값 | 디바이스의 위치가 정면(0) | -3.14 ~ 3.14 |
5. x, y, z 축을 각도로 변환
💡 x, y, z 축을 각도로 변환
- 디바이스 모션으로 추출한 값을 직관적으로 이해하기 어렵기에 이를 위해 각각의 값을 각도로 계산을 하였습니다.
- 해당 계산법은 DeviceMotion 메서드를 통해서 도출되는 Alpha, beta, Gamma 값을 통해서 각각 이동된 값을 실시간으로 도출된 값을 통해서 각도를 계산합니다.
- 이러한 값을 기반으로 ‘(값) / 3.14 x 180’이라는 계산식을 통해서 각도로 변환을 하였습니다.
5) DeviceMotion 활용하기
1. DeviceMotion 추가
# 패키지 매니저로 npm을 사용하는 경우
$ npm i expo-sensors
# 패키지 매니저로 yarn을 사용하는 경우
$ yarn add expo-sensors
# expo cli를 사용하는 경우
$ npx expo install expo-sensors
2. DeviceMotion 리스너 구성
💡 DeviceMotion 리스너 구성
- 디바이스 모션을 수행하기 위한 함수를 구성하였습니다.
- 해당 함수에서는 디바이스 모션 정보를 가져올 시간을 정하고, 리스너를 등록하여 지정한 시간마다 alpha(x), beta(y), gamma(z)의 값을 가져오는 형태로 구성을 하였습니다.
import { DeviceMotion, DeviceMotionMeasurement } from "expo-sensors";
/**
* 디바이스 모션 수행을 관리합니다.
*/
const handleDeviceMotion = () => {
// [STEP1] 리스너로 디바이스 모션을 통해 가져 올 시간을 지정합니다.(ms 기준)
DeviceMotion.setUpdateInterval(1000);
// [STEP2] 디바이스 모션 리스너를 등록합니다.
DeviceMotion.addListener((decMotionResult: DeviceMotionMeasurement) => {
const { alpha, beta, gamma } = decMotionResult.rotation;
console.log("alpha :: [", alpha, "] beta :: [", beta, "] gamma :: [", gamma, "] ")
})
}
💡 [참고] DeviceMotionMeasurement의 속성값
속성 | 반환 값 | 설명 |
acceleration | x, y, z | 기기의 가속도를 표현하는 값입니다. (m/s^2) |
accelerationIncludingGravity | x, y, z | 중력을 포함한 가속도를 표현하는 값입니다. (m/s^2) |
interval | number | 기본 플랫폼에서 데이터를 가져오는 초 입니다 |
orientation | DeviceMotionOrientation | 화면 회전에 따른 장치 방향입니다. 값은 0, 90, 180, -90 중의 값을 가집니다. |
rotation | alpha, beta, gamma | 알파, 베타, 감마 키가 있는 객체로서 공간에서의 장치 방향입니다. 여기서 알파는 Z축 중심 회전, 베타는 X축 회전, 감마는 Y축 회전을 나타냅니다. |
rotationRate | alpha, beta, gamma | 초당 각도(deg/s)로 표시되는 공간 내 장치의 회전 속도입니다. |
💡 결과 화면
- 아래와 같이 디바이스를 움직일 때마다 값이 출력되는 것을 확인할 수 있습니다.
3. DeviceMotion 계산식 적용
💡 DeviceMotion 계산식 적용
- 각각 축에 따라 각도 형태로 반환하도록 구성하였습니다.
- 이전의 값을 기반으로 ‘(값) / 3.14 x 180’이라는 계산식을 통해서 각도로 변환을 하였습니다.
import { DeviceMotion, DeviceMotionMeasurement } from "expo-sensors";
/**
* 디바이스 모션 수행을 관리합니다.
*/
const handleDeviceMotion = () => {
// [STEP1] 리스너로 디바이스 모션을 통해 가져 올 시간을 지정합니다.(ms 기준)
DeviceMotion.setUpdateInterval(1000);
// [STEP2] 디바이스 모션 리스너를 등록합니다.
DeviceMotion.addListener((decMotionResult: DeviceMotionMeasurement) => {
const { alpha, beta, gamma } = decMotionResult.rotation;
// [STEP3] 수평 기준의 디바이스 위치 값 계산식
const round_alpha: number = Math.round(alpha * 100) / 100;
const round_beta: number = Math.round(beta * 100) / 100;
const round_gamma: number = Math.round(gamma * 100) / 100;
// [STEP4] 위치 값에 따른 계산식 : (값) / 3.14 x 180
const x_axis_angle = Math.round(round_alpha / 3.14 * 180);
const y_axis_angle = Math.round(round_beta / 3.14 * 180);
const z_axis_angle = Math.round(round_gamma / 3.14 * 180);
const deviceMotionObj = {
x: x_axis_angle,
y: y_axis_angle,
z: z_axis_angle,
}
console.log("DeviceMotion :: ", deviceMotionObj)
})
}
💡결과화면
- 아래와 같은 결과값을 출력합니다.
4. DeviceMotion Cleanup + 최종 소스코드
💡 DeviceMotion Cleanup
1. 최초 화면이 렌더링 되면 DeviceMotion이 수행된 리스너가 있는지 확인을 하고 존재하면 리스너를 모두 제거합니다.
2. Devicemotion 리스너를 생성하는 함수를 호출합니다.
3. 해당 페이지에서 clean-up 시점에 리스너를 종료시킵니다.
import { DeviceMotion, DeviceMotionMeasurement } from "expo-sensors";
import { useEffect } from "react";
const DeviceMotionTestScreen = () => {
useEffect(() => {
// 1. 최초 화면이 렌더링 되면 DeviceMotion이 수행된 리스너가 있는지 확인을 하고 존재하면 리스너를 모두 제거합니다.
if (DeviceMotion.getListenerCount() >= 1) {
DeviceMotion.removeAllListeners(); // 루프를 수행하면서 쌓인 리스너 취소
}
// 2. Devicemotion 리스너를 생성하는 함수를 호출합니다.
handleDeviceMotion();
return () => {
// 3. 해당 페이지에서 clean-up 시점에 리스너를 종료시킵니다.
console.log("clean-up")
DeviceMotion.removeAllListeners(); // 루프를 수행하면서 쌓인 리스너 취소
};
}, []);
/**
* 디바이스 모션 수행을 관리합니다.
*/
const handleDeviceMotion = () => {
// [STEP1] 리스너로 디바이스 모션을 통해 가져 올 시간을 지정합니다.(ms 기준)
DeviceMotion.setUpdateInterval(1000);
// [STEP2] 디바이스 모션 리스너를 등록합니다.
DeviceMotion.addListener((decMotionResult: DeviceMotionMeasurement) => {
const { alpha, beta, gamma } = decMotionResult.rotation;
// [STEP3] 수평 기준의 디바이스 위치 값 계산식
const round_alpha: number = Math.round(alpha * 100) / 100;
const round_beta: number = Math.round(beta * 100) / 100;
const round_gamma: number = Math.round(gamma * 100) / 100;
// [STEP4] 위치 값에 따른 계산식 : (값) / 3.14 x 180
const x_axis_angle = Math.round(round_alpha / 3.14 * 180);
const y_axis_angle = Math.round(round_beta / 3.14 * 180);
const z_axis_angle = Math.round(round_gamma / 3.14 * 180);
const deviceMotionObj = {
x: x_axis_angle,
y: y_axis_angle,
z: z_axis_angle,
}
console.log("DeviceMotion :: ", deviceMotionObj)
})
}
}
export default DeviceMotionTestScreen;
💡 결과화면
- 페이지를 이동하는 경우 clean-up을 하면서 DeviceMotion 리스너를 종료합니다.
오늘도 감사합니다. 😀
반응형