안녕하세요! 오늘은 Flutter에서 백그라운드 위치 추적을 구현하는 방법에 대해 자세히 알아보겠습니다.
특히 조깅 앱을 예시로 들어 실제 구현 방법을 상세히 설명해드리려고 합니다.
📱 백그라운드 위치 추적의 도전과제
조깅 앱을 개발할 때 가장 큰 도전과제는 앱이 백그라운드에 있거나 화면이 꺼져있을 때도 지속적으로 위치 정보를 추적해야 한다는 점입니다.
Android에서는 배터리 최적화로 인해 백그라운드 작업을 제한하는데, 이를 해결하기 위해서는 Foreground Service를 사용해야 합니다.
🛠 필요한 패키지들
dependencies:
flutter:
sdk: flutter
workmanager: ^0.5.1
geolocator: ^9.0.0
hive: ^2.2.3
hive_flutter: ^1.1.0
flutter_riverpod: ^2.3.0
flutter_local_notifications: ^13.0.0
🏗 MVVM 아키텍처와 Riverpod
먼저 MVVM 패턴에 따라 프로젝트 구조를 설계해보겠습니다.
lib/
├── models/
│ └── location_record.dart
├── views/
│ └── tracking_page.dart
├── view_models/
│ └── tracking_view_model.dart
└── services/
├── location_service.dart
└── database_service.dart
1️⃣ 모델 정의
@HiveType(typeId: 0)
class LocationRecord extends HiveObject {
@HiveField(0)
final double latitude;
@HiveField(1)
final double longitude;
@HiveField(2)
final DateTime timestamp;
LocationRecord({
required this.latitude,
required this.longitude,
required this.timestamp,
});
}
2️⃣ 위치 서비스 구현
class LocationService {
static Future<void> initializeService() async {
// Foreground 서비스 설정
await FlutterLocalNotificationsPlugin().initialize(
InitializationSettings(
android: AndroidInitializationSettings('@mipmap/ic_launcher'),
),
);
// 위치 권한 요청
await Geolocator.requestPermission();
// WorkManager 초기화
await Workmanager().initialize(callbackDispatcher);
}
static void callbackDispatcher() {
Workmanager().executeTask((task, inputData) async {
// Foreground 서비스 시작
await _startForegroundService();
// 위치 추적 로직
final position = await Geolocator.getCurrentPosition();
// Hive에 저장
final box = await Hive.openBox<LocationRecord>('locations');
await box.add(LocationRecord(
latitude: position.latitude,
longitude: position.longitude,
timestamp: DateTime.now(),
));
return true;
});
}
static Future<void> _startForegroundService() async {
const androidNotificationDetails = AndroidNotificationDetails(
'location_tracking_channel',
'Location Tracking',
importance: Importance.high,
priority: Priority.high,
);
await FlutterLocalNotificationsPlugin().show(
888,
'위치 추적 중',
'운동 기록을 저장하고 있습니다',
NotificationDetails(android: androidNotificationDetails),
);
}
}
3️⃣ ViewModel 구현
final trackingViewModelProvider = StateNotifierProvider<TrackingViewModel, bool>((ref) {
return TrackingViewModel();
});
class TrackingViewModel extends StateNotifier<bool> {
TrackingViewModel() : super(false);
Future<void> startTracking() async {
await LocationService.initializeService();
await Workmanager().registerPeriodicTask(
'locationTracking',
'trackLocation',
frequency: Duration(minutes: 15),
constraints: Constraints(
networkType: NetworkType.not_required,
requiresBatteryNotLow: true,
requiresCharging: false,
requiresDeviceIdle: false,
),
);
state = true;
}
Future<void> stopTracking() async {
await Workmanager().cancelAll();
state = false;
}
}
4️⃣ UI 구현
class TrackingPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final isTracking = ref.watch(trackingViewModelProvider);
return Scaffold(
appBar: AppBar(title: Text('조깅 트래커')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
isTracking ? '운동 기록 중...' : '운동을 시작해보세요!',
style: Theme.of(context).textTheme.headline6,
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
final viewModel = ref.read(trackingViewModelProvider.notifier);
isTracking ? viewModel.stopTracking() : viewModel.startTracking();
},
child: Text(isTracking ? '운동 종료' : '운동 시작'),
),
],
),
),
);
}
}
💡 주요 포인트 설명
- Foreground 서비스
- flutter_local_notifications 패키지를 사용하여 포그라운드 서비스를 구현
- 지속적인 알림을 표시함으로써 시스템이 앱을 종료하지 않도록 방지
- WorkManager
- 주기적인 백그라운드 작업 스케줄링
- 배터리 최적화를 고려한 제약조건 설정
- 15분마다 위치 정보를 저장하도록 설정
- Hive
- 로컬 데이터베이스로 위치 정보 저장
- 오프라인 상태에서도 데이터 보존 가능
- Riverpod
- 상태 관리 및 의존성 주입
- StateNotifierProvider를 사용하여 추적 상태 관리
⚙️ Android 설정
android/app/src/main/AndroidManifest.xml에 다음 권한을 추가해야 합니다.
<manifest ...>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
...
</manifest>
🔍 기술적 분석
이 구현의 핵심은 Foreground 서비스와 WorkManager의 조합입니다.
Foreground 서비스는 지속적인 알림을 통해 시스템에 앱이 활성 상태임을 알리고, WorkManager는 배터리 최적화를 고려하면서도 안정적인 백그라운드 작업을 보장합니다.
MVVM 패턴을 통해 비즈니스 로직과 UI를 분리하고, Riverpod를 사용하여 상태 관리를 단순화했습니다.
이는 코드의 테스트 용이성과 유지보수성을 크게 향상시킵니다.
🎯 성능 최적화 팁
- 위치 업데이트 주기를 너무 짧게 설정하지 않기
- 배터리 레벨이 낮을 때는 위치 업데이트 빈도 줄이기
- 불필요한 위치 데이터는 주기적으로 정리하기
마치며
이상으로 Flutter에서 백그라운드 위치 추적 기능을 구현하는 방법을 알아보았습니다.
이 구현은 실제 프로덕션 환경에서도 안정적으로 동작하면서도, 배터리 소모를 최소화할 수 있는 방법을 제시합니다.
질문이나 제안사항이 있으시다면 댓글로 남겨주세요!
다음에는 이 구현을 기반으로 실시간 경로 시각화하는 방법에 대해 다루어보도록 하겠습니다.
구독!! 공감과 댓글,
광고 클릭은 저에게 큰 힘이 됩니다.
Starting Google Play App Distribution! "Tester Share" for Recruiting 20 Testers for a Closed Test.
Tester Share [테스터쉐어] - Google Play 앱
Tester Share로 Google Play 앱 등록을 단순화하세요.
play.google.com
'Flutter > Study' 카테고리의 다른 글
iOS에서 백그라운드 위치 추적 구현하기 (1) | 2025.02.12 |
---|---|
Flutter 실시간 경로 시각화: 조깅 앱에 Google Maps 적용하기 (0) | 2025.02.12 |
플러터에서 조깅 앱을 만들 때 백그라운드에서 GPS 위치를 계속 저장하는 방법 (1) | 2025.02.12 |
플러터에서 WorkManager로 GPS 위치를 백그라운드에서 저장하는 방법 (0) | 2025.02.12 |
플러터 TextFormField에서 블루투스 키보드 입력 제한하기: inputFormatters 사용법 (0) | 2024.12.03 |