- 리액트 네이티브에서는 내비게이션 기능을 지원하지 않아, 외부 라이브러리를 이용 -> 리액트 내비게이션n(React Navigation) 라이브러리
- 화면 간의 전환을 관리하는 등에 사용.
8.1 리액트 네비게이션
기본 세팅
npx create-expo-app my-app
cd navigation
npm install @react-navigation/native
npm install styled-components
- 리액트 내비게이션 라이브러리가 지원하는 내비게이션 종류 : 스택(stack) 내비게이션, 탭(tab) 내비게이션, 드로어(drawer) 내비게이션
내비게이션 구조
- Screen 컴포넌트 : 화면으로 사용되는 컴포넌트
- 필요한 속성 : name(화면 이름), component(화면으로 사용될 컴포넌트)
- 화면으로 사용되는 컴포넌트에는 항상 navigation과 route가 props로 전달됨.
- Navigator 컴포넌트 : 화면을 관리하는 중간 관리자 역할, 내비게이션을 구성하며 여러 개의 Screen 컴포넌트를 자식 컴포넌트로 갖고 있음.
- NavigationContainer 컴포넌트 : 내비게이션의 계층 구조와 상태를 관리하는 컨테이너 역할, 최상위 컴포넌트
설정 우선 순위
- Navigator 컴포넌트의 속성으로 수정 -> 자식 컴포넌트로 존재하는 모든 컴포넌트에 적용됨.
- Screen 컴포넌트의 속성으로 수정
- 화면으로 사용되는 컴포넌트의 props로 전달되는 navigation을 이용해서 수정
- 작은 범위일수록 우선순위가 높음.
폴더 구조와 라이브러리 설치
# 대부분의 내비게이션에서 반드시 필요한 종속성 설치
npx expo install react-native-gesture-handler
npx expo install react-native-reanimated
npx expo install react-native-screens
npx expo install react-native-safe-area-context
npx expo install @react-native-community/masked-view
8.2 스택 네비게이션
npm install @react-navigation/stack
- 일반적으로 가장 많이 사용되는 내비게이션
- 현재 화면 위에 다른 화면을 쌓으면서 화면 이동 (push / pop)
// app/navigations/Stack.js
import React from "react";
import { createStackNavigator } from "@react-navigation/stack";
import Home from "../screens/Home";
import List from "../screens/List";
import Item from "../screens/Item";
const Stack = createStackNavigator();
const StackNavigation = () => {
return (
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="List" component={List} />
<Stack.Screen name="Item" component={Item} />
</Stack.Navigator>
);
};
export default StackNavigation;
// app/App.js
import React from "react";
// import { NavigationContainer } from "@react-navigation/native";
import StackNavigation from "./navigations/Stack";
const App = () => {
return <StackNavigation />;
};
export default App;
- createStackNavigator() : 스택 내비게이션 생성 -> 화면을 구성하는 Screen 컴포넌트, Screen 컴포넌트를 관리하는 Navigator 컴포넌트
- initialRouteName : 가장 먼저 보일 컴포넌트를 명시적으로 표시 (default : 가장 위에 있는 컴포넌트가 제일 먼저 보임.)
- Expo SDK를 사용하는 경우 Expo SDK 내에서 기본 내비게이션 환경이 자동으로 설정되므로, NavigationContainer를 명시적으로 추가하지 않아도 됨. 하지만, 이는 Expo의 관리되는 환경에서만 가능함.
화면 이동
// app/screens/List.js
const List = ({ navigation }) => {
const onPress = (item) =>
navigation.navigate("Item", { id: item.id, name: item.name });
return (
<Container>
<StyledText>List</StyledText>
{items.map((item) => (
<Button key={item.id} title={item.name} onPress={() => onPress(item)} />
))}
</Container>
);
};
// app/screens/Item.js
const Item = ({ route }) => {
return (
<Container>
<StyledText>Item</StyledText>
<StyledText>ID: {route.params.id}</StyledText>
<StyledText>Name: {route.params.name}</StyledText>
</Container>
);
};
- Screen 컴포넌트의 components로 지정된 컴포넌트는 화면으로 이용되고 navigation이 props로 전달됨.
- navigation.navigate('Screen 컴포넌트의 name')
- 2번째 파라미터에 객체 형식으로 데이터를 전달할 수 있음. -> 컴포넌트의 props로 전달되는 route.params를 통해 받을 수 있음.
화면 배경색 수정하기
문제 : 내비게이션은 화면 전체를 차지하고 있지만 화면으로 사용된 컴포넌트의 영역이 전체를 차지하고 있지 않아 뒤의 배경이 보이는 경우
- 스타일에 flex: 1을 설정
- Navigator 컴포넌트의 screenOptions 속성을 활용하여 스택 내비게이션의 화면 배경색 수정
const StackNavigation = () => {
return (
<Stack.Navigator
screenOptions={{ cardStyle: { backgroundColor: "lightblue" } }}
>
<Stack.Screen name="Home" component={Home} />
...
</Stack.Navigator>
);
};
- 화면의 배경색은 일반적으로 동일하게 사용하므로, Screen 컴포넌트보다 Navigator 컴포넌트의 screenOptions에 설정해서 화면 전체에 적용되도록 하는 것이 편함.
- 별도로 배경색을 지정하지 않으면 screenOptions에서 지정한 색으로 보임.
헤더 수정하기
- 스택 내비게이션의 헤더(header) : 뒤로 가기 버튼을 제공 / 타이틀(title)을 통해 현재 화면을 알려주는 역할
- 헤더 타이틀 default : Screen 컴포넌트의 name 속성
- name 자체를 수정해도 괜찮지만, name 속성을 이용한 곳을 모두 찾아서 수정해야 함.
- Screen 컴포넌트 options 속성에 있는 headerTitle 속성으로 수정 가능.
<Stack.Screen name="Item" component={Item} options={{ headerTitle: "Item Detail" }} />
- 모든 화면에서 같은 타이틀이 나타나게 하려면 Navigator 컴포넌트의 screenOptions 속성에 headerTitle 지정
- 헤더 스타일 수정 - headerStyle (ex. backgroundColor 등)
- 헤더 타이틀 스타일 수정 - headerTitleStyle (ex. fontSize 등)
<Stack.Navigator
screenOptions={{
cardStyle: { backgroundColor: "lightblue" },
headerStyle: {
height: 110,
backgroundColor: "#95a5a6",
borderBottomWidth: 5,
borderBottomColor: "#34495e",
},
headerTitleStyle: { color: "white", fontSize: 24 },
}}
>
- 타이틀 정렬(headerTitleAlign)은 현재 두 플랫폼 모두 center가 default로 바뀜.
- 타이틀 컴포넌트 변경 : 타이틀에 문자열이 아닌 것을 렌더링하고 싶을 때
-> headerTitle에 컴포넌트를 반환하는 함수를 지정- 함수가 설정되면 해당 함수의 파라미터로 style과 tintColor 등이 포함된 객체가 전달됨.
- style == headerTitleStyle / tintColor == headerTintColor
import { MaterialCommunityIcons } from "@expo/vector-icons";
...
<Stack.Navigator
screenOptions={{
headerStyle: { ... },
headerTitleStyle: { color: "white", fontSize: 24 },
headerTitle: ({ style }) => (
<MaterialCommunityIcons name="react" style={style} />
),
}}
>
Note vector-icons : Expo 프로젝트에서 기본적으로 설치되는 라이브러리
- 버튼 수정하기 (헤더 왼쪽에 있는 뒤로 가기 버튼)
- headerBackTitle : 버튼 타이틀 텍스트 변경
- 버튼 타이틀에 빈 값을 주고 싶으면 "" 혹은 null을 넣는다.
- headerBackTitle : 버튼 타이틀 텍스트 변경
<Stack.Screen
...
options={{ headerBackTitle: "Prev" }}
/>
- 버튼 스타일 수정하기
- headerBackTitleStyle : 버튼 타이틀 글자 색/크기 등의 스타일 지정
- headerTintColor : 헤더에 있는 요소 전체의 색을 통일 (headerTitleStyle / headerBackTitleStyle이 더 우선순위가 높음.)
<Stack.Screen
...
options={{
headerTitleStyle: { fontSize: 24 },
...
}}
/>
- 버튼 컴포넌트 변경
- headerBackImage : 뒤로 가기 버튼 아이콘 변경 (컴포넌트를 반환하는 함수를 전달해서 두 플랫폼이 동일한 이미지를 렌더링하게 함.)
headerBackImage: ({ tintColor }) => {
const style = {
marginRight: 5,
marginLeft: Platform.OS === "ios" ? 11 : 0,
};
return (
<MaterialCommunityIcons
name="keyboard-backspace"
size={30}
color={tintColor}
style={style}
/>
);
},
- headerLeft[headerRight] : 뒤로 가기 버튼의 이미지가 아니라 버튼 전체를 변경 (컴포넌트를 반환하는 함수 전달)
// app/screens/Item.js
const Item = ({ navigation, route }) => {
useLayoutEffect(() => {
navigation.setOptions({
headerBackTitleVisible: false,
headerTintColor: white,
headerLeft: ({ onPress, tintColor }) => {
return (
<MaterialCommunityIcons
name="keyboard-backspace"
size={30}
style={{ marginLeft: 11 }}
color={tintColor}
onPress={onPress}
/>
);
},
headerRight: ({ tintColor }) => (
<MaterialCommunityIcons
name="home-variant"
size={30}
style={{ marginRight: 11 }}
color={tintColor}
onPress={() => navigation.popToTop()}
/>
),
});
}, []);
return ( ... );
};
- headerLeft 함수의 파라미터 중 onPress에는 뒤로 가기 버튼 기능이 전달됨. (뒤로 가기 버튼의 기능을 그대로 기용하고 싶을 때 좋음.)
- headerRight 함수의 파라미터에는 tintColor만 전달되므로 onPress에 원하는 행동을 정의해야 함.
- navigation.popToTop() : 현재 쌓여 있는 모든 화면을 내보내고 첫 화면으로 돌아감.
Note. useLayoutEffect VS useEffect
useLayoutEffect useEffect 실행 시점 렌더링 직후, 화면에 반영되기 전 렌더링 후, 화면에 반영된 후 동기 / 비동기 동기 비동기 UI 변경 영향 여부 레이아웃 측정, DOM 수정 등 비동기 작업, API 호출 등 주요 사용처 UI 변경 전에 실행되므로 UI에 영향을 미칠 수 있음. UI가 반영된 후에 실행되어 UI에 영향 없음.
- 헤더 감추기
- headerMode : Navigator 컴포넌트의 속성
- float : 헤더가 상단에 유지, 하나의 헤더 사용 (iOS)
- screen : 각 화면마다 헤더를 가짐, 화면 변경과 함께 나타나거나 사라짐. (안드로이드)
- none : 헤더 X
- headerShown : 헤더 렌더링 여부 (default : true)
- headerMode : Navigator 컴포넌트의 속성
8.3 탭 내비게이션
npm install @react-navigation/bottom-tabs
화면 구성
// app/navigation/Tab.js
import React from "react";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { Mail, Meet, Settings } from "../screens/TabScreens";
const Tab = createBottomTabNavigator();
const TabNavigation = () => {
return (
<Tab.Navigator>
<Tab.Screen name="Mail" component={Mail} />
<Tab.Screen name="Meet" component={Meet} />
<Tab.Screen name="Settings" component={Settings} />
</Tab.Navigator>
);
};
export default TabNavigation;
// app/App.js
import React from "react";
// import { NavigationContainer } from "@react-navigation/native";
import TabNavigation from "./navigations/Tab";
const App = () => {
return <TabNavigation />;
};
export default App;
- createBottomTabnavigator() : 탭 내비게이션 생성
- 탭 바에 있는 버튼 순서는 Navigator 컴포넌트의 자식인 Screen 컴포넌트의 순서와 동일
- initialRouteName : 탭 버튼의 순서는 변경하지 않고 렌더링되는 첫 번째 화면 변경 (default : 첫 번째 Screen 컴포넌트)
- Expo SDK를 사용하는 경우 Expo SDK 내에서 기본 내비게이션 환경이 자동으로 설정되므로, NavigationContainer를 명시적으로 추가하지 않아도 됨. 하지만, 이는 Expo의 관리되는 환경에서만 가능함.
탭 바 수정하기
- tabBarIcon : 버튼 아이콘 설정 (default : 비어 있음)
- 컴포넌트를 반환하는 함수를 지정하면 아이콘 자리에 컴포넌트가 렌더링됨.
- 파라미터로 color, size, focused 값을 포함한 객체 전달
const TabNavigation = () => {
const TabIcon = ({ name, size, color }) => (
<MaterialCommunityIcons name={name} size={size} color={color} />
);
return (
<Tab.Navigator>
<Tab.Screen
name="Mail"
component={Mail}
options={{
tabBarIcon: (props) => TabIcon({ ...props, name: "email" }),
}}
/>
...
</Tab.Navigator>
);
};
- Screen 컴포넌트마다 탭 버튼 아이콘을 지정하지 않고 한곳에서 모든 버튼의 아이콘을 관리하려면, Navigator 컴포넌트의 screenOptions 속성 사용
const TabNavigation = () => {
const TabIcon = ({ name, size, color }) => (
<MaterialCommunityIcons name={name} size={size} color={color} />
);
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: (props) => {
let name = "";
if (route.name === "Mail") name = "email";
else if (route.name === "Meet") name = "video";
else name = "cog";
return TabIcon({ ...props, name });
},
})}
>
...
</Tab.Navigator>
);
};
- 라벨 수정하기
- tabBarLabel : 탭 버튼 아이콘 아래의 라벨(label) 변경 (default : Screen 컴포넌트의 name)
- tabBarLabelPosition : 라벨 위치 조정
- below-icon(default), beside-icon(오른쪽)
- tabBarLabelStyle : 라벨 렌더링 여부
- { display: ''none" / "flex" }
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: (props) => { ... },
tabBarLabelPosition: "beside-icon",
tabBarLabelStyle: { display: "none" },
})}
>
...
</Tab.Navigator>
- 스타일 수정하기
- tabBarStyle : 탭 바 스타일 수정
- tabBar[Inactive]ActiveTintColor : 활성화된 탭의 아이콘/라벨의 색상 설정
<Tab.Navigator
screenOptions={({ route }) => ({
...
tabBarStyle: { backgroundColor: "#54b7f9" },
tabBarActiveTintColor: "black",
})}
>
...
</Tab.Navigator>
- 예제 : 활성화 상태에 따라 다른 아이콘이 렌더링되게 변경
<Tab.Navigator screenOptions={{ ... }}>
<Tab.Screen
name="Mail"
component={Mail}
options={{
tabBarIcon: (props) =>
TabIcon({
...props,
name: props.focused ? "email" : "email-outline",
}),
}}
/>
...
</Tab.Navigator>
추가 : 교재에서 쓴 tabBarOptions는 현재 버전에서는 더 이상 사용하지 않으며, 대신 screenOptions로 설정해야 함.
- labelPosition -> tabBarLabelPosition
- showLabel -> tabBarLabelStyle: { display: '~~' }
- style -> tabBarStyle: { backgroundColor: '~~' }
- [in]activeTintColor -> tabBar[InActive]ActiveTintColor
본문에서 설명하는 내용과 사용한 이미지는 책 '처음 배우는 리액트 네이티브'를 참고하였습니다.
'처음 배우는 리액트 네이티브' 카테고리의 다른 글
9장 채팅 애플리케이션 - 1 (9.1~9.4) (0) | 2025.02.06 |
---|---|
추가 : ScrollView 대신 FlatList로 무한 스크롤 구현 (1) | 2025.01.31 |
7장 Context API (1) | 2025.01.23 |
6장 Hooks (1) | 2025.01.23 |
5장 할 일 관리 애플리케이션 (0) | 2025.01.22 |