태그
목차

탭 네비게이션

생성일: 2024-04-05

수정일: 2024-04-05

모바일 앱에서 가장 일반적인 탐색 스타일 중 하나는 탭 기반 탐색이다. 이는 화면 하단이나 헤더 바로 아래(또는 헤더 대신)에 탭이 있는 것을 말한다.

이 가이드에서는 createBottomTabNavigator 에 대해 다룬다. createMaterialBottomTabNavigatorcreateMaterialTopTabNavigator 를 사용하여 앱에 탭을 추가할 수도 있다.

시작하기 전에 먼저 @react-navigation/bottom-tabs 를 설치한다:

npm install @react-navigation/bottom-tabs

탭 기반 탐색의 기본 예제


import * as React from 'react';
import { Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

function HomeScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Home!</Text>
    </View>
  );
}

function SettingsScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Settings!</Text>
    </View>
  );
}

const Tab = createBottomTabNavigator();

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

외관 커스터마이징하기

스택 내비게이터를 커스터마이징 하는 방식과 유사하다 - 탭 내비게이터를 초기화할 때 사용하는 속성들과 각 화면별로 커스터마이징 할 수 있는 options 속성들이 있다.

// Expo를 사용한다면 @expo/vector-icons/Ionicons에서 import할 수 있고,
// 그렇지 않다면 react-native-vector-icons/Ionicons에서 import한다.
import Ionicons from 'react-native-vector-icons/Ionicons';

// (...)

export default function App() {
  return (
    <NavigationContainer>
      <Tab.Navigator
        screenOptions={({ route }) => ({
          tabBarIcon: ({ focused, color, size }) => {
            let iconName;

            if (route.name === 'Home') {
              iconName = focused
                ? 'ios-information-circle'
                : 'ios-information-circle-outline';
            } else if (route.name === 'Settings') {
              iconName = focused ? 'ios-list' : 'ios-list-outline';
            }

            // 여기에서 원하는 컴포넌트를 사용할 수 있다!
            return <Ionicons name={iconName} size={size} color={color} />;
          },
          tabBarActiveTintColor: 'tomato',
          tabBarInactiveTintColor: 'gray',
        })}
      >
        <Tab.Screen name="Home" component={HomeScreen} />
        <Tab.Screen name="Settings" component={SettingsScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

위의 예시를 하나씩 살펴보자:

아이콘에 배지 추가하기

때로는 일부 아이콘에 배지를 추가하고 싶을 수 있다. 이를 위해 tabBarBadge 옵션을 사용할 수 있다:

<Tab.Screen name="Home" component={HomeScreen} options={{ tabBarBadge: 3 }} />

UI 관점에서 이 컴포넌트는 사용 준비가 되어 있지만, 여전히 React Context, Redux, MobX 또는 이벤트 전파자를 사용하여 어딘가에서 적절하게 배지 개수를 전달하는 방법을 찾아야 한다.

탭 간 이동하기

한 탭에서 다른 탭으로 전환하는 것은 익숙한 API인 navigation.navigate 를 사용한다.

function HomeScreen({ navigation }) {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Home!</Text>
      <Button
        title="Go to Settings"
        onPress={() => navigation.navigate('Settings')}
      />
    </View>
  );
}

function SettingsScreen({ navigation }) {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Settings!</Text>
      <Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
    </View>
  );
}

각 탭에 대한 스택 내비게이터

종종 탭은 단일 화면만을 표시하지 않는다 - 예를 들어, 트위터 피드에서 트윗을 탭하면 해당 탭 내에서 모든 답글이 표시되는 새 화면으로 이동한다. 이를 각 탭 내에 별도의 네비게이션 스택이 있다고 생각할 수 있으며, 이것이 바로 React Navigation에서 모델링하는 방식이다.

import * as React from 'react';
import { Button, Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

function DetailsScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Details!</Text>
    </View>
  );
}

function HomeScreen({ navigation }) {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Home screen</Text>
      <Button
        title="Go to Details"
        onPress={() => navigation.navigate('Details')}
      />
    </View>
  );
}

