본문 바로가기
Flutter/Study

iOS에서 백그라운드 위치 추적 구현하기

by Maccrey Coding 2025. 2. 12.
반응형

 

안드로이드에서는 Foreground Service를 활용하면 되지만, iOS는 백그라운드에서 GPS를 실행할 수 있는 정책이 다릅니다.

iOS는 사용자의 배터리 소모를 줄이기 위해 앱이 백그라운드 상태일 때 대부분의 작업을 제한합니다.

하지만 Background ModesSignificant Location Changes (중요 위치 변경), Region Monitoring을 사용하면 조깅 앱에서도 지속적인 위치 추적이 가능합니다.

1️⃣ iOS에서 백그라운드 위치 추적을 위한 설정

🔹 (1) Info.plist에 백그라운드 권한 추가

ios/Runner/Info.plist 파일을 열고 다음을 추가합니다.

<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>조깅 기록을 위해 사용자의 위치를 백그라운드에서도 추적합니다.</string>

<key>NSLocationWhenInUseUsageDescription</key>
<string>조깅 기록을 위해 위치를 사용합니다.</string>

<key>NSLocationAlwaysUsageDescription</key>
<string>조깅 기록을 위해 백그라운드에서도 위치를 추적합니다.</string>

<key>UIBackgroundModes</key>
<array>
    <string>location</string>
    <string>fetch</string>
</array>

설명

  • NSLocationAlwaysAndWhenInUseUsageDescription: 앱이 포그라운드 & 백그라운드 모두에서 위치 정보를 사용할 것임을 알림.
  • NSLocationWhenInUseUsageDescription: 앱이 포그라운드에서 위치를 사용할 것임을 알림.
  • NSLocationAlwaysUsageDescription: 항상 위치를 추적할 권한 요청.
  • UIBackgroundModes: location을 추가하면 앱이 백그라운드에서도 위치를 추적할 수 있음.

2️⃣ Flutter에서 iOS 백그라운드 위치 추적 구현

🔹 (1) geolocator 패키지 설정

geolocator 패키지를 사용하면 백그라운드에서 GPS 위치를 추적할 수 있습니다.

import 'package:geolocator/geolocator.dart';

class LocationService {
  Future<Position?> getCurrentPosition() async {
    bool serviceEnabled;
    LocationPermission permission;

    // 위치 서비스 활성화 확인
    serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      return Future.error('위치 서비스가 비활성화되어 있습니다.');
    }

    // 위치 권한 확인
    permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        return Future.error('위치 권한이 거부되었습니다.');
      }
    }

    if (permission == LocationPermission.deniedForever) {
      return Future.error('위치 권한이 영구적으로 거부되었습니다. 설정에서 변경해야 합니다.');
    }

    // 현재 위치 반환
    return await Geolocator.getCurrentPosition(
      desiredAccuracy: LocationAccuracy.best,
    );
  }
}

설명

  • 위치 서비스가 활성화되었는지 확인
  • 위치 권한 요청 (사용자가 허용하지 않으면 오류 반환)
  • 현재 위치 가져오기 (LocationAccuracy.best 옵션 사용)

🔹 (2) 백그라운드에서도 위치 업데이트 받기

iOS에서 백그라운드 실행을 지속하려면 "Significant Location Change" 또는 "Region Monitoring" 방식을 사용해야 합니다.

1) 중요한 위치 변경(Significant Location Change)

이 방법은 배터리 소모를 줄이면서도 백그라운드에서 지속적인 위치 업데이트를 받을 수 있습니다.

import 'package:geolocator/geolocator.dart';

class BackgroundLocationService {
  static Future<void> startTracking() async {
    Geolocator.getPositionStream(
      locationSettings: LocationSettings(
        accuracy: LocationAccuracy.high,
        distanceFilter: 50, // 50m 이동 시 업데이트
      ),
    ).listen((Position position) {
      print('새로운 위치: ${position.latitude}, ${position.longitude}');
      // Hive 또는 서버에 저장
    });
  }
}

설명

  • getPositionStream(): 위치가 업데이트될 때마다 호출
  • distanceFilter: 50 → 50m 이상 이동해야 업데이트됨 (배터리 절약)
  • accuracy: high → GPS 정확도를 최대한 높임

 

2) 지오펜싱(Region Monitoring)

특정 지역을 설정하면 앱이 백그라운드에서도 해당 지역에 들어오거나 벗어날 때 이벤트를 감지할 수 있습니다.

import 'package:geolocator/geolocator.dart';
import 'package:geofence_service/geofence_service.dart';

class RegionMonitoringService {
  final _geofenceService = GeofenceService.instance;

  void startGeofencing() {
    _geofenceService.start(
      geofenceList: [
        Geofence(
          id: 'home',
          latitude: 37.5665,
          longitude: 126.9780,
          radius: [100.0], // 100m 반경 감지
          triggers: [GeofenceTrigger.enter, GeofenceTrigger.exit],
        ),
      ],
    );
  }
}

설명

  • Geofence 설정 → 사용자가 설정한 위치(예: 집, 공원)에 들어오거나 벗어날 때 이벤트 발생
  • 배터리를 적게 소모하면서도 특정 지역에서 자동 감지가 가능

3️⃣ 조깅 앱 MVVM 패턴 적용

🔹 (1) Location Model

import 'package:hive/hive.dart';

@HiveType(typeId: 1)
class LocationModel {
  @HiveField(0)
  final double latitude;
  @HiveField(1)
  final double longitude;
  @HiveField(2)
  final DateTime timestamp;

  LocationModel({required this.latitude, required this.longitude, required this.timestamp});
}

🔹 (2) Location ViewModel

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive/hive.dart';
import 'package:geolocator/geolocator.dart';

final locationProvider = StateNotifierProvider<LocationNotifier, List<LocationModel>>((ref) {
  return LocationNotifier();
});

class LocationNotifier extends StateNotifier<List<LocationModel>> {
  LocationNotifier() : super([]);

  Future<void> addLocation(Position position) async {
    final location = LocationModel(
      latitude: position.latitude,
      longitude: position.longitude,
      timestamp: DateTime.now(),
    );

    final box = await Hive.openBox<LocationModel>('locationBox');
    box.add(location);

    state = [...state, location];
  }
}

설명

  • Hive를 사용하여 GPS 데이터를 로컬에 저장
  • Riverpod을 활용한 상태 관리

iOS에서는 Foreground Service를 사용할 수 없기 때문에, 아래 3가지 방법을 조합해서 백그라운드 위치 추적을 수행해야 합니다.

  1. 백그라운드 모드 활성화 (UIBackgroundModes → location 추가)
  2. Significant Location Change 방식 사용 (배터리 절약 가능)
  3. Region Monitoring을 활용한 특정 지역 감지

📝 정리

  • iOS에서 Foreground Service가 없는데 백그라운드에서 GPS를 지속적으로 추적하려면?
    Background Modes + Significant Location Change + Region Monitoring 조합
  • WorkManager 대신 무엇을 사용해야 할까?
    Geolocator + 백그라운드 위치 업데이트 방식
  • MVVM 패턴 적용이 가능할까?
    Riverpod과 Hive를 활용하여 상태 관리 가능

이제 이 방법을 적용하면 iOS에서도 백그라운드에서 GPS를 추적하는 조깅 앱을 만들 수 있습니다! 🚀

반응형