앞으로 React Native를 앱, 웹뷰를 웹이라 칭하겠습니다.

1. 앱 → 웹으로 메시지 전송

1. 앱 : 메시지 송신

export default function sendMessageToWebview(
  webViewRef: RefObject<WebView>,
  message: {
    type: string;
    data: any;
  },
) {
  webViewRef.current?.postMessage(JSON.stringify(message));
}

webViewRef를 이용하여 웹뷰에 메시지를 전송한다.. .

<aside> ❓ webViewRef?

여기서 webViewRef는 WebView컴포넌트에 넘기는 ref객체이다.

const webViewRef = useRef(null);
// ..
return <WebView ref={webViewRef} />

</aside>

2. 웹 : 메시지 수신

// hooks/useListenMessageToReactNative.ts
export default function useListenMessageToReactNative() {
  const isapp = getIsApp();
  useEffect(() => {
    if (!isapp) return;
    const listener = async (event: any) => {
      const { type, data } = JSON.parse(event.data);
      alert('test');
      switch (type) {
        case 'FCM_TOKEN':
          postFCMToken({ token: data });
          break;
        case 'TOKEN': {
          const { accessToken, refreshToken, userId } = data;
          setTokenAtCookie({ accessToken, refreshToken, userId });
          break;
        }
      }
    };

    document.addEventListener('message', listener);
    window.addEventListener('message', listener);
    return () => {
      document.removeEventListener('message', listener);
      window.removeEventListener('message', listener);
    };
  });
}

컴포넌트가 마운트되었을 때, 즉 클라이언트에서만 동작을 하도록 하기 위해 useEffect훅을 사용했다. document 혹은 window에 event를 추가하는 방식이기에 클라이언트에서만 동작을 해야한다.

<aside> ❓ document와 window 두 개에 event를 추가하는 이유는?

<aside> 📚 document와 window의 차이

  1. window : 브라우저 탭에 존재하는 Javascript 전역 최상위 객체

    Screenshot 2024-01-30 at 9.56.47 PM.png

  2. document : 잠재적으로 보여질 수 있는 돔에 대한 정보가 담겨있다.

    Screenshot 2024-01-30 at 9.56.58 PM.png

</aside>

최상위 객체가 Android 웹뷰는 document, iOS 웹뷰는 window이기 때문에, 둘 모두 추가해야한다.

</aside>

<aside> ❓ return() ⇒ {} 안에서 remove로 제거하는 이유는?

useEffect()의 콜백 함수 내부의 return함수는 컴포넌트가 언마운트될 때 실행된다. 컴포넌트가 언마운트 될 때, 추가한 event를 제거함으로써 부수 효과를 줄이기 위함이다. 계속해서 API 호출, 이벤트 리스너가 추가되면 부수효과가 생기고, 이러한 것이 계속되면 불필요한 리소스 사용이나 메모리 누수를 초래할 수 있다.

</aside>

// app/template.tsx
export default function Template({ children }) {
  useListenMessageToReactNative();
  return children;
}

<aside> 📚 template.js(tsx)

template.js는 layout과 유사하게 모든 layout과 페이지를 감싼다. layout과 달리 template은 자식요소마다 새로운 인스턴스를 생성한다.

useEffect, useState에 의존하는 경우 사용한다.

</aside>

훅을 layout에서 사용할 수 없다. 그렇기에, 훅을 사용할 수 있는 template.js에서 useListenMessageToReactNative을 호출한다.

2. 웹 → 앱으로 메시지 전송

웹 : 메시지 송신

const sendMessageToReactNative = ({ type, data }: SendMessageToReactNativeProps) => {
  window.ReactNativeWebView &&
    window.ReactNativeWebView.postMessage(
      JSON.stringify({
        data,
        type,
      })
    );
};

export default sendMessageToReactNative;