이 문서는 Flutter 공식 webview_flutter 패키지를 Bootpay용으로 포크할 때 필요한 작업 목록입니다.
# 변경 전
name: webview_flutter_wkwebview
description: A Flutter plugin...
# 변경 후
name: bootpay_webview_flutter_wkwebview
description: webview_flutter_wkwebview 를 국내 결제환경에 맞게 fork 떠서 관리하는 프로젝트 입니다.
repository: https://github.com/bootpay/bootpay_webview_flutter_wkwebview
issue_tracker: https://github.com/bootpay/bootpay_webview_flutter_wkwebview/issues
flutter:
plugin:
implements: bootpay_webview_flutter_platform_interface # 중요: platform interface 패키지를 구현
platforms:
ios:
pluginClass: BTWebViewFlutterPlugin # BT 접두사 사용
dartPluginClass: BTWebKitWebViewPlatform
sharedDarwinSource: true
macos:
pluginClass: BTWebViewFlutterPlugin
dartPluginClass: BTWebKitWebViewPlatform
sharedDarwinSource: true
dependencies:
bootpay_webview_flutter_platform_interface:
path: ../bootpay_webview_flutter_platform_interfacewebview_flutter_platform_interface→bootpay_webview_flutter_platform_interface- 로컬 경로 의존성 사용:
path: ../bootpay_webview_flutter_platform_interface
mv lib/webview_flutter_wkwebview.dart lib/bootpay_webview_flutter_wkwebview.dartlib/src/ 디렉토리의 모든 파일에서:
// 변경 전
import 'package:bootpay_webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
// 변경 후
import 'package:bootpay_webview_flutter_platform_interface/bootpay_webview_flutter_platform_interface.dart';수정할 파일들:
lib/src/common/instance_manager.dartlib/src/common/platform_webview_controller_creation_params.dartlib/src/common/web_kit.g.dartlib/src/common/weak_reference_utils.dartlib/src/foundation/foundation.dartlib/src/ui_kit/ui_kit.dartlib/src/webkit_proxy.dartlib/src/webkit_ssl_auth_error.dartlib/src/webkit_webview_controller.dartlib/src/webkit_webview_cookie_manager.dartlib/src/webkit_webview_platform.dart
주요 클래스들:
WebKitWebViewPlatform→BTWebKitWebViewPlatform(Dart 플러그인 클래스)
# 파일 이름 변경
mv darwin/webview_flutter_wkwebview.podspec darwin/bootpay_webview_flutter_wkwebview.podspec# podspec 내용 수정
Pod::Spec.new do |s|
s.name = 'bootpay_webview_flutter_wkwebview'
s.version = '0.0.1'
s.summary = 'A WebView Plugin for Flutter (Bootpay Fork).'
s.homepage = 'https://github.com/bootpay/bootpay_webview_flutter_wkwebview'
s.license = { :type => 'BSD', :file => '../LICENSE' }
s.author = { 'Bootpay' => 'bootpay.co.kr' }
s.source_files = 'bootpay_webview_flutter_wkwebview/Sources/bootpay_webview_flutter_wkwebview/**/*.swift'
s.resource_bundles = {
'bootpay_webview_flutter_wkwebview_privacy' => [
'bootpay_webview_flutter_wkwebview/Sources/bootpay_webview_flutter_wkwebview/Resources/PrivacyInfo.xcprivacy'
]
}
s.public_header_files = 'bootpay_webview_flutter_wkwebview/Sources/bootpay_webview_flutter_wkwebview/**/*.h'
s.module_map = 'bootpay_webview_flutter_wkwebview/Sources/bootpay_webview_flutter_wkwebview/include/bootpay_webview_flutter_wkwebview.modulemap'
s.dependency 'Flutter'
s.platform = :ios, '13.0'
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
s.swift_version = '5.0'
end# 디렉토리 이름 변경
cd darwin
mv webview_flutter_wkwebview bootpay_webview_flutter_wkwebview
# Sources 내부도 확인
cd bootpay_webview_flutter_wkwebview/Sources
mv webview_flutter_wkwebview bootpay_webview_flutter_wkwebviewWebViewFlutterPlugin.swift 핵심 수정:
// 파일: darwin/bootpay_webview_flutter_wkwebview/Sources/bootpay_webview_flutter_wkwebview/WebViewFlutterPlugin.swift
// 클래스 이름을 BTWebViewFlutterPlugin으로 변경
public class BTWebViewFlutterPlugin: NSObject, FlutterPlugin {
var proxyApiRegistrar: ProxyAPIRegistrar?
init(binaryMessenger: FlutterBinaryMessenger) {
proxyApiRegistrar = ProxyAPIRegistrar(
binaryMessenger: binaryMessenger)
proxyApiRegistrar?.setUp()
}
public static func register(with registrar: FlutterPluginRegistrar) {
#if os(iOS)
let binaryMessenger = registrar.messenger()
#else
let binaryMessenger = registrar.messenger
#endif
let plugin = BTWebViewFlutterPlugin(binaryMessenger: binaryMessenger)
let viewFactory = FlutterViewFactory(instanceManager: plugin.proxyApiRegistrar!.instanceManager)
// ⚠️ 충돌 방지: webview_flutter와 동시 사용을 위해 Bootpay 전용 네임스페이스 사용
registrar.register(viewFactory, withId: "kr.co.bootpay/webview")
registrar.publish(plugin)
}
public func detachFromEngine(for registrar: FlutterPluginRegistrar) {
proxyApiRegistrar!.ignoreCallsToDart = true
proxyApiRegistrar!.tearDown()
proxyApiRegistrar = nil
}
}WebViewFlutterWKWebViewExternalAPI.swift 수정:
// 파일: darwin/bootpay_webview_flutter_wkwebview/Sources/bootpay_webview_flutter_wkwebview/WebViewFlutterWKWebViewExternalAPI.swift
@objc(BTWebViewFlutterWKWebViewExternalAPI)
public class BTWebViewFlutterWKWebViewExternalAPI: NSObject {
@objc(webViewForIdentifier:withPluginRegistry:)
public static func webView(
forIdentifier identifier: Int64, withPluginRegistry registry: FlutterPluginRegistry
) -> WKWebView? {
// 플러그인 이름을 BTWebViewFlutterPlugin으로 수정
let plugin = registry.valuePublished(byPlugin: "BTWebViewFlutterPlugin") as! BTWebViewFlutterPlugin
let webView: WKWebView? = plugin.proxyApiRegistrar?.instanceManager.instance(
forIdentifier: identifier)
return webView
}
}// darwin/bootpay_webview_flutter_wkwebview/Package.swift
let package = Package(
name: "bootpay_webview_flutter_wkwebview",
platforms: [
.iOS("13.0"),
.macOS("10.15")
],
products: [
.library(name: "bootpay-webview-flutter-wkwebview", targets: ["bootpay_webview_flutter_wkwebview"])
],
dependencies: [],
targets: [
.target(
name: "bootpay_webview_flutter_wkwebview",
dependencies: [],
exclude: ["include"],
resources: [
.process("Resources")
],
cSettings: [
.headerSearchPath("include/bootpay_webview_flutter_wkwebview")
]
)
]
)dependencies:
flutter:
sdk: flutter
bootpay_webview_flutter_platform_interface:
path: ../../bootpay_webview_flutter_platform_interface
bootpay_webview_flutter_wkwebview:
path: ../// import 문 수정
import 'package:bootpay_webview_flutter_platform_interface/bootpay_webview_flutter_platform_interface.dart';
import 'package:bootpay_webview_flutter_wkwebview/bootpay_webview_flutter_wkwebview.dart';# 루트에서
flutter pub get
# example에서
cd example
flutter pub getcd example/ios
pod install
cd ../..# iOS 디바이스에서 테스트
flutter run -d <device-id>
# 또는 Xcode에서 직접 빌드
open example/ios/Runner.xcworkspace-
pubspec.yaml패키지명, implements, pluginClass 수정 - 메인 라이브러리 파일 이름 변경 (
lib/bootpay_webview_flutter_wkwebview.dart) - 모든 Dart 파일의 import 문 수정 (platform_interface 경로)
- podspec 파일 이름 및 내용 수정
- darwin 디렉토리 구조 변경
-
WebViewFlutterPlugin.swift에서 클래스명을BTWebViewFlutterPlugin으로 변경 -
WebViewFlutterWKWebViewExternalAPI.swift에서 플러그인 참조 수정 -
Package.swift수정 - example/pubspec.yaml 의존성 수정
- example/lib/main.dart import 문 수정
-
flutter pub get실행 -
pod install실행 - 빌드 테스트 (iOS 디바이스)
⚠️ 매우 중요: 이 섹션의 처리를 하지 않으면webview_flutter와bootpay_flutter_webview를 동시에 사용할 때 충돌이 발생합니다!
문제: 공식 webview_flutter와 플랫폼 뷰 타입 이름이 동일하면 Flutter 엔진에서 충돌 발생
해결: Bootpay 전용 네임스페이스 사용
파일: darwin/bootpay_webview_flutter_wkwebview/Sources/bootpay_webview_flutter_wkwebview/WebViewFlutterPlugin.swift
// ❌ 잘못된 예 (webview_flutter와 충돌)
registrar.register(viewFactory, withId: "plugins.flutter.io/webview")
// ✅ 올바른 예 (충돌 없음)
registrar.register(viewFactory, withId: "kr.co.bootpay/webview")파일: lib/src/webkit_webview_controller.dart (2군데)
// ❌ 잘못된 예
viewType: 'plugins.flutter.io/webview',
// ✅ 올바른 예
viewType: 'kr.co.bootpay/webview',파일: lib/src/legacy/webview_cupertino.dart (1군데)
// ❌ 잘못된 예
viewType: 'plugins.flutter.io/webview',
// ✅ 올바른 예
viewType: 'kr.co.bootpay/webview',# 올바르게 변경되었는지 확인
grep -r "kr.co.bootpay/webview" .
# 결과: Swift 파일 1개, Dart 파일 3개에서 발견되어야 함
# 잘못된 값이 남아있는지 확인
grep -r "plugins.flutter.io/webview" .
# 결과: 아무것도 나오지 않아야 함 (주석, 문서 제외)실제로 두 패키지를 함께 사용하는 테스트 앱을 만들어 검증:
# test_app/pubspec.yaml
dependencies:
webview_flutter: ^4.0.0 # 공식 패키지
bootpay_webview_flutter: ^3.0.0 # Bootpay 패키지// 두 WebView를 동시에 사용
Column(
children: [
Expanded(
child: webview_flutter.WebViewWidget(...), // 공식
),
Expanded(
child: bootpay_webview_flutter.WebViewWidget(...), // Bootpay
),
],
)성공 조건:
- ✅ 두 WebView가 모두 정상 표시
- ✅ 에러나 충돌 없음
- ✅ 각각 독립적으로 작동
실패 시 에러 예시:
Error: The platform view type 'plugins.flutter.io/webview' is already registered.
- 패키지명:
bootpay_접두사 사용 - iOS 네이티브 클래스:
BT접두사 사용 (예:BTWebViewFlutterPlugin) - Dart 클래스:
BT접두사 사용 (예:BTWebKitWebViewPlatform)
implements필드는 메인 패키지를 지정해야 함- 예:
implements: bootpay_webview_flutter(bootpay_webview_flutter의 패키지명) - ❌ 잘못된 예 1:
implements: bootpay_webview_flutter_wkwebview(자기 자신을 implements하면 안됨) - ❌ 잘못된 예 2:
implements: bootpay_webview_flutter_platform_interface(인터페이스 패키지가 아닌 메인 패키지를 지정)
이유: Flutter의 플러그인 시스템은 3-tier 구조입니다:
- 메인 패키지 (
bootpay_webview_flutter) - 사용자가 import하는 패키지 - 플랫폼 구현 (
bootpay_webview_flutter_wkwebview) - iOS/macOS 구현 - 플랫폼 인터페이스 (
bootpay_webview_flutter_platform_interface) - 공통 인터페이스
메인 패키지가 default_package로 플랫폼 구현을 자동 선택하므로, 구현 패키지는 메인 패키지를 implements해야 합니다.
- 반드시
bootpay_webview_flutter_platform_interface사용 - 로컬 경로 의존성으로 설정:
path: ../bootpay_webview_flutter_platform_interface
GeneratedPluginRegistrant.m이 자동으로BTWebViewFlutterPlugin을 찾음- pubspec.yaml의
pluginClass와 Swift 클래스명이 정확히 일치해야 함 WebViewFlutterWKWebViewExternalAPI.swift의 플러그인 조회 시에도 동일한 이름 사용
- 경고:
UIScene lifecycle will soon be required. Failure to adopt will result in an assert in the future. - iOS 13부터 도입된 UIScene lifecycle이 iOS 26에서 필수가 될 예정
- 해결 방법:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>#pragma mark - UISceneSession lifecycle
- (UISceneConfiguration *)application:(UIApplication *)application
configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession
options:(UISceneConnectionOptions *)options {
return [[UISceneConfiguration alloc] initWithName:@"Default Configuration"
sessionRole:connectingSceneSession.role];
}
- (void)application:(UIApplication *)application
didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {
// Release resources specific to discarded scenes
}#import <UIKit/UIKit.h>
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
@property (strong, nonatomic) UIWindow *window;
@end#import "SceneDelegate.h"
@implementation SceneDelegate
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
// Scene setup
}
- (void)sceneDidDisconnect:(UIScene *)scene { }
- (void)sceneDidBecomeActive:(UIScene *)scene { }
- (void)sceneWillResignActive:(UIScene *)scene { }
- (void)sceneWillEnterForeground:(UIScene *)scene { }
- (void)sceneDidEnterBackground:(UIScene *)scene { }
@end- Asset Catalog 에러: "Failed to launch AssetCatalogSimulatorAgent via CoreSimulator spawn"
- 이는 macOS/Xcode 시스템 이슈로 코드 문제가 아님
- 해결 방법:
- Xcode에서 직접 빌드
- CoreSimulator 재시작:
killall -9 com.apple.CoreSimulator.CoreSimulatorService - USB 유선 연결 사용 (무선 대신)