일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- picker
- androidstudio
- ReactNative
- localserver
- linux
- avds
- Chrome
- node
- 맥
- centos
- webpack
- 개발
- VirtualBox
- react
- xcode
- 센토스
- qunit
- MAC
- 리눅스
- IOS
- Android
- unittest
- MachineLearning
- TensorFlow
- PYTHON
- 네트워크
- jest
- build
- vsCode
- Today
- Total
로메오의 블로그
[React Native] Webview와 interface 하기 본문
react native에서 webview를 만들고 javascript와 react native간 interface를 해보겠습니다.
web소스는 firebase hosting을 이용합니다.
위 포스트에서 firebase hosting으로 hello world를 찍고 오시기 바랍니다.
프로젝트 생성
$ cd /my/project/path
$ react-native init secondProject --version react-native@0.59.8
$ cd secondProject
$ code .
secondProject 프로젝트를 생성합니다.
react-native 버전은 0.59.8로 설치합니다.
(최신버전인 0.60을 설치했는데, android에서 webview가 제대로 작동 안하네요. -_-
니네 테스트 제대로 안하고 출시하냐?)
VSCode로 프로젝트를 열어서 터미널 화면을 두개로 분할합니다.
한쪽 터미널에는 react-native start 명령을 하시고
다른 한쪽은 react-native run-ios 명령을 합니다.
$ react-native start --reset-cache
$ react-native run-ios
보이는 메인화면은 react-native 0.60 메인입니다. (0.59.8은 다르게 생겼어요.)
webview 설치
$ npm install react-native-webview
$ react-native link react-native-webview
$ cd ios
$ pod install
일반적으로 link만 잡아주면 plugin 설치에 문제없는데, react-native 0.6부터는 webview를 사용하기 위해서 pod install을 해줘야 하네요.
App.js
import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
import { WebView } from 'react-native-webview'
const domain = 'https://firstproject-a04bf.firebaseapp.com/'
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
indexPage: { uri: domain + '?ver=1' }
};
}
render() {
return (
<View style={styles.container}>
<WebView
style={styles.webview}
source={this.state.indexPage}
originWhitelist={['*']}
ref={webview => this.appWebview = webview}
javaScriptEnabled={true}
useWebKit={true}
/>
</View>
);
}
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 30
},
webview: {
flex: 1
}
});
기존코드를 다 지우고 위와 같이 코딩 합니다.
$ react-native run-ios
webview에 firebase의 firstProject가 표시됩니다.
화면 수정하기
화면에서 버튼을 누르면 네이티브에서 fetch 통신을 해서 결과를 다시 화면에 넘겨주는 로직을 구현합니다.
firebase의 firstProject에서 코딩하겠습니다.
$ pwd
/my/project/path/firstProject
$ touch public/index.js
$ touch public/webViewBridge.js
$ touch public/movie.json
public/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Welcome to Firebase Hosting</title>
<!-- update the version number as needed -->
<script defer src="/__/firebase/6.2.4/firebase-app.js"></script>
<!-- include only the Firebase features as you need -->
<script defer src="/__/firebase/6.2.4/firebase-auth.js"></script>
<script defer src="/__/firebase/6.2.4/firebase-database.js"></script>
<script defer src="/__/firebase/6.2.4/firebase-messaging.js"></script>
<script defer src="/__/firebase/6.2.4/firebase-storage.js"></script>
<!-- initialize the SDK after all desired features are loaded -->
<script defer src="/__/firebase/init.js"></script>
</head>
<body>
<h1>Hello world</h1>
<button id="btnSend">통신하기</button>
<p id="result"></p>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="webViewBridge.js?ver=4"></script>
<script src="index.js?ver=4"></script>
</body>
</html>
public/index.js
(function () {
// react-native에 통신을 요청한다.
$('#btnSend').on('click', function() {
var option = {
url: '/movie.json'
}
// "getMovieList"는 react-native에서 받는 메서드 이름입니다.
window.webViewBridge.send('getMovieList', option, function(res) {
$('#result').html(JSON.stringify(res))
}, function(err) {
console.error(err)
})
})
}());
public/webViewBridge.js
(function () {
var promiseChain = Promise.resolve();
var callbacks = {};
var init = function () {
// 유니크한 아이디를 생성한다.
// native에서 callback 받을때 id의 callback을 호출한다.
const guid = function () {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
}
return s4() + s4() + "-" + s4() + "-" + s4() + "-" + s4() + "-" + s4() + s4() + s4();
}
/**
* javascript => react-native
* javascript에서 react-native에 메세지를 보낸다.
*/
window.webViewBridge = {
send: function (targetFunc, data, success, error) {
var msgObj = {
targetFunc: targetFunc,
data: data || {}
};
if (success || error) {
msgObj.msgId = guid();
}
var msg = JSON.stringify(msgObj);
promiseChain = promiseChain.then(function () {
return new Promise(function (resolve, reject) {
console.log("react native에 메세지를 보냄 " + msgObj.targetFunc);
if (msgObj.msgId) {
callbacks[msgObj.msgId] = {
onsuccess: success,
onerror: error
};
}
window.ReactNativeWebView.postMessage(msg);
resolve();
})
}).catch(function (e) {
console.error('메세지 실패 ' + e.message);
});
},
};
/**
* react-native => javascript
* react native에서 화면에 결과를 넘겨준다.
*/
window.document.addEventListener('message', function (e) {
console.log("react native에서 메세지를 받음", JSON.parse(e.data));
var message;
try {
message = JSON.parse(e.data)
}
catch (err) {
console.error("메세지를 파싱할수 없음 " + err);
return;
}
// callback을 트리거한다.
if (callbacks[message.msgId]) {
if (message.isSuccessfull) {
callbacks[message.msgId].onsuccess.call(null, message);
} else {
callbacks[message.msgId].onerror.call(null, message);
}
delete callbacks[message.msgId];
}
});
}
init();
}());
public/movie.json
{
"title": "The Basics - Networking",
"description": "Your app fetched this from a remote endpoint!",
"movies": [
{ "id": "1", "title": "Star Wars", "releaseYear": "1977" },
{ "id": "2", "title": "Back to the Future", "releaseYear": "1985" },
{ "id": "3", "title": "The Matrix", "releaseYear": "1999" },
{ "id": "4", "title": "Inception", "releaseYear": "2010" },
{ "id": "5", "title": "Interstellar", "releaseYear": "2014" }
]
}
아름다운 화면이 완성되었습니다.
firebase에 배포합니다.
firebase deploy
$ firebase deploy
react native에서 interface 코딩
react native에서 새로고침 하면 firebase 화면이 바뀝니다.
캐시때문에 변경이 안되면 url에 parameter를 주면 cache가 변경됩니다.
constructor(props) {
super(props);
this.state = {
indexPage: { uri: domain + '?ver=2' }
};
}
webview에 onMessage 속성을 추가합니다
<WebView
....
onMessage={this.onWebViewMessage}
....
/>
onWebViewMessage 메서드를 만듭니다.
import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
import { WebView } from 'react-native-webview'
export default class App extends Component {
....
/**
* 화면에서 post를 던지면 react-native에서 받음
*/
onWebViewMessage = event => {
console.log('onWebViewMessage', JSON.parse(event.nativeEvent.data))
let msgData;
try {
msgData = JSON.parse(event.nativeEvent.data) || {}
} catch (error) {
console.error(error)
return
}
this[msgData.targetFunc].apply(this, [msgData]);
}
/**
* 영화목록을 받아와서 화면에 전달한다.
*/
getMovieList = msgData => {
const option = {
method: 'GET',
timeout: 60000
}
let url = domain + msgData.data.url
fetch(url, option)
.then(res => {
return res.json()
})
.then(response => {
console.log('<====== response', response)
msgData.isSuccessfull = true
msgData.data = response
this.appWebview.postMessage(JSON.stringify(msgData), '*');
})
.catch(error => {
console.log(error)
})
}
....
};
전체코드는 아래와 같습니다.
import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
import { WebView } from 'react-native-webview'
const domain = 'https://firstproject-a04bf.firebaseapp.com/'
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
indexPage: { uri: domain + '?ver=3' }
};
}
/**
* 화면에서 post를 던지면 react-native에서 받음
*/
onWebViewMessage = event => {
console.log('onWebViewMessage', JSON.parse(event.nativeEvent.data))
let msgData;
try {
msgData = JSON.parse(event.nativeEvent.data) || {}
} catch (error) {
console.error(error)
return
}
this[msgData.targetFunc].apply(this, [msgData]);
}
/**
* 영화목록을 받아와서 화면에 전달한다.
*/
getMovieList = msgData => {
const option = {
method: 'GET',
timeout: 60000
}
let url = domain + msgData.data.url
fetch(url, option)
.then(res => {
return res.json()
})
.then(response => {
console.log('<====== response', response)
msgData.isSuccessfull = true
msgData.data = response
this.appWebview.postMessage(JSON.stringify(msgData), '*');
})
.catch(error => {
console.log(error)
})
}
render() {
return (
<View style={styles.container}>
<WebView
style={styles.webview}
source={this.state.indexPage}
originWhitelist={['*']}
ref={webview => this.appWebview = webview}
onMessage={this.onWebViewMessage}
javaScriptEnabled={true}
useWebKit={true}
/>
</View>
);
}
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 30
},
webview: {
flex: 1
}
});
iphone에서 Command + D를 누르고 Remote JS debugging을 확성화시키면 크롬 개발자툴에서 디버깅을 할 수 있습니다.
통신하기 버튼을 누르면 크롬 콘솔에서 movie.json을 받아옵니다.
그런데 iOS에서는 react native에서 화면으로 post message를 던져도 받지를 못합니다.
예전에 react native에 Webview가 포함되어 있을때는 문제 없이 받았는데
react-native-webview로 별도로 독립하고 나서는 post message를 못받습니다. -_-
이번에 react-native 0.6으로 버전업 해서 혹시나 iOS에서 해봤는데..마찬가지네요.
이걸 해결한 다른 webview가 있는데..그건 다음에 ㅋㅋ (급하신분은 댓글 남겨주시면 알려드릴께요.)
Android에서 테스트 계속 해보겠습니다.
$ react-native run-android
통신하기 버튼을 누르면 react native의 fetch 함수로 통신해서 결과값을 javascript 화면으로 전달해 주는 로직이었습니다.
package.json 최종입니다.
{
"name": "secondProject",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"react": "16.8.3",
"react-native": "0.59.8",
"react-native-webview": "^5.12.1"
},
"devDependencies": {
"@babel/core": "7.5.4",
"@babel/runtime": "7.5.4",
"babel-jest": "24.8.0",
"jest": "24.8.0",
"metro-react-native-babel-preset": "0.55.0",
"react-test-renderer": "16.8.3"
},
"jest": {
"preset": "react-native"
}
}
'App & OS > Hybrid' 카테고리의 다른 글
[React Native] 리소스 파일 다운로드하기 (zip 압축해제) (2) | 2019.07.13 |
---|---|
[React Native] 디바이스에 Local Server 구동하기 (0) | 2019.07.13 |
[React Native] release 모드로 build 하기 (0) | 2019.07.09 |
[React Native] Project 이름 변경하기 (2) | 2019.07.06 |
[React Native] 패키지명, 번들명 변경하기 Package, Bundle [Android, iOS] (0) | 2019.07.06 |