React Native WebView: A complete guide
https://blog.logrocket.com/react-native-webview-complete-guide/
이 글을 위 내용을 공부하면서 번역한 것입니다. 저작권에 문제 있으면 언제 든지 알려주세요.
모바일 애플리케이션에서 웹 컴포넌트를 임베드할 수 있게 해주는 WebView에 대해 설명하겠습니다. 웹 컴포넌트는 간단한 HTML 파일부터 전체 웹페이지 또는 애플리케이션에 이르기까지 다양합니다.
React Native에서 웹 콘텐츠를 임베드하기 위해 사용하는 react-native-webview
패키지는 오픈 소스이며, React Native 커뮤니티에 의해 활발히 유지 관리되고 있습니다. 이 포스트에서는 React Native WebView의 가장 일반적인 사용 사례를 살펴보겠습니다.
우리는 이 튜토리얼의 마지막 결과로, 랜덤한 프로그래밍 언어를 표시하고 최종적으로 LogRocket 블로그 페이지로 리디렉션하는 데모 앱을 만들어 보겠습니다.
React Native WebView 데모 앱 최종 결과
사전 준비
다음 사항을 설치해 두셔야 합니다:
- Node 런타임 환경
- Visual Studio Code와 같은 텍스트 에디터
- 또한, JavaScript, React/React Native, HTML에 대한 기본적인 지식이 필요합니다.
React Native 앱 시작하기
저는 React Native 앱을 개발할 때 Expo를 사용합니다. 새로운 Expo 앱을 만드는 가장 빠른 방법은 create-expo-app
을 사용하는 것입니다. 이 명령어는 필요한 모든 설정이 완료된 새로운 Expo 프로젝트를 생성합니다.
프로젝트 디렉토리로 이동한 후 터미널에서 다음 명령어를 실행하세요:
npx create-expo-app@latest
이 명령어는 프로젝트 이름을 물어보며, 프로젝트 생성 후 필요한 종속성을 설치합니다.
그 후, 생성된 프로젝트 디렉토리로 이동하여 npx expo start
또는 npm run start
명령어를 실행해 개발 서버를 시작하세요. Expo를 사용하면 작업 중인 장치를 선택할 수 있습니다: 실제 Android/iOS 장치 또는 Android/iOS 에뮬레이터/시뮬레이터입니다.
따라서, 개발 환경 설정은 장치 선택에 따라 달라질 수 있습니다. Expo 문서에는 자세한 설정 가이드가 포함되어 있습니다.
앱을 시작하기 전에 React Native WebView 패키지를 설치해야 합니다. 프로젝트 디렉토리에서 다음 명령어를 실행하세요:
expo install react-native-webview
이 패키지는 Android 및 iOS 장치에서 모두 작동합니다.
터미널 명령어 요약
# 프로젝트를 저장할 디렉토리로 이동
$ cd dir
# expo 프로젝트 초기화
$ npx create-expo-app@latest
# 생성된 프로젝트로 이동
$ cd my-project
# webview 패키지 설치
$ expo install react-native-webview
# 개발 서버 실행
$ npx expo start
React Native WebView로 웹 콘텐츠 임베드하기
웹 콘텐츠를 React Native 애플리케이션에 임베드하는 가장 간단한 방법은 임베드할 웹 콘텐츠의 URL을 제공하는 것입니다:
import { WebView } from 'react-native-webview';
export default function HomeScreen() {
return <WebView source={{ uri: "https://blog.logrocket.com/" }} />;
}
위 코드를 보면 WebView 컴포넌트를 사용하기 위해 먼저 이를 임포트해야 합니다. 그런 다음, source
속성에 웹 콘텐츠의 URI를 설정하면 됩니다. source
속성의 값으로 객체를 전달해야 하며, 이 예에서는 URI를 제공했습니다.
React Native WebView에 인라인 HTML 임베드하기
웹 콘텐츠를 WebView에 임베드하는 또 다른 방법은 인라인 HTML을 사용하는 것입니다. 간단한 예를 살펴보겠습니다:
import { WebView } from "react-native-webview";
export default function HomeScreen() {
const customHTML = `
<body style="display:flex; flex-direction: column;justify-content: center;
align-items:center; background-color: black; color:white; height: 100%;">
<h1 style="font-size:100px; padding: 50px; text-align: center;"
id="h1_element">
This is simple html
</h1>
<h2 style="display: block; font-size:80px; padding: 50px;
text-align: center;" id="h2_element">
This text will be changed later!
</h2>
</body>`;
return <WebView source={{ html: customHTML }} />;
}
위 코드에서 우리는 customHTML
변수를 선언했습니다. 이 변수는 <body>
태그 안에 h1
과 h2
요소를 포함한 문자열입니다. 또한, 요소를 중앙에 배치하고 배경색을 설정하는 기본적인 인라인 스타일도 포함되어 있습니다.
React Native와 JavaScript 간의 통신
다음 코드를 살펴보겠습니다:
import { WebView } from "react-native-webview";
export default function HomeScreen() {
const customHTML = `
<body style="display:flex; flex-direction: column;justify-content: center;
align-items:center; background-color: black; color:white; height: 100%;">
<h1 style="font-size:100px; padding: 50px; text-align: center;"
id="h1_element">
This is simple html
</h1>
<h2 style="display: block; font-size:80px; padding: 50px;
text-align: center;" id="h2_element">
This text will be changed later!
</h2>
</body>`;
const runFirst = `
setTimeout(function() {
window.alert("Click me!");
document.getElementById("h1_element").innerHTML =
"What is your favourite language?";
document.getElementById("h2_element").innerHTML =
"We will see!";
}, 1000);
true;
`;
const runBeforeFirst = `
window.isNativeApp = true;
true;
`;
return (
<WebView
source={{ html: customHTML }}
onMessage={(event) => {}}
injectedJavaScript={runFirst}
injectedJavaScriptBeforeContentLoaded={runBeforeFirst}
/>
);
}
이 코드에서 우리는 onMessage
, injectedJavaScript
, injectedJavaScriptBeforeContentLoaded
라는 세 가지 새로운 속성을 소개했습니다. injectedJavaScript
속성에 전달된 JavaScript 코드는 리소스가 처음 로드된 후 한 번 실행됩니다.
반면에, injectedJavaScriptBeforeContentLoaded
속성에 전달된 코드는 페이지가 처음 로드되기 전에 실행됩니다. 이 코드는 애플리케이션의 시각적 요소에 영향을 미치지 않습니다. onMessage
속성은 비어 있는 함수라도 반드시 필요합니다. 그렇지 않으면 스크립트가 실행되지 않습니다.
React Native WebView의 injectJavaScript 메소드
이 섹션에서는 데모 앱의 마지막 기능을 구현합니다: 프로그래밍 언어를 랜덤으로 표시하고 LogRocket 블로그 랜딩 페이지로 리디렉션합니다.
import { WebView } from "react-native-webview";
import { useRef } from "react";
export default function HomeScreen() {
const webRef = useRef(null);
const customHTML = `
<body style="display:flex; flex-direction: column;justify-content: center;
align-items:center; background-color: black; color:white; height: 100%;">
<h1 style="font-size:100px; padding: 50px; text-align: center;"
id="h1_element">
This is simple html
</h1>
<h2 style="display: block; font-size:80px; padding: 50px;
text-align: center;" id="h2_element">
This text will be changed later!
</h2>
</body>`;
const runFirst = `
setTimeout(function() {
window.alert("Click me!");
document.getElementById("h1_element").innerHTML =
"What is your favourite language?";
document.getElementById("h2_element").innerHTML =
"We will see!";
}, 1000);
true;
`;
const runBeforeFirst = `
window.isNativeApp = true;
true;
`;
const injectedJavaScript = `
const languages = [
"Rust",
"Python",
"JavaScript",
"TypeScript",
"C++",
"Go",
"R",
"Java",
"PHP",
"Kotlin",
];
const headerElement = document.getElementById("h2_element");
let counter = 0;
const setIntervalId = setInterval(() => {
if (counter === languages.length) {
clearInterval(setIntervalId);
window.ReactNativeWebView.postMessage(
"You are now getting redirected!"
);
window.location = "https://blog.logrocket.com";
return;
}
if (document.body.style.backgroundColor === "white") {
document.body.style.backgroundColor = "black";
document.body.style.color = "white";
} else {
document.body.style.backgroundColor = "white";
document.body.style.color = "black";
}
headerElement.textContent = languages[counter++] + "?";
window.ReactNativeWebView.postMessage("counter: " + counter.toString());
}, 1_000);
true;
`;
const onLoadHandler = ({ nativeEvent }) => {
if (!nativeEvent.url.startsWith("http")) {
webRef.current.injectJavaScript(injectedJavaScript);
}
};
return (
<WebView
source={{ html: customHTML }}
ref={webRef}
onMessage={(event) => {
console.log(event.nativeEvent.data);
}}
onLoad={onLoadHandler}
injectedJavaScript={runFirst}
injectedJavaScriptBeforeContentLoaded={runBeforeFirst}
/>
);
}
위 코드에서 우리는 WebView 컴포넌트를 참조하기 위해 useRef
훅을 선언했습니다. onLoad
이벤트 핸들러에서 WebView가 완전히 로드된 후 injectJavaScript
메소드를 사용해 코드를 주입합니다.
React Native WebView에서 탐색 및 URL 변경 처리
때로는 WebView에서 URL 변경을 모니터링하고, URL이 변경될 때 적절한 조치를 취해야 할 수 있습니다. 이를 통해 실시간 모니터링, 사용자 상호 작용 추적, 특정 URL 허용 또는 차단이 가능합니다.
React Native WebView는 탐색 및 URL 변경을 처리하기 위한 여러 내장 API를 제공합니다. 예를 들어, onNavigationStateChange
함수를 사용하여 탐색 및 URL 변경을 모니터링할 수 있습니다. 이 함수는 WebView의 로딩이 시작되고 끝날 때마다 호출됩니다:
<WebView
source={{ uri: 'https://blog.logrocket.com/' }}
onNavigationStateChange={(navState) => {
console.log(navState)
}}
/>
navState
매개변수는 URL, 로딩 상태, 제목, 뒤로 가기 가능 여부, 앞으로 가기 가능 여부 등의 정보를 포함하는 객체입니다. URL 변경을 모니터링하고 로딩 상태를 관리하는 데 사용할 수 있습니다.
비슷하게 onShouldStartLoadWithRequest
함수를 사용해 WebView 내의 네트워크 요청을 관리할 수도 있습니다. 이 함수는 네트워크 요청을 모니터링하고 탐색을 제한할 때 유용합니다. 콜백 함수는 요청 로딩을 계속하려면 true
를 반환하고, 그렇지 않으면 false
를 반환해야 합니다. 다음 예제에서는 로드된 웹사이트 내의 탐색을 제한합니다:
<WebView
source={{ uri: 'https://blog.logrocket.com/' }}
onShouldStartLoadWithRequest={(request) => {
return request.url.startsWith('https://blog.logrocket.com/');
}}
/>
React Native에서 WebView 커스터마이징 기술
웹 콘텐츠의 스타일을 변경하려면, 주입된 JavaScript 코드가 style
요소를 생성하고 이를 문서의 head
요소에 추가하도록 할 수 있습니다:
import { WebView } from "react-native-webview";
export default function HomeScreen()() {
const injectedJavaScript = `
const style = document.createElement('style');
style.innerHTML = 'body { background-color: grey; }';
document.head.appendChild(style);
true;
`;
return (
<WebView
source={{ uri: "https://blog.logrocket.com/" }}
injectedJavaScript={injectedJavaScript}
/>
);
}
웹 콘텐츠가 로드되는 동안 로딩 인디케이터를 표시하고, 성공적으로 로드된 후 WebView를 렌더링하도록 할 수 있습니다. 예를 들어, React Native의 내장 ActivityIndicator
를 사용해 웹 콘텐츠 로딩 동안 로딩 인디케이터를 표시하고, 로드가 완료되면 WebView를 렌더링하는 방법은 다음과 같습니다:
export default function HomeScreen() = () => {
return (
<WebView
source={{ uri: "https://blog.logrocket.com/" }}
startInLoadingState={true}
renderLoading={() => <ActivityIndicator size="large" />}
/>
);
};
renderLoading
속성은 로딩 인디케이터를 반환하는 함수입니다. 위 예제에서는 내장된 ActivityIndicator
컴포넌트를 반환합니다. 사용자 정의 컴포넌트를 렌더링할 수도 있습니다:
<WebView
source={{ uri: "https://blog.logrocket.com/" }}
startInLoadingState={true}
renderLoading={() => <LoadingIndicator />}
/>
renderLoading
속성을 사용할 때는 startInLoadingState
를 true
로 설정해야 합니다. 또한 renderError
속성을 사용해 오류를 관리할 수 있습니다. 오류가 발생하면, renderError
속성에 전달된 함수가 오류 이름을 인자로 받아 호출됩니다:
export default function HomeScreen() = () => {
const [error, setError] = useState(null);
return (
<>
{error ? (
<Error errorMessage={error.message} />
) : (
<WebView
source={{ uri: "https://blog.logrocket.com/" }}
onError={(error) => setError(error)}
startInLoadingState={true}
renderLoading={() => <ActivityIndicator size="large" />}
/>
)}
</>
);
};
위 예제에서는 WebView가 로드되는 동안 로딩 인디케이터를 렌더링합니다. 오류가 발생하면 상태를 업데이트하고 사용자 정의 오류 컴포넌트를 렌더링합니다.
결론
WebView는 모바일 애플리케이션에 웹 콘텐츠를 임베드할 때 유용합니다. React Native에서는 react-native-webview
패키지를 사용해 이를 구현할 수 있습니다.
React Native WebView를 사용해 웹 컴포넌트를 임베드하려면, 웹 콘텐츠의 URL을 제공하거나 인라인 HTML을 임베드할 수 있습니다. react-native-webview
패키지는 JavaScript 코드 주입, WebView 내 탐색 및 URL 변경 관리, 로드된 웹 콘텐츠의 커스터마이징 등 여러 내장 기능을 제공합니다.