본문 바로가기
프로젝트 기록/Flutter, Bootstrap을 활용한 크로스 플랫폼 웹 개발

flutter 웹 개발 index.html과 main.dart, 실행 순서에 대한 이해

by jeong11 2024. 11. 15.
반응형

1. Flutter 웹 개발 시 파일 실행 순서

index.html 실행 → flutter.js 로드 → main.dart 실행

 

1) index.html 실행 

▷ 브라우저는 웹 애플리케이션 로드 시 index.html 파일을 가장 먼저 처리함 

→ index.html 파일에서는 기본적인 HTML 구조를 정의함

→ index.html dpsms <script src="flutter.js" async></script> 코드가 포함되어 있어 Flutter 앱의 엔트리포인트인 main.dart를 불러오는 역할을 함 

 

2) flutter.js 로드

→ index.html 내 <script src="flutter.js async"></script>에 의해 flutter.js 파일이 로드됨

→ Flutter 엔진과 Dart 코드(main.dart)를 브라우저 환경에서 실행하기 위한 준비 작업을 수행 

 

3) main.dart 실행

→ flutter.js가 로드된 후 main.dart가 실행

→ main.dart의 main() 함수가 Flutter 애플리케이션의 진입점이며, 이곳에서 Flutter 위젯 트리를 구성하는 작업이 이루어짐

→ runApp(MyApp()) 함수를 통해 Flutter 애플리케이션을 브라우저에 렌더링한다 

(Flutter UI 위젯이 index.html 기본 HTML 구조 위에 렌더링해 웹 페이지를 표시함)

 

 

2. 실행 순서가 헷갈리는 이유

index.html보다 main .dart가 예외적으로 먼저 처리되는 것처럼 느껴질 때가 있는데 주로 다음과 같은 상황에서 발생한다

2-1. 비동기 로딩

index.html에 포함된 스크립트가 비동기로 로드되면서 특정 자바스크립트 또는 플러그인이 Flutter 엔진이 로드되기 전 실행을 시도할 때, 초기화되지 않은 상태에서 접근하려 하면 예외가 발생할 수 있다 

 

2-2. DOMContentLoaded 이벤트 사용 필요 

JavaScript 코드를 DOMContentLoaded 이벤트 안에서 정의하면, Flutter 위젯이 모든 HTML 요소를 렌더링한 이후, 

DOM 접근을 보장할 수 있어 JavaScript나 Flutter 상호작용을 안전하게 처리할 수 있음

 

 

3. Flutter의 index.html

 index.html의 역할 : 웹 애플리케이션의 진입점 역할을 한다 
브라우저에서 실행할 때, web/index.html 파일이 가장 먼저 로드됨 

 

위에서 얘기한 HTML 구조 제공, 외부 리소스 로드 기능을 수행한다 

외부 리소스 로드는 Bootstrap과 같은 외부 CSS 스타일이나 JavaScript 파일을 포함하여 Flutter에서 사용할 수 있도록 준비해주는 기능을 말하고, index.html 파일에 링크를 추가함으로써 외부 리소스를 쉽게 사용할 수 있다 

 

 

4. Flutter의 main.dart

main.dart의 역할 : 앱의 주요 로직을 담당하는 코드 파일로 Dart 언어로 작성된다 
void main() 함수로, 이 함수가 Flutter 앱의 실행을 시작한다 

 

앱 초기화 → UI 구성으로 실행

▷ 앱 초기화 : main() 함수에서 runApp() 메소드를 호출하여 Flutter 앱의 최상위 위젯을 실행한다

▷ UI 구성 : main.dart 파일의 MyApp 또는 MaterialApp 위젯이 전체 앱의 구조와 화면을 정의한다 

 

 

5. 좋은 패턴 만들기

▷ 비동기 처리 패턴을 적용하는 것이 중요하다 

5-1. DOMContentLoaded 이벤트나 addPostFrameCallback 사용

index.html의 HTML 요소나 상호작용이 필요한 JavaScript 코드는 DOMContentLoaded 이벤트나 addPostFrameCallback을 통해 Flutter 초기화 후  실행하도록 처리하는 것이 좋다 

 

①  DOMContentLoaded 이벤트 사용

index.html에서 DOM 요소에 접근할 때는 해당 이벤트를 사용해 

모든 HTML 요소가 로드된 후 스크립트를 실행하도록 처리할 수 있다 

// index.html

<!DOCTYPE html>
<html>
<head>
	<!-- 기본 설정, CSS, JavaScript 로드 -->
