Flutter vs JavaScript: 네트워크 감지, 누가 더 잘할까?

Heina·2025년 4월 25일
0

noWifi

어쩌다가?

비행기 안에서 앱을 켜보신 QA 천재 개발자님의 피드백으로부터 시작된 이 이야기. 기내 Wi-Fi가 없는 환경에서 앱이 정상 작동하지 않는다는 아주 소중한 리포트를 받고, 고민을 시작했다.

"우리 앱은 Vue3 + Flutter인데, 네트워크 감지를 웹에서 할까, 아니면 네이티브(Futter)에서 할까?"

Javascript에서는 어떻게 처리하는데?

웹에서는 아주 간단하게 네트워크 감지를 할 수 있는 내장 API가 존재한다.

Javascript 처리 하는 방법

  1. 브라우저가 네트워크에 연결되어 있는지 확인하는 내장 API navigator.onLine 사용하기

  2. 브라우저에 네트워크 상태가 바뀔 때 연결 상태 변화 감지할수 있는 addEventListener 사용하기

  3. 초기값을 설정하고 네트워크 변경을 확인하여 UI를 변경하거나 네트워크 재시도 요청 시도 하기

이것저것 사부작거린 코드 내용들

<script setup>
onMounted(() => {
  console.log('현재 상태:', navigator.onLine ? '온라인' : '오프라인');

  window.addEventListener('online', () => {
    console.log('📡 온라인으로 복구됨');
  });

  window.addEventListener('offline', () => {
    console.log('📴 오프라인으로 전환됨');
  });
}
          
          
const isOnline = ref(navigator.onLine);

const updateNetworkStatus = () => {
  isOnline.value = navigator.onLine;
  console.log('####', navigator.onLine);
  /*  if (isOnline.value) {
    retryCallbacks.forEach((cb) => cb());
  }*/
};
</script>

Javascript에서의 단점

이렇게 순조롭게 일이 풀리면 내가 아니지?
생각해보니 앱 진입 시점에서 네트워크가 없다면 웹을 가져오지도 못하기 때문에 원하는 화면을 보여줄수도 없잖아!!!!

또한 인터넷 연결 여부를 완전히 보장하지도 않는다. 예로 wifi에 연결되어 있더라도 인터넷이 안될수있다.

OVANDE

그렇다면 Flutter에서는?

아주 만족스럽게도 flutter에서 제공하는 패키지가 있다.

connectivity_plus

https://pub.dev/packages/connectivity_plus

패키지에서 제공하는 플랫폼인데 얼마나 아름다운가!

그리고 요구되는 버전도 잘 파악하길 바란다.

해당 패키지를 통해 Wi-Fi, Mobile, No Connection 등의 구분이 명확하게 가능하고, javascript보다 세밀하게 제어가 가능하다.

그래서 내가 생각한 구성
1. 실시간 감지하는 감시자 생성
2. 네트워크 상태의 따라 화면 분기처리
-> 이론은 항상 완벽하다.

자 그럼 시작해볼까

🧠 Step 1: 네트워크 상태를 감지하는 감시자 만들기

network_connectivity_observer.dart

import 'package:connectivity_plus/connectivity_plus.dart';

enum Status {
  available,
  unavailable,
}

class NetworkConnectivityObserver {
//connectivity_plus 패키지를 이용해서 Wi-Fi, LTE 등 모든 연결 상태를 감지
  final Connectivity _connectivity = Connectivity();

  Stream<Status> observe() async* {
    // 처음 연결 상태 체크
    final initial = await _connectivity.checkConnectivity();
    yield _convertToStatus(initial); // 리스트로 감싸서 전달

    // 이후 상태 변경 감지
    yield* _connectivity.onConnectivityChanged.map((List<ConnectivityResult> results) {
      return _convertToStatus(results); // 리스트로 처리
    });
  }

  // 리스트 형태로 연결 상태 확인
  Status _convertToStatus(List<ConnectivityResult> results) {
    // 리스트에서 하나라도 연결되어 있으면 available로 처리
    for (var result in results) {
      if (result != ConnectivityResult.none) {
        return Status.available;
      }
    }
    return Status.unavailable;
  }
}
  • connectivity_plus 패키지를 사용해서 네트워크 상태를 감지
  • enum Status를 통해 연결 여부를 명확하게 표현
  • 스트림으로 상태를 보내주기 때문에 실시간으로 감지

🧱 Step 2: 연결 상태에 따라 UI를 분기하는 WebviewContainer

webview_container.dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:safeit_mobile_native/app/safeit_app/webview/views/screen/offline_screen.dart';
import 'package:safeit_mobile_native/app/safeit_app/webview/views/screen/webview.dart';
import 'package:safeit_mobile_native/app/utils/service/network_connectivity_observer.dart';


class WebviewContainer extends StatefulWidget{
  const WebviewContainer({super.key});

  
  State<WebviewContainer> createState() => _WebviewContainerState();
}

class _WebviewContainerState extends State<WebviewContainer> {
  final observer = NetworkConnectivityObserver();
  late StreamSubscription<Status> _subscription;
  Status? _status;

  
  void initState() {
    super.initState();

    _subscription = observer.observe().listen((status) {
      setState(() {
        _status = status;
      });
    });
  }

  
  void dispose() {
    _subscription.cancel();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    if(_status == Status.unavailable){
      return const OfflineScreen();
    }
    return Webview();
  }
}
  • 앱이 실행되면 네트워크 상태를 구독하고 _status를 업데이트함
  • 오프라인이면 OfflineScreen, 연결되면 WebView를 보여주기

📵 Step 3: 오프라인 안내 전용 화면 만들기

offline_screen.dart

class OfflineScreen extends StatelessWidget {
  final VoidCallback? onRetry;

  const OfflineScreen({super.key, this.onRetry});

  
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: PreferredSize(
        preferredSize: const Size.fromHeight(0),
        child: AppBar(elevation: 0),
      ),
      body: SafeArea(
        bottom: Platform.isAndroid ? true : false,
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              SvgPicture.asset("assets/images/icons/no_signal.svg",
                width: MediaQuery.sizeOf(context).width / 5,
              ),
              const SizedBox(height: 20),
              Text("인터넷 연결이 끊어졌습니다.", style: TextStyle(fontSize: 22, height: 2)),
              Text("원활한 이용을 위해 네트워크 상태를"),
              Text("확인한 후 다시 시도해 주세요."),
              const SizedBox(height: 50),
              SizedBox(
                width: 120,
                child: Button(
                  text: '재시도',
                  func: () {
                    // 추후 재시도 기능 구현 가능
                  },
                  borderColor: const Color(0xFF2e81ff),
                  backgroundColor: Colors.white,
                  fontColor: const Color(0xFF2e81ff),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

  • 사용자에게 현재 네트워크가 끊겼다는 점 알려주기
  • "재시도" 버튼은 향후 기능 확장을 위해 추가
  • 지금은 재시도 하는 '척'.. 엣큥ㅎ

느낀점..

Flutter로 구현하면서 웹보다 훨씬 정교하고 강력한 제어가 가능하다는 걸 느꼈고 마음이 매우 편안했다.
인터넷이 안 되더라도 우아하게 안내 가넝!
이걸로 며칠을 고민하다니!!!.. 따흐흑 그래도 인터넷 제발 끊기지는 말아죠..

0개의 댓글