개발/프론트엔드

리액트 네이티브 Nesting navigators

paice 2024. 9. 2. 11:05

배경

 

리액트 네이티브 프로젝트를 처음 시작할 때, 페이지 간 이동을 쉽게 구현하기 위해 `Tab Navigator`와 `Stack Navigator`를 함께 사용하고자 했다.

`Tab Navigator`는 하단에 탭 메뉴를 배치하여 사용자 경험을 향상시키는 데 유용하며,

`Stack Navigator`는 각 탭 내에서 화면 간 전환을 관리하는 데 효과적이다.

이 두 종류 말고도 'Drawer navigation' 도 존재하는데, 화면 간 이동을 위해 왼쪽(or 오른쪽)의 서랍을 사용하는 것이다.

(Sidenavigation을 만들 때 사용하는 것 같다.)

 

문제 발생


Tab Navigator와 Stack Navigator를 함께 사용하는 과정에서 두 개의 NavigationContainer를 사용하면 안 된다는 문제에 직면했다. 이 문제는 React Navigation 라이브러리의 기본 원칙에 위배되기 때문에 발생하였는데,

NavigationContainer는 네비게이션 상태를 관리하는 최상위 컴포넌트로, 앱 내에서 단 하나만 존재해야 한다.

이를 위반할 경우, 충돌이 발생하여 node_modules에서 에러가 발생하는 것을 볼 수 있다.


문제의 원인


`NavigationContainer`는 리액트 네이티브 앱의 네비게이션 상태를 전역적으로 관리한다. 따라서 앱의 루트 컴포넌트에 한 번만 정의되어야 하며, 여러 개의 `NavigationContainer`를 중복해서 사용하면 각각의 네비게이터들이 독립적으로 동작하게 되어 에러가 발생하게 된다.


해결 방법


`Stack Navigator`와 `Tab Navigator`를 동시에 사용하려면, 둘 중 하나를 다른 네비게이터의 자식으로 중첩하여 사용해야 한다.

예를 들어, `Tab Navigator`를 최상위 네비게이터로 설정하고, 각 탭 내부에 `Stack Navigator`를 배치하여 독립적인 화면 전환을 관리할 수 있다.

반대인 경우도 마찬가지!


아래는 `Stack Navigator`를 최상위에 두고, 특정 화면에 `Tab Navigator`를 중첩하여 사용하는 코드 예시이다.

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();

function HomeTabs() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Settings" component={SettingsScreen} />
    </Tab.Navigator>
  );
}

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="HomeTabs" component={HomeTabs} />
        <Stack.Screen name="Details" component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

function HomeScreen() {
  return (
    // ... 홈 화면의 JSX 코드 ...
  );
}

function SettingsScreen() {
  return (
    // ... 설정 화면의 JSX 코드 ...
  );
}

function DetailsScreen() {
  return (
    // ... 상세 화면의 JSX 코드 ...
  );
}


위 코드에서는 `HomeTabs`라는 컴포넌트 안에 `Tab Navigator`가 있고, `Stack Navigator`의 한 화면으로 이를 포함시킨 형태가 된다. 이렇게 하면 두 네비게이터가 충돌하지 않고 정상적으로 동작한다.

결론


리액트 네이티브 프로젝트에서 `Stack Navigator`와 `Tab Navigator`를 함께 사용할 때는 반드시 하나의 `NavigationContainer` 만 사용해야 한다. 이를 통해 앱의 네비게이션 상태를 일관성 있게 관리하고, 페이지 간 이동이 원활하게 이루어지도록 할 수 있다. 


네비게이션의 Nesting navigators(네비게이터 중첩) 에 관한 공식 문서 정리

 

네비게이터 중첩(Nesting Navigators)은 다른 네비게이터의 화면 내부에 네비게이터를 렌더링하는 것을 의미한다.

예를 들어, Tab Navigator를 Stack Navigator의 한 화면으로 중첩할 수 있다. 아래는 Tab Navigator를 Stack Navigator 내부에 중첩한 예시이다.

function Home() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Feed" component={Feed} />
      <Tab.Screen name="Messages" component={Messages} />
    </Tab.Navigator>
  );
}

function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen
          name="Home"
          component={Home}
          options={{ headerShown: false }}
        />
        <Stack.Screen name="Profile" component={Profile} />
        <Stack.Screen name="Settings" component={Settings} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

위 예시에서 Home 컴포넌트에는 Tab Navigator가 포함되어 있으며, Stack Navigator 내의 한 화면으로 설정된다. 이는 Stack Navigator 내에서 Tab Navigator를 중첩하는 대표적인 방식이다.

  • Stack.Navigator
    • Home( Tab.Navigator)
      • Feed( Screen)
      • Messages( Screen)
    • Profile( Screen)
    • Settings( Screen)

구조는 이런 식이다.

 

아니면, Group Component를 사용해 보는 것은 어떨까?

 

Group components는 네비게이터 내에서 여러 화면을 그룹화하여 관리 목적으로 사용된다.

별도의 네비게이터를 사용하는 대신 Group Component를 활용할 수 있다는 장점이 있다.

또한, 그룹 내의 화면들에 공통된 옵션을 적용하는 데도 유용하다.

예를 들어, 특정 그룹의 모든 화면에 동일한 헤더 스타일을 적용할 수 있다.

 

Group component는 createXNavigator 함수에서 반환되는 Navigator 객체의 자식 요소로 사용된다.

아래 예시는 Stack Navigator를 생성하고, 이를 그룹화하는 방법을 보여준다.

const Stack = createStackNavigator(); // Stack은 Screen과 Navigator 속성을 포함한다.

<Stack.Navigator>
  <Stack.Group
    screenOptions={{ headerStyle: { backgroundColor: 'papayawhip' } }}
  >
    <Stack.Screen name="Home" component={HomeScreen} />
    <Stack.Screen name="Profile" component={ProfileScreen} />
  </Stack.Group>
  <Stack.Group screenOptions={{ presentation: 'modal' }}>
    <Stack.Screen name="Search" component={SearchScreen} />
    <Stack.Screen name="Share" component={ShareScreen} />
  </Stack.Group>
</Stack.Navigator>

 

이처럼 Stack.Group을 사용하면 화면을 그룹화하여 관리하기 쉬워지고, 네비게이터 중첩 없이도 원하는 UI를 구현할 수 있다!

 

필자는 Stack Navigator를 사용하면서도, 특정 화면에서만 탭 바를 노출하는 등, 사용자 경험을 고려한 네비게이션 구조를 반영하기 위해 해당 문서를 참고하였다.