</head>
<body>
	<script src="flutter.js" async></script>
	
    <div class="container mt-5">
    	<h2>Contact Us</h2>
        <form id="contact-form">
        	<button type="submit" class="btn btn-primary">Submit</button>
        </form>
    </div>
    
    <script>
    	// DOMContentLoaded 이벤트를 통해 HTML이 완전히 로드된 후 실행
        document.addEventListener("DOMContentLoaded", function() {
        	const form = document.getElementById("contact-form");
        	form.addEventListener("submit", function(event) {
            	event.preventDefault();
                alert("Form Submitted!");
            });  
        });
    </script>
</body>
</html>

HTML 요소가 모두 로드된 후 DOMContentLoaded 이벤트가 발생하고 

그 이후에 submit 버튼의 클릭 이벤트가 안전하게 작동하게 됨 

 

② 네이티브와 통신 시 메소드 채널 설정과 addPostFrameCallback 사용

Flutter에서 네이티브 플랫폼(Android, ios)과 통신하는 경우도 있다 

이때는 WidgetsBinding.instance.addPostFrameCallback을 사용해 Flutter 위젯이 초기화 된 이후 안전하게 호출해야 함

//main.dart

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

void main() {
	WidgetsFlutterBinding.ensureInitialized();
    runApp(MyApp());
}

class MyApp extends StatelessWidget {
	static const platform = MethodChannel('com.example/native_channel');
    
    @override
    Widget build(BuildContext context) {
    	WidgetsBinding.instance.addPostFrameCallback((_) {
        	sendMessageToNative();
        });
        
        return MaterialApp(
        	home: Scaffold(
            	appBar: AppBar(
                	title: const Text('Flutter & Native Channel Example'),
                ),
                body: Center(child: Text("Hello, Flutter!")),
            ),
        );
    }
    
    Future<void> sendMessageToNative() async {
    	try{
        	final String result = awiat platform.invokeMethod('sendMessage', {'message': 'Hello from Flutter!'});
            print('Message from Native: $result');
        } on PlatformException catch(e) {
        	print('Failed to send message to native: ${e.message}');
        }
    }
}

 

 

5-2. Flutter의 WidgetsBinding.instance.addPostFrameCallback 메소드를 활용

플러그인이나 메소드 채널 같은 Flutter 네이티브 기능과 통신할 때는 

Flutter의 WidgetsBinding.instance.addPostFrameCallback 메소드를 활용해 

Flutter 앱이 초기화된 이후 작업을 수행하도록 코드를 구성하면 안정적으로 동작한다 

 

① WidgetsBinding.instance.addPostFrameCallback 메소드

해당 메소드를 사용하면 Flutter 위젯이 완전히 빌드된 후 특정 코드를 실행할 수 있다 

Flutter가 초기화된 이후 네이티브 메소드를 호출하거나 초기화가 필요한 작업을 수행할 때 유용

// main.dart

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

void main() {
	WidgetsFlutterBinding.ensureInitialized();
    runApp(MyApp());
}

class MyApp extends StatelessWidget {
	@override
    Widget build(BuildContext context) {
    	// Flutter 위젯이 렌더링된 후 실행될 콜백
        WidgetsBinding.instance.addPostFrameCallback((_) {
        	initializeFlutterApp();
    	});
        
        return MaterialApp(
        	home: Scaffold(
            	appBar: AppBar(
                	title: const Text('Flutter & DOM Initialization Example'),
                ),
                body: Center(child: Text("Hello, Flutter!")),
            ),
        );
    }
    
    //초기화 작업 예시 함수
    void initializeFlutterApp() {
    	print("Flutter App Initialized");
        //여기에 초기화할 코드 작성
    }
}

 

 

6.  정리 

1) 브라우저가 index.html 로드 

브라우저는 index.html 파일을 로드하고, HTML 구조와 외부 리소스를 준비한다 

이를 통해 Flutter가 웹페이지에 렌더링될 준비를 완료한다 

 

2) Flutter가 main.dart 실행 

index.html이 로드된 이후 Flutter 엔진은 main.dart 파일을 실행한다 

여기서 runApp() 메소드를 통해 앱의 UI와 로직이 브라우저 화면에 표시된다 

 

 

flutter

 

참고

<A complete guide to Flutter architecture>

https://blog.logrocket.com/complete-guide-flutter-architecture/

 

A complete guide to Flutter architecture - LogRocket Blog

Learn about Flutter's architecture to help when structuring an app, updating state, building widgets or screens, and maintaining the app.

blog.logrocket.com

 

반응형