본문 바로가기
React Native

[RN] expo Stack과 Tabs의 차이 이전화면으로 뒤로 가기 안되는 이유

by 어느새벽 2025. 7. 7.
반응형

회사에서 프로젝트 하나를 처음부터 혼자 맡아서 해보고 있다.

api 나오기 전에 얼추 ui는 완성 했는데

탭화면을 이동하다가 각 기종별로 제스처에 뒤로가기를 하면 이전화면으로 돌아갈 수 있도록 하라는 피드백을 받게 됐다.

import { openModal } from "@/components/atom/Modal";
import { useFocusEffect, useRouter, useSegments } from "expo-router";
import { useCallback } from "react";
import { BackHandler } from "react-native";
import { colors } from "../constants";

export const useBackHandler = () => {
  const router = useRouter();
  const segments = useSegments();

  useFocusEffect(
    useCallback(() => {
      const onBackPress = () => {
        const isAtHome = segments[2] === "home";

        if (isAtHome) {
          openModal({
            title: "알림",
            content: "앱을 종료하시겠습니까?",
            buttons: [
              { text: "취소", color: colors.gray[200] },
              {
                text: "종료",
                color: colors.red[100],
                onPress: () => BackHandler.exitApp(),
              },
            ],
          });
          return true;
        }

        if (router.canGoBack()) {
          router.back();
          return true;
        }
        router.navigate("/(drawer)/(tabs)/home");
        return true;
      };

      const sub = BackHandler.addEventListener("hardwareBackPress", onBackPress);
      return () => sub.remove();
    }, [router, segments]),
  );
};

 

그래서 이러한 hook을 탭 레이아웃 화면에 호출해서 해봤는데 

그럼에도 무조건 홈으로 이동하는 문제가 남았다.

일단 expo router는 static 경로로 거의 항상 canGoBack이 false라고 한다.

Tabs는 Stack 처럼 위로 쌓이는 구조가 아니라 화면전환만 하기에 이전 화면이라는 기록이 없어 불가하다고 한다....

그래서 stack 구조로 바꿔야 할 것 같다.

 

이것저것 시도 해봤으니 Tabs로만 이루어진 화면구성으로는 이전화면으로 동작할 수가 없었다.

그래서 Stack으로 전부 교체하고

BottomTabs는 컴포넌트로만 살려서 Tabs처럼 사용하기로 ! 

 

수정전 폴더 (화면) 구조

 

보는 것처럼 drawer 내부에 tabs를 넣고 각각 화면들을 넣었다.

stack은 최상단 Index.tsx와 (drawer) 였다.

 

수정 후

 

//(stacks)/_layout.tsx

import { openModal } from "@/components/atom/Modal";
import BottomTab from "@/components/ui/BottomTab";
import CustomHeader from "@/components/ui/CustomHeader";
import { colors } from "@/lib";
import { Stack, useRouter, useSegments } from "expo-router";
import React, { useEffect } from "react";
import { BackHandler, View } from "react-native";

export default function StackLayout() {
  const router = useRouter();
  const segments = useSegments();

  useEffect(() => {
    const backAction = () => {
      if (router.canGoBack()) {
        router.back();
        return true;
      }

      const isAtHome = segments[2] === "home";

      if (isAtHome) {
        openModal({
          title: "앱 종료",
          content: "앱을 종료하시겠습니까?",
          buttons: [
            { text: "취소", color: colors.gray[200] },
            { text: "종료", color: colors.red[100], onPress: () => BackHandler.exitApp() },
          ],
        });
        return true;
      }

      return false;
    };

    const backHandler = BackHandler.addEventListener("hardwareBackPress", backAction);

    return () => backHandler.remove();
  }, [segments, router]);

  return (
    <View style={{ flex: 1 }}>
      <Stack
        initialRouteName="home"
        screenOptions={{
          header: () => <CustomHeader />,
          animation: "none",
          contentStyle: { backgroundColor: colors.white },
        }}
      >
        <Stack.Screen name="home" />
        <Stack.Screen name="history" />
        <Stack.Screen name="scan" />
        <Stack.Screen name="alarm" />
        <Stack.Screen name="settings" />
      </Stack>
      <BottomTab />
    </View>
  );
}

 

stacks의 레이아웃 파일 내부에 BackHandler 기능을 넣어주고

BottomTab은 컴포넌트로 넣었다.

이렇게 하니 이전화면 및 앱종료 기능 해결했다!

 

화면 이동을 어디까지 다룰지 미리 생각하고 stack과 tabs를 구분해서 사용해야겠다.

반응형