Navigation 기초 — React Navigation으로 화면 이동하기
React Navigation은 React Native에서 가장 널리 사용되는 네비게이션 라이브러리입니다.
모바일 앱은 여러 화면으로 구성됩니다. 웹에서 URL 기반 라우팅을 하듯, React Native에서는 React Navigation 으로 화면 전환을 관리합니다.
설치
# 핵심 패키지
npm install @react-navigation/native
# 필수 의존성 (Expo)
npx expo install react-native-screens react-native-safe-area-context
# 필수 의존성 (CLI)
npm install react-native-screens react-native-safe-area-context
cd ios && pod install
# Stack Navigator
npm install @react-navigation/native-stack
# Bottom Tab Navigator
npm install @react-navigation/bottom-tabs
# Drawer Navigator
npm install @react-navigation/drawer
Stack Navigator — 화면 쌓기
웹의 페이지 이동과 가장 유사한 패턴입니다. 새 화면이 위에 쌓이고, 뒤로 가면 제거됩니다.
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { View, Text, Pressable, StyleSheet } from 'react-native';
// 타입 정의 — 화면별 파라미터
type RootStackParamList = {
Home: undefined;
Detail: { id: number; title: string };
Settings: undefined;
};
const Stack = createNativeStackNavigator<RootStackParamList>();
// 앱 진입점
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: '홈' }}
/>
<Stack.Screen
name="Detail"
component={DetailScreen}
options={{ title: '상세' }}
/>
<Stack.Screen
name="Settings"
component={SettingsScreen}
options={{ title: '설정' }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
화면 이동과 파라미터 전달
import { NativeStackScreenProps } from '@react-navigation/native-stack';
type HomeProps = NativeStackScreenProps<RootStackParamList, 'Home'>;
function HomeScreen({ navigation }: HomeProps) {
return (
<View style={styles.container}>
<Pressable
style={styles.button}
// 파라미터와 함께 화면 이동
onPress={() => navigation.navigate('Detail', {
id: 1,
title: '첫 번째 아이템',
})}
>
<Text style={styles.buttonText}>상세 화면으로</Text>
</Pressable>
{/* push — 같은 화면도 중복 쌓기 가능 */}
<Pressable onPress={() => navigation.push('Detail', { id: 2, title: '두 번째' })}>
<Text>Push로 이동</Text>
</Pressable>
</View>
);
}
type DetailProps = NativeStackScreenProps<RootStackParamList, 'Detail'>;
function DetailScreen({ route, navigation }: DetailProps) {
// 전달받은 파라미터
const { id, title } = route.params;
return (
<View style={styles.container}>
<Text>ID: {id}</Text>
<Text>제목: {title}</Text>
{/* 뒤로 가기 */}
<Pressable onPress={() => navigation.goBack()}>
<Text>뒤로</Text>
</Pressable>
{/* 특정 화면으로 돌아가기 */}
<Pressable onPress={() => navigation.popToTop()}>
<Text>최상위로</Text>
</Pressable>
</View>
);
}
navigate와push의 차이가 면접에서 나올 수 있습니다.navigate는 이미 스택에 있는 화면이면 그 화면으로 이동하고,push는 항상 새 화면을 쌓습니다.
Bottom Tab Navigator
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
type TabParamList = {
HomeTab: undefined;
SearchTab: undefined;
ProfileTab: undefined;
};
const Tab = createBottomTabNavigator<TabParamList>();
function TabNavigator() {
return (
<Tab.Navigator
screenOptions={{
tabBarActiveTintColor: '#007AFF',
tabBarInactiveTintColor: '#999',
tabBarStyle: {
height: 60,
paddingBottom: 8,
},
}}
>
<Tab.Screen
name="HomeTab"
component={HomeScreen}
options={{
title: '홈',
tabBarIcon: ({ color, size }) => (
<Text style={{ color, fontSize: size }}>🏠</Text>
),
tabBarBadge: 3, // 배지 표시
}}
/>
<Tab.Screen
name="SearchTab"
component={SearchScreen}
options={{
title: '검색',
tabBarIcon: ({ color, size }) => (
<Text style={{ color, fontSize: size }}>🔍</Text>
),
}}
/>
<Tab.Screen
name="ProfileTab"
component={ProfileScreen}
options={{
title: '프로필',
tabBarIcon: ({ color, size }) => (
<Text style={{ color, fontSize: size }}>👤</Text>
),
}}
/>
</Tab.Navigator>
);
}
네비게이터 중첩
실제 앱은 Tab 안에 Stack이 있는 형태가 일반적입니다.
// Tab + Stack 중첩 구조
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
{/* 메인 탭 */}
<Stack.Screen
name="MainTabs"
component={TabNavigator}
options={{ headerShown: false }}
/>
{/* 탭 위에 쌓이는 모달/상세 화면 */}
<Stack.Screen name="Detail" component={DetailScreen} />
<Stack.Screen
name="Modal"
component={ModalScreen}
options={{ presentation: 'modal' }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
헤더 커스터마이징
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
title: '홈',
headerStyle: {
backgroundColor: '#007AFF',
},
headerTintColor: 'white',
headerTitleStyle: {
fontWeight: 'bold',
},
// 커스텀 헤더 버튼
headerRight: () => (
<Pressable onPress={() => alert('설정')}>
<Text style={{ color: 'white' }}>설정</Text>
</Pressable>
),
// 헤더 숨기기
// headerShown: false,
}}
/>
동적 헤더 옵션
function DetailScreen({ navigation, route }: DetailProps) {
// 화면 진입 후 헤더 동적 변경
React.useLayoutEffect(() => {
navigation.setOptions({
title: route.params.title,
headerRight: () => (
<Pressable onPress={handleShare}>
<Text>공유</Text>
</Pressable>
),
});
}, [navigation, route.params.title]);
return <View />;
}
화면 간 데이터 전달 정리
// 1. params로 전달 (순방향)
navigation.navigate('Detail', { id: 1 });
// 받기: route.params.id
// 2. 콜백으로 결과 받기 (역방향)
// 화면 A
navigation.navigate('Picker', {
onSelect: (value: string) => setValue(value),
});
// 화면 B
route.params.onSelect('선택된 값');
navigation.goBack();
// 3. 전역 상태 사용 (권장)
// Context API, Zustand, Redux 등으로 관리
네비게이션 이벤트
import { useFocusEffect } from '@react-navigation/native';
import { useCallback } from 'react';
function ProfileScreen() {
// 화면에 포커스될 때마다 실행 (탭 전환 시 유용)
useFocusEffect(
useCallback(() => {
console.log('화면 포커스됨 — 데이터 새로고침');
return () => {
console.log('화면 벗어남 — 정리 작업');
};
}, [])
);
return <View />;
}
정리
- Stack: 화면을 쌓고 뒤로 가는 기본 네비게이션
- Bottom Tab: 하단 탭으로 주요 섹션 전환
- Drawer: 사이드 메뉴
- 실제 앱에서는 Tab 안에 Stack을 중첩 하는 패턴이 가장 흔합니다
- TypeScript로
ParamList타입을 정의하면 화면 이동 시 타입 안전성을 확보할 수 있습니다 navigate는 기존 화면으로 이동,push는 항상 새 화면 생성 — 이 차이를 기억하세요
댓글 로딩 중...