[Flutter] Webview 라이브러리 - webview_flutter
공식 문서 : https://pub.dev/packages/webview_flutter
flutter에서 webview_flutter 라이브러리는 애플리케이션 내에서 웹 콘텐츠를 표시할 수 있도록 도와주는 공식 패키지입니다. 이를 통해 웹 페이지, 로컬 HTML 파일, 또는 웹 기반 애플리케이션을 Flutter 앱 내에 통합할 수 있습니다. **webview_flutter**는 다양한 플랫폼(안드로이드, iOS)에서 일관된 웹뷰 경험을 제공하며, 웹 페이지와의 상호작용을 가능하게 합니다.
📘 목차
- webview_flutter 소개
- 설치 및 설정
- 기본 사용법
- 고급 기능
- 보안 고려 사항
- 문제 해결 및 자주 발생하는 문제
- Best Practices (최고의 사용 방법)
- 추가 리소스
📘 1. webview_flutter 소개
- *webview_flutter*는 Flutter 애플리케이션 내에서 웹 콘텐츠를 표시할 수 있는 웹뷰(WebView)를 제공하는 공식 패키지입니다. 이 패키지를 사용하면 사용자가 앱 내에서 직접 웹 페이지를 탐색할 수 있으며, 웹 기반 기능을 앱에 통합할 수 있습니다.
주요 특징:
- 플랫폼 지원: 안드로이드 및 iOS에서 지원됩니다.
- JavaScript 지원: JavaScript 실행 및 상호작용이 가능합니다.
- 로컬 HTML 파일 로드: 앱 내에 포함된 로컬 HTML 파일을 로드할 수 있습니다.
- Flutter와 웹뷰 간 통신: JavaScript와 Flutter 간의 양방향 통신을 지원합니다.
- 커스터마이즈 가능: 웹뷰의 다양한 속성을 설정하여 맞춤형 웹 경험을 제공할 수 있습니다.
📘 2. 설치 및 설정
🔍 1️⃣ pubspec.yaml에 패키지 추가
먼저, 프로젝트의 pubspec.yaml 파일에 webview_flutter 패키지를 추가해야 합니다. 최신 버전을 사용하도록 권장합니다.
ependencies:
flutter:
sdk: flutter
webview_flutter: ^4.0.7 # 최신 버전 확인 후 적용
패키지를 추가한 후, 터미널에서 다음 명령어를 실행하여 패키지를 설치합니다.
flutter pub get
🔍 2️⃣ 안드로이드 설정
안드로이드에서 웹뷰를 사용하려면 몇 가지 추가 설정이 필요합니다.
- AndroidManifest.xml 수정
- android/app/src/main/AndroidManifest.xml 파일을 열고, 인터넷 권한을 추가합니다.
- Android Gradle 설정
android { defaultConfig { applicationId "com.example.your_app" minSdkVersion 19 targetSdkVersion 33 // 기타 설정 } }
- 최소 SDK 버전이 19 이상인지 확인합니다. android/app/build.gradle 파일에서 minSdkVersion을 확인하고 필요 시 수정합니다.
🔍 3️⃣ iOS 설정
iOS에서도 웹뷰를 사용하려면 몇 가지 설정이 필요합니다.
- Info.plist 수정
주의: NSAllowsArbitraryLoads를 true로 설정하면 모든 HTTP 요청이 허용되므로, 보안에 민감한 앱에서는 특정 도메인만 허용하도록 설정하는 것이 좋습니다.<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>
- ios/Runner/Info.plist 파일을 열고, App Transport Security Settings를 추가하여 HTTP 요청을 허용하거나, 특정 도메인에 대한 예외를 설정할 수 있습니다.
- WKWebView 설정 (선택 사항)
- 기본적으로 **webview_flutter**는 WKWebView를 사용합니다. 필요에 따라 추가 설정을 할 수 있습니다.
📘 3. 기본 사용법
- *webview_flutter*를 사용하여 간단한 웹뷰를 구현하는 방법을 소개합니다.
🔍 1️⃣ 간단한 웹뷰 예제
아래는 Flutter 애플리케이션 내에 웹뷰를 삽입하여 웹 페이지를 로드하는 기본 예제입니다.
main.dart:
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'WebView Flutter Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const WebViewExample(),
);
}
}
class WebViewExample extends StatefulWidget {
const WebViewExample({super.key});
@override
State createState() => _WebViewExampleState();
}
class _WebViewExampleState extends State {
late final WebViewController _controller;
@override
void initState() {
super.initState();
// 초기화 시점에 WebViewController 설정
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted) // JavaScript 허용
..setBackgroundColor(const Color(0x00000000)) // 배경색 설정
..setNavigationDelegate(
NavigationDelegate(
onPageStarted: (String url) {
print('페이지 로딩 시작: $url');
},
onPageFinished: (String url) {
print('페이지 로딩 완료: $url');
},
onWebResourceError: (WebResourceError error) {
print('웹 리소스 오류: ${error.description}');
},
),
)
..loadRequest(Uri.parse('<https://flutter.dev>'));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('WebView Example'),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () {
_controller.reload(); // 페이지 새로고침
},
),
],
),
body: WebViewWidget(controller: _controller),
);
}
}
설명:
- WebViewController 초기화:
- WebViewController를 사용하여 웹뷰의 동작을 제어합니다.
- setJavaScriptMode(JavaScriptMode.unrestricted): JavaScript 실행을 허용합니다.
- setNavigationDelegate: 웹뷰의 내비게이션 이벤트를 처리할 수 있는 델리게이트를 설정합니다.
- loadRequest: 로드할 웹 페이지의 URL을 지정합니다.
- WebViewWidget:
- WebViewWidget을 사용하여 웹뷰를 화면에 표시합니다. controller를 전달하여 웹뷰를 제어합니다.
- AppBar:
- 페이지 상단에 새로고침 버튼을 추가하여 웹페이지를 새로고침할 수 있습니다.
📘 4. 고급 기능
🔍 1️⃣ JavaScript 실행
웹뷰 내에서 JavaScript를 실행할 수 있습니다. 예를 들어, 특정 함수를 호출하거나 웹 페이지와 상호작용할 수 있습니다.
예제: JavaScript 코드 실행
// 버튼을 눌러 JavaScript 실행
ElevatedButton(
onPressed: () {
_controller.runJavaScript('alert("Hello from Flutter!");');
},
child: const Text('JavaScript 실행'),
),
결과: 웹 페이지 내에서 "Hello from Flutter!"라는 경고창이 표시됩니다.
🔍 2️⃣ 웹 페이지 로딩 상태 관리
웹뷰의 로딩 상태를 관리하여 사용자에게 로딩 스피너 등을 표시할 수 있습니다.
예제: 로딩 인디케이터 추가
class _WebViewExampleState extends State {
late final WebViewController _controller;
bool _isLoading = true; // 로딩 상태 변수
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onPageStarted: (String url) {
setState(() {
_isLoading = true;
});
},
onPageFinished: (String url) {
setState(() {
_isLoading = false;
});
},
onWebResourceError: (WebResourceError error) {
setState(() {
_isLoading = false;
});
print('웹 리소스 오류: ${error.description}');
},
),
)
..loadRequest(Uri.parse('<https://flutter.dev>'));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('WebView Example'),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () {
_controller.reload();
},
),
],
),
body: Stack(
children: [
WebViewWidget(controller: _controller),
if (_isLoading)
const Center(
child: CircularProgressIndicator(),
),
],
),
);
}
}
설명:
- _isLoading 변수: 웹 페이지 로딩 상태를 추적합니다.
- onPageStarted: 페이지 로딩 시작 시 _isLoading을 true로 설정하여 로딩 인디케이터를 표시합니다.
- onPageFinished 및 onWebResourceError: 페이지 로딩 완료 또는 오류 발생 시 _isLoading을 false로 설정하여 로딩 인디케이터를 숨깁니다.
- Stack 위젯: 웹뷰와 로딩 인디케이터를 겹쳐서 표시합니다.
🔍 3️⃣ 웹뷰와 Flutter 간의 통신
웹뷰 내의 JavaScript와 Flutter 코드 간에 상호작용할 수 있습니다. 이를 통해 양방향 통신이 가능합니다.
예제: JavaScript에서 Flutter로 메시지 보내기
- Flutter 측 설정:
- @override void initState() { super.initState(); _controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..setBackgroundColor(const Color(0x00000000)) ..addJavaScriptChannel( 'FlutterChannel', onMessageReceived: (JavaScriptMessage message) { // JavaScript에서 보낸 메시지 처리 print('JavaScript로부터 메시지 수신: ${message.message}'); }, ) ..setNavigationDelegate( NavigationDelegate( onPageStarted: (String url) { setState(() { _isLoading = true; }); }, onPageFinished: (String url) { setState(() { _isLoading = false; }); }, onWebResourceError: (WebResourceError error) { setState(() { _isLoading = false; }); print('웹 리소스 오류: ${error.description}'); }, ), ) ..loadRequest(Uri.parse('<https://your-web-page.com>')); }
- 웹 페이지 JavaScript 코드:
<!DOCTYPE html> <html> <head> <title>Flutter WebView Communication</title> <script type="text/javascript"> function sendMessageToFlutter() { FlutterChannel.postMessage('Hello from JavaScript!'); } </script> </head> <body> <h1>Flutter WebView Communication</h1> <button onclick="sendMessageToFlutter()">Send Message to Flutter</button> </body> </html>
- 웹 페이지 내에서 Flutter로 메시지를 보내려면, FlutterChannel.postMessage를 사용합니다.
설명:
- JavaScriptChannel 추가: addJavaScriptChannel을 사용하여 Flutter와 JavaScript 간의 통신 채널을 설정합니다.
- JavaScript에서 메시지 보내기: 웹 페이지 내에서 FlutterChannel.postMessage를 호출하여 Flutter로 메시지를 보냅니다.
- Flutter에서 메시지 수신: Flutter의 onMessageReceived 콜백에서 메시지를 처리합니다.
📘 5. 보안 고려 사항
웹뷰를 사용할 때 보안은 매우 중요한 요소입니다. 다음은 웹뷰 사용 시 고려해야 할 주요 보안 사항입니다:
🔍 1️⃣ JavaScript 실행 제한
JavaScript 실행을 완전히 허용할 경우, 악의적인 스크립트가 실행될 수 있으므로 신뢰할 수 없는 콘텐츠를 로드할 때는 주의가 필요합니다.
예시: JavaScript 실행 제한
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.disabled) // JavaScript 비활성화
..loadRequest(Uri.parse('<https://secure-website.com>'));
🔍 2️⃣ HTTPS 사용 권장
민감한 데이터를 다루는 경우, 항상 HTTPS를 통해 안전하게 데이터를 전송하도록 합니다.
🔍 3️⃣ 외부 콘텐츠 로드 제한
앱 내에서 로드할 수 있는 콘텐츠의 출처를 제한하여, 신뢰할 수 없는 도메인에서의 콘텐츠 로드를 방지합니다.
예시: URL 필터링
_controller.setNavigationDelegate(
NavigationDelegate(
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('<https://trusted-domain.com>')) {
return NavigationDecision.navigate;
} else {
return NavigationDecision.prevent;
}
},
),
);
🔍 4️⃣ 사용자 인증 정보 보호
웹뷰 내에서 로그인 정보나 인증 토큰을 다루는 경우, 해당 정보가 안전하게 처리되도록 주의합니다.
📘 6. 문제 해결 및 자주 발생하는 문제
🔍 1️⃣ 웹뷰가 로드되지 않음
- 원인: 인터넷 권한이 설정되지 않았거나, 잘못된 URL을 로드하려는 경우
- 해결 방법:
- 안드로이드와 iOS 설정에서 인터넷 권한이 제대로 설정되었는지 확인합니다.
- 로드하려는 URL이 올바르고, 웹 서버가 정상적으로 작동하는지 확인합니다.
🔍 2️⃣ JavaScript 실행이 되지 않음
- 원인: JavaScript 모드가 비활성화되어 있거나, JavaScript 코드에 오류가 있는 경우
- 해결 방법:
- JavaScriptMode.unrestricted로 설정되어 있는지 확인합니다.
- 웹 페이지의 JavaScript 코드에 오류가 없는지 확인합니다.
🔍 3️⃣ 웹뷰와 Flutter 간 통신이 되지 않음
- 원인: JavaScriptChannel이 제대로 설정되지 않았거나, JavaScript 코드에서 올바르게 메시지를 보내지 않은 경우
- 해결 방법:
- addJavaScriptChannel을 통해 채널이 제대로 추가되었는지 확인합니다.
- JavaScript 코드에서 postMessage를 올바르게 호출했는지 확인합니다.
🔍 4️⃣ 웹뷰 내에서 네비게이션 컨트롤이 작동하지 않음
- 원인: NavigationDelegate가 올바르게 설정되지 않았거나, 특정 네비게이션 요청이 제한된 경우
- 해결 방법:
- NavigationDelegate의 콜백 함수가 올바르게 구현되었는지 확인합니다.
- 필요한 경우 특정 네비게이션 요청을 허용하도록 설정합니다.
📘 7. Best Practices (최고의 사용 방법)
🔍 1️⃣ 최소한의 권한 부여
웹뷰를 사용할 때 필요한 최소한의 권한만 부여하여 보안 위험을 줄입니다. 예를 들어, 인터넷 권한 외에 추가적인 권한이 필요하지 않다면 요청하지 않습니다.
🔍 2️⃣ 신뢰할 수 있는 콘텐츠만 로드
웹뷰에서 로드하는 콘텐츠는 신뢰할 수 있는 출처에서만 로드하도록 제한합니다. 이를 통해 악의적인 스크립트 실행을 방지할 수 있습니다.
🔍 3️⃣ 사용자 입력 검증
웹뷰 내에서 사용자로부터 입력받은 데이터를 처리할 때는 항상 입력을 검증하고, 필요한 경우 이스케이프 처리를 합니다.
🔍 4️⃣ 보안 업데이트 유지
webview_flutter 패키지와 Flutter SDK를 최신 버전으로 유지하여 보안 패치를 적용받도록 합니다.
🔍 5️⃣ 웹뷰의 상태 관리
웹뷰의 상태(로딩 상태, 오류 상태 등)를 적절히 관리하여 사용자에게 명확한 피드백을 제공합니다.
🔍 6️⃣ 성능 최적화
웹뷰 내에서 불필요한 리소스를 로드하지 않도록 최적화하고, 필요한 경우 캐싱을 활용하여 성능을 향상시킵니다.