Skip to content

[feat] 앱 Lifecycle 관리 및 네트워크 상태 체크 추가#4

Merged
hyejj19 merged 3 commits intomainfrom
app-lifecycle-network
Jan 29, 2026
Merged

[feat] 앱 Lifecycle 관리 및 네트워크 상태 체크 추가#4
hyejj19 merged 3 commits intomainfrom
app-lifecycle-network

Conversation

@hyejj19
Copy link
Copy Markdown
Collaborator

@hyejj19 hyejj19 commented Jan 27, 2026

변경 사항

  • 앱 Lifecycle 관리: 5분 이상 백그라운드에 있다가 포그라운드로 복귀 시 WebView 자동 새로고침
  • 네트워크 상태 체크: 오프라인 시 에러 화면 표시 및 온라인 복구 시 자동 새로고침
  • connectivity_plus 패키지 추가 (네트워크 모니터링)
  • Android ACCESS_NETWORK_STATE 권한 추가

주요 변경 파일

파일 내용
lib/utils/network/connectivity_service.dart 네트워크 상태 관리 서비스 (신규)
lib/ui/offline_screen.dart 오프라인 에러 화면 위젯 (신규)
lib/web_view/web_view.dart Lifecycle + 네트워크 로직 통합

테스트

  • 로컬 테스트 완료
  • iOS 빌드/실행 확인
  • Android 빌드/실행 확인

테스트 시나리오

  • 비행기 모드 ON → 앱 실행 → 오프라인 화면 표시 확인
  • "다시 시도" 버튼 → 비행기 모드 OFF → WebView 정상 로드 확인
  • 앱 5분+ 백그라운드 → 포그라운드 복귀 → WebView 자동 새로고침 확인

스크린샷 (UI 변경 시)

오프라인 화면 (WiFi 아이콘 + "인터넷 연결 없음" + 다시 시도 버튼)

관련 이슈

N/A

🤖 Generated with Claude Code

- Add connectivity_plus dependency for network monitoring
- Add ACCESS_NETWORK_STATE permission for Android
- Create ConnectivityService for real-time network status
- Create OfflineScreen widget for offline error display
- Integrate lifecycle management in WebView (5min+ background auto-refresh)
- Integrate network status check (offline screen, auto-refresh on reconnect)
Copilot AI review requested due to automatic review settings January 27, 2026 13:16
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

앱 생명주기 관리와 네트워크 상태 체크 기능을 추가하는 PR입니다. 5분 이상 백그라운드 후 포그라운드 복귀 시 WebView 자동 새로고침, 오프라인 시 에러 화면 표시 및 온라인 복구 시 자동 새로고침 기능을 구현했습니다.

변경사항:

  • ConnectivityService 추가로 네트워크 상태 모니터링
  • OfflineScreen 위젯으로 오프라인 에러 UI 제공
  • BottleNoteWebView에 lifecycle 및 네트워크 로직 통합

Reviewed changes

Copilot reviewed 5 out of 7 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
lib/utils/network/connectivity_service.dart 네트워크 상태 관리 서비스 신규 추가
lib/ui/offline_screen.dart 오프라인 상태 표시 UI 위젯 신규 추가
lib/web_view/web_view.dart Lifecycle 관리 및 네트워크 상태 체크 로직 통합
pubspec.yaml connectivity_plus 패키지 의존성 추가
android/app/src/main/AndroidManifest.xml ACCESS_NETWORK_STATE 권한 추가
pubspec.lock 패키지 lock 파일 업데이트
ios/Podfile.lock iOS 의존성 lock 파일 업데이트
Comments suppressed due to low confidence (1)

