티스토리 뷰

728x90
반응형

프로젝트를 진행하면서 React Native WebView를 적용해야하는 일이 있어서 정리를 하면서 적용을 해 보았습니다.

웹뷰는 적용은 Android 및 ios를 같이 진행하겠습니다.

또한 참고 하시는 분들은 각자의 프로젝트 마다 구조가 다르니 중요한 컴포넌트와 프로세스만 정리를 하겠습니다.

Step 01 - WebView로 이동할 스택을 정하자!

  • 저는 아래 사진에 있는 공지사항 클릭 시 웹뷰로 보내는 프로세스 입니다.
  • Stack 코드를 보시면 StackNavigation안에 Stack.Screen을 정의하고 있습니다.
  • Stack 코드의 36번 라인의 isCanBack은 뒤로 갈 수 있는지 판별하는 변수입니다.
  • Stack 코드의 38번 라인의 if문은 웹뷰 상태에서 뒤로갈 곳이 있으면 이전 페이지로 돌아가게되며 그것이 아니라면 어플로 돌아오게 됩니다.

참고 * 9번 라인의 LogBox.ignoreLogs는 웹뷰의 경고처리를 나오지 안도록 해주는 역할 입니다.
https://stackoverflow.com/questions/60954742/how-to-pass-parent-function-to-child-screen-in-react-navigation-5

 

How to pass parent function to child screen in React Navigation 5?

Recently I have started using React Native 0.61.5 and React navigation 5.x. Now I need to pass a parent function into a child screen. For example, I want to change navbar icon by pressing button in...

stackoverflow.com

 

 

  • 아래 js는 위에 사진에 보이는 공지사항 버튼 입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
<Button
  title={"공지사항"}
  iconname={faMegaphone}
  onPress={() =>
    navigation.navigate("CommonStack", {
      screen: "Board",
      params: {
        isCanBack: null,
      },
    })
  }
  activeOpacity={0.8}
/>
cs

 

  • 아래 js는 공지사항의 Stack 구조입니다.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import React from "react";
import { createStackNavigator } from "@react-navigation/stack";
import { Button, LogBox } from "react-native";
import Board from "../screens/board/index";
 
const Stack = createStackNavigator();
LogBox.ignoreLogs([
  "Non-serializable values were found in the navigation state",
]);
 
export default function CommonStack() {
  return (
    <Stack.Navigator
      initialRouteName="Home"
      screenOptions={{
        gestureEnabled: false,
        headerShown: true,
        cardStyle: { backgroundColor: "transparent" },
        headerBackTitleVisible: false,
        headerTintColor: "#414141",
        headerStyle: {
          borderBottomWidth: 1,
          borderBottomColor: "#F0F0F0",
          elevation: 0//for android
          shadowOpacity: 0// for ios
        },
      }}
    >
      <Stack.Screen
        name="Board"
        options={(navigation) => ({
          title: "공지사항",
          headerLeft: (props) => {
 
            // Android 및 ios에서 뒤로가기 클릭시 보내는 파라미터
            const isCanBack = navigation.route.params.isCanBack;
 
            if (isCanBack) {
              return (
                <Button
                  {...props}
                  title="Back"
                  color="#00cc00"
                  onPress={isCanBack.onPress}
                />
              );
            }
            return <Button title="Back" color="red" {...props} />;
          },
        })}
        component={Board}
      />
    </Stack.Navigator>
  );
}
 
cs

 

Step 02 - ios, Android를 구분해주자!

  • 위의 Stack에서 선언해 놓은 Board 컴포넌트입니다.
  • 해당 Board 컴포넌트는 ios 및 Android에 따라서 보여주는 웹뷰의 형태가 달라집니다.
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
31
32
33
34
import React from "react";
import { StyleSheet, View, Platform } from "react-native";
import IosPlatform from "../platform/iosPlatform";
import AndroidPlatform from "../platform/androidPlatform";
 
export default function Board() {
  const url = "https://kdg-is.tistory.com/";
 
  return (
    <View style={styles.root}>
      <View style={styles.browser}>
        {Platform.OS === "ios" ? (
          <IosPlatform url={url} />
        ) : (
          <AndroidPlatform url={url} />
        )}
      </View>
    </View>
  );
}
 
const styles = StyleSheet.create({
  browser: {
    flex: 1,
    flexDirection: "row",
  },
  root: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
    display: "flex",
  },
});
cs
 

