(Android/안드로이드) WebView JavaScript Bridge 설계 패턴 - AppBridge 구조 설계 가이드

개요


1. 구조


2. 단순 구현 방식 (권장 ❌)

class SimpleBridge {

    @JavascriptInterface
    fun showToast(message: String) {
        Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
    }

    @JavascriptInterface
    fun openCamera() {
        // ...
    }
}

3. AppBridge 설계 목표


4. AppBridge 권장 아키텍처

📌 JS 호출 형식 통일

window.AppBridge.postMessage(JSON.stringify({
    module: "Image",
    method: "showImage",
    params: {
    url: "https://..."
},
    callbackId: "cb_1"
}))

📌 Android 공통 Bridge Entry

class AppBridge(
    private val dispatcher: BridgeDispatcher
) {

    @JavascriptInterface
    fun postMessage(json: String) {
        dispatcher.dispatch(json)
    }
}

📌 Dispatcher 설계

class BridgeDispatcher(
    private val modules: Map<String, BridgeModule>,
    private val webView: WebView
) {

    fun dispatch(json: String) {
        val message = Gson().fromJson(json, BridgeMessage::class.java)

        val module = modules[message.module] ?: return
        val result = module.invoke(message.method, message.params)

        sendCallback(message.callbackId, result)
    }

    private fun sendCallback(callbackId: String?, result: Any?) {
        if (callbackId == null) return

        val js = """
            window.__appBridgeCallback &&
            window.__appBridgeCallback("$callbackId", ${Gson().toJson(result)});
        """.trimIndent()

        webView.post {
            webView.evaluateJavascript(js, null)
        }
    }
}

📌 BridgeMessage 모델

data class BridgeMessage(
    val module: String,
    val method: String,
    val params: JsonObject?,
    val callbackId: String?
)

5. 모듈화 패턴

📌공통 인터페이스

interface BridgeModule {
    fun invoke(method: String, params: JsonObject?): Any?
}

📌ImageModule 예시

class ImageModule : BridgeModule {

    override fun invoke(method: String, params: JsonObject?): Any? {
        return when (method) {
            "showImage" -> {
                val url = params?.get("url")?.asString
                showImage(url)
                mapOf("status" to "ok")
            }
            else -> null
        }
    }

    private fun showImage(url: String?) {
        // 이미지 뷰어 실행
    }
}

등록

val dispatcher = BridgeDispatcher(
    modules = mapOf(
        "Image" to ImageModule(),
        "App" to AppModule()
    ),
    webView = webView
)

webView.addJavascriptInterface(AppBridge(dispatcher), "AppBridge")

6. Reflection 기반 자동 라우팅 (고급)

method 이름과 함수 이름을 자동 매핑 가능:

class ReflectionModule : BridgeModule {

    override fun invoke(method: String, params: JsonObject?): Any? {
        val function = this::class.members
            .firstOrNull { it.name == method } ?: return null

        return function.call(this, params)
    }

    fun showImage(params: JsonObject?) {
        // ...
    }
}

7. 보안 주의사항 (매우 중요)


8. AppBridge 설계 장점

항목 단순 방식 AppBridge 구조
확장성 낮음 높음
모듈화 불가 가능
보안 통제 어려움 가능
유지보수 어려움 용이
콜백 통일 없음 있음


Related Posts