BackHandler는 Android의 물리적/소프트웨어 뒤로 가기 버튼을 가로채서 커스텀 동작을 구현할 수 있게 해주는 API이다.
여기서 중요한 점은, BackHandler 는 Android 전용이다. ios에는 뒤로 가기 버튼이 없으므로 이 API는 아무 동작도 하지 않는다.
사용자가 뒤로 가기 버튼 누름 → 앱이 바로 종료되거나 이전 화면으로 돌아감
하지만 실무에서는 이런 상황이 자주 발생한다.
이럴 때 BackHandler를 사용하면 좋다.
1) useExitApp.android.ts
import { useFocusEffect } from '@react-navigation/native';
import { useCallback, useRef } from 'react';
import { BackHandler, ToastAndroid } from 'react-native';
const useExitApp = () => {
// 마지막으로 뒤로가기를 누른 시점 저장
const lastBackPressed = useRef<number>(0);
useFocusEffect(
useCallback(() => {
const onBackPress = () => {
const now = Date.now();
// 2초(2000ms) 안에 다시 눌렀는지 확인
if (lastBackPressed.current && now - lastBackPressed.current < 2000) {
BackHandler.exitApp(); // 앱 종료
return true;
}
lastBackPressed.current = now;
ToastAndroid.show('한 번 더 누르면 종료됩니다.', ToastAndroid.SHORT);
// true를 반환해야 기본 동작(앱 종료/이전 화면 이동)을 막습니다.
return true;
};
// 이벤트 리스너 등록
const subscription = BackHandler.addEventListener('hardwareBackPress', onBackPress);
return () => {
// 이벤트 리스너 해제
subscription.remove();
};
}, []),
);
};
export default useExitApp;
2) useExitApp.ios.ts
iOS는 시스템 구조상 앱 내에서 강제로 종료하는 기능을 지양하며, 하드웨어 뒤로가기 버튼도 존재하지 않기에 아무런 동작도 하지 않고 null 을 반환하는 함수를 사용한다. 시스템 홈 스와이프로 종료된다.
const useExitApp = () => null;
export default useExitApp;
return시 true 와 false 의 차이?return true : "이벤트를 수동으로 처리했기에, 시스템에 아무것도 하지 말라는 신호를 보냄" (이벤트 전파 중단) -> 뒤로 가기 막힘
return false : "나는 처리 안 할래. 시스템 너 할 일 해." -> 기본 동작 수행 (이전 화면 이동 or 앱 종료)
BackHandler는 리스너 체인으로 동작한다. 내가 등록한 함수가 true를 반환하면 뒤에 있는 리스너나 시스템의 기본 동작은 실행되지 않는다. 따라서 뒤로 가기를 막고 싶다면 반드시 true를 반환해야 한다.
useExitApp 훅은 앱의 첫 진입 화면(예: HomeTab, MainScreen)에서만 사용해야 한다. 만약 상세 페이지(Detail Screen)에서 이 훅을 사용하면, 사용자가 뒤로 가서 목록을 보고 싶은데 "한 번 더 누르면 종료됩니다" 토스트가 뜨는 현상이 발생한다.
또한 React Navigation은 내부적으로 BackHandler를 이미 사용하고 있다. 우리가 등록한 리스너는 React Navigation보다 나중에 등록(useFocusEffect)되므로 우선순위가 높다. 즉, 상세 페이지에서는 이 훅이 실행되지 않도록 하는게 좋은 것이다.