플러터(Flutter)는 구글에서 만든 크로스 플랫폼 프레임워크다.
이번 글에서는 모바일이 지원되는 웹사이트를 코드 몇 줄만으로 손쉽게 웹앱으로 만들어 본다.
웹뷰(Web View)
웹뷰는 프레임워크에 내장된 브라우저를 앱의 네이티브 컴포넌트에 임베딩하는 기능이다. 네이티브 컴포넌트에 비해 속도가 느리고 애니메이션이 부자연스럽다는 단점이 있지만 이미 만들어 놓은 웹사이트를 손쉽게 활용할 수 있어 많이 활용된다.
웹사이트의 URL을 입력하면 해당 웹사이트를 화면에 보여주는 역할을 한다. 콜백 함수 및 웹뷰 컨트롤러를 통해 뒤로 가기, 앞으로 가기 등 여러 가지 처리를 해 줄 수 있다.
또한 결제 모듈을 PG(Payment Gateway)사에서 웹으로 이미 기능을 구현해두었기 때문에 웹뷰를 사용하면 굳이 시간을 들여 결제 기능을 개발할 필요가 없어진다.
웹뷰 위젯의 속성
- setJavascriptMode : 웹뷰에서 자바스크립트 실행 허용 여부 결정
- JavascriptMode.unrestricted : 제한 없이 실행
- JavascriptMode.disabled : 실행 제한
- setBackgroundColor : 배경색 지정
- loadRequest : 새로운 URL로 이동
- setNavigationDelegate : 다양한 콜백함수가 있는 NavigationDelegate 객체를 연결
프로젝트에 웹뷰 플러그인 추가
웹뷰를 사용하기 위해 플러터 플러그인을 프로젝트에 추가해야 한다. pubspec.yaml 파일을 수정하거나 pub add 명령으로 추가할 수 있다. 나는 pub add 명령으로 추가하는 것을 선호하기에 그렇게 하기로 하겠다.
flutter pub add webview_flutter
위와 같이 터미널에서 명령을 실행하면 최신버전의 웹뷰 플러그인이 추가되며 pubspec.yaml 파일에도 자동으로 종속성(dependencies)에 추가된다. 아래는 안드로이드 스튜디오의 터미널을 이용해 위 명령을 실행한 스크린샷이다.

주요 pub 명령
- flutter pub get : pubspec.yaml 파일에 등록한 플러그인들 다운로드
- flutter pub add {plugin name} : pubspec.yaml 파일에 {plugin name} 플러그인추가 및 다운로드
- flutter pub upgrade : pubspec.yaml 파일에 존재하는 플러그인들을 모두 최신 버전으로 업데이트
- flutter pub run : 프로젝트 실행
네이티브에 권한 설정
인터넷 사용을 위해 인터넷 사용 권한을 추가해보자.
안드로이드
안드로이드 설정 파일 : android/src/main/AndroidManifest.xml
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
...
위와 같이 ‘INTERNET’ 권한을 추가해 준다.
자주 사용되는 안드로이드 권한
- INTERNET : 인터넷 사용 권한
- CAMERA : 카메라 사용 권한
- WRITE_EXTERNAL_STORAGE : 앱 외부에 파일을 저장할 수 있는 권한
- READ_EXTERNAL_STORAGE : 앱 외부의 파일을 읽을 수 있는 권한
- ACCESS_FINE_LOCATION : GPS와 네트워크를 모두 사용하여 정확한 현재 위치 정보를 가져올 수 있는 권한
- VIBRATE : 진동을 일으킬 수 있는 권한
- ACCESS_COARSE_LOCATION : 네트워크만 사용하여 대략적인 위치 정보를 가져올 수 있는 권한
- BILLING : 인앱 결제를 할 수 있는 권한
- CALL_PHONE : 전화기 앱을 사용하지 않고 전화를 할 수 있는 권한
- NETWORK_STATE : 네트워크 상태를 가져올 수 있는 권한
- RECORD_AUDIO : 음성을 녹음할 수 있는 권한
iOS
iOS 설정 파일 : ios/Runner/Info.plist
Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
...
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent/key>
<true/>
</dict>
</dict>
</plist>
위와 같이 NSAppTransportSecurity 권한을 설정해 준다.
권한 메시지가 필요하다면 다음과 같이 <string> tag를 등록 해주면 된다.
<key>NSAppleMusicUsageDescription</key>
<string>음악 재생 권한이 필요합니다.</string>
자주 사용되는 iOS 권한
- NSCalendarsUsageDescription : 달력 사용 권한 메시지
- NSCameraUsageDescription : 카메라 사용 권한 메시지
- NSContactsUsageDescription : 연락처 사용 권한 메시지
- NSLocationUsageDescription : 위치 정보 사용 권한 메시지
- NSPhotoLibraryUsageDescription : 사진 접근 권한 메시지
- NSFaceIDUsageDescription : FaceID 사용 권한 메시지
- NSMicrophoneUsageDescription : 마이크 사용 권한 메시지
- NSSiriUsageDescription : Siri 사용 권한 메시지
구현
먼저 파일 구조를 잡아본다.
화면 파일들을 따로 분류해 주기 위해 screen 폴더를 하나 만들어서 사용 하였다.

