cap-health-sensor
v0.0.9
Published
Health Sensor
Readme
Cap-Health-Sensor
iOS 및 Android 플랫폼에서 만보계(pedometer) 기능을 제공하는 Capacitor 플러그인입니다. 이 플러그인을 사용하면 걸음 수, 이동 거리, 소모 칼로리 등의 데이터를 수집하고 백그라운드에서도 계속 작동하도록 할 수 있습니다.
주요 기능
- 만보계 기능: 걸음 수, 거리, 칼로리, 속도 측정
- 백그라운드 실행: 앱이 백그라운드에 있어도 계속 동작
- 자동 시작: 앱 시작 시 자동으로 만보계 실행
- 일일 데이터 관리: 날짜별 데이터 저장 및 조회
- 주기적 업데이트: 설정된 간격으로 데이터 업데이트 이벤트 발생
- 데이터 초기화: 현재 데이터 또는 일일 데이터 초기화 기능
설치 방법
npm install cap-health-sensor
npx cap sync권한 설정
iOS
Info.plist 파일에 다음 권한을 추가합니다:
<key>NSMotionUsageDescription</key>
<string>걸음 수를 측정하기 위해 모션 센서 접근 권한이 필요합니다.</string>백그라운드 모드에서 작동하려면 Xcode에서 앱의 Capabilities 설정에서 'Background Modes'를 활성화하고 'Motion Usage'를 체크해야 합니다.
Android
AndroidManifest.xml에 필요한 권한은 이미 플러그인에 포함되어 있지만, Android 10(API 29) 이상에서는 권한을 별도로 요청해야 합니다. 플러그인 내부에서 자동으로 처리합니다.
사용 가이드
1. 플러그인 임포트
import { CapHealthSensor } from 'cap-health-sensor';2. 사용 순서 및 기본 흐름
일반적인 사용 순서는 다음과 같습니다:
- 만보계 사용 가능 여부 확인
- 자동 시작 설정 (선택사항)
- 만보계 시작 및 백그라운드 서비스 시작
- 주기적 업데이트 설정 (선택사항)
- 이벤트 리스너 등록
- 필요 시 데이터 조회 및 관리
3. 만보계 기능 사용 가능 여부 확인
만보계 기능을 사용하기 전에 해당 기능이 기기에서 사용 가능한지 확인합니다.
const checkAvailability = async () => {
const result = await CapHealthSensor.isPedometerAvailable();
if (result.available) {
console.log('만보계 기능 사용 가능');
} else {
console.log('만보계 기능을 사용할 수 없습니다');
}
};4. 자동 시작 설정
앱 실행 시 자동으로 만보계를 시작하도록 설정할 수 있습니다.
// 자동 시작 활성화
const enableAutoStart = async () => {
const result = await CapHealthSensor.setAutoStart({ enabled: true });
console.log(result.message); // 자동 시작 설정이 활성화되었습니다.
};
// 자동 시작 비활성화
const disableAutoStart = async () => {
const result = await CapHealthSensor.setAutoStart({ enabled: false });
console.log(result.message); // 자동 시작 설정이 비활성화되었습니다.
};
// 자동 시작 상태 확인
const checkAutoStartStatus = async () => {
const result = await CapHealthSensor.isAutoStartEnabled();
console.log('자동 시작 활성화 상태:', result.enabled);
};5. 만보계 시작 및 중지
// 만보계 시작
const startPedometer = async () => {
await CapHealthSensor.startPedometer();
console.log('만보계 시작됨');
};
// 만보계 중지
const stopPedometer = async () => {
await CapHealthSensor.stopPedometer();
console.log('만보계 중지됨');
};6. 백그라운드 서비스 관리
백그라운드 서비스를 시작하면 앱이 백그라운드로 전환되어도 계속 만보계가 작동합니다.
// 백그라운드 서비스 시작
const startBackgroundService = async () => {
const result = await CapHealthSensor.startBackgroundService();
console.log(result.message); // 백그라운드 서비스가 시작되었습니다.
};
// 백그라운드 서비스 중지
const stopBackgroundService = async () => {
const result = await CapHealthSensor.stopBackgroundService();
console.log(result.message); // 백그라운드 서비스가 중지되었습니다.
};
// 백그라운드 서비스 상태 확인
const checkBackgroundServiceStatus = async () => {
const result = await CapHealthSensor.isBackgroundServiceRunning();
console.log('백그라운드 서비스 실행 중:', result.running);
};7. 만보계 데이터 가져오기
const getPedometerData = async () => {
const data = await CapHealthSensor.getPedometerData();
console.log('걸음 수:', data.steps);
console.log('거리(미터):', data.distance);
console.log('소모 칼로리:', data.calories);
console.log('속도(미터/초):', data.speed);
console.log('타임스탬프:', new Date(data.timestamp));
};8. 주기적인 데이터 업데이트 설정
// 업데이트 간격 설정 (밀리초 단위)
const setUpdateInterval = async () => {
await CapHealthSensor.setUpdateInterval({ interval: 5000 }); // 5초마다 업데이트
};
// 주기적인 업데이트 시작
const startPeriodicUpdates = async () => {
await CapHealthSensor.startPeriodicUpdates({ interval: 10000 }); // 10초마다 업데이트
};
// 주기적인 업데이트 중지
const stopPeriodicUpdates = async () => {
await CapHealthSensor.stopPeriodicUpdates();
};9. 이벤트 리스너 등록
리액트에서는 useEffect 훅을 사용하여 이벤트 리스너를 등록하고 해제할 수 있습니다.
import { useEffect } from 'react';
import { CapHealthSensor } from 'cap-health-sensor';
function App() {
useEffect(() => {
// 만보계 데이터 업데이트 리스너
const pedometerListener = CapHealthSensor.addListener('pedometerUpdate', (data) => {
console.log('새로운 만보계 데이터:', data);
// 데이터 처리 로직
});
// 일일 데이터 업데이트 리스너
const dailyDataListener = CapHealthSensor.addListener('dailyDataUpdate', (data) => {
console.log('일일 데이터 업데이트:', data);
// 일일 데이터 처리 로직
});
// 컴포넌트 언마운트 시 리스너 제거
return () => {
pedometerListener.remove();
dailyDataListener.remove();
};
}, []);
// 컴포넌트 나머지 부분
}10. 일일 데이터 관리
// 오늘 날짜의 데이터 가져오기
const getTodayData = async () => {
const data = await CapHealthSensor.getDailyData();
console.log('오늘의 걸음 수:', data.steps);
};
// 특정 날짜의 데이터 가져오기 (yyyy-MM-dd 형식)
const getSpecificDateData = async () => {
const data = await CapHealthSensor.getDailyData({ date: '2023-05-15' });
console.log('2023-05-15의 걸음 수:', data.steps);
};
// 모든 일일 데이터 가져오기
const getAllDailyData = async () => {
const result = await CapHealthSensor.getAllDailyData();
console.log('모든 일일 데이터:', result.data);
// 날짜별 데이터 접근 예시
Object.entries(result.data).forEach(([date, data]) => {
console.log(`${date}: ${data.steps}걸음, ${data.distance.toFixed(2)}m`);
});
};
// 특정 날짜의 데이터 삭제
const clearSpecificDateData = async () => {
const result = await CapHealthSensor.clearDailyData({ date: '2023-05-15' });
console.log(result.message); // 지정된 날짜의 데이터가 삭제되었습니다.
};
// 모든 일일 데이터 삭제
const clearAllDailyData = async () => {
const result = await CapHealthSensor.clearAllDailyData();
console.log(result.message); // 모든 일일 데이터가 삭제되었습니다.
};11. 만보계 데이터 초기화
const clearPedometerData = async () => {
const result = await CapHealthSensor.clearPedometerData();
console.log(result.message); // 만보계 데이터가 초기화되었습니다.
};종합 사용 예제 (React/Ionic)
아래는 React와 Ionic을 사용한 완전한 예제입니다. 이 예제는 만보계의 모든 주요 기능을 보여줍니다.
import React, { useEffect, useState } from 'react';
import {
IonButton,
IonContent,
IonPage,
IonText,
IonItem,
IonLabel,
IonToggle,
IonCard,
IonCardContent,
IonCardHeader,
IonCardTitle,
IonList,
IonInput,
IonItemDivider,
IonLoading,
} from '@ionic/react';
import { CapHealthSensor } from 'cap-health-sensor';
import { PedometerData } from 'cap-health-sensor/dist/esm/definitions';
const PedometerApp: React.FC = () => {
// 상태 관리
const [isAvailable, setIsAvailable] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [isPedometerRunning, setIsPedometerRunning] = useState<boolean>(false);
const [isBackgroundRunning, setIsBackgroundRunning] = useState<boolean>(false);
const [isAutoStartEnabled, setIsAutoStartEnabled] = useState<boolean>(false);
const [data, setData] = useState<PedometerData | null>(null);
const [dailyData, setDailyData] = useState<{ [date: string]: PedometerData }>({});
const [updateInterval, setUpdateInterval] = useState<number>(5000);
const [specificDate, setSpecificDate] = useState<string>('');
// 초기화 및 상태 확인
useEffect(() => {
const initialize = async () => {
try {
// 만보계 가용성 확인
const availabilityResult = await CapHealthSensor.isPedometerAvailable();
setIsAvailable(availabilityResult.available);
if (availabilityResult.available) {
// 백그라운드 서비스 상태 확인
const bgStatus = await CapHealthSensor.isBackgroundServiceRunning();
setIsBackgroundRunning(bgStatus.running);
// 자동 시작 설정 확인
const autoStartStatus = await CapHealthSensor.isAutoStartEnabled();
setIsAutoStartEnabled(autoStartStatus.enabled);
// 만보계 데이터 가져오기
const pedometerData = await CapHealthSensor.getPedometerData();
setData(pedometerData);
// 모든 일일 데이터 가져오기
const allDailyData = await CapHealthSensor.getAllDailyData();
setDailyData(allDailyData.data);
// 만보계 실행 중인지 간접적으로 확인 (백그라운드 서비스 또는 자동 시작이 활성화되어 있으면)
setIsPedometerRunning(bgStatus.running || autoStartStatus.enabled);
}
} catch (error) {
console.error('초기화 오류:', error);
} finally {
setIsLoading(false);
}
};
initialize();
// 이벤트 리스너 등록
const pedometerListener = CapHealthSensor.addListener('pedometerUpdate', (newData: PedometerData) => {
setData(newData);
});
const dailyDataListener = CapHealthSensor.addListener(
'dailyDataUpdate',
(newDailyData: { data: { [date: string]: PedometerData } }) => {
setDailyData(newDailyData.data);
},
);
return () => {
pedometerListener.remove();
dailyDataListener.remove();
};
}, []);
// 만보계 시작
const startPedometer = async () => {
try {
await CapHealthSensor.startPedometer();
await CapHealthSensor.startBackgroundService();
await CapHealthSensor.startPeriodicUpdates({ interval: updateInterval });
setIsPedometerRunning(true);
setIsBackgroundRunning(true);
} catch (error) {
console.error('만보계 시작 오류:', error);
}
};
// 만보계 중지
const stopPedometer = async () => {
try {
await CapHealthSensor.stopPedometer();
await CapHealthSensor.stopBackgroundService();
await CapHealthSensor.stopPeriodicUpdates();
setIsPedometerRunning(false);
setIsBackgroundRunning(false);
} catch (error) {
console.error('만보계 중지 오류:', error);
}
};
// 자동 시작 설정 변경
const toggleAutoStart = async (enabled: boolean) => {
try {
const result = await CapHealthSensor.setAutoStart({ enabled });
setIsAutoStartEnabled(enabled);
console.log(result.message);
// 자동 시작이 활성화되면 만보계 시작
if (enabled && !isPedometerRunning) {
startPedometer();
}
} catch (error) {
console.error('자동 시작 설정 오류:', error);
}
};
// 업데이트 간격 변경
const changeUpdateInterval = async () => {
try {
await CapHealthSensor.setUpdateInterval({ interval: updateInterval });
// 주기적 업데이트가 이미 실행 중이면 재시작
if (isPedometerRunning) {
await CapHealthSensor.stopPeriodicUpdates();
await CapHealthSensor.startPeriodicUpdates({ interval: updateInterval });
}
} catch (error) {
console.error('업데이트 간격 설정 오류:', error);
}
};
// 특정 날짜 데이터 조회
const getSpecificDateData = async () => {
if (!specificDate) return;
try {
const dateData = await CapHealthSensor.getDailyData({ date: specificDate });
alert(
`${specificDate} 데이터:\n걸음 수: ${dateData.steps}\n거리: ${dateData.distance.toFixed(2)}m\n칼로리: ${dateData.calories.toFixed(2)}kcal`,
);
} catch (error) {
console.error('날짜 데이터 조회 오류:', error);
}
};
// 데이터 초기화
const clearData = async () => {
try {
const result = await CapHealthSensor.clearPedometerData();
console.log(result.message);
// 데이터 다시 가져오기
const pedometerData = await CapHealthSensor.getPedometerData();
setData(pedometerData);
} catch (error) {
console.error('데이터 초기화 오류:', error);
}
};
// 모든 일일 데이터 초기화
const clearAllDailyData = async () => {
try {
const result = await CapHealthSensor.clearAllDailyData();
console.log(result.message);
setDailyData({});
} catch (error) {
console.error('일일 데이터 초기화 오류:', error);
}
};
if (isLoading) {
return <IonLoading isOpen={true} message={'초기화 중...'} />;
}
return (
<IonPage>
<IonContent className="ion-padding">
<IonCard>
<IonCardHeader>
<IonCardTitle>만보계 앱</IonCardTitle>
</IonCardHeader>
<IonCardContent>
{isAvailable ? (
<IonText color="success">만보계 사용 가능</IonText>
) : (
<IonText color="danger">만보계 사용 불가</IonText>
)}
</IonCardContent>
</IonCard>
{isAvailable && (
<>
{/* 만보계 제어 */}
<IonCard>
<IonCardHeader>
<IonCardTitle>만보계 제어</IonCardTitle>
</IonCardHeader>
<IonCardContent>
<IonItem>
<IonLabel>자동 시작</IonLabel>
<IonToggle checked={isAutoStartEnabled} onIonChange={(e) => toggleAutoStart(e.detail.checked)} />
</IonItem>
<div className="ion-padding-top">
{!isPedometerRunning ? (
<IonButton expand="block" onClick={startPedometer}>
만보계 시작 (백그라운드 포함)
</IonButton>
) : (
<IonButton expand="block" color="danger" onClick={stopPedometer}>
만보계 중지
</IonButton>
)}
</div>
<div className="ion-padding-top">
<IonItem>
<IonLabel position="stacked">업데이트 간격 (ms)</IonLabel>
<IonInput
type="number"
value={updateInterval}
onIonChange={(e) => setUpdateInterval(parseInt(e.detail.value!, 10))}
/>
</IonItem>
<IonButton expand="block" fill="outline" onClick={changeUpdateInterval}>
업데이트 간격 설정
</IonButton>
</div>
<div className="ion-padding-top">
<IonButton expand="block" color="warning" onClick={clearData}>
만보계 데이터 초기화
</IonButton>
</div>
</IonCardContent>
</IonCard>
{/* 만보계 데이터 */}
{data && (
<IonCard>
<IonCardHeader>
<IonCardTitle>현재 만보계 데이터</IonCardTitle>
</IonCardHeader>
<IonCardContent>
<IonList>
<IonItem>
<IonLabel>걸음 수</IonLabel>
<IonText slot="end">{data.steps}</IonText>
</IonItem>
<IonItem>
<IonLabel>거리</IonLabel>
<IonText slot="end">{data.distance.toFixed(2)} 미터</IonText>
</IonItem>
<IonItem>
<IonLabel>칼로리</IonLabel>
<IonText slot="end">{data.calories.toFixed(2)} kcal</IonText>
</IonItem>
<IonItem>
<IonLabel>속도</IonLabel>
<IonText slot="end">{data.speed.toFixed(2)} m/s</IonText>
</IonItem>
<IonItem>
<IonLabel>마지막 업데이트</IonLabel>
<IonText slot="end">{new Date(data.timestamp).toLocaleTimeString()}</IonText>
</IonItem>
</IonList>
</IonCardContent>
</IonCard>
)}
{/* 일일 데이터 관리 */}
<IonCard>
<IonCardHeader>
<IonCardTitle>일일 데이터 관리</IonCardTitle>
</IonCardHeader>
<IonCardContent>
<IonItem>
<IonLabel position="stacked">날짜 (yyyy-MM-dd)</IonLabel>
<IonInput
value={specificDate}
placeholder="2023-05-15"
onIonChange={(e) => setSpecificDate(e.detail.value!)}
/>
</IonItem>
<IonButton expand="block" fill="outline" onClick={getSpecificDateData}>
특정 날짜 데이터 조회
</IonButton>
<div className="ion-padding-top">
<IonButton expand="block" color="warning" onClick={clearAllDailyData}>
모든 일일 데이터 초기화
</IonButton>
</div>
{Object.keys(dailyData).length > 0 && (
<>
<IonItemDivider>저장된 일일 데이터</IonItemDivider>
<IonList>
{Object.entries(dailyData).map(([date, data]) => (
<IonItem key={date}>
<IonLabel>
<h2>{date}</h2>
<p>
걸음 수: {data.steps}, 거리: {data.distance.toFixed(2)}m
</p>
</IonLabel>
</IonItem>
))}
</IonList>
</>
)}
</IonCardContent>
</IonCard>
</>
)}
</IonContent>
</IonPage>
);
};
export default PedometerApp;주요 이벤트 종류
이 플러그인은 두 가지 주요 이벤트를 제공합니다:
pedometerUpdate: 만보계 데이터가 업데이트될 때 발생
CapHealthSensor.addListener('pedometerUpdate', (data) => { console.log('만보계 업데이트:', data); });dailyDataUpdate: 일일 데이터가 업데이트될 때 발생
CapHealthSensor.addListener('dailyDataUpdate', (data) => { console.log('일일 데이터 업데이트:', data.data); });
주의사항
백그라운드 모드 설정:
- iOS에서는 백그라운드 모드에서 오래 실행될 수 있도록 앱 Capabilities에서 'Background Modes'를 활성화하고 'Motion Usage'를 체크해야 합니다.
- Android에서는 백그라운드 서비스가 사용자에게 알림을 표시하므로, 알림 채널 설정이 필요합니다.
배터리 소모:
- 지속적인 만보계 사용은 배터리 소모가 큽니다. 필요하지 않을 때는 만보계와 백그라운드 서비스를 중지해주세요.
- 업데이트 간격을 너무 짧게 설정하면 배터리 소모가 증가합니다.
데이터 정확성:
- 만보계 데이터는 기기의 센서와 알고리즘에 따라 정확도가 달라질 수 있습니다.
- 칼로리 계산은 대략적인 추정치이며, 사용자의 신체 정보(키, 체중)를 고려하지 않습니다.
iOS 제한 사항:
- iOS에서는 앱이 완전히 종료된 경우(사용자가 스와이프로 앱을 종료) 백그라운드 서비스가 동작하지 않습니다.
- 앱이 다시 시작될 때 자동 시작 설정이 활성화되어 있으면 자동으로 만보계가 다시 시작됩니다.
권한 요청:
- iOS와 Android 모두 첫 실행 시 권한 요청이 발생합니다. 사용자가 권한을 거부하면 만보계 기능을 사용할 수 없습니다.
- 사용자가 권한을 거부한 경우, 앱 설정에서 권한을 다시 부여하도록 안내해야 합니다.
문제 해결
만보계가 백그라운드에서 동작하지 않는 경우
iOS의 경우:
- Xcode에서 백그라운드 모드 권한이 제대로 설정되었는지 확인하세요.
Info.plist에NSMotionUsageDescription권한이 있는지 확인하세요.- 사용자가 앱을 완전히 종료하지 않았는지 확인하세요.
Android의 경우:
- 배터리 최적화 설정을 확인하고, 앱을 배터리 최적화 예외 목록에 추가하세요.
- 권한이 제대로 부여되었는지 확인하세요.
데이터가 정확하지 않은 경우
- 기기의 센서 품질에 따라 정확도가 달라질 수 있습니다.
- 일부 기기에서는 걸음 수가 계단 오르기 또는 달리기와 같은 활동에서 부정확할 수 있습니다.
- 거리 계산은 평균 보폭을 기반으로 하므로 실제 거리와 다를 수 있습니다.
배터리 소모가 심한 경우
- 업데이트 간격을 늘려보세요 (예: 5초에서 30초로).
- 앱을 사용하지 않을 때는 백그라운드 서비스를 중지하세요.
- 자동 시작 기능은 지속적인 모니터링이 필요한 경우에만 활성화하세요.
백그라운드 데이터 저장 및 자동 실행 설정 가이드
이 플러그인은 앱이 백그라운드에 있거나 종료된 상태에서도 걸음 수 데이터를 지속적으로 수집하고 저장할 수 있습니다. 이 기능을 제대로 활용하기 위한 설정 방법을 안내합니다.
1. 데이터 자동 저장 설정
이 플러그인은 다음 세 가지 방식으로 데이터를 저장합니다:
- SharedPreferences/UserDefaults: 주 저장소로 사용되며 빠른 액세스를 제공합니다.
- 백업 파일: 주 저장소가 손상될 경우를 대비한 백업 메커니즘입니다.
- 일일 백업 파일: 날짜별로 데이터를 별도 저장하여 데이터 손실을 최소화합니다.
모든 데이터는 자동으로 저장되므로 별도의 설정이 필요하지 않습니다.
2. 앱 실행 없이 만보기 자동 작동 설정
앱을 직접 실행하지 않아도 만보기가 자동으로 동작하게 하려면:
// 자동 시작 기능 활성화
await CapHealthSensor.setAutoStart({ enabled: true });
// 백그라운드 서비스 시작
await CapHealthSensor.startBackgroundService();이렇게 설정하면 다음과 같은 상황에서도 만보기가 계속 작동합니다:
- 앱이 백그라운드로 전환된 경우
- 기기가 재부팅된 후
- 앱이 종료된 후 다시 시작될 때 (자동으로 이전 데이터 로드)
3. 기기 재부팅 후 자동 시작 동작 방식
기기가 재부팅된 후에도 만보기가 자동으로 시작되는 메커니즘:
BootCompletedReceiver가 기기 부팅 완료 이벤트를 감지합니다.- 저장된 설정을 확인하여 서비스 실행 상태나 자동 시작 설정이 활성화되어 있는지 확인합니다.
- 시스템이 완전히 초기화될 때까지 잠시 대기한 후 서비스를 시작합니다.
- 만보기 서비스가 시작되고 이전에 저장된 데이터가 로드됩니다.
주의: Android에서는 일부 기기 제조사가 백그라운드 서비스 제한 정책을 적용하는 경우가 있습니다. 배터리 최적화 설정에서 앱을 예외 목록에 추가하도록 사용자에게 안내하는 것이 좋습니다.
4. 앱 다시 실행 시 데이터 로드 방식
앱이 종료되었다가 다시 시작되면 다음 순서로 데이터를 로드합니다:
- 먼저 SharedPreferences/UserDefaults에서 데이터 로드 시도
- 실패하면 백업 파일에서 데이터 로드 시도
- 그래도 실패하면 일일 백업 파일에서 최신 데이터 로드 시도
- 로드된 데이터는 이벤트를 통해 UI로 전달됩니다
앱 시작 시 이전 데이터가 자동으로 로드되는 이벤트를 처리하려면:
// Web 환경에서 데이터 로드 이벤트 수신 (React/Ionic)
useEffect(() => {
const handleDataLoaded = (event) => {
const loadedData = JSON.parse(event.detail);
console.log('저장된 데이터 로드됨:', loadedData);
// UI 업데이트 로직
};
window.addEventListener('pedometerDataLoaded', handleDataLoaded);
return () => {
window.removeEventListener('pedometerDataLoaded', handleDataLoaded);
};
}, []);5. 주의사항 및 권장 사항
iOS 특이사항
- iOS에서는 Foreground Service와 같은 개념이 없으므로, 앱이 완전히 종료된 경우 백그라운드에서 데이터 수집이 중단될 수 있습니다.
- 앱이 다시 시작되면 자동 시작 설정에 따라 만보기가 다시 시작되고 이전 데이터를 복원합니다.
- 최소 iOS 14.0 이상을 지원합니다.
Android 특이사항
- Android 10(API 29) 이상에서는 신체 활동 권한(
ACTIVITY_RECOGNITION)을 명시적으로 요청해야 합니다. - Android 13(API 33) 이상에서는 알림 권한(
POST_NOTIFICATIONS)도 필요합니다. - 백그라운드 서비스를 사용할 때는 포그라운드 서비스 권한(
FOREGROUND_SERVICE)이 필요합니다. - 기기 제조사별 백그라운드 서비스 제한 정책에 영향을 받을 수 있습니다.
배터리 사용 최적화
- 너무 짧은 업데이트 간격은 배터리 소모가 심합니다. 보통 30초~1분 정도로 설정하는 것이 권장됩니다.
- 백그라운드에서 데이터 저장 주기는 1분 간격으로 설정되어 있어 과도한 디스크 I/O를 방지합니다.
- 사용자가 필요로 할 때만 자동 시작 기능을 활성화하도록 안내하세요.
데이터 백업 전략
- 데이터는 최대 7일간 일일 백업 파일에 저장됩니다. 중요한 데이터는 별도로 백업하는 것이 좋습니다.
- 원격 서버에 데이터를 백업하는 기능을 추가로 구현하는 것을 고려하세요.
자주 묻는 질문 (FAQ)
Q: 앱을 종료해도 만보기가 계속 작동하나요?
A: 자동 시작 기능과 백그라운드 서비스를 활성화했다면, 앱을 (정상적인 방법으로) 종료해도 계속 작동합니다. 단, 기기 재부팅 시에는 시스템에서 자동으로 서비스를 재시작합니다.
Q: 걸음 수 데이터가 정확한가요?
A: 걸음 수 데이터는 기기의 내장 센서와 알고리즘을 사용하므로, 기기별로 정확도가 다를 수 있습니다. 일반적으로 대략적인 참고 용도로 사용하는 것이 좋습니다.
Q: 데이터가 저장되지 않는 경우 어떻게 해야 하나요?
A: 다음 사항을 확인해보세요:
- 기기의 저장 공간이 충분한지 확인
- 앱 권한이 정상적으로 부여되었는지 확인
- 배터리 최적화 설정에서 앱이 제한되지 않았는지 확인
- 로그를 확인하여 오류 메시지가 있는지 확인
Q: 다른 건강 데이터(심박수, 수면 등)도 수집할 수 있나요?
A: 현재 버전에서는 걸음 수 관련 데이터만 지원합니다. 추가 건강 데이터 수집을 위해서는 플러그인을 확장하거나 각 플랫폼별 건강 API(HealthKit, Google Fit)를 직접 활용하는 것이 필요합니다.
구현 및 변경사항 안내
Android 서비스 구현 변경
최신 버전에서는 Android의 백그라운드 서비스 구현 방식이 변경되었습니다:
포그라운드 서비스 알림 변경: 이전 버전에서는 항상 알림을 표시했지만, 현재 버전에서는 사용자 설정에 따라 알림 표시 여부를 조정할 수 있습니다.
ScheduledExecutorService 사용: 백그라운드 작업을 위해
Handler와Runnable대신ScheduledExecutorService를 사용하여 안정성과 성능을 개선했습니다.데이터 저장 최적화: 배터리 사용량을 줄이기 위해 데이터 저장 주기를 최적화했습니다. 걸음 수 변화가 없을 때는 저장 주기가 자동으로 늘어납니다.
이중 저장 메커니즘 구현
데이터 손실 방지를 위해 다음과 같은 이중 저장 구조를 구현했습니다:
┌─────────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ SharedPreferences/ │ │ │ │ 일일 백업 파일 │
│ UserDefaults │◄──►│ 백업 파일 │◄──►│ (날짜별 저장) │
│ (주 저장소) │ │ (보조 저장소) │ │ (장기 저장소) │
└─────────────────────┘ └─────────────────┘ └────────────────────┘각 저장소는 다음 역할을 수행합니다:
- 주 저장소: 빠른 읽기/쓰기 작업, 현재 상태 유지
- 보조 저장소: 주 저장소 손상 시 복구용
- 장기 저장소: 날짜별 데이터 기록, 통계 및 분석용
설정 및 권한 관리
이 버전에서는 Android 13 이상에서 필요한 권한 관리 기능이 강화되었습니다:
// Android 13(API 33) 이상에서 필요한 알림 권한 요청 코드 예시
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
activity.requestPermissions(
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
REQUEST_NOTIFICATION_PERMISSION
)
}iOS에서는 CoreMotion 권한 관리를 통해 안정적인 만보계 기능을 제공합니다:
// iOS에서 CoreMotion 사용 가능 여부 확인
if CMPedometer.isStepCountingAvailable() && CMPedometer.isDistanceAvailable() {
// 만보계 기능 시작
pedometer.startUpdates(from: Date()) { (data, error) in
guard let data = data, error == nil else {
return
}
// 걸음 수 데이터 처리
let steps = data.numberOfSteps.intValue
// 거리 데이터 처리
if let distance = data.distance {
let distanceValue = distance.doubleValue
}
}
}백그라운드 처리 메커니즘
iOS 백그라운드 실행 시 다음과 같은 고급 처리를 구현했습니다:
┌───────────────────┐ ┌───────────────────────┐ ┌────────────────────┐
│ 앱 백그라운드 진입 │────▶│ 백그라운드 작업 등록 │────▶│ 주기적 데이터 저장 │
└───────────────────┘ └───────────────────────┘ └────────────────────┘
│
┌───────────────────┐ ┌───────────────────────┐ │
│ 앱 포그라운드 복귀 │◀────│ 데이터 자동 복원 │◀──────────────┘
└───────────────────┘ └───────────────────────┘백그라운드 실행 시 iOS의 시스템 제한을 고려한 최적화:
백그라운드 작업 시간 관리: iOS는 백그라운드 실행 시간을 약 3분으로 제한합니다. 이 시간을 최대한 활용하기 위해 주기적으로 데이터를 저장합니다.
메모리 사용량 최적화: 백그라운드에서는 최소한의 메모리만 사용하도록 구현했습니다.
다중 레이어 데이터 저장: 서비스 중단 시 데이터 손실을 방지하기 위해 UserDefaults와 파일 시스템을 함께 활용합니다.
iOS와 Android 구현 차이점
두 플랫폼 간 차이점을 고려한 구현 방식:
서비스 관리:
- iOS:
UIBackgroundTaskIdentifier와beginBackgroundTask를 사용하여 제한된 시간 동안 백그라운드 작업 수행 - Android: 포그라운드 서비스를 사용하여 지속적인 백그라운드 실행 보장
- iOS:
센서 접근:
- iOS:
CMPedometerAPI를 사용하여 통합된 만보계 데이터 접근 - Android:
SensorManager와StepCounter센서를 사용하여 저수준 센서 데이터 수집 및 처리
- iOS:
백그라운드 제약:
- iOS: 백그라운드 모드에서 제한된 시간만 실행 가능하며, 앱이 완전히 종료되면 데이터 수집 중단
- Android: 포그라운드 서비스를 통해 앱이 종료되어도 계속 실행 가능하며, 기기 재부팅 시에도 자동 시작 가능
배터리 최적화 예외:
- iOS: 시스템 수준의 배터리 최적화에서 제외할 수 없음
- Android: 배터리 최적화 예외 설정을 통해 서비스의 지속적인 실행 보장 가능
배터리 최적화 우회 방법
사용자를 위한 배터리 최적화 설정 안내:
- 설정 > 앱 > [앱 이름] > 배터리 메뉴로 이동
- 배터리 최적화 안 함 옵션 선택
- 또는 앱에서 다음 코드로 설정 화면으로 이동:
// 배터리 최적화 설정 화면으로 이동
import { Capacitor } from '@capacitor/core';
const openBatterySettings = async () => {
if (Capacitor.getPlatform() === 'android') {
await Capacitor.Plugins.App.openUrl({
url: 'package:com.android.settings/.Settings$HighPowerApplicationsActivity',
});
}
};개선된 동작 감지 알고리즘
만보계의 정확도를 높이기 위한 개선 사항:
- 걷기 패턴 필터링: 걷기, 달리기, 계단 오르기 등 다양한 동작 감지
- 오탐지 필터링: 진동, 휴대폰 흔들림 등에 의한 오탐지 감소
- 센서 융합: 가속도계와 자이로스코프 센서를 결합하여 정확도 향상
사용자 팁과 모범 사례
최상의 결과를 얻기 위한 사용자 팁:
- 앱 초기 설정 시 권한 승인: 모든 필요 권한을 승인하여 기능을 완전히 활성화
- 30초 이상의 업데이트 간격 설정: 배터리 소모 최소화
- 주머니/가방에 휴대폰 위치: 더 정확한 걸음 수 측정을 위해 휴대폰을 주머니나 가방에 넣고 사용
- 정기적인 데이터 백업: 중요 데이터는 클라우드나 서버에 별도 백업
문제 해결 흐름도
문제 발생 시 다음 흐름도를 따라 해결할 수 있습니다:
만보계 작동 안 함
↓
권한 확인 → 거부됨 → 앱 설정에서 권한 부여
↓ 허용됨
센서 가용성 확인 → 불가 → 기기 문제 (재부팅 또는 서비스 센터 문의)
↓ 가능
백그라운드 서비스 확인 → 중지됨 → 서비스 재시작
↓ 실행 중
배터리 최적화 확인 → 최적화됨 → 배터리 최적화에서 제외
↓ 제외됨
로그 확인 및 버그 리포트 제출