1- import React , { useCallback } from 'react' ;
2- import { SafeAreaView , StyleSheet , Dimensions , Linking } from 'react-native' ;
3- import { WebView } from 'react-native-webview-bootpay' ;
1+ import React , { useCallback , useRef } from 'react' ;
2+ import {
3+ SafeAreaView ,
4+ StyleSheet ,
5+ Dimensions ,
6+ Linking ,
7+ Button ,
8+ View ,
9+ } from 'react-native' ;
10+ import { WebView , WebViewMessageEvent } from 'react-native-webview-bootpay' ;
411
512const { width : deviceWidth , height : deviceHeight } = Dimensions . get ( 'window' ) ;
613
714export default function App ( ) {
15+ const webViewRef = useRef < WebView > ( null ) ;
16+
817 const handleError = useCallback ( ( error ) => {
918 console . error ( 'WebView Error:' , error ) ;
1019 } , [ ] ) ;
1120
12- const handleMessage = useCallback ( ( message ) => {
21+ const handleMessage = useCallback ( ( message : WebViewMessageEvent ) => {
1322 console . log ( 'WebView Message:' , message . nativeEvent . data ) ;
23+ try {
24+ // 메시지 데이터 파싱
25+ const data = JSON . parse ( message . nativeEvent . data ) ;
26+ // 메시지 타입에 따른 처리
27+ if ( data . type === 'webToApp' ) {
28+ // 웹에서 앱으로 보낸 메시지 처리
29+ alert ( `웹에서 받은 메시지:\n${ data . message } ` ) ;
30+ } else {
31+ // 기타 메시지 타입 처리
32+ console . log ( '메시지 타입:' , data . type ) ;
33+ }
34+ } catch ( error ) {
35+ console . error ( '메시지 파싱 에러:' , error ) ;
36+ }
1437 } , [ ] ) ;
1538
1639 const shouldStartLoadWithRequest = useCallback ( ( event ) => {
@@ -23,17 +46,126 @@ export default function App() {
2346 return true ;
2447 } , [ ] ) ;
2548
49+ const sendMessageToWeb = useCallback ( ( ) => {
50+ console . log ( 'Sending message to web...' ) ;
51+ if ( webViewRef . current ) {
52+ try {
53+ const message = JSON . stringify ( {
54+ type : 'greeting' ,
55+ message : '안녕하세요! 네이티브 앱에서 보낸 메시지입니다.' ,
56+ } ) ;
57+ console . log ( 'Message content:' , message ) ;
58+ webViewRef . current . postMessage ( message ) ;
59+ console . log ( 'Message sent successfully' ) ;
60+ } catch ( error ) {
61+ console . error ( 'Error sending message:' , error ) ;
62+ }
63+ } else {
64+ console . error ( 'WebView reference is not available' ) ;
65+ }
66+ } , [ ] ) ;
67+
68+ // JS 주입: 웹에서 postMessage 처리하는 스크립트
69+ const injectedJavaScript = `
70+ (function() {
71+ // BootpayRNWebView 인터페이스 확인 및 설정
72+ window.BootpayRNWebView = window.BootpayRNWebView || {};
73+
74+ // 디버깅용 로그
75+ console.log('BootpayRNWebView 인터페이스 존재 여부:', !!window.BootpayRNWebView);
76+
77+ // 웹→앱 메시지 전송 함수 구현
78+ if (!window.BootpayRNWebView.postMessage) {
79+ window.BootpayRNWebView.postMessage = function(data) {
80+ console.log('웹에서 앱으로 메시지 전송:', data);
81+ window.postMessage(data, '*');
82+ };
83+ }
84+
85+ // 웹 페이지에 메시지 전송 버튼 추가
86+ const createMessageButton = function() {
87+ const buttonContainer = document.createElement('div');
88+ buttonContainer.style.position = 'fixed';
89+ buttonContainer.style.bottom = '70px';
90+ buttonContainer.style.left = '50%';
91+ buttonContainer.style.transform = 'translateX(-50%)';
92+ buttonContainer.style.backgroundColor = '#007bff';
93+ buttonContainer.style.color = 'white';
94+ buttonContainer.style.padding = '10px 15px';
95+ buttonContainer.style.borderRadius = '5px';
96+ buttonContainer.style.zIndex = '9999';
97+ buttonContainer.style.cursor = 'pointer';
98+ buttonContainer.innerText = '앱으로 메시지 보내기';
99+
100+ buttonContainer.addEventListener('click', function() {
101+ try {
102+ window.BootpayRNWebView.postMessage(JSON.stringify({
103+ type: 'webToApp',
104+ message: '웹에서 앱으로 보낸 메시지입니다!'
105+ }));
106+ } catch(e) {
107+ alert('메시지 전송 실패: ' + e.message);
108+ }
109+ });
110+
111+ document.body.appendChild(buttonContainer);
112+ };
113+
114+ // 앱→웹 메시지 수신 처리
115+ const messageHandler = function(event) {
116+ console.log('메시지 수신:', event.data);
117+ try {
118+ // 문자열이면 JSON 파싱
119+ let data = event.data;
120+ if (typeof data === 'string') {
121+ data = JSON.parse(data);
122+ }
123+
124+ // 메시지 타입에 따른 처리
125+ if (data.type === 'greeting' || data.type === 'pageLoaded') {
126+ alert('앱에서 온 메시지: ' + data.message);
127+ } else if (data.what === 'clearSelection') {
128+ window.getSelection()?.removeAllRanges();
129+ }
130+ } catch(e) {
131+ console.error('메시지 처리 오류:', e);
132+ }
133+ };
134+
135+ // 메시지 이벤트 리스너 등록
136+ window.addEventListener('message', messageHandler);
137+ document.addEventListener('message', messageHandler);
138+
139+ // DOM이 로드된 후 버튼 생성
140+ if (document.readyState === 'loading') {
141+ document.addEventListener('DOMContentLoaded', createMessageButton);
142+ } else {
143+ createMessageButton();
144+ }
145+
146+ // 페이지 로드 완료 알림
147+ console.log('웹뷰 스크립트 초기화 완료');
148+
149+ return true;
150+ })();
151+ ` ;
152+
26153 return (
27154 < SafeAreaView style = { styles . container } >
28155 < WebView
156+ ref = { webViewRef }
29157 source = { { uri : 'https://dev-js.bootapi.com/test/payment/' } }
30158 startInLoadingState
31159 scalesPageToFit
32160 style = { styles . webview }
33161 onError = { handleError }
34- onMessage = { handleMessage }
162+ onMessage = { handleMessage }
35163 onShouldStartLoadWithRequest = { shouldStartLoadWithRequest }
164+ injectedJavaScript = { injectedJavaScript }
36165 />
166+ < View style = { styles . buttonContainer } >
167+ < Button title = "웹으로 메시지 보내기" onPress = { sendMessageToWeb } />
168+ </ View >
37169 </ SafeAreaView >
38170 ) ;
39171}
@@ -46,6 +178,11 @@ const styles = StyleSheet.create({
46178 } ,
47179 webview : {
48180 width : deviceWidth ,
49- height : deviceHeight ,
181+ height : deviceHeight - 50 , // 버튼을 위한 공간 확보
182+ } ,
183+ buttonContainer : {
184+ position : 'absolute' ,
185+ bottom : 20 ,
186+ width : '90%' ,
50187 } ,
51188} ) ;
0 commit comments