home_screen.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class HomeScreen extends StatelessWidget {
final WebViewController webViewController = WebViewController() // 1. 웹뷰를 조작하는 컨트롤러 선언
..loadRequest(Uri.parse('https://blog.codefactory.ai')) // 1-1. 선언과 동시에 URL 호출
..setJavaScriptMode(JavaScriptMode.unrestricted); // 1-2. 자바스크립트 허용
HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.orange,
title: Text('Code Factory'),
centerTitle: true,
actions: [
IconButton(
onPressed: () {
webViewController.loadRequest(Uri.parse('https://blog.codefactory.ai')); // 3. 컨트롤러로 URL 호출
},
icon: Icon(
Icons.home,
),
),
IconButton(
onPressed: () {
webViewController.goBack(); // 3. 컨트롤러로 뒤로 가기 동작
},
icon: Icon(
Icons.arrow_back,
),
),
IconButton(
onPressed: () {
webViewController.goForward(); // 3. 컨트롤러로 앞으로 가기 동작
},
icon: Icon(
Icons.arrow_forward,
),
),
],
),
body: WebViewWidget(
controller: webViewController, // 2. 웹뷰 위젯에 컨트롤러 설정
),
);
}
}
- 웹뷰를 컨트롤 하는 컨트롤러를 선언하면서 동시에 목표하는 URL로 설정하고 자바스크립트를 허용해 주었다.
- 웹뷰 위젯을 body에 구현하면서 위에서 만든 컨트롤러를 설정해 주었다.
- 위에서 만든 웹뷰 컨트롤러를 통해 ‘홈’, ‘뒤로’, ‘앞으로’ 버튼을 구현하였다.
main.dart
import 'package:blog_web_app/screen/home_screen.dart'; // 1. screen 폴더의 home_screen 임포트
import 'package:flutter/material.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized(); // 2. flutter 프레임워크가 앱을 실행할 준비가 될 때까지 기다리는 코드
runApp(
MaterialApp(
home: HomeScreen(),
)
);
}
- 임포트 경로는 ‘package:{프로젝트 이름}/{lib 폴더로부터의 위치}/{파일명}’ 형태이다.
- 플러터 프레임워크가 앱을 실행할 준비가 될 때까지 기다리는 코드이다. Stateless 위젯에서 웹뷰 컨트롤러 프로퍼티로 사용하다보니 명시적 호출이 필요하였다. 본래는 웹뷰 컨트롤러는 Stateful 위젯에서 사용하는 것이 맞다.
“Flutter | Exercise Example | WebView, Web APP”에 대한 1개의 생각