ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • React Native WebView 에서 window.open 핸들링
    Tech 2024. 5. 15. 21:26

    최근 React Native을 개발하고 있다가, 어떠한 요구사항으로 웹뷰를 만들었는데, 웹뷰 안에 이벤트를 핸들링해야 하는 일이 생겼다.

     

    혹시라도 나와 같은 사람이 있을까봐 게시물로 작성.. 먼저 방법은 2개입니다.

     

     

    1. 그런 상황을 만들지 않기

    뭔 개소리냐 하겠지만.. 일단 네이티브 iOS Android 둘 다 정책적으로 있긴 합니다.

    근데 브라우저 앱을 만들지 않는 이상 단순히 보이는 화면에 웹뷰 콘텐츠가 추가된 (내경우는 게시물) 정도인데

    이거를 여러 개 띄운다는 개념을 초반부터 심어 놓지 않는다면 약간 애매한 문제가 발생합니다.

     

    물론 이는 같은 페이지에서 이동으로 강제 대체등 있으니,

    개념단위에서 아예 이런 구조를 불가능하게 (제한적인 샌드박스)로 운영할 건지 아님 예외를 대응할 건지를 고민을 해야 합니다.

     

    2. 지원하기

    근데 현실은 해야 할 때는 해야 합니다, 예를 들어..

     

    "공지사항을 볼 때 이벤트 참여를 누르면 사용자당 고유 URL로 를 가지고 외부페이지로 이동하여 이벤트 참여"

    + " 해당 링크는 업체에서 제공함 우리가 핸들링 불가능 ^^"

     

    뭔가 안 되는데요? 들었을 때 머리가 아파진다.. 해서 다음과 같이 찾아봄

     


     

    리서치

     

     

    관련된 메서드는onOpenWindow, JavascriptCanOpenwindowsAutomatically, setSupportMultipleWindows 3개

     

     

     

     

     

    찾았으니 분석을 해보자. 해보자.

     

    1. Android

     

    먼저 안드로이드 전용 옵션인 setSupportMultipleWindows 기능을 찾아보니

     

    '팝업이 가능' 한 거지 이걸 핸들링할 수 있는 건 없어 보인다.

     

    출처: https://m.blog.naver.com/hando1220/221884820467

     

    그럴 수밖에 없는 게 이 코드는 그냥 webview settings에 flag 넣어주는 게 전부다..

     

     

     

    JavascriptCanOpenwindowsAutomatically 도 마찬가지 허용해 주는 거지 무언가 콜백을 유도되는 구조가 아님.

    https://github.com/react-native-webview/react-native-webview/blob/4e1ede4ba3cfc770f9d4a2485c4850c4f3f1a708/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt#L690

     

     

    마지막으로 onOpenWindow을 찾아가 보니

     

     

    플래그에 따라서 처리만 하는 로직이 있다. 특정 도메인이나 이런 걸 받아서 체크하는 게 아니라.. 그냥 쓰기는 어려워 보인다. 

     

    2. iOS

     

    우선 위에도 있는 JavascriptCanOpenwindowsAutomatically 코드를 찾아보면 WKPreferences로 연결되어있다.

     

     

    하지만 이것도 팝업을 열수 있도록 허용하는 플래그 정도지 (팝업차단 방지 같은..) 실제 팝업을 핸들링하지 않다.

     

    onOpenWindow을 찾아보도록 하자

     

     

    iOS는 그냥 nil 처리하고 있다. 안드로이드는 처리하든 말든 true 주고.. 

     

    결국 여기서 찾은 답은 뭔가

     

    - 시스템적으로 웹뷰에 옵션은 줄 수 있으나

    - 상세하게 직접 컨트롤은 불가능하다.

     

    에 도달했다. 그리고 안된다고 말하면? 아마 내 월급도 안될 거라고 말할 것이다. 해결책을 찾아야 한다.

     

     


    해결방안

     

    1. 리서치 결과로 봤을 때는 기본 제공 설정으로는 커스텀하기 어려움

    2. <a href="#" target="_blank"> 인경우는 어차피 _blank가 부서지면 자체로 이동하기 때문에 '일반 페이지 이동으로' 간주해서 처리할 수 있는 여지가 있음

    3. 하지만 window.open는 처리가 어려움 <- 오늘의 주제

    슬슬 짜증나기 시작...

     

     

    그래서 찾은 방안!
    window.open을 재구성, 말 그대로 window.open 을 재할당 해버립시다.

     

    기본 옵션으로 injectedJavascript이 있습니다. 여기서는 브라우저에서 임의 스크립트가 실행 가능합니다. 마치 크롬 익스텐션류 같은..

     

    - injectedJavaScriptBeforeContentLoaded는 콘텐츠 로드 전에 하는 거라 로직오류가 발생할 수 있어서

    - 웹페이지 로드 후 스크립트 삽입하는 injectedJavascript을 썼습니다.

     

    window.open = function(url, target, windowFeatures) {
      window.ReactNativeWebView.postMessage(JSON.stringify({ 'contentType': 'newtab', url }));
    }

     

    을 하면 onMessage에서 { contentType: 'newtab', url: 'https://~~' }을 받을 수 있습니다.

     

    해결완료!

     

     

    ....

     

    가 아니었고 iOS에서 문제가 발생했는데, 외부 업체에서 iframe을 시전 해서 문제가 발생..

    참고로 injectedJavaScriptForMainFrameOnly을 쓰면 MainFrame이 아닌 iframe에도 window.open을 재할당 할 수 있습니다. 

     

    문제는 window.ReactNativeWebView는 root window에서만 존재하고

    window.top으로 접근 시 Security Error (iframe -> parant access) 발생합니다.

     

     

    그래서 추가 고안.. 은 아예 페이지를 이동하는데 웹뷰만 알아먹게 하는 것

    window.open = function(url, target, windowFeatures) {
      if (url.startsWith('https://')) location.href = 'newtabs://' + url.slice(8)
      else if (url.startsWith('http://')) location.href = 'newtab://' + url.slice(7)
      else if (url.startsWith('//')) location.href = `newtabs://${location.hostname}/${url.slice(2)}`
    }

     

    이렇게 변경하고,

    <WebView
    originWhitelist={['newtab://*', 'newtabs://*']}
    ...
    />

     

    추가해 줍니다.

     

    그런 뒤 onShouldStartLoadWithRequest에서 newtab, newtabs을 받아서 도메인 확인해서 내부 처리를 하던 지지고 볶으면 됩니다

    여기서는 return boolean이라 특정 도메인인 경우 처리 안 함도 가능합니다.


    요약

     

     

    1. react-native-webview에 window.open 핸들링은 기본으로 불가

    2. window.open을 재할당을 하는 방법으로 가능

    3. 비슷하게 window.alert 같은 것도 가능.. 근데 다만 최대한 그런 상황을 방지하는 게 좋음. 웹은 예외 상황이 너무 많이 발생함.

    댓글

Designed by Tistory.