My Boundary As Much As I Experienced

RN) 초기 앱 라우터 구조 짜기 (Stack, BottomTab) 본문

FrontEnd/React Native

RN) 초기 앱 라우터 구조 짜기 (Stack, BottomTab)

Bumang 2024. 8. 14. 02:41

현재 상황

회사에서 오늘의 일기라는 신규 서비스를 개발 중이다.

'앤트타임' 개발은 이미 다 짜여진 구조 안에 조금씩 추가하는 느낌이었는데

이번 제품은 초기세팅, 라우팅 구조부터 내가 손수 짜는거라 배우는게 많다.

회사에서 돈 받으면서 독서실 공부하는 느낌이어서 기분 좋다ㅎㅎ..

 

사수는 양자 시뮬레이터 정부사업 하느라 바빠서 자사 서비스는 다 내가 맡게되네? 오히려 좋아..

 

리액트 네비게이션의 Stack개념

이전 포스팅에서도 몇 번 얘기했다. 

웹에선 한 페이지에서 다른 페이지로 넘어갈 때 모든 상태가 사라지지만

모바일에선 어떤 페이지로 이동할 때 기존 화면 위에 새로운 탭이 쌓인다.

그래서 아래에 있는 페이지의 state나 여러 상태들이 보존된다!

 

이를 리액트네이티브에서 구현할 수 있는게 '스택' 컴포넌트인 것이다.

오늘 내가 '오늘의 일기'의 스택을 어떻게 구성하였는지 설명해보겠다.

 

 

1. 최상위 스택 구조 생성 (depth: 스플래시 스크린, 메인 스크린)

아래는 최상위 스택의 예시이다.

RootStack이라고 이름지었는데 스플래시(앱구동화면) 스크린과

실제 메인 스크린 간의 스위칭해준다.

 

RootStack을 생성하고, RootStack의 컴포넌트들을 JSX형식으로 리턴해주면 된다.

Navigator로 하나의 같은 depth들의 스크린들을 묶는다.

export interface RootStackParamList extends ParamListBase {
  Splash: undefined;
  Main: NavigatorScreenParams<MainStackParamList>;
}

// 앱 최상단 스택(스플래시, 실제화면 간 스위칭) 생성
const RootStack = createNativeStackNavigator<RootStackParamList>();

const RootStackNavigation = () => {
  return (
    <RootStack.Navigator>
      <RootStack.Screen
        name="Splash" {/* Screen에 이름을 꼭 지어줘야하며 고유한 값이어여 한다. */}
        component={SplashScreen}
        options={{ animation: "fade" }} {/* 여러 옵션으로 페이지를 조작할 수 있다. */}
      />
      <RootStack.Screen
        name="Main"
        component={MainStackNavigation}
        options={{ animation: "fade", headerShown: false }}
      />
    </RootStack.Navigator>
  );
};

 

// App.tsx에 RootStack을 넣어줬다.
const App = () => {
  return (
    <SafeAreaProvider>
      <NavigationContainer> {/* 전체 컨테이너*/}
        <RootStackNavigation /> {/*에 RootStack을 넣어줬다. */}
      </NavigationContainer>
    </SafeAreaProvider>
  );
};

 

 

2. 실제 페이지들이 수납될 스택 생성 ( depth: 바텀네비게이션, 로그인, 회원가입, ...)

위의 RootStack에서 메인페이지에 속한 컴포넌트로 넣었던 MainStackNavigation은 아래와 같다.

(StackNavigation도 하나의 컴포넌트라는 것을 유념하면 이해하기 편하다.)

export interface MainStackParamList extends ParamListBase {
  Login: {
    redirectTo?: string;
  };
  LoginWithEmail: undefined;
  BottomTab: NavigatorScreenParams<BottomTabParamList>;
}

// 실제 페이지 구조(바텀네비게이션, 로그인, 회원가입)
const MainStack = createNativeStackNavigator<MainStackParamList>();

