You are currently viewing “React Native WebView 사용법: 모바일 앱에서 웹 콘텐츠 임베드 및 커스터마이징”

“React Native WebView 사용법: 모바일 앱에서 웹 콘텐츠 임베드 및 커스터마이징”

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> 태그 안에 h1h2 요소를 포함한 문자열입니다. 또한, 요소를 중앙에 배치하고 배경색을 설정하는 기본적인 인라인 스타일도 포함되어 있습니다.

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 속성을 사용할 때는 startInLoadingStatetrue로 설정해야 합니다. 또한 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 변경 관리, 로드된 웹 콘텐츠의 커스터마이징 등 여러 내장 기능을 제공합니다.

답글 남기기