lib/web_view/web_view.dart:140

  • [P0] _setupPullToRefresh()onRefresh 콜백에서 _webviewController를 접근하고 있습니다. PullToRefresh가 WebView 생성 전에 트리거될 수 있으며, 이 경우 LateInitializationError로 크래시됩니다.
  void _setupPullToRefresh() {
    _pullToRefreshController = PullToRefreshController(
      settings: PullToRefreshSettings(
        color: const Color(0xffe58257),
        backgroundColor: Colors.white,
      ),
      onRefresh: () async {
        if (Platform.isAndroid) {
          _webviewController.reload();
        } else if (Platform.isIOS) {
          _webviewController.loadUrl(
            urlRequest: URLRequest(url: await _webviewController.getUrl()),
          );
        }
      },
    );

Comment on lines +32 to +43
void _updateStatus(List<ConnectivityResult> results) {
final isConnected =
results.any((result) => result != ConnectivityResult.none);

final newStatus = isConnected ? NetworkStatus.online : NetworkStatus.offline;

if (newStatus != _currentStatus) {
_currentStatus = newStatus;
_networkStatusController.add(newStatus);
_logger.d('Network status changed: $newStatus');
}
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] _networkStatusController.add() 호출 시 stream이 이미 닫혔는지 확인하지 않습니다. dispose() 후에도 _connectivity.onConnectivityChanged 이벤트가 발생하면 StateError가 발생할 수 있습니다.

Copilot uses AI. Check for mistakes.
Comment on lines +198 to +200
body: _isOffline && !_initialLoadCompleted
? OfflineScreen(onRetry: _retryConnection)
: _buildBody(colors),
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 오프라인 화면 표시 조건이 _isOffline && !_initialLoadCompleted로 제한되어 있습니다. 이미 로드가 완료된 후(_initialLoadCompleted == true) 오프라인 상태가 되면 사용자에게 네트워크 끊김을 알릴 방법이 없습니다. 온라인 복구 시 자동 새로고침만 되고 오프라인 상태에 대한 피드백이 없습니다.

Copilot uses AI. Check for mistakes.
Comment on lines +74 to +82
Future<void> _initializeConnectivity() async {
await _connectivityService.initialize();

final isConnected = await _connectivityService.checkConnectivity();
if (!isConnected) {
setState(() {
_isOffline = true;
});
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] _initializeConnectivity()에서 비동기 작업 완료 후 setState()를 호출하고 있지만 mounted 체크가 없습니다. 앱 초기화 중 위젯이 dispose되면 크래시가 발생할 수 있습니다.

Copilot uses AI. Check for mistakes.

// Offline -> Online: auto reload
if (wasOffline && status == NetworkStatus.online && _initialLoadCompleted) {
_webviewController.reload();
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P0] _webviewControllerlate로 선언되어 있지만 WebView 생성 전(onWebViewCreated 콜백 실행 전)에 네트워크 상태 변경 또는 앱 복귀 시 reload()가 호출될 수 있습니다. 이 경우 LateInitializationError로 앱이 크래시됩니다.

Suggested change
_webviewController.reload();
try {
_webviewController.reload();
} on LateInitializationError catch (e, stackTrace) {
logger.w('WebViewController not initialized yet on connectivity change', e, stackTrace);
}

Copilot uses AI. Check for mistakes.
Comment on lines +114 to +123
void _handleAppResumed() {
if (_backgroundTime != null && _initialLoadCompleted) {
final duration = DateTime.now().difference(_backgroundTime!);
if (duration > _refreshThreshold) {
logger.d('Long background duration: ${duration.inSeconds}s. Reloading WebView.');
_webviewController.reload();
}
_backgroundTime = null;
}
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P0] _handleAppResumed()에서 _webviewController.reload() 호출 시 컨트롤러 초기화 여부를 확인하지 않습니다. 앱 시작 직후(WebView 생성 전) 백그라운드→포그라운드 전환이 발생하면 LateInitializationError로 크래시됩니다.

Copilot uses AI. Check for mistakes.
Comment on lines 80 to +149
_pullToRefreshController.dispose();
_webviewController.dispose();
_networkSubscription?.cancel();
_connectivityService.dispose();
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] dispose()에서 _webviewController.dispose() 전에 _networkSubscription?.cancel()_connectivityService.dispose()를 먼저 호출해야 합니다. 현재 순서에서는 네트워크 이벤트가 도착하여 이미 dispose된 _webviewController에 접근할 수 있습니다.

Suggested change
_pullToRefreshController.dispose();
_webviewController.dispose();
_networkSubscription?.cancel();
_connectivityService.dispose();
_networkSubscription?.cancel();
_connectivityService.dispose();
_pullToRefreshController.dispose();
_webviewController.dispose();

Copilot uses AI. Check for mistakes.
Comment on lines +84 to +95
_networkSubscription =
_connectivityService.networkStatusStream.listen((status) {
final wasOffline = _isOffline;
setState(() {
_isOffline = status == NetworkStatus.offline;
});

// Offline -> Online: auto reload
if (wasOffline && status == NetworkStatus.online && _initialLoadCompleted) {
_webviewController.reload();
}
});
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P0] Stream listener에서 setState()_webviewController.reload() 호출 전 mounted 체크가 없습니다. 위젯이 dispose된 후 네트워크 상태가 변경되면 크래시가 발생합니다.

Copilot uses AI. Check for mistakes.
Comment on lines +153 to +172
Future<void> _retryConnection() async {
final isConnected = await _connectivityService.checkConnectivity();
if (isConnected) {
setState(() {
_isOffline = false;
});
if (_initialLoadCompleted) {
_webviewController.reload();
}
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('인터넷 연결을 확인해 주세요.'),
duration: Duration(seconds: 2),
),
);
}
}
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] _retryConnection()에서 비동기 작업 완료 후 setState()_webviewController.reload() 호출 전 mounted 체크가 없습니다. 재시도 중 위젯이 dispose되면 크래시가 발생합니다.

Copilot uses AI. Check for mistakes.
- Add _isWebViewCreated flag to prevent LateInitializationError
- Add mounted checks before setState() in async callbacks
- Fix dispose() order: cancel network subscription before webview dispose
- Add _isDisposed flag in ConnectivityService to prevent StateError
@hyejj19
Copy link
Copy Markdown
Collaborator Author

hyejj19 commented Jan 27, 2026

리뷰 코멘트 수정 완료 (f5f42aa)

모든 Copilot 리뷰 이슈를 수정했습니다:

P0 (크리티컬)

  • LateInitializationError 방지: _isWebViewCreated 플래그 추가하여 WebView 컨트롤러 초기화 전 reload() 호출 방지
  • mounted 체크: Stream listener에서 setState() 호출 전 mounted 체크 추가

P1 (중요)

  • 비동기 작업 후 mounted 체크: _initializeConnectivity(), _retryConnection()에 추가
  • dispose 순서 변경: 네트워크 구독 취소를 WebView dispose 전에 실행

P2 (권장)

  • ConnectivityService: _isDisposed 플래그 + isClosed 체크 추가
  • 로드 완료 후 오프라인 UX: 의도된 동작 (캐시된 콘텐츠 유지, 온라인 복구 시 자동 새로고침)

@hyejj19 hyejj19 merged commit 10fc21c into main Jan 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants