React Native의 네비게이션 설정 법 (1)

eeensu·2026년 5월 10일

React Native

목록 보기
36/41

앱에서 사용자가 여러 화면(페이지) 사이를 이동하고 탐색할 수 있게 해주는 시스템을 의미한다.

웹에서는 <a> 태그나 URL 변경으로 페이지 이동을 하지만, 모바일 앱은 화면이 하나씩 쌓이거나 탭으로 전환되는 방식이 일반적이기 때문에 네비게이션이 훨씬 더 중요하고 복잡하다.

이때 RN에서 표준적으로 사용되는 라이브러리가 바로 @react-navigation/native 이다.


1. 특징

  • 컴포넌트 기반 : 라우트 설정을 정적인 파일 등이 아닌 자바스크립트 컴포넌트로 정의한다.
  • 네이티브 뷰 바인딩 : 과거에는 자바스크립트로만 애니메이션을 처리했으나, 현재는 @react-navigation/native-stack을 통해 iOS의 UINavigationController와 Android의 Fragment를 직접 제어한다. 이를 위해 react-native-screens 라이브러리를 의존성으로 사용한다.
  • 상태 관리 : 앱의 네비게이션 트리(Navigation Tree)와 히스토리 상태를 메모리상에서 관리하며, 딥링크(Deep Link) 연동 시 URL을 네비게이션 상태 객체로 변환한다.

2. 핵심 요소

  • NavigationContainer
    • 네비게이션 트리의 최상위 루트(Root) 컴포넌트다.
    • 네비게이션 상태(State)를 관리하고, 딥링크 처리 및 앱의 뒤로 가기 버튼(Back Handler) 연동을 담당한다.
    • React의 Context API를 사용하여 하위 컴포넌트에 네비게이션 객체를 주입한다.
  • Navigator
    • 화면의 전환 방식과 구조를 정의한다.
    • Stack, Tab, Drawer 등이 있으며, 서로 중첩(Nesting)하여 복합적인 구조를 만든다.
  • Screen
    • 실제 렌더링 될 React 화면 컴포넌트를 라우트 이름과 메핑한다.
    • 웹으로 치면 Page 개념이다.

3. 주요 네비게이터 종류

  • Native Stack Navigator
    • 동작 : 새로운 화면을 스택 자료구조(LIFO)처럼 쌓는다.
    • react-native-screens 라이브러리를 내부에서 사용하여 OS의 네이티브 네비게이션 primitive를 사용한다.
    • JS 스레드가 차단되어도 화면 전환 애니메이션이 부드럽게 작동한다.
    • 일반적인 페이지 이동(상세 페이지 진입 등)에 사용된다.
  • Bottom Tab Navigator
    • 동작 : 하단 탭을 통해 여러 화면을 전환한다.
    • 탭을 이동해도 이전 화면이 언마운트(Unmount) 되지 않고 메모리에 유지된다(설정으로 변경 가능).
    • 앱의 메인 카테고리 분류 등에 사용된다.
  • Drawer Navigator
    • 동작 : 화면 측면에서 제스처로 열리는 네비게이션 뷰를 생성한다.
    • react-native-reanimatedreact-native-gesture-handler를 사용하여 제스처 처리를 최적화한다.

4. 데이터 흐름과 hooks

  • useNavigation
    • navigation 객체에 접근한다.
    • navigate('RouteName', params)
      • Stack: 같은 라우트면 동작 X, 다른 라우트면 push.
        getId가 지정된 경우 같은 ID의 화면이 스택에 있으면 그 화면으로 이동하며 params를 갱신.
      • Tab/Drawer: 해당 탭/스크린으로 전환하며 params 업데이트.
    • push('RouteName', params) : Stack 전용. 동일 화면이라도 무조건 새로 쌓는다.
    • goBack() : 현재 화면을 닫고 이전 화면으로 돌아간다.
    • pop(count?) : Stack 전용. count만큼 뒤로 간다.
    • popTo('RouteName', params?) : Stack 전용. 스택에 있는 특정 화면까지 한 번에 돌아간다.
    • popToTop() : Stack 전용. 스택의 첫 화면까지 돌아간다.
    • replace('RouteName', params?) : 현재 화면을 새 화면으로 교체. 로그인 → 메인 전환에 자주 사용.
    • reset({ index, routes }) : 네비게이션 히스토리를 통째로 새 상태로 교체.
    • setParams(params) : 현재 화면의 params를 부분 갱신(shallow merge).
  • useRoute
    • 현재 활성화된 화면의 route 객체에 접근한다.
    • route.params : 이전 화면에서 전달받은 매개변수 객체
    • route.name : 현재 화면의 라우트 이름
    • route.key : 화면의 고유 식별자