Step 03 - ios 웹뷰작업 해주자!

  • IosPlatform 컴포넌트는 Board 컴포넌트로 url를 받아서 경로를 지정해줍니다.
  • 다른 코드들도 있지만 우리가 봐야할 코드는 WebView의 onNavigationStateChange 입니다.
  • onNavigationStateChange란?
    • 웹뷰 로딩이 시작되거나 끝나면 호출하는 함수입니다. navState로 url이 변경되었는지 감지 할 수 있습니다.
  • 16번 라인의 onNavigationStateChange 함수
    • 인자로 navState를 받아서 navState속에 있는 canGoBack이라는 변수를 가져옵니다.
    • 해당 19번 라인의 if ~ else문이 Stack.Screen의 Board에서 중요하게 사용하고 있는 isCanBack의 변수를 설정하는 곳입니다.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import React, { useState } from "react";
import { useNavigation } from "@react-navigation/core";
import { StyleSheet, View, ActivityIndicator } from "react-native";
import { WebView } from "react-native-webview";
 
const INJECTED_JAVASCRIPT = `(function() {
  AsyncStorage.setItem("memberCode"1);
  AsyncStorage.setItem("isApp"true);
 
})();`;
 
export default function IosPlatform({ url }) {
  const navigation = useNavigation();
  const [browserRef, setBrowserRef] = useState(null);
 
  const onNavigationStateChange = (navState) => {
    const { canGoBack } = navState;
 
    if (canGoBack) {
      navigation.setParams({
        isCanBack: {
          title: "",
          onPress: () => browserRef.goBack(),
        },
      });
    } else {
      navigation.setParams({
        isCanBack: null,
      });
    }
  };
 
  return (
    <View style={styles.root}>
      <View style={styles.browserContainer}>
        <WebView
          ref={(ref) => {
            setBrowserRef(ref);
          }}
          source={{
            uri: url,
          }}
          startInLoadingState
          originWhitelist={["*"]}
          renderLoading={() => (
            <View style={{ flex: 1, alignItems: "center" }}>
              <ActivityIndicator size="large" />
            </View>
          )}
          allowsBackForwardNavigationGestures
          onNavigationStateChange={(navState) =>
            onNavigationStateChange(navState)
          }
          injectedJavaScript={INJECTED_JAVASCRIPT}
          onMessage={(event=> {}}
        />
      </View>
    </View>
  );
}
 
const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  browserContainer: {
    flex: 2,
  },
});
 
cs

 

ios 결과 화면

 

Step 04 - Android 웹뷰작업 해주자!

  • 안드로이드는 기본적으로 아래에 뒤로가기 버튼도 있지만 ios처럼 상단에 뒤로가기 버튼도 만들어 주었습니다.
  • AndroidPlatform 컴포넌트도 ios 컴포넌트처럼 봐야할 코드는 WebView의 onNavigationStateChange 입니다.
  • 58번 라인에서 하드웨어적인 뒤로가기 처리와 소프트적인 뒤로가기 처리를 해주고 있습니다.
  • backPress 함수는 ios 컴포넌트와 마찬가지로 동작 방식은 동일합니다.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import React, { useRef, useState } from "react";
import { BackHandler } from "react-native";
import { useNavigation } from "@react-navigation/core";
import { useFocusEffect } from "@react-navigation/native";
import { WebView } from "react-native-webview";
 
const INJECTED_JAVASCRIPT = `(function() {
  AsyncStorage.setItem("memberCode"1);
  AsyncStorage.setItem("isApp"true);
})();`;
 
export default function AndroidPlatform({ url }) {
  const navigation = useNavigation();
  const webview = useRef(null);
  const [canGoBack, SetCanGoBack] = useState(false);
 
  // 안드로이드의 하드웨어적인 뒤로가기 설정
  useFocusEffect(
    React.useCallback(() => {
      const onBackPress = () => {
        if (webview.current && canGoBack) {
          webview.current.goBack();
          return true;
        } else {
          return false;
        }
      };
      BackHandler.addEventListener("hardwareBackPress", onBackPress);
      return () =>
        BackHandler.removeEventListener("hardwareBackPress", onBackPress);
    }, [canGoBack])
  );
 
  // 안드로이드의 소프트웨어적인 뒤로가기 설정
  const backPress = (navState) => {
    const { canGoBack } = navState;
 
    if (canGoBack) {
      navigation.setParams({
        isCanBack: {
          title: "",
          onPress: () => webview.current.goBack(),
        },
      });
    } else {
      navigation.setParams({
        isCanBack: null,
      });
    }
  };
 
  return (
    <WebView
      source={{
        uri: url,
      }}
      ref={webview}
      onNavigationStateChange={(navState) => {
        SetCanGoBack(navState.canGoBack) + backPress(navState);
      }}
      injectedJavaScript={INJECTED_JAVASCRIPT}
      onMessage={(event=> {}}
    />
  );
}
cs

Android 결과 화면


참고 사이트)
https://www.reactnativeschool.com/integrating-react-navigation-back-button-with-a-webview

* 궁금하신 내용이 있으면 댓글을 달아주시면 답변드리겠습니다!

728x90
반응형