const MainStackNavigation = () => {
  return (
    <MainStack.Navigator
      screenOptions={{
        presentation: "card", // 화면 전환 스타일을 카드 스타일로 설정
      }}
    >
      <MainStack.Screen
        name="BottomTab"
        component={BottomTabNavigation}
        options={{
          headerShown: false, // 헤더 안 보이게 설정
        }}
      />
      <MainStack.Screen
        name="Login"
        component={LoginPage}
        options={{
          presentation: "fullScreenModal",
          header: () => (
            <CustomHeader
              isClose={true}
              backgroundColor={theme.colors.gray.BACKGROUND}
            />
          ),
        }}
      />
      <MainStack.Screen
        name="LoginWithEmail"
        component={LoginPage}
        options={{
          header: () => <CustomHeader isGoBack={true} />,
        }}
      />
      <MainStack.Screen
        name="SignIn"
        component={SignInScreen}
        options={{
          headerShown: false,
          animation: "fade",
        }}
      />
      <MainStack.Screen
        name="AddNewDiary"
        component={AddNewDiary}
        options={{
          presentation: "fullScreenModal",
          header: () => <CustomHeader isClose={true} />,
        }}
      />
    </MainStack.Navigator>
  );
};

 

 

3. 바텀 탭 생성 (홈, 주제, 일기장, 내정보)

mainStack에 포함되어 있던 BottomTab의 구조는 아래와 같다.

요런 바텀 네비게이션이 포함된 스크린의 묶음이다.

BottomTab으로 묶어서 구성하였다.

이렇게 묶으면 React Navigation이 자동으로 바텀탭바 UI를 생성해준다.

export interface BottomTabParamList extends ParamListBase {
  Home: undefined;
  Topics: undefined;
  Diary: undefined;
  AddNewDiary: undefined;
  MyPage: undefined;
}

// 탭(홈, 일기, 새로운 글짓기, 내정보)
const BottomTab = createBottomTabNavigator<BottomTabParamList>();

const BottomTabNavigation = ({
  navigation,
}: {
  navigation: CombinedNavigationProp;
}) => {
  return (
    <BottomTab.Navigator
      initialRouteName="Home" // 초기 화면을 Home으로 설정
      screenOptions={{
        // 키보드가 나타날 때 탭 바 숨김
        tabBarHideOnKeyboard: true,
      }}
    >
      <BottomTab.Screen //
        name="Home"
        component={Home}
        options={{
          tabBarLabel: "홈",
          header: () => <CustomHeader type="HOME" />,
        }}
      />
      <BottomTab.Screen
        name="Topics"
        component={Topics}
        options={{
          tabBarLabel: "주제",
          header: () => <CustomHeader />,
        }}
      />
      <BottomTab.Screen
        name="ToAddNewDiary"
        component={() => null}
        options={{
          tabBarLabel: "글쓰기",
          tabBarButton: (props) => (
            <CustomTabBarButton
              {...props}
              onPress={() => addNewDiaryWithAuth(navigation)}
            />
          ),
        }}
      />
      <BottomTab.Screen
        name="Diary"
        component={Diary}
        options={{
          tabBarLabel: "일기장",
          header: () => <CustomHeader rightNode={<ToMyDiary />} />,
        }}
      />
      <BottomTab.Screen
        name="MyPage"
        component={MyPage}
        options={{
          tabBarLabel: "내정보",
          header: () => <CustomHeader />,
        }}
      />
    </BottomTab.Navigator>
  );
};

 

 

지금까지의 구조

 

지금까지 내가 만든 구조를 아주 대충 표현하자면 아래와 같다.😂

'로그인, 이메일로 회원가입, 새글쓰기, 바텀네비게이션' 등의 페이지도 지금 다 묶여있는 상태인데,

회원가입/로그인 플로우에 포함된 페이지가 더 많이 나올거 같아 signinStackNavigation같은걸 새로 만들어 묶을지도 모른다.

- 스플래시
- 메인 ㄱ
       ㅏ 로그인
       ㅏ 회원가입
       ㅏ 바텀네비화면들    ㅜ 홈(큐레이션)
       ㄴ 새글쓰기         ㅏ 주제
                        ㅏ 일기장
                        ㄴ 내정보