Flutter Web에서 dart:js를 활용한 HTTP 요청 구현

Flutter를 배우기 시작했을 때, 웹 플랫폼에서 HttpClient를 사용하려고 하면 "Platform not supported" 오류가 발생한다. 이는 Flutter의 기본 HTTP 클라이언트가 브라우저 환경에서 지원되지 않기 때문이다. 이 문제를 해결하기 위해 JavaScript 라이브러iri와 dart:js의 상호 운용성을 활용하는 방법을 알아본다.

먼저 Dart 측面的 코드를 살펴보자. main.dart 파일에서 JavaScript 함수를 호출하고, JavaScript에서 결과를 다시 Dart로 전달받는 구조다.

import 'package:flutter/material.dart';
import 'dart:js';

void executeNetworkRequest(){
  context['console'].callMethod('log', ['API 요청 시작']);
  JsObject(context['fetchDataFromServer'], ['https://api.example.com/data', '{userId: 5678, nickname: "Developer"}']);
}

void handleServerResponse(responseData){
  print('서버 응답: $responseData');
}

void main() {
  context['receiveData'] = handleServerResponse;
  runApp(new SampleApplication());
}

class SampleApplication extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return new MaterialApp(
        home: new DashboardPage(),
      );
  }
}

class DashboardPage extends StatefulWidget {
  DashboardPage({Key key}) : super(key: key);

  @override
  _DashboardPageState createState() => new _DashboardPageState();
}

class _DashboardPageState extends State<DashboardPage> {
  @override
  Widget build(BuildContext context) {
    var spacing = new SizedBox(height: 48.0);

    return new Scaffold(
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text('Dart-JavaScript 연동 데모'),
            new Text('네트워크 요청 실행'),
            spacing,
            new RaisedButton(
              onPressed: (){
                executeNetworkRequest();
              },
              child: new Text('요청 보내기'),
            ),
          ],
        ),
      ),
    );
  }
}

이제 웹 환경의 index.html 파일을 수정해야 한다. Flutter가 생성한 HTML 파일에 필요한 스크립트를 추가하고, JavaScript 함수를 정의한다.

<html>
<head>
  <meta charset="UTF-8">
  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
  <meta name="description" content="A new Flutter project.">

  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="sample_app">
  <link rel="apple-touch-icon" href="icons/Icon-192.png">

  <link rel="shortcut icon" type="image/png" href="favicon.png"/>

  <title>sample_app</title>
  <link rel="manifest" href="manifest.json">
</head>
<body>
  <script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js" type="application/javascript"></script>
  <script>
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', function () {
        navigator.serviceWorker.register('flutter_service_worker.js');
      });
    }
    
    function fetchDataFromServer(requestUrl, parameters){
        let result = '';
        axios({
            method: 'get',
            url: requestUrl,
            params: parameters
        })
        .then(function(response) {
            result = response.data;
            receiveData(result);
        }).catch(function(error){
          receiveData(error);
        });
    }
  </script>
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>

이 구현의 핵심 원리는 다음과 같다. Dart 측면에서 JavaScript의 전역 객체를 통해 JS 함수에 접근하고, JavaScript側에서는 axios를 사용하여 HTTP 요청을 수행한 후 Dart에서 등록한 콜백 함수를 통해 결과를 반환한다.

다른 대안으로는 http 패키지의 BrowserClient를 사용하는 방법도 있지만, 특정 요구사항에 따라 dart:js를 활용하는 것이 더 적합한 경우도 있다.

태그: Flutter Dart web javascript-interop HTTP

7월 13일 20:09에 게시됨