⚡ React-Native BackHandler
앱 제작 중 로그인 화면이나 메인 화면에서 뒤로 가면 즉시 앱이 종료되어 불편함을 느꼈습니다.
곰곰히 생각해보니 기존에 사용하던 어플들은 앱을 종료할 때
모달창으로 한번 더 ‘종료 하시겠습니까?’ 하고 물어보던 것이 생각이 나서 적용해봤습니다.
그럼 기능부터 사용법, 문제 해결, hooks으로 분리한 과정을 설명하겠습니다.
📌 BackHandler 주요 기능
import { BackHandler } from 'react-native';
...
useEffect(()=>{
const backAction = () => {
// 여기에 뒤로 가기 버튼을 눌렀을 때 수행할 작업을 정의합니다.
// 작업을 수행한 후에는 true 또는 false를 반환합니다.
// true를 반환하면 기본적인 뒤로 가기 동작을 막습니다.
// false를 반환하면 기본 동작인 앱을 종료하지 않고 뒤로 갑니다.
// 일반적으로 작업이 처리되었으면 true를 반환하여 기본 동작을 막습니다.
return true;
};
// 리스너 등록
const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
return () => {
// 이벤트 리스너 해제
backHandler.remove();
}
},[])
기본적인 기능은 위 코드와 같이 정의하면 손쉽게 동작합니다.
BackHandler의 대표적인 기능들은 다음과 같습니다.
🔎 BackHandler.addEventListener
이 함수를 사용하여 리스너를 등록할 수 있습니다. 등록된 리스너는 사용자가 뒤로 가기 버튼을 누를 때마다 호출됩니다.
🔎 BackHandler.removeEventListener
이 함수를 사용하여 등록된 리스너를 제거할 수 있습니다. 컴포넌트가 언마운트될 때 리스너를 반드시 제거해야 합니다.
🔎 BackHandler.exitApp
이 함를 호출하면 앱이 종료됩니다.
📌 Alert을 이용한 앱 종료 묻기
import { BackHandler } from 'react-native';
...
useEffect(() => {
const backAction = () => {
Alert.alert('앱 종료', '앱을 종료하시겠습니까?', [
{ text: '취소', onPress: () => null },
{ text: '확인', onPress: () => BackHandler.exitApp() },
]);
return true;
};
// 리스너 등록
const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
return () => {
// 이벤트 리스너 해제
backHandler.remove();
};
}, []);
간단하게도 위와 같은 코드 작성만으로 기능은 만들어졌습니다.
그러나 완성 했다고 생각했을 때 문제점을 하나 발견했습니다.
📌 스택 네비게이션의 unmount 문제
원래는 BackHandler가 적용되지 않은 스크린으로 이동했다면
useEffect의 return을 통해 이벤트 리스너가 제거되어 기본적인 뒤로 가기 동작이 실행돼야 하지만
어느 스크린을 가도 이벤트 리스너에 의해 계속해서 종료를 묻는 모달창이 올라왔습니다.
콘솔을 찍어보니 다른 스크린으로 이동할 때 unmount가 작동하지 않는 것을 확인하곤
스택 네비게이션은 스크린의 이동이 아닌 스크린을 스택에 push하고 pop하는 구조여서
최하단 스택은 unmount 되지 않겠다고 생각해 다른 방법을 찾았습니다.
const navigation = useNavigation<NativeStackNavigationProp<RootStackParam>>();
useEffect(() => {
const backAction = () => {
if (navigation.isFocused()) {
Alert.alert('앱 종료', '앱을 종료하시겠습니까?', [
{ text: '취소', onPress: () => null },
{ text: '확인', onPress: () => BackHandler.exitApp() },
]);
return true;
}
};
// 리스너 등록
const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
return () => {
// 이벤트 리스너 해제
backHandler.remove();
};
}, []);
useNavigation
훅에 현재 화면이 포커스 되어 있는지 여부를 확인하는데 사용되는isFocused()
함수를 통해
action 함수 내부에 조건문을 넣어 선언한 스크린 이외의 스크린에서는 BackHandler가 작동하지 않도록 했습니다.
타입스크립트 + 스택 네비게이션 사용법은 아래 글을 참고해주세요.
[React-Native + TypeScript] Stack Navigation, 스택 네비게이션 사용법
⚡ react-native의 네비게이션 화면에서 navigation을 이용하여 다른 화면으로 이동하곤 합니다. react web에서는 말 그대로 URL을 통한 페이지 → 페이지 의 이동이었다면 앱에서는 스크린 간 이동이 아
lasbe.tistory.com
📌 Custom Hook으로 분리
이 로직을 하나의 스크린에서만 사용하지 않아서 아래와 같이 깔끔하게 훅으로 분리해봤습니다.
import { useEffect } from 'react';
import { Alert, BackHandler } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { RootStackParam } from '../screens/HomeScreen';
export const useBackHandler = () => {
const navigation = useNavigation<NativeStackNavigationProp<RootStackParam>>();
useEffect(() => {
const backAction = () => {
if (navigation.isFocused()) {
Alert.alert('앱 종료', '앱을 종료하시겠습니까?', [
{ text: '취소', onPress: () => null },
{ text: '확인', onPress: () => BackHandler.exitApp() },
]);
return true;
}
};
const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
return () => {
backHandler.remove();
};
}, []);
};
이렇게 분리한 로직은 BackHandler를 사용할 스크린에서 다음과 같이 단 한 줄로 사용하면 됩니다.
export const AnyScreen = () => {
useBackHandler();
...
}
도움이 되셨길 바랍니다!
'React-Native' 카테고리의 다른 글
[React Native] Android Native-Module Event Listener 만들기 (0) | 2025.01.10 |
---|---|
[React-Native] ScrollView 스크롤 버벅임 최적화하기 (2) | 2023.10.14 |
[React-Native + TypeScript] Stack Navigation, 스택 네비게이션 사용법 (1) | 2023.07.18 |
React Native 장단점과 CLI, Expo 비교와 후기 (0) | 2023.07.13 |
[React-Native] styled-components, Unknown property: 'elevation' ts-styled-plugin(9999) 경고 제거 (0) | 2023.06.20 |
댓글