WebView와 네이티브 간 통신: 구현 가이드

Sean L.·2025년 1월 25일

React

목록 보기
5/6
post-thumbnail

WebView와 네이티브 간 통신: 구현 가이드

1. 들어가며

하이브리드 애플리케이션 개발에서 WebView는 웹 콘텐츠와 네이티브 코드의 연결을 담당하는 중요한 역할을 합니다. 특히, iOS와 Android에서 WebView와 네이티브 코드 간의 원활한 통신을 구현하려면 플랫폼별 요구 사항을 충족해야 합니다. 이 글에서는 WebView와 네이티브 통신을 지원하는 TypeScript 기반 인터페이스와 각 플랫폼(iOS, Android, React Native, Flutter)에서의 연동 방법을 설명합니다.

2. 웹뷰와 네이티브 간 통신이란?

웹뷰와 네이티브 간 통신은 웹 콘텐츠(JavaScript)와 네이티브 코드(Swift, Kotlin/Java 등)가 서로 데이터를 주고받는 과정을 의미합니다. 이를 통해 웹뷰에서 네이티브 기능을 호출하거나, 네이티브 코드에서 웹뷰를 제어할 수 있습니다.

주요 활용 사례:

웹 콘텐츠에서 네이티브 기능 호출 (예: 카메라, 파일 업로드)

네이티브에서 웹뷰의 JavaScript 함수 실행

웹뷰에서 네이티브로 데이터 전달 (예: 사용자 입력 데이터)

네이티브에서 웹 콘텐츠 업데이트

3. WebView Interface 설계

WebView와 네이티브 간 통신은 JavaScript 함수를 통해 이루어집니다. TypeScript를 사용하여 다음과 같은 인터페이스를 설계할 수 있습니다:

webView 통신 함수(TypeScript)

interface webViewApp {
  foo: () => string;
  bar?: () => Promise<void>;
  getDeviceInfo?: () => Record<string, any>;
}

declare global {
  interface Window {
    webView: webViewApp;
    webKit?: {
      messageHandlers: {
        foo: {
          postMessage: (message: string) => void;
        },
        bar?: {
          postMessage: (message: string) => void;
        };
      };
    };
  }
}

const webViewInterface = {
  foo(): string {
    if (window.webView && typeof window.webView.foo === 'function') {
      return window.webView.foo();
    } else if (
      window.webKit &&
      window.webKit.messageHandlers.foo &&
      typeof window.webKit.messageHandlers.foo.postMessage === 'function'
    ) {
      window.webKit.messageHandlers.foo.postMessage('');
      return 'Message sent to iOS';
    }
    throw new Error('No suitable webView interface available for foo method');
  },
  
  async bar(): Promise<void> {
    if (window.webView && typeof window.webView.bar === 'function') {
      await window.webView.bar();
    } else if (
      window.webKit &&
      window.webKit.messageHandlers.bar &&
      typeof window.webKit.messageHandlers.bar.postMessage === 'function'
    ) {
      window.webKit.messageHandlers.bar.postMessage('');
    } else {
      throw new Error('No suitable webView interface available for bar method');
    }
  },
};
export default webViewInterface;

iOS (Swift WKWebView)

import WebKit

class ViewController: UIViewController, WKScriptMessageHandler {
    override func viewDidLoad() {
        let contentController = WKUserContentController()
        contentController.add(self, name: "foo") // 메시지 핸들러 추가

        let config = WKWebViewConfiguration()
        config.userContentController = contentController

        let webView = WKWebView(frame: self.view.bounds, configuration: config)
        self.view.addSubview(webView)

        let url = URL(string: "https://example.com")!
        webView.load(URLRequest(url: url))
    }

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "foo" {
            print("foo method called from WebView")
        }
    }
}

Android (Kotlin WebView)

import android.webkit.JavascriptInterface
import android.webkit.WebView
import android.webkit.WebViewClient

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val webView = WebView(this).apply {
            settings.javaScriptEnabled = true
            webViewClient = WebViewClient()
            addJavascriptInterface(WebAppInterface(), "webView")
            loadUrl("file:///android_asset/index.html")
        }
        setContentView(webView)
    }

    inner class WebAppInterface {
        @JavascriptInterface
        fun foo() {
            println("foo method called from WebView")
        }
    }
}

React Native

import React from 'react';
import { WebView } from 'react-native-webview';

const App = () => {
  return (
    <WebView
      source={{ uri: 'https://example.com' }}
      onMessage={(event) => {
        if (event.nativeEvent.data === 'foo') {
          alert('foo method called');
        }
      }}
    />
  );
};

export default App;

Flutter

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

class WebViewExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return WebView(
      initialUrl: 'https://example.com',
      javascriptMode: JavascriptMode.unrestricted,
      javascriptChannels: <JavascriptChannel>{
        JavascriptChannel(
          name: 'foo',
          onMessageReceived: (message) {
            print('foo method called from WebView');
          },
        ),
      },
    );
  }
}

4. 결론

WebView와 네이티브 간 통신은 하이브리드 앱 개발에서 필수적입니다. 위에서 설명한 방식으로 통신 인터페이스를 설계하면, iOS, Android, React Native, Flutter 등 다양한 플랫폼에서 일관된 동작을 보장할 수 있습니다. 여러분의 프로젝트에 이 코드를 적용해보세요! 🚀

추가적인 질문이 있다면 댓글로 알려주세요. 😊

profile
3년차 프론트엔드 개발자입니다

0개의 댓글