6. 바텀 메뉴 네비게이션 UI 구현

(...전략...)

💡 headerShown: false를 탭에서 끄는 이유는 "탭이라서 헤더가 없어야" 한다기보다,
보통 탭이 RootStack 안에 중첩되어 있어 헤더는 RootStack이 그리고, 안쪽 탭의 헤더는 꺼서 헤더가 두 번 쌓이지 않게 하는 패턴이기 때문이다.
탭 화면별로 다른 헤더가 필요하면 오히려 켜두기도 한다.


6. 바텀 메뉴 네비게이션 UI 구현

React Native에서 하단 탭 메뉴(Bottom Tab Navigation)를 구현하려면 @react-navigation/native 코어 패키지 외에 @react-navigation/bottom-tabs라는 별도의 패키지가 필요하다.

최신 v7에서는 createBottomTabNavigator에 객체 설정값(Configuration Object)을 넘겨서 정의한다.

import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

import { createStaticNavigation } from '@react-navigation/native';
import { View, Text } from 'react-native';
// 아이콘 라이브러리 (예: react-native-vector-icons 혹은 lucide-react-native)
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; 
// 1. 탭에 들어갈 화면 컴포넌트들
const HomeScreen = () => <View><Text>홈 화면</Text></View>;
const SettingsScreen = () => <View><Text>설정 화면</Text></View>;
// 2. 바텀 탭 네비게이터 정의 (객체 방식)
const MyBottomTabs = createBottomTabNavigator({
  screenOptions: {
    // 탭 바 활성/비활성 색상
    tabBarActiveTintColor: '#e91e63', // 선택된 탭 색상
    tabBarInactiveTintColor: 'gray',  // 선택 안 된 탭 색상
    headerShown: false,               // 상단 헤더 숨김 (보통 탭에선 숨김)
    tabBarStyle: { height: 60, paddingBottom: 5 }, // 탭 바 높이 및 스타일
  },
  screens: {
    Home: {
      screen: HomeScreen,
      options: {
        title: '홈', // 탭 아래 텍스트
        // 탭 아이콘 렌더링 함수 (focused 여부에 따라 아이콘 변경 가능)
        tabBarIcon: ({ color, size, focused }) => (
          <Icon name={focused ? "home" : "home-outline"} color={color} size={size} />
        ),
      },
    },
    Settings: {
      screen: SettingsScreen,
      options: {
        title: '설정',
        tabBarIcon: ({ color, size, focused }) => (
          <Icon name={focused ? "cog" : "cog-outline"} color={color} size={size} />
        ),
        tabBarBadge: 3, // 알림 뱃지 (숫자 3 표시)
      },
    },
  },
});
// 3. 네비게이션 생성
const Navigation = createStaticNavigation(MyBottomTabs);
export default function App() {
  return <Navigation />;
}

보통 앱은 "로그인(Stack) -> 메인 탭(Tab) -> 상세 페이지(Stack)" 구조로 되어 있다.

따라서 탭 네비게이터를 단독으로 쓰기보다는, 루트 스택(Root Stack)의 한 화면으로 탭 네비게이터를 등록해서 호출한다.

// RootStack (전체 앱 구조)
const RootStack = createNativeStackNavigator({
  screens: {
    // 1. 로그인 화면 (스택)
    Login: LoginScreen,
    
    // 2. 메인 탭 화면 (여기에 위에서 만든 MyBottomTabs를 통째로 넣음)
    MainTab: {
      screen: MyBottomTabs, // 아까 만든 탭 네비게이터
      options: { headerShown: false }, // 탭 자체의 헤더도 숨김
    },
    
    // 3. 탭 밖의 상세 페이지 (탭 바를 가리면서 열림)
    Detail: DetailScreen,
  },
});

Login에서 로그인이 완료되면 navigation.replace('MainTab')을 호출한 후 Bottom Tab이 있는 메인 홈 화면으로 전환된다.

탭 내부에서 아이템을 클릭하면 navigation.navigate('Detail')을 호출한다.

Detail 화면은 탭 네비게이터보다 상위 스택에 있으므로, 탭 바 위로 덮어씌워지며 열린다. (일반적인 앱 UX)

headerShown: false를 탭에서 끄는 이유는 "탭이라서 헤더가 없어야" 한다기보다,
보통 탭이 RootStack 안에 중첩되어 있어 헤더는 RootStack이 그리고, 안쪽 탭의 헤더는 꺼서 헤더가 두 번 쌓이지 않게 하는 패턴이기 때문이다.
탭 화면별로 다른 헤더가 필요하면 오히려 켜두기도 한다.

profile
안녕하세요! 프론트엔드 개발자입니다! (2024/03 ~)

0개의 댓글