# 웹뷰 브릿지

## 1. 개요

모바일 앱에서 웹뷰를 사용할 경우 Mobile SDK, Web SDK 의 중복 사용으로 인해 동작에 문제가 생길 수 있습니다. 이 경우 Mobile SDK 가 Web SDK의 이벤트를 받아서 처리하도록 브릿지 코드를 추가할 수 있습니다.

### 필수 연동 <a href="#pre-work" id="pre-work"></a>

{% content-ref url="../sdk-integrating/initialize/web-sdk" %}
[web-sdk](https://docs.marketap.io/t3ZS4WXNMj0HK27EtIMV/developer/sdk-integrating/initialize/web-sdk)
{% endcontent-ref %}

### 플랫폼 별 선택사항 <a href="#platform" id="platform"></a>

<table data-view="cards" data-full-width="false"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>iOS SDK</td><td><a href="../sdk-integrating/initialize/ios-sdk">ios-sdk</a></td></tr><tr><td>Android SDK</td><td><a href="../sdk-integrating/initialize/android">android</a></td></tr><tr><td>Flutter SDK</td><td><a href="../sdk-integrating/initialize/flutter-sdk">flutter-sdk</a></td></tr><tr><td>React Native SDK</td><td><a href="../sdk-integrating/initialize/react-native-sdk">react-native-sdk</a></td></tr></tbody></table>

## 2. 브릿지 등록

Web SDK의 동작을 Mobile SDK가 받아서 사용하도록 플렛폼별로 브릿지를 등록합니다.

{% hint style="info" %}
브릿지가 등록된 웹뷰에서 인앱메시지를 트리거할 경우 **팝업이 웹뷰 안에 노출**됩니다. 웹뷰 외부 컨텍스트에 팝업을 노출해야 한다면 플랫폼별 MarketapWebBride 생성자에 `handleInAppInWebView` 를 false로 지정해주세요.
{% endhint %}

### 2-1. Android

MarketapWebBridge 클래스를 사용하시는 WebView에 등록해줍니다. 이때 JavascriptInterface의 이름은 반드시 `MarketapWebBridge.NAME` 이어야 합니다.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val webView = findViewById<WebView>(R.id.webView)
webView.addJavascriptInterface(
    MarketapWebBridge(webView),
    MarketapWebBridge.NAME
)
```

{% endtab %}

{% tab title="Java" %}

```java
WebView webView = findViewById(R.id.webView);
webView.addJavascriptInterface(
    new MarketapWebBridge(),
    MarketapWebBridge.NAME
);
```

{% endtab %}
{% endtabs %}

### 2-2. IOS

{% tabs %}
{% tab title="Swift" %}

```javascript
webView.configuration.userContentController.add(
    MarketapWebBridge(),
    name: MarketapWebBridge.name
)
```

{% endtab %}

{% tab title="Objective-C" %}

```objective-c
[webView.configuration.userContentController addScriptMessageHandler:[[MarketapWebBridge alloc] initWithHandleInAppInWebView:YES] name:[MarketapWebBridge name]];
```

{% endtab %}
{% endtabs %}

### 2-3. Flutter

Flutter는 WebView 기능을 기본으로 제공하지 않기 때문에, Marketap SDK는 브릿지 이름(`MarketapWebBridge.name`)과 메시지를 처리하는 `handleMessage(String)` 함수만 제공합니다.

대표적인 WebView 라이브러리들의 연동 예시는 아래와 같으며, 이 외의 라이브러리에서도 JavaScript 브릿지 이름을 동일하게 설정하고, 첫 번째 인자를 문자열로 전달하여 `_bridge.handleMessage`를 호출하면 정상적으로 작동합니다.

{% hint style="warning" %}
아래 예시 이외의 라이브러리를 사용하는 경우, 안정적인 사용 지원을 위해 마켓탭 개발팀에 한번 더 확인 요청해주세요.
{% endhint %}

{% tabs %}
{% tab title="webview\_flutter" %}

<pre class="language-dart"><code class="lang-dart">import 'package:webview_flutter/webview_flutter.dart';

class _MarketapWebViewFlutterState extends State&#x3C;MarketapWebViewFlutter> {
  late final WebViewController _controller;
<strong>  MarketapWebBridge? _bridge;
</strong>
  @override
  void initState() {
    super.initState();

    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
<strong>      ..addJavaScriptChannel(
</strong><strong>        MarketapWebBridge.name,
</strong><strong>        onMessageReceived: (JavaScriptMessage javaScriptMessage) {
</strong><strong>          _bridge?.handleMessage(javaScriptMessage.message);
</strong><strong>        },
</strong><strong>      )
</strong>      ..loadRequest(Uri.parse(widget.url));

<strong>    _bridge = MarketapWebBridge(
</strong><strong>      evaluateJavascript: (script) {
</strong><strong>        _controller.runJavaScript(script);
</strong><strong>      }
</strong><strong>    );
</strong>  }

  @override
  void dispose() {
<strong>    _bridge?.dispose();
</strong>    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return WebViewWidget(controller: _controller);
  }
}
</code></pre>

{% endtab %}

{% tab title="flutter\_inappwebview" %}

<pre class="language-dart"><code class="lang-dart">import 'package:flutter_inappwebview/flutter_inappwebview.dart';

class _MarketapFlutterInAppWebViewState
    extends State&#x3C;MarketapFlutterInAppWebView> {
<strong>  MarketapWebBridge? _bridge;
</strong>
  @override
  void dispose() {
<strong>    _bridge?.dispose();
</strong>    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return InAppWebView(
      initialUrlRequest: URLRequest(url: WebUri(widget.url)),
      onWebViewCreated: (controller) async {
<strong>        _bridge = MarketapWebBridge(
</strong><strong>          evaluateJavascript: (script) {
</strong><strong>            controller.evaluateJavascript(source: script);
</strong><strong>          }
</strong><strong>        );
</strong>
        if (Platform.isIOS) {
<strong>          await controller.addWebMessageListener(
</strong><strong>            WebMessageListener(
</strong><strong>              jsObjectName: MarketapWebBridge.name,
</strong><strong>              onPostMessage: (message, origin, isMainFrame, replyProxy) {
</strong><strong>                final data = message?.data;
</strong><strong>                if (data is String) {
</strong><strong>                  _bridge?.handleMessage(data);
</strong><strong>                }
</strong><strong>              },
</strong><strong>              allowedOriginRules: {'*'},
</strong><strong>            ),
</strong><strong>          );
</strong>        } else if (Platform.isAndroid) {
<strong>          controller.addJavaScriptHandler(
</strong><strong>            handlerName: MarketapWebBridge.name,
</strong><strong>            callback: (args) {
</strong><strong>              if (args.isNotEmpty &#x26;&#x26; args[0] is String) {
</strong><strong>                _bridge?.handleMessage(args[0]);
</strong><strong>              }
</strong><strong>              return null;
</strong><strong>            },
</strong><strong>          );
</strong><strong>        }
</strong>      },
    );
  }
}
</code></pre>

{% endtab %}
{% endtabs %}

### 2-4. React Native

{% tabs %}
{% tab title="Typesccript" %}

<pre class="language-typescript"><code class="lang-typescript">import React, {useEffect, useRef} from 'react';
import {WebView} from 'react-native-webview';
import {MarketapWebBridge} from 'react-native-marketap-sdk';

export default function MarketapWebViewScreen() {
  const webViewRef = useRef&#x3C;WebView>(null);
<strong>  const bridgeRef = useRef&#x3C;MarketapWebBridge | null>(null);
</strong>
  useEffect(() => {
<strong>    bridgeRef.current = new MarketapWebBridge(
</strong><strong>      (script: string) => webViewRef.current?.injectJavaScript(script),
</strong><strong>      true // handleInAppInWebView (기본값 true)
</strong><strong>    );
</strong>
<strong>    return () => {
</strong><strong>      bridgeRef.current?.dispose();
</strong><strong>      bridgeRef.current = null;
</strong><strong>    };
</strong>  }, []);

  return (
    &#x3C;WebView
      ref={webViewRef}
      source={{uri: 'https://YOUR_WEB_URL'}}
<strong>      onMessage={(event) => {
</strong><strong>        bridgeRef.current?.handleMessage(event.nativeEvent.data);
</strong><strong>      }}
</strong>    />
  );
}
</code></pre>

{% endtab %}
{% endtabs %}

## 3. 완료

모든 작업을 완료했다면 이제 Web SDK 의 데이터 수집, 인앱 메시지 노출 등 모든 동작을 Mobile SDK 가 대신 처리하게 됩니다.

### FAQ

<details>

<summary>Q1. 웹뷰 브릿지를 연동할 때 웹, 모바일에서 각각 초기화 함수(<code>initialize</code>)를 호출해야 하나요?</summary>

* **모바일 SDK에서는 필수입니다.**
* **웹 SDK에서는 필수가 아닙니다.**

웹뷰 브릿지가 연동된 상태에서는 웹 SDK의 `initialize` 호출은 내부적으로 무시되며, 모든 동작은 모바일 SDK 기준으로 처리됩니다.\
다만 **모바일 웹뷰와 일반 웹 환경에서 동일한 웹 코드를 공용으로 사용하는 경우**, 웹에서 `initialize`를 호출해도 문제는 없으므로 굳이 제거할 필요는 없습니다.

</details>

<details>

<summary>Q2. 웹뷰 브릿지를 연동하면 JavaScript에서 사용 중이던 <code>mtap.track</code> 같은 함수들을 수정해야 하나요?</summary>

* **수정할 필요 없습니다.**

Web SDK가 내부적으로 웹뷰 브릿지를 통해 이벤트를 모바일 SDK로 전달합니다.\
따라서 기존에 사용하던 `mtap.track`, `mtap.identify` 등의 호출은 그대로 유지해도 되며, 모바일 SDK와 통합된 환경에서 정상적으로 데이터가 수집됩니다.

</details>

<details>

<summary>Q3. 기존에는 웹에서만 사용 중이었는데, 앱 도입을 위해 웹뷰 브릿지를 연동하려고 합니다. 주의할 점이 있나요?</summary>

* **모바일 SDK에서의 `initialize` 호출은 필수입니다.**
* 웹뷰 브릿지 연동만 완료하면, **웹 쪽에서 마켓탭 SDK 관련 코드를 추가로 수정할 필요는 없습니다.**

모바일 SDK 초기화 이후, 웹뷰 브릿지가 연결되면 이벤트 수집 및 인앱 메시지 노출 등은 자동으로 모바일 SDK 기준으로 처리됩니다.

</details>