function SettingsScreen({ navigation }) {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Settings screen</Text>
      <Button
        title="Go to Details"
        onPress={() => navigation.navigate('Details')}
      />
    </View>
  );
}

const HomeStack = createNativeStackNavigator();

function HomeStackScreen() {
  return (
    <HomeStack.Navigator>
      <HomeStack.Screen name="Home" component={HomeScreen} />
      <HomeStack.Screen name="Details" component={DetailsScreen} />
    </HomeStack.Navigator>
  );
}

const SettingsStack = createNativeStackNavigator();

function SettingsStackScreen() {
  return (
    <SettingsStack.Navigator>
      <SettingsStack.Screen name="Settings" component={SettingsScreen} />
      <SettingsStack.Screen name="Details" component={DetailsScreen} />
    </SettingsStack.Navigator>
  );
}

const Tab = createBottomTabNavigator();

export default function App() {
  return (
    <NavigationContainer>
      <Tab.Navigator screenOptions={{ headerShown: false }}>
        <Tab.Screen name="HomeStack" component={HomeStackScreen} />
        <Tab.Screen name="SettingsStack" component={SettingsStackScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

왜 TabNavigator가 필요할까? 다른 독립 컴포넌트로는 부족한가?

앱에서 사용하는 네비게이션 라이브러리에 통합하지 않고 독립된 탭바 컴포넌트를 사용하려고 시도하는 경우가 많다. 경우에 따라서는 괜찮을 수 있다! 하지만 이렇게 하면 예기치 않은 불편한 문제에 직면할 수 있다는 점을 알아야 한다.

React Navigation의 탭 내비게이터를 사용하는 이유는 독립적인 탭바 컴포넌트를 사용하는 것보다 더 많은 이점이 있기 때문이다.

  1. 플랫폼 기본 동작 지원

    • 탭 내비게이터는 Android 백 버튼 처리를 자동으로 해준다. 반면 독립된 컴포넌트는 대개 이를 지원하지 않는다.
  2. 탐색 작업의 일관성

    • 탭 간 이동 뿐만 아니라 "이 탭으로 이동한 다음 이 화면으로 이동"과 같은 작업을 하나의 API(navigation.navigate)로 수행할 수 있다.
    • 독립된 컴포넌트를 사용하면 탭 이동과 화면 이동을 분리된 API로 처리해야 하므로 작업이 더 복잡해진다.
  3. 모바일 UI 디자인 세부 사항 처리

    • 모바일 UI에는 다양한 디자인 세부 사항이 있으며, 각 컴포넌트는 레이아웃이나 다른 컴포넌트의 존재를 인식해야 한다.
    • 예를 들어 반투명 탭바가 있다면 콘텐츠가 그 아래로 스크롤되어야 하고, 스크롤 뷰에는 탭바 높이만큼의 여백이 있어야 한다.
    • 또한 탭바를 두 번 탭하면 활성 탐색 스택이 상단으로 이동하고, 다시 탭하면 그 스택의 활성 스크롤 뷰가 맨 위로 스크롤되어야 한다.
    • React Navigation은 이러한 디자인 세부 사항을 점차 구현해 나갈 예정이다.
  4. 일관된 탐색 경험

    • 독립된 탭 컴포넌트를 사용하면 앱의 다른 부분과 탐색 경험이 일관되지 않을 수 있다.
    • React Navigation을 사용하면 전체 앱에서 일관된 탐색 경험을 제공할 수 있다.

물론 간단한 앱의 경우 독립된 탭 컴포넌트로도 충분할 수 있지만, 복잡한 앱이나 향후 기능 확장을 고려한다면 React Navigation의 탭 내비게이터를 사용하는 것이 더 나은 선택이 될 것이다.

탭 내비게이터에 스택이 포함되어 있는 상황에서 특정 화면에서 탭바를 숨기고 싶은 경우

여기 문서를 확인한다.