웹뷰를 구현하다 보면 단순히 웹 페이지를 띄우는 것 외에 통신하며 데이터를 주고 받는 동작도 필요합니다. 그렇다면 웹뷰와 앱은 어떻게 통신하게 될까요?
안드로이드의 네이티브 코드와 웹 페이지는 서로 분리된 환경이기 때문에 상호간의 통신을 위한 연결 다리가 필요한데, 브릿지가 그러한 역할을 합니다. ✨
즉, Bridge(브릿지)란 안드로이드 웹뷰의 통신을 위해 만들어진 JavaScript용 Interface라고 할 수 있습니다. 안드로이드와 웹뷰는 각자의 환경에 존재하는 메소드를 직접 호출할 수 없기 때문에 브릿지를 통해 호출합니다.
안드로이드 ↔ 웹뷰 통신을 위해 호출되는 함수들을 정의할 AndroidBridge
라는 클래스를 만들어보겠습니다.
class AndroidBridge(private val context: Context) {
...
}
그리고 이 인터페이스를 사용하기 위해 addJavascriptInterface
를 사용해 웹뷰에 인터페이스를 붙여줘야 합니다. 해당 브릿지 클래스의 인스턴스와 호출되는 데에 사용할 이름 문자열을 전달해줍니다.
addJavascriptInterface(AndroidBridge(context), "AndroidBridge")
🤔 웹뷰에서 안드로이드 토스트를 띄워줘야 하는 상황이 있다고 가정해봅시다.
그렇다면 안드로이드에서 토스트를 띄우는 함수를 정의하고, 이 함수를 웹뷰에서 호출할 수 있도록 해줘야겠죠 ? 아래와 같이 아까 정의한 AndroidBridge 클래스 안에 토스트를 띄우는 함수를 정의해보겠습니다.
class AndroidBridge(private val context: Context) {
@JavascriptInterface
fun showToast(toast: String) {
Toast.makeText(context, toast, Toast.LENGTH_LONG).show()
}
}
💡 @JavascriptInterface ?
여기서 함수 위에 붙은@JavascriptInterface
은 해당 함수를 자바 스크립트에서 이용할 수 있도록 만들어주는 어노테이션입니다.
<body>
<h1>WebviewSample</h1>
<button onclick="showAndroidToast('Hello Android from Web')">안드로이드 토스트 띄우기</button>
<script>
function showAndroidToast(toast) {
AndroidBridge.showToast(toast)
}
</script>
</body>
웹뷰에서는 위와 같이 버튼을 클릭해 안드로이드 토스트를 띄우는 함수를 실행시킬 수 있습니다. 아까 인터페이스를 웹뷰에 붙여줄 때 지정한 AndroidBridge와 함께 사용할 함수를 호출해주면 됩니다.
🤔 안드로이드에서 문자열을 전달해 웹뷰에서 알럿을 띄워야하는 상황이 있다고 가정해봅시다. (샘플을 위한 예시 상황인지라 어색한 감이 있긴 하네요.💦)
<script>
function showWebViewAlert(text) {
alert(text);
}
</script>
웹 쪽에는 위와 같이 문자열을 받아 알럿을 띄울 수 있는 함수가 선언되어 있습니다.
class AndroidBridge(
private val context: Context,
private val webView: WebView,
) {
...
fun showWebViewAlert(text: String) {
val script = "window.showWebViewAlert('$text');"
evaluateWebViewFunction(script) { result ->
Log.d("tag_test", result)
}
}
private fun evaluateWebViewFunction(
script: String,
callback: ((String) -> Unit)? = null,
) {
return webView.evaluateJavascript(script, callback)
}
}
그리고 안드로이드에서는 웹의 showWebViewAlert 함수를 호출할 수 있는 함수를 브릿지 클래스 안에 정의해줍니다.
evaluateWebViewFunction
는 웹뷰의 자바 스크립트 함수를 실행하기 위해 중복적으로 호출되는 evaluateJavascript
를 래핑한 함수입니다.
위의 코드에서는 evaluateJavascript()에 웹뷰에서 알럿을 띄우기 위해 텍스트를 받아 showWebViewAlert를 실행하는 스크립트와, 콜백에 대한 결과값을 로그로 출력하는 동작을 전달했습니다.
💡 evaluateJavascript ?
웹의 함수를 호출하려면 evaluateJavascript()를 사용해야 합니다.public void evaluateJavascript (String script, ValueCallback<String> resultCallback)
비동기로 자바스크립트 함수를 실행할 수 있습니다.
- 첫 번째 인자인 script에는 실행할 JavaScript 코드를 문자열로 전달하고
- 두 번째 인자인 resultCallBack에는 필요 시 스크립트 실행 후 반환 값을 다루는 로직을 전달할 수 있습니다.
콜백을 코루틴을 이용한 suspend function으로 바꿔 활용할 수도 있습니다.
suspend fun showWebViewAlertWithCoroutine(text: String) {
val script = "window.showWebViewAlert('$text');"
val result = evaluateWebViewFunctionWithCoroutine(script)
Log.d("tag_test", result)
}
private suspend fun evaluateWebViewFunctionWithCoroutine(script: String): String {
return suspendCoroutine { cont ->
webView.evaluateJavascript(script) { result ->
cont.resume(result)
}
}
}
다음 실행 화면은 버튼 클릭 이벤트로 ‘안드로이드’라는 문자열을 전달해 showWebViewAlert()를 실행한 모습입니다.
간단하고, 잘 쓰이지 않는 예시 상황이었을 수 있지만 기본적인 동작들을 통해 안드로이드와 웹뷰 간 상호작용이 어떻게 일어나는지 짚고 넘어가 볼 수 있어 좋았습니다 ! 편의성을 위해 기본 함수들을 커스텀하게 래핑해서 사용하다보면 래핑한 윗단의 함수들만 사용하게 되고, 그 속을 이루고 있는 로우 레벨의 기본 함수들이 어떤 것들이 있고 어떤 일을 하는지 잊기 쉬운 것 같습니다. 꽤나 많은 것을 놓치고 써왔음에 반성하는 마음이 들기도 하네요 😓 그래도 이렇게라도 되짚어보니 뿌듯한 마음도 듭니다!
좋은 글 감사합니다!