Flutter로 앱을 개발할 때, 스톱워치나 타이머 기능을 구현하면 앱이 백그라운드에 내려가면서 멈추는 현상을 종종 겪게 됩니다. 이는 Dart로 작성된 코드가 UI 쓰레드에 묶여 있기 때문인데요. 실제로 플랫폼에 따라 UI가 백그라운드 상태로 전환되면 Flutter의 Timer나 Future도 중단됩니다. 이 문제를 해결하기 위해서는 백그라운드 서비스를 사용해야 합니다. 특히 flutter_background_service 패키지를 이용하면 Android와 iOS 양쪽에서 백그라운드 코드를 실행할 수 있는데, 오늘은 이 패키지를 통해 Flutter 앱에 백그라운드 서비스를 어떻게 적용할 수 있는지 단계별로 살펴보겠습니다.
flutter_background_service 패키지란?
flutter_background_service는 Flutter 앱에서 백그라운드 Dart 코드를 실행할 수 있도록 도와주는 플러그인입니다. UI가 종료되거나 백그라운드로 전환되어도 별도의 Dart isolate를 통해 코드를 계속 수행할 수 있습니다. 특히 스톱워치나 백그라운드 데이터 동기화, 푸시 알림 처리 같은 기능에 적합하며, Android에서는 포그라운드 서비스 기반으로 안정적으로 동작합니다.
예시: 스톱워치 앱에서 백그라운드로 내렸을 때에도 시간 카운트가 유지되도록 구현한다면, flutter_background_service를 통해 정확한 시간 추적 로직을 백그라운드 isolate로 실행하도록 구성해야 합니다.
백그라운드 서비스 초기 설정과 구현
flutter_background_service 패키지를 활용하려면 앱 초기화 시점에 서비스 설정을 구성하고, Android와 iOS 환경에 맞춰 각각 추가 설정을 해주어야 합니다.
1. 초기화 및 서비스 시작
우선 main() 함수에서 서비스 초기화를 수행합니다. 이 과정에서 FlutterBackgroundService.configure()를 통해 서비스 동작 방식, 알림 구성 등을 설정할 수 있어요. 아래는 기본적인 설정 예시입니다.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeService(); // 서비스 초기화
runApp(MyApp());
}
Future<void> initializeService() async {
final service = FlutterBackgroundService();
await service.configure(
androidConfiguration: AndroidConfiguration(
onStart: onStart,
autoStart: true,
isForegroundMode: true,
notificationChannelId: 'my_foreground',
initialNotificationTitle: '서비스 시작됨',
initialNotificationContent: '작동 중...',
foregroundServiceNotificationId: 888,
),
iosConfiguration: IosConfiguration(
autoStart: true,
onForeground: onStart,
onBackground: onIosBackground,
),
);
}
2. 백그라운드 코드 작성
서비스가 시작되면 onStart 함수가 별도 isolate에서 실행됩니다. 여기서는 타이머를 사용해 1초마다 콘솔 로그를 남기거나, 필요에 따라 서버와 통신을 할 수도 있습니다.
@pragma('vm:entry-point')
void onStart(ServiceInstance service) async {
Timer.periodic(const Duration(seconds: 1), (timer) {
print("백그라운드 실행 중: ${DateTime.now()}");
});
service.on('stop').listen((event) {
service.stopSelf(); // 서비스 종료
});
}
3. Android 설정
Android에서는 포그라운드 서비스를 사용하므로 AndroidManifest.xml에 권한과 서비스 선언이 필요합니다.
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application>
...
<service
android:name="id.flutter.flutter_background_service.BackgroundService"
android:foregroundServiceType="location" />
</application>
또한 Android 14 이상에서는 SDK 정책에 따라 foregroundServiceType을 명시해줘야 앱이 제대로 동작합니다.
4. iOS 설정
iOS에서는 백그라운드 작업이 제한적이지만 onBackground를 통해 15분 간격의 간헐적 실행을 구현할 수 있습니다. Info.plist에는 다음 설정을 추가하세요.
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>dev.flutter.background.refresh</string>
</array>
그리고 AppDelegate.swift에 식별자를 등록해야 합니다.
SwiftFlutterBackgroundServicePlugin.taskIdentifier = "dev.flutter.background.refresh"
이렇게 설정을 마치면 Android에서는 실시간 백그라운드 실행, iOS에서는 제한적 주기적 실행이 가능합니다.
실전에서의 활용과 주의할 점
flutter_background_service 패키지를 실무에 적용하면 유용한 활용 사례들이 다양하게 펼쳐집니다. 예를 들어, 스톱워치나 타이머 기능을 구현할 때 앱이 백그라운드로 전환되어도 카운트가 정확히 유지되도록 만들 수 있어요. 또한, 소켓 통신 기반의 실시간 알림 시스템이나, 서버 상태 주기 확인 같은 작업도 백그라운드에서 계속 수행할 수 있습니다.
하지만 이 기능을 적용할 때는 몇 가지 현실적인 제약과 주의점도 함께 고려해야 합니다.
배터리 최적화 예외 설정
특히 Android의 경우, 기기 제조사마다 배터리 최적화 정책이 다르기 때문에 일부 기기에서는 서비스가 비정상적으로 종료될 수 있습니다. 이럴 때는 사용자에게 “배터리 최적화 예외” 설정을 유도해야 서비스가 중단 없이 실행될 수 있어요. Xiaomi, Huawei 같은 제조사의 커스텀 OS에서는 백그라운드 서비스가 자동 종료되는 일이 자주 발생합니다.
권한 요청과 UI/Service 분리
서비스 실행 전에 필요한 권한(FOREGROUND_SERVICE, POST_NOTIFICATIONS 등)이 부여되어야 하며, 서비스는 별도의 isolate에서 실행되기 때문에 UI와의 데이터 공유가 직접적이지 않습니다. 데이터를 주고받을 땐 invoke() 또는 on() 메서드를 통해 메시지 방식으로 처리해야 해요. 이 점은 앱 설계 시 고려해 UI와 로직을 잘 분리해야 안정적인 서비스를 만들 수 있습니다.
릴리즈 모드 테스트
개발 모드에서는 잘 작동하는 백그라운드 코드도 릴리즈 빌드에서는 동작 방식이 다를 수 있어요. 특히 onStart() 함수에는 반드시 @pragma('vm:entry-point')를 추가해야 릴리즈에서도 제대로 실행됩니다. 그리고 iOS는 구조적으로 백그라운드 서비스를 장시간 실행할 수 없기 때문에, 이런 한계를 감안해 설계를 해야 합니다.
마치며...
flutter_background_service 패키지를 활용하면 Flutter 앱에서도 백그라운드에서 Dart 코드를 실행하는 것이 가능해져, 실시간성이나 지속성이 필요한 기능들을 안정적으로 구현할 수 있어요. 특히 스톱워치처럼 시간 흐름을 정확히 유지해야 하는 기능이나, 사용자와 상호작용 없이도 동작해야 하는 서비스에는 매우 유용합니다.
하지만 플랫폼별 제약, 권한 문제, 성능 최적화 등도 함께 고려해야 하기 때문에 단순한 구현을 넘어 구조적인 설계와 테스트가 필요합니다. 기능 그 자체보다는 사용자 환경 전체를 고려한 접근이 더 중요하다는 점, 꼭 기억해 주세요!
'IT' 카테고리의 다른 글
플러터, 구글에 배포할 때 주의할 점 (0) | 2025.06.24 |
---|---|
DUNS 발급 및 조회 방법 (0) | 2025.06.23 |
flutter run과 flutter build의 차이는 무엇일까? (2) | 2025.06.20 |
앱에서 회원탈퇴 기능 구현하기 (4) | 2025.06.19 |
Supabase Realtime으로 실시간 협업 구현 (2) | 2025.06.18 |