[React Native] 뒤로가기 버튼 이벤트

호이·2023년 2월 1일
0

React Native

목록 보기
2/10

※ 이 포스트는 클래스형 컴포넌트를 기준으로 작성되었다.

1. 왜 필요한가?

React Native로 웹뷰 앱을 개발하던 중 휴대폰의 뒤로가기 버튼을 누르면 앱이 바로 종료되는 현상이 있었다. 그 현상을 방지하고 뒤로가기 버튼을 눌렀을 경우 웹뷰의 페이지가 이전 페이지로 이동하고 더 이상 이동할 페이지가 없을 경우 앱을 종료할건지 물어보는 창이 나오도록 만들었다.

2. 과정

여러 구글링을 통하여 관련 코드를 찾아서 분석하고 참고하여 코딩을 진행하였지만 원하는 결과물이 나오지 않았다. 구글링해서 찾은 코드들도 잘 작동하는 코드들이 많았지만 필자입장에서는 똑같은 기능을 하는 코드더라도 조금 더 간결하게 작성된 코드를 원하였다. 그래서 React Native 공식 홈페이지에서의 예졔를 참고하였다.
https://reactnative.dev/docs/backhandler

3. 결과

우선 react-native라이브러리에서 Alert, BackHandler를 import한다.

import { Alert, BackHandler } from "react-native";

뒤로가기 버튼을 누른다고 해서 무작정 이전 페이지로 이동할 수는 없다. 모든 페이지에는 메인페이지가 있으며 계속 뒤로가기 버튼을 누르다보면 메인페이지로 이동할 것이다. 그것을 방지하기 위해 이전 페이지가 이동할 수 있는지 여부를 저장하는 state를 생성하였다.

this.state = {
      url: "",
      canGoBack: false,
    };

여기서 url변수까지 생성한 이유는 뒤로가기 버튼을 누르다가 메인페이지로 이동되었을때 앱을 종료할건지 물어보는 창을 띄울수 있게 만들었기 때문이다.

BackHandler의 내장 함수인 addEventListener를 호출하여 뒤로가기 버튼 이벤트와 버튼을 눌렀을 경우 실행할 함수를 인자값으로 넣어준다.

BackHandler.addEventListener("hardwareBackPress", this.handleBackButton);

여기서 this.handleBackButton은 뒤로가기 버튼을 눌렀을 때 실행할 함수이다. 물론 바로 익명의 콜백함수를 넣어도 되지만 필자는 따로 함수를 만들었다.

handleBackButton = () => {
    if (this.state.canGoBack) {
      if (this.state.url === "https://www.naver.com") {
        Alert.alert("종료하시겠어요?", "확인을 누르면 종료합니다.", [
          {
            text: "취소",
            onPress: () => {},
            style: "cancel",
          },
          { text: "확인", onPress: () => BackHandler.exitApp() },
        ]);
      } else {
        this.webview.current.goBack();
      }
    }
    return true;
  };

이렇게 함수를 정의해주면 뒤로가기 버튼을 누를 경우 해당 함수가 작동하면서 웹뷰 페이지가 이전 페이지로 이동할수 있을 경우만 이동하게 되고 이전 페이지로 이동 중 메인페이지를 만나게 되면 앱을 종료할 것 인지 물어보는 창이 나올 수 있다. 마지막에 true값을 반환하지 않으면 기본적인 뒤로가기 버튼의 이벤트가 작동한다.

하지만 위의 방식으로는 처음 앱을 실행할 경우 뒤로가기 버튼을 눌러 앱을 종료할 수 없다. 혹시나 사용자가 실수로 앱을 실행하여 바로 종료를 하고 싶을 경우를 대비하여 다음과 같이 수정할 수 있다.

function close() {
  Alert.alert("종료하시겠어요?", "확인을 누르면 종료합니다.", [
    {
      text: "취소",
      onPress: () => {},
      style: "cancel",
    },
    { text: "확인", onPress: () => BackHandler.exitApp() },
  ]);
}
handleBackButton = () => {
    if (this.state.canGoBack) {
      if (this.state.url === "https://www.naver.com") {
        close();
      } else {
        this.webview.current.goBack();
      }
    } else {
      close();
    }
    return true;
  };

종료 창을 별도의 함수로 정의하여 뒤로가기 버튼을 눌렀을 경우 돌아갈 이전 페이지가 없거나 처음 앱을 실행하여 바로 종료를 원할 경우 종료 창을 실행하는 함수를 실행하도록 수정하면 된다.

특정 노드에 참조할수 있도고록 React라이브러리의 createRef함수를 이용하여 Ref를 만들어준다.

this.webview = React.createRef();

WebView에 생성한 ref를 설정해두고 onNavigationStateChange을 이용하여 페이지가 이동할 때 마다 현재 페이지의 url 및 이동할 수 있는 지에 대한 여부를 변수에 저장한다.

<WebView
        source={{ uri: "https://www.naver.com" }}
        ref={this.webview}
        onNavigationStateChange={(navState) => {
          this.setState({ url: navState.url, canGoBack: navState.canGoBack });
        }}
      />

전체 코드는 다음과 같다.

import React from "react";
import { Alert, BackHandler } from "react-native";
import { WebView } from "react-native-webview";

// 종료 창
function close() {
  Alert.alert("종료하시겠어요?", "확인을 누르면 종료합니다.", [
    {
      text: "취소",
      onPress: () => {},
      style: "cancel",
    },
    { text: "확인", onPress: () => BackHandler.exitApp() },
  ]);
}

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.webview = React.createRef();
    this.state = {
      url: "",
      canGoBack: false,
    };
  }

  // 이벤트 등록
  componentDidMount() {
    BackHandler.addEventListener("hardwareBackPress", this.handleBackButton);
  }

  // 이벤트 해제
  componentWillUnmount() {
    BackHandler.removeEventListener("hardwareBackPress", this.handleBackButton);
  }

  // 이벤트 동작
  handleBackButton = () => {
    if (this.state.canGoBack) {
      if (this.state.url === "https://www.naver.com") {
        close();
      } else {
        this.webview.current.goBack();
      }
    } else {
      close();
    }
    return true;
  };

  render() {
    return (
      <WebView
        source={{ uri: "https://www.naver.com" }}
        ref={this.webview}
        onNavigationStateChange={(navState) => {
          this.setState({ url: navState.url, canGoBack: navState.canGoBack });
        }}
      />
    );
  }
}
profile
기억하기 싫어서 기록하는 작은 공간

0개의 댓글