📚 Reference
📜 Chapter
Web → RN: Next.js 페이지에서 window.ReactNativeWebView.postMessage(...)
Web → RN 송신
// Web → RN: 메시지 전송
(window as any).ReactNativeWebView?.postMessage(
JSON.stringify({ type: 'HELLO_FROM_WEB', payload: { text: 'hello from Web' } })
);
RN → Web 수신
// RN이 보낸 postMessage 수신
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
try {
const msg = JSON.parse(event.data);
if (msg.type === 'PING') {
console.log('RN에서 받은 메시지:', msg.payload);
}
} catch {}
};
window.addEventListener('message', handleMessage);
document.addEventListener('message', handleMessage as any); // 일부 안드로이드 대응
return () => {
window.removeEventListener('message', handleMessage);
document.removeEventListener('message', handleMessage as any);
};
}, []);
RN → Web: RN에서 webViewRef.current.injectJavaScript('...')
또는 webViewRef.current.postMessage(...)
(신버전)로 웹 쪽에 커스텀 이벤트를 발생시키게 하거나, 웹에 만들어 둔 함수 호출
RN → Web 송신
// RN → Web: 메시지 전송
webViewRef.current?.postMessage(
JSON.stringify({ type: 'PING', payload: { text: 'hello from RN' } })
);
Web → RN 수신
// RN에서 Web의 postMessage 수신
<WebView
ref={webViewRef}
source={{ uri: '<https://your-next-app.vercel.app/mobile>' }}
onMessage={(event) => {
const msg = JSON.parse(event.nativeEvent.data);
if (msg.type === 'HELLO_FROM_WEB') {
console.log('웹에서 받은 메시지:', msg.payload);
}
}}
sharedCookiesEnabled
thirdPartyCookiesEnabled
/>
메시지는 문자열만 오가므로 JSON 문자열로 표준화하는 게 좋고, type
/payload
필드 형태의 간단한 프로토콜을 잡아두면 확장/디버그가 쉽다.
{
"type": "EVENT_NAME",
"payload": {
"key": "value"
}
}
type
: 이벤트 이름 (예: PING
, PONG
, OPEN_BOTTOM_SHEET
, UPLOAD_DONE
)payload
: 데이터 본문방향 | 설명 | 주요 코드 |
---|---|---|
RN → Web (송신) | React Native에서 Next.js 웹으로 데이터 전송 | webViewRef.current.postMessage(JSON.stringify(data)) |
Web → RN (송신) | Next.js 웹에서 React Native로 데이터 전송 | window.ReactNativeWebView.postMessage(JSON.stringify(data)) |
RN 수신 | onMessage 이벤트로 웹에서 보낸 데이터 수신 |
onMessage={(e) => handle(JSON.parse(e.nativeEvent.data))} |
Web 수신 | window.addEventListener('message', handler) 로 RN이 보낸 데이터 수신 |
event.data 로 접근 |
// React Native (Expo도 동일)
import React, { useRef, useCallback } from 'react';
import { View } from 'react-native';
import { WebView, WebViewMessageEvent } from 'react-native-webview';
type BusMsg = { type: string; payload?: any };
export default function MyWebView() {
const webRef = useRef<WebView>(null);
// Web -> RN
const onMessage = useCallback((e: WebViewMessageEvent) => {
try {
const msg = JSON.parse(e.nativeEvent.data) as BusMsg;
switch (msg.type) {
case 'HELLO_FROM_WEB':
console.log('웹이 보낸 메시지:', msg.payload);
// 필요시 네이티브 로직 실행 후 웹에 응답
sendToWeb({ type: 'PONG', payload: { ok: true } });
break;
case 'OPEN_BOTTOM_SHEET':
// 예: 바텀시트 열기
break;
}
} catch {
// 문자열이 JSON이 아닐 수도 있으니 방어
console.warn('Invalid message from web:', e.nativeEvent.data);
}
}, []);
// RN -> Web
const sendToWeb = useCallback((msg: BusMsg) => {
// 방법 A) postMessage (권장, 지원되는 버전이라면)
webRef.current?.postMessage(JSON.stringify(msg));
// 방법 B) injectJavaScript 로 message 이벤트 디스패치
// webRef.current?.injectJavaScript(`
// window.dispatchEvent(new MessageEvent('message', { data: ${JSON.stringify(JSON.stringify(msg))} }));
// true;
// `);
}, []);
// 최초 핸드셰이크(웹 DOM 준비 후에 받도록 약간 지연)
const onLoadEnd = () => {
sendToWeb({ type: 'PING', payload: { platform: 'react-native' } });
};
return (
<View style={{ flex: 1 }}>
<WebView
ref={webRef}
source={{ uri: '<https://your-next-app.example.com/mobile>' }}
onMessage={onMessage}
onLoadEnd={onLoadEnd}
// 인증/쿠키 관련 옵션들
sharedCookiesEnabled
thirdPartyCookiesEnabled
// (선택) 외부 링크 열기, 특정 URL 인터셉트 등
// onShouldStartLoadWithRequest={(req) => { ...; return true; }}
/>
</View>
);
}
sharedCookiesEnabled
(iOS), thirdPartyCookiesEnabled
(Android) 켜두는 게 좋다.onShouldStartLoadWithRequest
로 외부 도메인 차단, 화이트리스트 운영.