이번에는 RNCWebViewManagerImpl 파일을 좀 더 자세히 살펴보자.
이전 글에서 설명했다시피 RNCWebViewManager에서는 UI와 관련된 기능 및 웹뷰 개별 인스턴스로 관리해야 하는 기능들이 들어가 있다고 했다.
그래서 우리가 웹뷰 컴포넌트를 사용하며 너무나 자주 사용하는 injectJavascript, postMessage, onMessage와 같은 기능들이 다 여기 들어가 있는 것이다.
우리가 알아볼 코드는 아래 링크를 접속하면 볼 수 있다.
일단 먼저 알아야 하는 개념이 있다.
어떻게 react native WebView 컴포넌트에서 메서드를 호출하면 네이티브에서도 동작할지? 내부 로직에 대해서 알아보자.
다시 WebView.android.tsx 파일로 돌아가자.
https://github.com/react-native-webview/react-native-webview/blob/master/src/WebView.android.tsx
아래 파일에서 주목해야 할 부분은 다음과 같다.
useImperativeHandle 훅은 다 알겠지만, ref를 사용할 때 부모 컴포넌트가 호출할 수 있는 메서드만을 정의해 두는 것이다.
훅 하나하나를 살펴보면 Commands라는 걸 사용한다.
useImperativeHandle(
ref,
() => ({
goForward: () =>
webViewRef.current && Commands.goForward(webViewRef.current),
goBack: () => webViewRef.current && Commands.goBack(webViewRef.current),
reload: () => {
setViewState('LOADING');
if (webViewRef.current) {
Commands.reload(webViewRef.current);
}
},
stopLoading: () =>
webViewRef.current && Commands.stopLoading(webViewRef.current),
postMessage: (data: string) =>
webViewRef.current && Commands.postMessage(webViewRef.current, data),
injectJavaScript: (data: string) =>
webViewRef.current &&
Commands.injectJavaScript(webViewRef.current, data),
requestFocus: () =>
webViewRef.current && Commands.requestFocus(webViewRef.current),
clearFormData: () =>
webViewRef.current && Commands.clearFormData(webViewRef.current),
clearCache: (includeDiskFiles: boolean) =>
webViewRef.current &&
Commands.clearCache(webViewRef.current, includeDiskFiles),
clearHistory: () =>
webViewRef.current && Commands.clearHistory(webViewRef.current),
}),
[setViewState, webViewRef]
);
import RNCWebView, { Commands, NativeProps } from './RNCWebViewNativeComponent';
export const Commands = codegenNativeCommands<NativeCommands>({
supportedCommands: [
'goBack',
'goForward',
'reload',
'stopLoading',
'injectJavaScript',
'requestFocus',
'postMessage',
'loadUrl',
'clearFormData',
'clearCache',
'clearHistory',
],
});
codegenNativeCommands를 통해 명령들을 정의했고 해당 명령을 호출하면 네이티브 모듈로 명령이 전달된다.
받은 명령을 처리하는 건 또 네이티브 모듈 몫인데 아래 코드에서 receiveCommand 함수를 보면 된다.
@Override
public void receiveCommand(@NonNull RNCWebViewWrapper reactWebView, String commandId, @Nullable ReadableArray args) {
mRNCWebViewManagerImpl.receiveCommand(reactWebView, commandId, args);
super.receiveCommand(reactWebView, commandId, args);
}
fun receiveCommand(viewWrapper: RNCWebViewWrapper, commandId: String, args: ReadableArray) {
val webView = viewWrapper.webView
when (commandId) {
"goBack" -> webView.goBack()
"goForward" -> webView.goForward()
"reload" -> webView.reload()
"stopLoading" -> webView.stopLoading()
"postMessage" -> try {
val eventInitDict = JSONObject()
eventInitDict.put("data", args.getString(0))
webView.evaluateJavascriptWithFallback(
"(function () {" +
"var event;" +
"var data = " + eventInitDict.toString() + ";" +
"try {" +
"event = new MessageEvent('message', data);" +
"} catch (e) {" +
"event = document.createEvent('MessageEvent');" +
"event.initMessageEvent('message', true, true, data.data, data.origin, data.lastEventId, data.source);" +
"}" +
"document.dispatchEvent(event);" +
"})();"
)
} catch (e: JSONException) {
throw RuntimeException(e)
}
"injectJavaScript" -> webView.evaluateJavascriptWithFallback(args.getString(0))
"loadUrl" -> {
if (args == null) {
throw RuntimeException("Arguments for loading an url are null!")
}
webView.progressChangedFilter.setWaitingForCommandLoadUrl(false)
webView.loadUrl(args.getString(0))
}
"requestFocus" -> webView.requestFocus()
"clearFormData" -> webView.clearFormData()
"clearCache" -> {
val includeDiskFiles = args != null && args.getBoolean(0)
webView.clearCache(includeDiskFiles)
}
"clearHistory" -> webView.clearHistory()
}
}
여기서 받은 명령을 처리하게 되는데, 여기서의 동작 구조도 이전 글을 이해했으면 쉽게 이해할 수 있을 거 같다.
여기까지만 하면 진짜 얼마 안 남았다.
postMessage
"postMessage" -> try {
val eventInitDict = JSONObject()
eventInitDict.put("data", args.getString(0))
webView.evaluateJavascriptWithFallback(
"(function () {" +
"var event;" +
"var data = " + eventInitDict.toString() + ";" +
"try {" +
"event = new MessageEvent('message', data);" +
"} catch (e) {" +
"event = document.createEvent('MessageEvent');" +
"event.initMessageEvent('message', true, true, data.data, data.origin, data.lastEventId, data.source);" +
"}" +
"document.dispatchEvent(event);" +
"})();"
)
} catch (e: JSONException) {
throw RuntimeException(e)
}
receiveCommand 함수에서 postMessage라는 Command를 받게 되면 위 로직을 실행한다.
이는 네이티브 모듈에서 수신된 메시지를 웹뷰 내의 자바스크립트 코드로 전달하기 위한 코드이다.
postMessage 명령 수신 시 args 배열에서 메시지 데이터를 가져와서 eventInitDict에 저장한다.
이후 자바스크립트 코드 문자열을 생성해 evaluateJavascriptWithFallback 함수를 호출한다.
이 함수는 네이티브 측에서 자바스크립트 코드를 실행하기 위해서 호출한다.
자바스크립트 코드에서는 MessageEvent를 생성하고 이를 dispatch해서 웹뷰 내에서 message 이벤트를 트리거하게 된다.
사실 나머지 메서드들이야 뭐 위 내용들 다 이해했으면 문제 없이 이해될 거 같아서 생략해 버렸다.
'프로젝트 개발일지 > webview-iloveu' 카테고리의 다른 글
[webview-iloveu] 안드로이드 딥링크 구현해보기 - 1 (url 스킴과 앱 링크) (0) | 2024.09.14 |
---|---|
[webview-iloveu] react-native-webview 구조 분석 - 1 (0) | 2024.07.05 |