Flutter 개발

Unity WebGL 게임을 Flutter WebView로 실행하기

빌드부터 JS Channel 통신까지

John Doe
2026년 4월 5일
9분 읽기
21

왜 WebGL + WebView인가

Unity 게임을 Flutter 앱 안에서 돌리는 방법은 여러 가지예요. flutter_unity_widget 같은 네이티브 임베딩도 있지만, 플랫폼별 빌드와 호환성 문제가 많습니다.

WebGL 빌드 + WebView 조합은 한 번 빌드하면 iOS/Android 모두에서 작동해요. 배포도 서버만 업데이트하면 되니 앱 심사 없이 게임을 갱신해요.

Unity WebGL 빌드 설정

Unity에서 WebGL 빌드 시 압축 포맷과 메모리 설정이 중요합니다. Brotli 압축이 기본인데 모바일에서는 Gzip이 더 호환성이 좋거든요.
bash
// ProjectSettings > Player > WebGL 설정
// Publishing Settings
Compression Format: Gzip  // 모바일 호환성
Decompression Fallback: true

// Memory Settings  
Initial Memory Size: 32  // MB, 모바일은 낮게
Maximum Memory Size: 256
Memory Growth Mode: Linear

// 빌드 커맨드 (CI용)
Unity -batchmode -nographics \
  -projectPath ./MyGame \
  -buildTarget WebGL \
  -executeMethod BuildScript.BuildWebGL \
  -quit

Firebase Hosting 배포

WebGL 빌드 결과물을 Firebase Hosting에 올립니다. CDN이 기본 포함이라 로딩 속도가 빠릅니다.
json
# Firebase CLI 설치 및 초기화
npm install -g firebase-tools
firebase init hosting

# firebase.json 설정
{
  "hosting": {
    "public": "Build/WebGL",
    "headers": [
      {
        "source": "**/*.@(js|wasm)",
        "headers": [
          { "key": "Content-Encoding", "value": "gzip" },
          { "key": "Cross-Origin-Opener-Policy", "value": "same-origin" },
          { "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" }
        ]
      }
    ]
  }
}

# 배포
firebase deploy --only hosting

Flutter WebView 연동

webview_flutter 패키지로 WebGL 게임을 로드합니다. 핵심은 JavaScript 활성화와 viewport 메타 태그입니다.
Dart
import 'package:webview_flutter/webview_flutter.dart';

class GameWebView extends StatefulWidget {
  @override
  State<GameWebView> createState() => _GameWebViewState();
}

class _GameWebViewState extends State<GameWebView> {
  late final WebViewController _controller;

  @override
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(NavigationDelegate(
        onPageFinished: (_) => _injectViewport(),
      ))
      ..loadRequest(Uri.parse(
        'https://my-game.web.app/index.html',
      ));
  }

  // 모바일 viewport 스케일링
  void _injectViewport() {
    _controller.runJavaScript('''
      var meta = document.createElement('meta');
      meta.name = 'viewport';
      meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';
      document.head.appendChild(meta);
    ''');
  }

  @override
  Widget build(BuildContext context) {
    return WebViewWidget(controller: _controller);
  }
}

JS Channel: Flutter ↔ Unity 통신

점수 전송, 게임 오버 이벤트 같은 데이터를 Flutter와 Unity 사이에 주고받아야 해요. JavaScript Channel이 다리 역할을 해요.
Dart
// Flutter 쪽 - JS Channel 등록
_controller
  ..addJavaScriptChannel(
    'GameBridge',
    onMessageReceived: (message) {
      final data = jsonDecode(message.message);
      switch (data['event']) {
        case 'gameOver':
          _handleGameOver(data['score'] as int);
          break;
        case 'purchase':
          _handlePurchase(data['itemId'] as String);
          break;
      }
    },
  );
JavaScript
// Unity C# 쪽 - Flutter로 메시지 전송
// jslib 플러그인 (Assets/Plugins/WebGL/bridge.jslib)
mergeInto(LibraryManager.library, {
  SendToFlutter: function(msgPtr) {
    var msg = UTF8ToString(msgPtr);
    // Flutter의 GameBridge 채널로 전송
    if (window.GameBridge) {
      window.GameBridge.postMessage(msg);
    }
  }
});

iOS WKWebView 제약사항

iOS의 WKWebView는 WebGL을 지원하지만 몇 가지 제약이 있거든요. SharedArrayBuffer가 기본 비활성이라 멀티스레드 Unity 빌드가 안 되죠.
COOP/COEP 헤더를 설정해도 WKWebView에서는 무시돼요. Unity 빌드 시 Player Settings > Threading Support를 끄세요.

성능 최적화 팁

WebGL은 네이티브 대비 성능이 60-70% 수준입니다. 모바일에서는 더 떨어지니 최적화가 필수입니다.

텍스처 크기를 절반으로 줄이고, Draw Call을 50 이하로 유지하세요. 프레임 예산은 30fps 기준으로 잡는 게 현실적예요.
#Unity
#WebGL
#Flutter
#WebView
#Firebase Hosting
#JS Channel